linux-stable/fs
Josef Bacik 977849e8ac btrfs: adjust subpage bit start based on sectorsize
[ Upstream commit e08e49d986 ]

When running machines with 64k page size and a 16k nodesize we started
seeing tree log corruption in production.  This turned out to be because
we were not writing out dirty blocks sometimes, so this in fact affects
all metadata writes.

When writing out a subpage EB we scan the subpage bitmap for a dirty
range.  If the range isn't dirty we do

	bit_start++;

to move onto the next bit.  The problem is the bitmap is based on the
number of sectors that an EB has.  So in this case, we have a 64k
pagesize, 16k nodesize, but a 4k sectorsize.  This means our bitmap is 4
bits for every node.  With a 64k page size we end up with 4 nodes per
page.

To make this easier this is how everything looks

[0         16k       32k       48k     ] logical address
[0         4         8         12      ] radix tree offset
[               64k page               ] folio
[ 16k eb ][ 16k eb ][ 16k eb ][ 16k eb ] extent buffers
[ | | | |  | | | |   | | | |   | | | | ] bitmap

Now we use all of our addressing based on fs_info->sectorsize_bits, so
as you can see the above our 16k eb->start turns into radix entry 4.

When we find a dirty range for our eb, we correctly do bit_start +=
sectors_per_node, because if we start at bit 0, the next bit for the
next eb is 4, to correspond to eb->start 16k.

However if our range is clean, we will do bit_start++, which will now
put us offset from our radix tree entries.

In our case, assume that the first time we check the bitmap the block is
not dirty, we increment bit_start so now it == 1, and then we loop
around and check again.  This time it is dirty, and we go to find that
start using the following equation

	start = folio_start + bit_start * fs_info->sectorsize;

so in the case above, eb->start 0 is now dirty, and we calculate start
as

	0 + 1 * fs_info->sectorsize = 4096
	4096 >> 12 = 1

Now we're looking up the radix tree for 1, and we won't find an eb.
What's worse is now we're using bit_start == 1, so we do bit_start +=
sectors_per_node, which is now 5.  If that eb is dirty we will run into
the same thing, we will look at an offset that is not populated in the
radix tree, and now we're skipping the writeout of dirty extent buffers.

The best fix for this is to not use sectorsize_bits to address nodes,
but that's a larger change.  Since this is a fs corruption problem fix
it simply by always using sectors_per_node to increment the start bit.

