Skip to content

Commit 2fb3056

Browse files
josefbaciksmb49
authored andcommitted
btrfs: delay blkdev_put until after the device remove
BugLink: https://bugs.launchpad.net/bugs/1949397 [ Upstream commit 3fa421d ] When removing the device we call blkdev_put() on the device once we've removed it, and because we have an EXCL open we need to take the ->open_mutex on the block device to clean it up. Unfortunately during device remove we are holding the sb writers lock, which results in the following lockdep splat: ====================================================== WARNING: possible circular locking dependency detected 5.14.0-rc2+ #407 Not tainted ------------------------------------------------------ losetup/11595 is trying to acquire lock: ffff973ac35dd138 ((wq_completion)loop0){+.+.}-{0:0}, at: flush_workqueue+0x67/0x5e0 but task is already holding lock: ffff973ac9812c68 (&lo->lo_mutex){+.+.}-{3:3}, at: __loop_clr_fd+0x41/0x660 [loop] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #4 (&lo->lo_mutex){+.+.}-{3:3}: __mutex_lock+0x7d/0x750 lo_open+0x28/0x60 [loop] blkdev_get_whole+0x25/0xf0 blkdev_get_by_dev.part.0+0x168/0x3c0 blkdev_open+0xd2/0xe0 do_dentry_open+0x161/0x390 path_openat+0x3cc/0xa20 do_filp_open+0x96/0x120 do_sys_openat2+0x7b/0x130 __x64_sys_openat+0x46/0x70 do_syscall_64+0x38/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae -> #3 (&disk->open_mutex){+.+.}-{3:3}: __mutex_lock+0x7d/0x750 blkdev_put+0x3a/0x220 btrfs_rm_device.cold+0x62/0xe5 btrfs_ioctl+0x2a31/0x2e70 __x64_sys_ioctl+0x80/0xb0 do_syscall_64+0x38/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae -> #2 (sb_writers#12){.+.+}-{0:0}: lo_write_bvec+0xc2/0x240 [loop] loop_process_work+0x238/0xd00 [loop] process_one_work+0x26b/0x560 worker_thread+0x55/0x3c0 kthread+0x140/0x160 ret_from_fork+0x1f/0x30 -> #1 ((work_completion)(&lo->rootcg_work)){+.+.}-{0:0}: process_one_work+0x245/0x560 worker_thread+0x55/0x3c0 kthread+0x140/0x160 ret_from_fork+0x1f/0x30 -> #0 ((wq_completion)loop0){+.+.}-{0:0}: __lock_acquire+0x10ea/0x1d90 lock_acquire+0xb5/0x2b0 flush_workqueue+0x91/0x5e0 drain_workqueue+0xa0/0x110 destroy_workqueue+0x36/0x250 __loop_clr_fd+0x9a/0x660 [loop] block_ioctl+0x3f/0x50 __x64_sys_ioctl+0x80/0xb0 do_syscall_64+0x38/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae other info that might help us debug this: Chain exists of: (wq_completion)loop0 --> &disk->open_mutex --> &lo->lo_mutex Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&lo->lo_mutex); lock(&disk->open_mutex); lock(&lo->lo_mutex); lock((wq_completion)loop0); *** DEADLOCK *** 1 lock held by losetup/11595: #0: ffff973ac9812c68 (&lo->lo_mutex){+.+.}-{3:3}, at: __loop_clr_fd+0x41/0x660 [loop] stack backtrace: CPU: 0 PID: 11595 Comm: losetup Not tainted 5.14.0-rc2+ #407 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-2.fc32 04/01/2014 Call Trace: dump_stack_lvl+0x57/0x72 check_noncircular+0xcf/0xf0 ? stack_trace_save+0x3b/0x50 __lock_acquire+0x10ea/0x1d90 lock_acquire+0xb5/0x2b0 ? flush_workqueue+0x67/0x5e0 ? lockdep_init_map_type+0x47/0x220 flush_workqueue+0x91/0x5e0 ? flush_workqueue+0x67/0x5e0 ? verify_cpu+0xf0/0x100 drain_workqueue+0xa0/0x110 destroy_workqueue+0x36/0x250 __loop_clr_fd+0x9a/0x660 [loop] ? blkdev_ioctl+0x8d/0x2a0 block_ioctl+0x3f/0x50 __x64_sys_ioctl+0x80/0xb0 do_syscall_64+0x38/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae RIP: 0033:0x7fc21255d4cb So instead save the bdev and do the put once we've dropped the sb writers lock in order to avoid the lockdep recursion. Reviewed-by: Anand Jain <[email protected]> Signed-off-by: Josef Bacik <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]> Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Kamal Mostafa <[email protected]> Signed-off-by: Kelsey Skunberg <[email protected]>
1 parent b7b68a8 commit 2fb3056

