110 lines
3.5 KiB
Diff
110 lines
3.5 KiB
Diff
diff --git c/fs/btrfs/zstd.c i/fs/btrfs/zstd.c
|
|
--- c/fs/btrfs/zstd.c
|
|
+++ i/fs/btrfs/zstd.c
|
|
@@ -589,10 +589,48 @@ int zstd_compress_bio(struct list_head *ws, struct compressed_bio *cb)
|
|
return ret;
|
|
}
|
|
|
|
+/*
|
|
+ * Map the destination for the next chunk of output.
|
|
+ *
|
|
+ * @decompressed is the offset of the next output byte inside the fully
|
|
+ * decompressed extent. If that offset has reached the current destination
|
|
+ * segment, its page-bounded bio_vec is kmapped so that zstd can write into the
|
|
+ * page cache directly, and the number of bytes writable there is returned.
|
|
+ * Otherwise @kaddr_ret is set to NULL and the number of bytes to skip before
|
|
+ * that segment is returned. This covers both the initial prefix and gaps in
|
|
+ * the destination bio.
|
|
+ */
|
|
+static u32 zstd_map_dest(struct compressed_bio *cb, u32 decompressed,
|
|
+ void **kaddr_ret)
|
|
+{
|
|
+ struct bio *orig_bio = &cb->orig_bbio->bio;
|
|
+ struct bio_vec bvec;
|
|
+ u32 bvec_offset;
|
|
+ u32 off;
|
|
+
|
|
+ bvec = bio_iter_iovec(orig_bio, orig_bio->bi_iter);
|
|
+ /*
|
|
+ * cb->start may underflow, but subtracting that value can still give us
|
|
+ * the correct offset inside the full decompressed extent.
|
|
+ */
|
|
+ bvec_offset = page_offset(bvec.bv_page) + bvec.bv_offset - cb->start;
|
|
+
|
|
+ if (decompressed < bvec_offset) {
|
|
+ *kaddr_ret = NULL;
|
|
+ return bvec_offset - decompressed;
|
|
+ }
|
|
+
|
|
+ off = decompressed - bvec_offset;
|
|
+ ASSERT(off < bvec.bv_len);
|
|
+ *kaddr_ret = bvec_kmap_local(&bvec) + off;
|
|
+ return bvec.bv_len - off;
|
|
+}
|
|
+
|
|
int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
|
{
|
|
struct btrfs_fs_info *fs_info = cb_to_fs_info(cb);
|
|
struct workspace *workspace = list_entry(ws, struct workspace, list);
|
|
+ struct bio *orig_bio = &cb->orig_bbio->bio;
|
|
struct folio_iter fi;
|
|
size_t srclen = bio_get_size(&cb->bbio.bio);
|
|
zstd_dstream *stream;
|
|
@@ -600,7 +638,6 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
|
const unsigned int min_folio_size = btrfs_min_folio_size(fs_info);
|
|
unsigned long folio_in_index = 0;
|
|
unsigned long total_folios_in = DIV_ROUND_UP(srclen, min_folio_size);
|
|
- unsigned long buf_start;
|
|
unsigned long total_out = 0;
|
|
|
|
bio_first_folio(&fi, &cb->bbio.bio, 0);
|
|
@@ -624,15 +661,26 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
|
workspace->in_buf.pos = 0;
|
|
workspace->in_buf.size = min_t(size_t, srclen, min_folio_size);
|
|
|
|
- workspace->out_buf.dst = workspace->buf;
|
|
- workspace->out_buf.pos = 0;
|
|
- workspace->out_buf.size = fs_info->sectorsize;
|
|
-
|
|
- while (1) {
|
|
+ while (orig_bio->bi_iter.bi_size) {
|
|
size_t ret2;
|
|
+ void *kaddr;
|
|
+ u32 dstlen;
|
|
+
|
|
+ dstlen = zstd_map_dest(cb, total_out, &kaddr);
|
|
+ if (kaddr) {
|
|
+ workspace->out_buf.dst = kaddr;
|
|
+ workspace->out_buf.size = dstlen;
|
|
+ } else {
|
|
+ workspace->out_buf.dst = workspace->buf;
|
|
+ workspace->out_buf.size = min_t(u32, dstlen,
|
|
+ fs_info->sectorsize);
|
|
+ }
|
|
+ workspace->out_buf.pos = 0;
|
|
|
|
ret2 = zstd_decompress_stream(stream, &workspace->out_buf,
|
|
&workspace->in_buf);
|
|
+ if (kaddr)
|
|
+ kunmap_local(kaddr);
|
|
if (unlikely(zstd_is_error(ret2))) {
|
|
struct btrfs_inode *inode = cb->bbio.inode;
|
|
|
|
@@ -643,14 +691,9 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
|
ret = -EIO;
|
|
goto done;
|
|
}
|
|
- buf_start = total_out;
|
|
total_out += workspace->out_buf.pos;
|
|
- workspace->out_buf.pos = 0;
|
|
-
|
|
- ret = btrfs_decompress_buf2page(workspace->out_buf.dst,
|
|
- total_out - buf_start, cb, buf_start);
|
|
- if (ret == 0)
|
|
- break;
|
|
+ if (kaddr)
|
|
+ bio_advance(orig_bio, workspace->out_buf.pos);
|
|
|
|
if (workspace->in_buf.pos >= srclen)
|
|
break;
|