在 2026/8/1 07:56, Cole Munz 写道:
Hi Qu,

The ram_bytes described the decompressed size of a compressed extent,
except inlined extents, the ram_bytes should always be fs block aligned.

I didn't see how things can go "larger than the extent's ram_bytes".

You're right, and the commit message overstates it. For regular extents
ram_bytes is block aligned and matches the frame's content size, exactly as
you describe. The case that actually fails is the one you carved out:
compressed inline extents.

For the inline path, current mainline compresses the whole first block:

   run_delalloc_inline():
     cb = btrfs_compress_bio(inode, 0, blocksize, compress_type, ...);

so the page is zero-padded past EOF and the resulting zstd frame declares
frameContentSize = blocksize. But the extent item gets the unaligned size:

   __cow_file_range_inline(inode, i_size, compressed_size, ...) ->
     insert_inline_extent() -> btrfs_set_file_extent_ram_bytes(leaf, ei, size);

So a 1900-byte file stored as a compressed inline extent has ram_bytes 1900
while the frame decodes to 4096. The kernel side never notices because
fs/btrfs/zstd.c zstd_decompress() streams into out_buf and copies out at most
destlen. U-Boot's decompress_zstd() is the one-shot zstd_decompress_dctx(),
and btrfs_read_extent_inline() sizes the destination with
dsize = btrfs_file_extent_ram_bytes(), so the frame fails the whole-frame
capacity check with dstSize_tooSmall - error code 70, which matches the
"failed to decompress: 70" in the Armbian reports.

Thanks a lot! Now I see where the problem is.


One data point from testing while chasing this: an image built with
mkfs.btrfs --rootdir --compress zstd (btrfs-progs 7.1) does NOT reproduce.
For a 1900-byte file, progs writes an inline extent with ram_bytes 1900 whose
frame also decodes to exactly 1900:

   item 8 key (257 EXTENT_DATA 0) itemoff 15316 itemsize 423
       generation 6 type 0 (inline)
       inline extent data size 402 ram_bytes 1900 compression 3 (zstd)

so mkfs-built images boot fine, and the failure only shows up on files
(re)written at runtime through the kernel - which fits the Armbian pattern of
/boot scripts and overlays breaking after a package update touched them.

I'll send a v2 with the commit message rewritten to name compressed inline
extents as the failing case instead of the hand-wave about sector padding. If
you'd like the on-disk evidence too, I can loop-mount a scratch fs with
compress=zstd, write an unaligned file, and include the dump-tree output plus
the frame header read from the leaf in the v2 cover.

Another thing is, since this bug only affects inlined extents, I'd prefer to have the fix located inside btrfs_read_extent_inline().

So that we allocate a full block for decompression, then only copy ram_bytes back to the destination.

With a mention about the limit during dbuf allocation inside btrfs_read_extent_inline().

Thanks a lot of the explanation and fix,
Qu


The fix itself stays in btrfs's decompress_zstd() rather than lib/zstd
because for FIT/ximg/ubifs an undersized destination really does mean corrupt
input; only btrfs hands the decompressor a destination smaller than the frame
on purpose. If you'd rather see it shaped differently - say, only engaging
the bounce path for inline extents - happy to do that in v2 as well.

Thanks,
Cole


Reply via email to