File tree

3 files changed

+30
-11
lines changed

3 files changed

+30
-11
lines changed

fs/btrfs/ioctl.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3243,6 +3243,8 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32433243
struct inode *inode = file_inode(file);
32443244
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
32453245
struct btrfs_ioctl_vol_args_v2 *vol_args;
3246+
struct block_device *bdev = NULL;
3247+
fmode_t mode;
32463248
int ret;
32473249

32483250
if (!capable(CAP_SYS_ADMIN))
@@ -3269,10 +3271,10 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32693271
}
32703272

32713273
if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) {
3272-
ret = btrfs_rm_device(fs_info, NULL, vol_args->devid);
3274+
ret = btrfs_rm_device(fs_info, NULL, vol_args->devid, &bdev, &mode);
32733275
} else {
32743276
vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
3275-
ret = btrfs_rm_device(fs_info, vol_args->name, 0);
3277+
ret = btrfs_rm_device(fs_info, vol_args->name, 0, &bdev, &mode);
32763278
}
32773279
btrfs_exclop_finish(fs_info);
32783280

@@ -3288,6 +3290,8 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32883290
kfree(vol_args);
32893291
err_drop:
32903292
mnt_drop_write_file(file);
3293+
if (bdev)
3294+
blkdev_put(bdev, mode);
32913295
return ret;
32923296
}
32933297

@@ -3296,6 +3300,8 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
32963300
struct inode *inode = file_inode(file);
32973301
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
32983302
struct btrfs_ioctl_vol_args *vol_args;
3303+
struct block_device *bdev = NULL;
3304+
fmode_t mode;
32993305
int ret;
33003306

33013307
if (!capable(CAP_SYS_ADMIN))
@@ -3317,7 +3323,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
33173323
}
33183324

33193325
vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
3320-
ret = btrfs_rm_device(fs_info, vol_args->name, 0);
3326+
ret = btrfs_rm_device(fs_info, vol_args->name, 0, &bdev, &mode);
33213327

33223328
if (!ret)
33233329
btrfs_info(fs_info, "disk deleted %s", vol_args->name);
@@ -3326,7 +3332,8 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
33263332
btrfs_exclop_finish(fs_info);
33273333
out_drop_write:
33283334
mnt_drop_write_file(file);
3329-
3335+
if (bdev)
3336+
blkdev_put(bdev, mode);
33303337
return ret;
33313338
}
33323339

fs/btrfs/volumes.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,7 +2062,7 @@ void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
20622062
}
20632063

20642064
int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
2065-
u64 devid)
2065+
u64 devid, struct block_device **bdev, fmode_t *mode)
20662066
{
20672067
struct btrfs_device *device;
20682068
struct btrfs_fs_devices *cur_devices;
@@ -2176,15 +2176,26 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
21762176
mutex_unlock(&fs_devices->device_list_mutex);
21772177

21782178
/*
2179-
* at this point, the device is zero sized and detached from
2180-
* the devices list. All that's left is to zero out the old
2181-
* supers and free the device.
2179+
* At this point, the device is zero sized and detached from the
2180+
* devices list. All that's left is to zero out the old supers and
2181+
* free the device.
2182+
*
2183+
* We cannot call btrfs_close_bdev() here because we're holding the sb
2184+
* write lock, and blkdev_put() will pull in the ->open_mutex on the
2185+
* block device and it's dependencies. Instead just flush the device
2186+
* and let the caller do the final blkdev_put.
21822187
*/
2183-
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state))
2188+
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
21842189
btrfs_scratch_superblocks(fs_info, device->bdev,
21852190
device->name->str);
2191+
if (device->bdev) {
2192+
sync_blockdev(device->bdev);
2193+
invalidate_bdev(device->bdev);
2194+
}
2195+
}
21862196

2187-
btrfs_close_bdev(device);
2197+
*bdev = device->bdev;
2198+
*mode = device->mode;
21882199
synchronize_rcu();
21892200
btrfs_free_device(device);
21902201

fs/btrfs/volumes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
465465
const u8 *uuid);
466466
void btrfs_free_device(struct btrfs_device *device);
467467
int btrfs_rm_device(struct btrfs_fs_info *fs_info,
468-
const char *device_path, u64 devid);
468+
const char *device_path, u64 devid,
469+
struct block_device **bdev, fmode_t *mode);
469470
void __exit btrfs_cleanup_fs_uuids(void);
470471
int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len);
471472
int btrfs_grow_device(struct btrfs_trans_handle *trans,

0 commit comments

Comments
 (0)