]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: reject single block sized compression early
authorQu Wenruo <wqu@suse.com>
Tue, 6 Jan 2026 02:50:30 +0000 (13:20 +1030)
committerDavid Sterba <dsterba@suse.com>
Tue, 3 Feb 2026 06:51:42 +0000 (07:51 +0100)
Currently for an inode that needs compression, even if there is a delalloc
range that is single fs block sized and can not be inlined, we will
still go through the compression path.

Then inside compress_file_range(), we have one extra check to reject
single block sized range, and fall back to regular uncompressed write.

This rejection is in fact a little too late, we have already allocated
memory to async_chunk, delayed the submission, just to fallback to the
same uncompressed write.

Change the behavior to reject such cases earlier at
inode_need_compress(), so for such single block sized range we won't
even bother trying to go through compress path.

And since the inline small block check is inside inode_need_compress()
and compress_file_range() also calls that function, we no longer need a
dedicate check inside compress_file_range().

Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index 247b373bf5cf0d9e7ce9d67b6b2b8a6893e8e981..be47aa58e944ee2e83375484e6dcd877583331f2 100644 (file)
@@ -816,6 +816,13 @@ static inline int inode_need_compress(struct btrfs_inode *inode, u64 start,
                return 0;
        }
 
+       /*
+        * If the delalloc range is only one fs block and can not be inlined,
+        * 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)
+               return 0;
        /* Defrag ioctl takes precedence over mount options and properties. */
        if (inode->defrag_compress == BTRFS_DEFRAG_DONT_COMPRESS)
                return 0;
@@ -953,18 +960,7 @@ again:
        if (actual_end <= start)
                goto cleanup_and_bail_uncompressed;
 
-       total_compressed = actual_end - start;
-
-       /*
-        * Skip compression for a small file range(<=blocksize) that
-        * isn't an inline extent, since it doesn't save disk space at all.
-        */
-       if (total_compressed <= blocksize &&
-          (start > 0 || end + 1 < inode->disk_i_size))
-               goto cleanup_and_bail_uncompressed;
-
-       total_compressed = min_t(unsigned long, total_compressed,
-                       BTRFS_MAX_UNCOMPRESSED);
+       total_compressed = min_t(unsigned long, actual_end - start, BTRFS_MAX_UNCOMPRESSED);
        total_in = 0;
        ret = 0;