summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2026-01-29 13:53:41 +1030
committerDavid Sterba <dsterba@suse.com>2026-02-03 07:59:06 +0100
commitc51173271d528561706a2ce3bacd4f6232f4375b (patch)
treee50595889bd0d0f43226bf9efb62fb4735785317
parent3d74a7556fbab89a3e78f514cf39d3413b9963d1 (diff)
btrfs: introduce btrfs_compress_bio() helper
The helper will allocate a new compressed_bio, do the compression, and return it to the caller. This greatly simplifies the compression path, as we no longer need to allocate a folio array thus no extra error path, furthermore the compressed bio structure can be utilized for submission with very minor modifications (like rounding up the bi_size and populate the bi_sector). Reviewed-by: Boris Burkov <boris@bur.io> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/compression.c68
-rw-r--r--fs/btrfs/compression.h13
2 files changed, 81 insertions, 0 deletions
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 4c6298cf01b2..6f123ae9a240 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -1064,6 +1064,74 @@ int btrfs_compress_folios(unsigned int type, int level, struct btrfs_inode *inod
return ret;
}
+/*
+ * Given an address space and start and length, compress the page cache
+ * contents into @cb.
+ *
+ * @type_level: is encoded algorithm and level, where level 0 means whatever
+ * default the algorithm chooses and is opaque here;
+ * - compression algo are 0-3
+ * - the level are bits 4-7
+ *
+ * @cb->bbio.bio.bi_iter.bi_size will indicate the compressed data size.
+ * The bi_size may not be sectorsize aligned, thus the caller still need
+ * to do the round up before submission.
+ *
+ * This function will allocate compressed folios with btrfs_alloc_compr_folio(),
+ * thus callers must make sure the endio function and error handling are using
+ * btrfs_free_compr_folio() to release those folios.
+ * This is already done in end_bbio_compressed_write() and cleanup_compressed_bio().
+ */
+struct compressed_bio *btrfs_compress_bio(struct btrfs_inode *inode,
+ u64 start, u32 len, unsigned int type,
+ int level, blk_opf_t write_flags)
+{
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ struct list_head *workspace;
+ struct compressed_bio *cb;
+ int ret;
+
+ cb = alloc_compressed_bio(inode, start, REQ_OP_WRITE | write_flags,
+ end_bbio_compressed_write);
+ cb->start = start;
+ cb->len = len;
+ cb->writeback = true;
+ cb->compress_type = type;
+
+ level = btrfs_compress_set_level(type, level);
+ workspace = get_workspace(fs_info, type, level);
+ switch (type) {
+ case BTRFS_COMPRESS_ZLIB:
+ ret = zlib_compress_bio(workspace, cb);
+ break;
+ case BTRFS_COMPRESS_LZO:
+ ret = lzo_compress_bio(workspace, cb);
+ break;
+ case BTRFS_COMPRESS_ZSTD:
+ ret = zstd_compress_bio(workspace, cb);
+ break;
+ case BTRFS_COMPRESS_NONE:
+ default:
+ /*
+ * This can happen when compression races with remount setting
+ * it to 'no compress', while caller doesn't call
+ * inode_need_compress() to check if we really need to
+ * compress.
+ *
+ * Not a big deal, just need to inform caller that we
+ * haven't allocated any pages yet.
+ */
+ ret = -E2BIG;
+ }
+
+ put_workspace(fs_info, type, workspace);
+ if (ret < 0) {
+ cleanup_compressed_bio(cb);
+ return ERR_PTR(ret);
+ }
+ return cb;
+}
+
static int btrfs_decompress_bio(struct compressed_bio *cb)
{
struct btrfs_fs_info *fs_info = cb_to_fs_info(cb);
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
index eee4190efa02..fd0cce5d07cf 100644
--- a/fs/btrfs/compression.h
+++ b/fs/btrfs/compression.h
@@ -146,6 +146,19 @@ int btrfs_compress_heuristic(struct btrfs_inode *inode, u64 start, u64 end);
int btrfs_compress_filemap_get_folio(struct address_space *mapping, u64 start,
struct folio **in_folio_ret);
+struct compressed_bio *btrfs_compress_bio(struct btrfs_inode *inode,
+ u64 start, u32 len, unsigned int type,
+ int level, blk_opf_t write_flags);
+
+static inline void cleanup_compressed_bio(struct compressed_bio *cb)
+{
+ struct bio *bio = &cb->bbio.bio;
+ struct folio_iter fi;
+
+ bio_for_each_folio_all(fi, bio)
+ btrfs_free_compr_folio(fi.folio);
+ bio_put(bio);
+}
int zlib_compress_folios(struct list_head *ws, struct btrfs_inode *inode,
u64 start, struct folio **folios, unsigned long *out_folios,