[BUG]
When running btrfs/284 with 64K page size and 4K fs block size, the
following ASSERT() can be triggered:
assertion failed: cb->bbio.bio.bi_iter.bi_size == disk_num_bytes :: 0, in inode.c:9991
------------[ cut here ]------------
kernel BUG at inode.c:9991!
Internal error: Oops - BUG:
00000000f2000800 [#1] SMP
CPU: 5 UID: 0 PID: 6787 Comm: btrfs Tainted: G OE 6.19.0-rc8-custom+ #1 PREEMPT(voluntary)
Hardware name: QEMU KVM Virtual Machine, BIOS unknown 2/2/2022
pc : btrfs_do_encoded_write+0x9b0/0x9c0 [btrfs]
lr : btrfs_do_encoded_write+0x9b0/0x9c0 [btrfs]
Call trace:
btrfs_do_encoded_write+0x9b0/0x9c0 [btrfs] (P)
btrfs_do_write_iter+0x1d8/0x208 [btrfs]
btrfs_ioctl_encoded_write+0x3c8/0x6d0 [btrfs]
btrfs_ioctl+0xeb0/0x2b60 [btrfs]
__arm64_sys_ioctl+0xac/0x110
invoke_syscall.constprop.0+0x64/0xe8
el0_svc_common.constprop.0+0x40/0xe8
do_el0_svc+0x24/0x38
el0_svc+0x3c/0x1b8
el0t_64_sync_handler+0xa0/0xe8
el0t_64_sync+0x1a4/0x1a8
Code:
91180021 90001080 9111a000 94039d54 (
d4210000)
---[ end trace
0000000000000000 ]---
[CAUSE]
After commit
e1bc83f8b157 ("btrfs: get rid of compressed_folios[] usage
for encoded writes"), the encoded write is changed to copy the content
from the iov into a folio, and queue the folio into the compressed bio.
However we always queue the full folio into the compressed bio, which
can make the compressed bio larger than the on-disk extent, if the folio
size is larger than the fs block size.
Although we have an ASSERT() to catch such problem, for kernels without
CONFIG_BTRFS_ASSERT, such larger than expected bio will just be
submitted, possibly overwrite the next data extent, causing data
corruption.
[FIX]
Instead of blindly queuing the full folio into the compressed bio, only
queue the rounded up range, which is the old behavior before that
offending commit.
This also means we no longer need to zero the tailing range until the
folio end (but still to the block boundary), as such range will not be
submitted anyway.
And since we're here, add a final ASSERT() into
btrfs_submit_compressed_write() as the last safety net for kernels with
btrfs assertions enabled
Fixes: e1bc83f8b157 ("btrfs: get rid of compressed_folios[] usage for encoded writes")
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
cb->start = ordered->file_offset;
cb->len = ordered->num_bytes;
+ ASSERT(cb->bbio.bio.bi_iter.bi_size == ordered->disk_num_bytes);
cb->compressed_len = ordered->disk_num_bytes;
cb->bbio.bio.bi_iter.bi_sector = ordered->disk_bytenr >> SECTOR_SHIFT;
cb->bbio.ordered = ordered;
int compression;
size_t orig_count;
const u32 min_folio_size = btrfs_min_folio_size(fs_info);
+ const u32 blocksize = fs_info->sectorsize;
u64 start, end;
u64 num_bytes, ram_bytes, disk_num_bytes;
struct btrfs_key ins;
ret = -EFAULT;
goto out_cb;
}
- if (bytes < min_folio_size)
- folio_zero_range(folio, bytes, min_folio_size - bytes);
- ret = bio_add_folio(&cb->bbio.bio, folio, folio_size(folio), 0);
+ if (!IS_ALIGNED(bytes, blocksize))
+ folio_zero_range(folio, bytes, round_up(bytes, blocksize) - bytes);
+ ret = bio_add_folio(&cb->bbio.bio, folio, round_up(bytes, blocksize), 0);
if (unlikely(!ret)) {
folio_put(folio);
ret = -EINVAL;