]> git.hungrycats.org Git - linux/commitdiff
btrfs: fix creation of compressed inline extents that don't save space
authorFilipe Manana <fdmanana@suse.com>
Mon, 14 Sep 2026 17:11:30 +0000 (18:11 +0100)
committerDavid Sterba <dsterba@suse.com>
Wed, 16 Sep 2026 13:30:22 +0000 (15:30 +0200)
If the compressed data of an inline extent is larger than or equals to the
size of the uncompressed data, we are still allowing the creation of the
compressed inline extent, which does not result in any benefits, quite the
contrary as we waste metadata space and have to decompress when reading.

This is a recent regression introduced in commit 3eaf5f082c4c ("btrfs:
extract inlined creation into a dedicated delalloc helper").

It happens because we are passing the block size to btrfs_compress_bio(),
so we don't get -E2BIG from the compression code anymore, but we can not
pass i_size either, because if i_size is smaller than sector size, we
end up never creating lzo compressed inline extent for such small i_size
values. So refuse the compressed result at run_delalloc_inline() if
its size is not smaller than the uncompressed size (i_size).

Reported-by: Hanabishi <i.r.e.c.c.a.k.u.n+kernel.org@gmail.com>
Link: https://lore.kernel.org/linux-btrfs/c97652a5-ac6b-4de6-aa23-3cdebc01d00b@gmail.com/
Fixes: 3eaf5f082c4c ("btrfs: extract inlined creation into a dedicated delalloc helper")
CC: stable@vger.kernel.org # 7.1+
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index 79f2181dc6310ac0418d51b5c919a1673785c99b..766dbdbf6e7d748bca3fdc4a690adf018d5383fc 100644 (file)
@@ -2342,12 +2342,27 @@ static int run_delalloc_inline(struct btrfs_inode *inode, struct folio *locked_f
                } else if (inode->prop_compress) {
                        compress_type = inode->prop_compress;
                }
+               /*
+                * We need to pass blocksize and not i_size, otherwise we can't
+                * create compressed inline extents for data smaller than sector
+                * size with lzo.
+                */
                cb = btrfs_compress_bio(inode, 0, blocksize, compress_type, compress_level, 0);
                if (IS_ERR(cb)) {
                        cb = NULL;
                        /* Just fall back to non-compressed case. */
                } else {
                        compressed_size = cb->bbio.bio.bi_iter.bi_size;
+                       /*
+                        * If we did not save space, it's pointless and wasteful
+                        * to have an inline compressed extent, so fallback to
+                        * an uncompressed inline extent.
+                        */
+                       if (compressed_size >= i_size) {
+                               cleanup_compressed_bio(cb);
+                               cb = NULL;
+                               compressed_size = 0;
+                       }
                }
        }
        if (!can_cow_file_range_inline(inode, 0, i_size, compressed_size)) {