Fixes: c4aec299fa ("btrfs: introduce submit_eb_subpage() to submit a subpage metadata page")
CC: stable@vger.kernel.org # 5.15+
Reviewed-by: Boris Burkov <boris@bur.io>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Sterba <dsterba@suse.com>
[ Adjust context ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2025-09-09 18:56:30 +02:00
..
9p
adfs
affs
afs
autofs
befs
bfs
btrfs btrfs: adjust subpage bit start based on sectorsize 2025-09-09 18:56:30 +02:00
cachefiles
ceph
coda
configfs
cramfs
crypto fscrypt: Don't use problematic non-inline crypto engines 2025-08-28 16:28:40 +02:00
debugfs
devpts
dlm
ecryptfs
efivarfs efivarfs: Fix slab-out-of-bounds in efivarfs_d_compare 2025-09-04 15:30:26 +02:00
efs
erofs erofs: fix atomic context detection when !CONFIG_DEBUG_LOCK_ALLOC 2025-09-04 15:30:19 +02:00
exfat exfat: add cluster chain loop check for dir 2025-08-28 16:28:28 +02:00
exportfs
ext2 ext2: Handle fiemap on empty files to prevent EINVAL 2025-08-28 16:28:15 +02:00
ext4 ext4: preserve SB_I_VERSION on remount 2025-08-28 16:28:44 +02:00
f2fs f2fs: fix to avoid out-of-boundary access in dnode page 2025-08-28 16:28:36 +02:00
fat
freevxfs
fscache
fuse
gfs2 gfs2: Set .migrate_folio in gfs2_{rgrp,meta}_aops 2025-08-28 16:28:15 +02:00
hfs hfs: fix not erasing deleted b-tree node issue 2025-08-28 16:28:15 +02:00
hfsplus hfsplus: don't use BUG_ON() in hfsplus_create_attributes_file() 2025-08-28 16:28:14 +02:00
hostfs
hpfs
hugetlbfs mm: update memfd seal write check to include F_SEAL_WRITE 2025-08-28 16:28:39 +02:00
iomap
isofs
jbd2 jbd2: prevent softlockup in jbd2_log_do_checkpoint() 2025-08-28 16:28:36 +02:00
jffs2
jfs jfs: upper bound check of tree index in dbAllocAG 2025-08-28 16:28:26 +02:00
kernfs
lockd
minix
netfs
nfs NFS: Fix a race when updating an existing write 2025-09-04 15:30:20 +02:00
nfs_common
nfsd NFSD: detect mismatch of file handle and delegation stateid in OPEN op 2025-08-28 16:28:11 +02:00
nilfs2
nls
notify
ntfs
ntfs3 fs/ntfs3: correctly create symlink for relative path 2025-08-28 16:28:15 +02:00
ocfs2 ocfs2: prevent release journal inode after journal shutdown 2025-09-09 18:56:26 +02:00
omfs
openpromfs
orangefs fs/orangefs: use snprintf() instead of sprintf() 2025-08-28 16:28:25 +02:00
overlayfs
proc proc: fix missing pde_set_flags() for net proc files 2025-09-09 18:56:26 +02:00
pstore
qnx4
qnx6
quota
ramfs
reiserfs
romfs
smb cifs: prevent NULL pointer dereference in UTF16 conversion 2025-09-09 18:56:28 +02:00
squashfs squashfs: fix memory leak in squashfs_fill_super 2025-08-28 16:28:43 +02:00
sysfs
sysv
tracefs tracefs: Add d_delete to remove negative dentries 2025-08-28 16:28:15 +02:00
ubifs
udf udf: Verify partition map count 2025-08-28 16:28:15 +02:00
ufs
unicode
vboxsf
verity
xfs xfs: do not propagate ENODATA disk errors into xattr code 2025-09-04 15:30:29 +02:00
zonefs
aio.c
anon_inodes.c
attr.c
bad_inode.c
binfmt_elf_fdpic.c
binfmt_elf_test.c
binfmt_elf.c
binfmt_flat.c
binfmt_misc.c
binfmt_script.c
buffer.c fs/buffer: fix use-after-free when call bh_read() helper 2025-08-28 16:28:44 +02:00
char_dev.c
compat_binfmt_elf.c
coredump.c
d_path.c
dax.c
dcache.c
direct-io.c
drop_caches.c
eventfd.c
eventpoll.c eventpoll: Fix semi-unbounded recursion 2025-08-28 16:28:12 +02:00
exec.c
fcntl.c
fhandle.c
file_table.c
file.c alloc_fdtable(): change calling conventions. 2025-08-28 16:28:50 +02:00
filesystems.c
fs_context.c
fs_parser.c
fs_pin.c
fs_struct.c
fs_types.c
fs-writeback.c fs: writeback: fix use-after-free in __mark_inode_dirty() 2025-09-09 18:56:20 +02:00
fsopen.c
init.c
inode.c
internal.h
ioctl.c
Kconfig
Kconfig.binfmt
kernel_read_file.c
libfs.c better lockdep annotations for simple_recursive_removal() 2025-08-28 16:28:15 +02:00
locks.c
Makefile
mbcache.c
mnt_idmapping.c
mount.h
mpage.c
namei.c
namespace.c use uniform permission checks for all mount propagation changes 2025-08-28 16:28:44 +02:00
nsfs.c
open.c
pipe.c
pnode.c
pnode.h
posix_acl.c
proc_namespace.c
read_write.c
readdir.c
remap_range.c
select.c
seq_file.c
signalfd.c
splice.c
stack.c
stat.c
statfs.c
super.c
sync.c
sysctls.c
timerfd.c
userfaultfd.c
utimes.c
xattr.c