Linux Btrfs filesystem development
 help / color / mirror / Atom feed
* [PATCH v2 0/2] btrfs: remove the tailing zeros from compressed inline extents
@ 2026-09-20 22:50 Qu Wenruo
  2026-09-20 22:50 ` [PATCH v2 1/2] btrfs: lzo: fix space saving checks Qu Wenruo
  2026-09-20 22:50 ` [PATCH v2 2/2] btrfs: remove the trailing zeros from compressed inline extent Qu Wenruo
  0 siblings, 2 replies; 3+ messages in thread
From: Qu Wenruo @ 2026-09-20 22:50 UTC (permalink / raw)
  To: linux-btrfs

[CHANGELOG]
v2:
- Remove the block based space saving checks completely from lzo
  Zlib and ZSTD do not have block based space saving in the first place.
  It's the caller's responsibility to check, and inlined and regular
  extents have different requirements.

  This simplify the first patch a lot.

- Update the cover letter
  Filipe's v3 fix is already merged, so we can not force push a fix but
  to co-operate the v3 fix.

Commit 3eaf5f082c4c ("btrfs: extract inlined creation into a dedicated
delalloc helper") changed the compression input from [0, i_size) to [0,
blocksize), which caused two problems:

- Other tools unable to decompress the inlined extent
  U-boot and btrfs-restore are affected, as they only allocated a buffer
  which is @ram_bytes sized.
  That buffer is too small to contain the decompressed data, which is
  @sectorsize.

  Those projects are fixed to have a more robust decompression routine
  which can handle both cases now.

- Worse ratio for those compressed inline extent.

  For the same 3K 0xcd filled range, the results are small but
  observable, 48 vs 61 bytes.

Filipe's v1 fix is very close to a proper fix, but btrfs will unable to
create inlined extents for lzo.
It turns out to be another bug in the copy_compressed_data_to_bio()
function.

Which is doing a premature block size based space saving checks,
meanwhile all other algorithms do not have block sized based checks, but
only a simple "@compressed >= @input" check.
Zlib and Zstd all rely on the caller to do proper space saving checks,
as regular and inlined extents have different requirements.

So this series is to properly fix the bug, firstly fix the lzo
regression which prevents inlined extent creation, then restore the old
i_size based inline extent creation.

Qu Wenruo (2):
  btrfs: lzo: fix space saving checks
  btrfs: remove the trailing zeros from compressed inline extent

 fs/btrfs/inode.c | 7 +------
 fs/btrfs/lzo.c   | 5 +++--
 2 files changed, 4 insertions(+), 8 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH v2 1/2] btrfs: lzo: fix space saving checks
  2026-09-20 22:50 [PATCH v2 0/2] btrfs: remove the tailing zeros from compressed inline extents Qu Wenruo
@ 2026-09-20 22:50 ` Qu Wenruo
  2026-09-20 22:50 ` [PATCH v2 2/2] btrfs: remove the trailing zeros from compressed inline extent Qu Wenruo
  1 sibling, 0 replies; 3+ messages in thread
From: Qu Wenruo @ 2026-09-20 22:50 UTC (permalink / raw)
  To: linux-btrfs

