]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: fix the inline compressed extent check in inode_need_compress()
authorQu Wenruo <wqu@suse.com>
Mon, 9 Feb 2026 00:21:09 +0000 (10:51 +1030)
committerDavid Sterba <dsterba@suse.com>
Tue, 7 Apr 2026 16:55:54 +0000 (18:55 +0200)
[BUG]
Since commit 59615e2c1f63 ("btrfs: reject single block sized compression
early"), the following script will result the inode to have NOCOMPRESS
flag, meanwhile old kernels don't:

  # mkfs.btrfs -f $dev
  # mount $dev $mnt -o max_inline=2k,compress=zstd
  # truncate -s 8k $mnt/foobar
  # xfs_io -f -c "pwrite 0 2k" $mnt/foobar
  # sync

Before that commit, the inode will not have NOCOMPRESS flag:

item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 9 size 8192 nbytes 4096
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 3 flags 0x0(none)

But after that commit, the inode will have NOCOMPRESS flag:

item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 10 size 8192 nbytes 4096
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 3 flags 0x8(NOCOMPRESS)

This will make a lot of files no longer to be compressed.

[CAUSE]
The old compressed inline check looks like this:

if (total_compressed <= blocksize &&
   (start > 0 || end + 1 < inode->disk_i_size))
goto cleanup_and_bail_uncompressed;

That inline part check is equal to "!(start == 0 && end + 1 >=
inode->disk_i_size)", but the new check no longer has that disk_i_size
check.

Thus it means any single block sized write at file offset 0 will pass
the inline check, which is wrong.

Furthermore, since we have merged the old check into
inode_need_compress(), there is no disk_i_size based inline check
anymore, we will always try compressing that single block at file offset
0, then later find out it's not a net win and go to the
mark_incompressible tag.

This results the inode to have NOCOMPRESS flag.

[FIX]
Add back the missing disk_i_size based check into inode_need_compress().

Now the same script will no longer cause NOCOMPRESS flag.

Fixes: 59615e2c1f63 ("btrfs: reject single block sized compression early")
Reported-by: Chris Mason <clm@meta.com>
Link: https://lore.kernel.org/linux-btrfs/20260208183840.975975-1-clm@meta.com/
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index f643a05208720d429edd2a0bb071949aa06f87b7..a4ad37fd65fbecbe731cb5a66d2818bcaf8eb2d0 100644 (file)
@@ -811,7 +811,8 @@ static inline int inode_need_compress(struct btrfs_inode *inode, u64 start,
         * do not even bother try compression, as there will be no space saving
         * and will always fallback to regular write later.
         */
-       if (start != 0 && end + 1 - start <= fs_info->sectorsize)
+       if (end + 1 - start <= fs_info->sectorsize &&
+           (start > 0 || end + 1 < inode->disk_i_size))
                return 0;
        /* Defrag ioctl takes precedence over mount options and properties. */
        if (inode->defrag_compress == BTRFS_DEFRAG_DONT_COMPRESS)