Re: [PATCH] btrfs: set the block size before starting a transaction
From: Qu Wenruo
Date: Wed Oct 28 2026 - 16:34:24 EST
在 2026/10/28 22:44, Qing Zhang 写道:
From: Qing Zhang <diasyzhang@xxxxxxxxxxx>
When adding a device, btrfs_init_new_device() calls set_blocksize()
with a transaction handle held. If the block size changes, this takes
the bdev inode's i_rwsem and triggers a lockdep dependency cycle:
bdev i_rwsem -> mmap_lock -> trans_num_writers
-> trans_num_extwriters -> bdev i_rwsem
The buffered read path nests mmap_lock under the bdev i_rwsem, while
mmapping a btrfs file can join a transaction through atime updates.
Since commit c0e473a0d226 ("block: fix race between set_blocksize and
read paths"), set_blocksize() takes the bdev inode's i_rwsem when
changing the block size.
Move set_blocksize() before btrfs_start_transaction() to break the
cycle. Also check its return value to reject devices whose logical
block size exceeds BTRFS_BDEV_BLOCKSIZE.
Fixes: c0e473a0d226 ("block: fix race between set_blocksize and read paths")
Reported-by: syzbot+5c01398781fb51df783a@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=5c01398781fb51df783a
There is already a better solution by not calling the stupid ste_blocksize() at all:
https://lore.kernel.org/linux-btrfs/3d98566e8aa2cd99a23ce89665f79281c614f05e.1792885061.git.wqu@xxxxxxxx/
Signed-off-by: Qing Zhang <diasyzhang@xxxxxxxxxxx>
---
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
fs/btrfs/volumes.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index d3e6a9b429dd..a7f0607a5b86 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -3019,6 +3019,10 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
if (ret)
goto error_free_device;
+ ret = set_blocksize(device->bdev_file, BTRFS_BDEV_BLOCKSIZE);
+ if (ret)
+ goto error_free_zone;
+
trans = btrfs_start_transaction(root, 0);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
@@ -3043,7 +3047,6 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
*/
atomic_inc(&device->dev_stats_ccnt);
device->dev_stats_valid = 1;
- set_blocksize(device->bdev_file, BTRFS_BDEV_BLOCKSIZE);
if (seeding_dev) {
/* GFP_KERNEL allocation must not be under device_list_mutex */