[BUG]
Since commit 3be8a788eed3 ("btrfs: lzo: introduce lzo_compress_bio()
helper"), the space saving check on lzo is broken:

- Space saving check is always done based on block size
  This prevents lzo_compress_bio() to do any compression if the @len is
  smaller than sectorsize.

  This prevents proper inlined extent creation.

- Space saving check is not including the padding zeros
  If the remaining space in the output folio is not enough, we add
  padding zeros (up to 3 bytes), which contributes to the total
  compressed size.

  But that is not checked.

[CAUSE]

The function copy_compressed_data_to_bio() is doing the space saving
check wrong:

		/* With the range copied, we're larger than the original range. */
		if (((*total_out + copy_len) >> sectorsize_bits) >=
		    max_out >> sectorsize_bits)

- If the input length is smaller than sectorsize (inlined case)
  Then @max_out >> sectorsize_bits returns 0, the above check always
  returns true and copy_compressed_data_to_bio() always return -E2BIG and
  failed to create an lzo compressed inlined extent.

This check is LZO specific, as other algorithms do not have any block
aligned check during compression.
The remaining two algorithms are all relying on regular input/output
size check, and the final common space saving check at
compress_file_extent().

And for the extra padding bytes, there is no space saving check at all.

[FIX]
- Do a simpler space saving check
  By just check if the compressed size meets or exceeds the input size.
  If so reject it with -E2BIG.

  With this fixed, lzo_compress_bio() can properly handle a range that is
  smaller than a sector, which is required to get better compression ratio
  for inlined extents.

- Do space saving check before padding
  This will ensure that the compressed size never meets or exceeds the
  input size.

Fixes: 3be8a788eed3 ("btrfs: lzo: introduce lzo_compress_bio() helper")
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/lzo.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c
index 2f0996692da0..4e7837dec332 100644
--- a/fs/btrfs/lzo.c
+++ b/fs/btrfs/lzo.c
@@ -224,8 +224,7 @@ static int copy_compressed_data_to_bio(struct btrfs_fs_info *fs_info,
 		u32 foffset = *total_out & (fsize - 1);
 
 		/* With the range copied, we're larger than the original range. */
-		if (((*total_out + copy_len) >> sectorsize_bits) >=
-		    max_out >> sectorsize_bits)
+		if (*total_out + copy_len >= max_out)
 			return -E2BIG;
 
 		if (!*out_folio) {
@@ -253,6 +252,8 @@ static int copy_compressed_data_to_bio(struct btrfs_fs_info *fs_info,
 	ASSERT(*out_folio);
 
 	/* The remaining size is not enough, pad it with zeros */
+	if (*total_out + sector_bytes_left >= max_out)
+		return -E2BIG;
 	folio_zero_range(*out_folio, offset_in_folio(*out_folio, *total_out), sector_bytes_left);
 	return write_and_queue_folio(out_bio, out_folio, total_out, sector_bytes_left);
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH v2 2/2] btrfs: remove the trailing zeros from compressed inline extent
  2026-09-20 22:50 [PATCH v2 0/2] btrfs: remove the tailing zeros from compressed inline extents Qu Wenruo
  2026-09-20 22:50 ` [PATCH v2 1/2] btrfs: lzo: fix space saving checks Qu Wenruo
@ 2026-09-20 22:50 ` Qu Wenruo
  1 sibling, 0 replies; 3+ messages in thread
From: Qu Wenruo @ 2026-09-20 22:50 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Hanabishi

[BEHAVIOR CHANGE]
After commit 3eaf5f082c4c ("btrfs: extract inlined creation into a
dedicated delalloc helper"), btrfs changed its behavior when generating
compressed inlined extents.

Previously the compression input was the file range [0, i_size), but
after that commit the input is file range [0, sectorsize).

This means the decompression handling needs to have a buffer that is no
smaller than sectorsize, or the decompression will fail.

This has already caused problems for other projects, like u-boot and
btrfs-restore from btrfs-progs.
Although those projects are fixed with a more robust decompression path,
this kernel change also causes extra space usage for compressed inlined
extents:

 All doing a 3K writes with content filled with 0xcd

 Before:
        item 6 key (257 EXTENT_DATA 0) itemoff 15794 itemsize 69
                generation 9 type 0 (inline)
                inline extent data size 48 ram_bytes 3072 compression 2 (lzo) encryption 0

 After:
         item 6 key (257 EXTENT_DATA 0) itemoff 15781 itemsize 82
                generation 9 type 0 (inline)
                inline extent data size 61 ram_bytes 3072 compression 2 (lzo) encryption 0

This behavior change also increased the lzo compressed size from 48 bytes to 61 bytes.

[FIX]
The previous patch "btrfs: lzo: fix space saving checks which prevents
inline extents" fixed a regression in lzo_compress_bio() where it doesn't
properly handle ranges smaller than a sector.

With that regression fixed, we can finally pass the range [0, i_size) into
btrfs_compress_bio(), and this gets rid of the trailing zeros,
restoring the old behavior, along with the older and better
compression ratio.

Fixes: 3eaf5f082c4c ("btrfs: extract inlined creation into a dedicated delalloc helper")
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/
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/inode.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 53f4532593b3..5e083ea5c577 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2342,12 +2342,7 @@ 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);
+		cb = btrfs_compress_bio(inode, 0, i_size, compress_type, compress_level, 0);
 		if (IS_ERR(cb)) {
 			cb = NULL;
 			/* Just fall back to non-compressed case. */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-20 22:51 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-20 22:50 [PATCH v2 0/2] btrfs: remove the tailing zeros from compressed inline extents Qu Wenruo
2026-09-20 22:50 ` [PATCH v2 1/2] btrfs: lzo: fix space saving checks Qu Wenruo
2026-09-20 22:50 ` [PATCH v2 2/2] btrfs: remove the trailing zeros from compressed inline extent Qu Wenruo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox