Skip to content

Commit 1a15eb7

Browse files
josefbacikkdave
authored andcommitted
btrfs: use btrfs_get_dev_args_from_path in dev removal ioctls
For device removal and replace we call btrfs_find_device_by_devspec, which if we give it a device path and nothing else will call btrfs_get_dev_args_from_path, which opens the block device and reads the super block and then looks up our device based on that. However at this point we're holding the sb write "lock", so reading the block device pulls in the dependency of ->open_mutex, which produces the following lockdep splat ====================================================== WARNING: possible circular locking dependency detected 5.14.0-rc2+ torvalds#405 Not tainted ------------------------------------------------------ losetup/11576 is trying to acquire lock: ffff9bbe8cded938 ((wq_completion)loop0){+.+.}-{0:0}, at: flush_workqueue+0x67/0x5e0 but task is already holding lock: ffff9bbe88e4fc68 (&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_get_by_dev.part.0+0x56/0x3c0 blkdev_get_by_path+0x98/0xa0 btrfs_get_bdev_and_sb+0x1b/0xb0 btrfs_find_device_by_devspec+0x12b/0x1c0 btrfs_rm_device+0x127/0x610 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/11576: #0: ffff9bbe88e4fc68 (&lo->lo_mutex){+.+.}-{3:3}, at: __loop_clr_fd+0x41/0x660 [loop] stack backtrace: CPU: 0 PID: 11576 Comm: losetup Not tainted 5.14.0-rc2+ torvalds#405 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:0x7f31b02404cb Instead what we want to do is populate our device lookup args before we grab any locks, and then pass these args into btrfs_rm_device(). From there we can find the device and do the appropriate removal. Suggested-by: Anand Jain <[email protected]> Reviewed-by: Anand Jain <[email protected]> Signed-off-by: Josef Bacik <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent faa775c commit 1a15eb7

File tree

3 files changed

+48
-36
lines changed

3 files changed

+48
-36
lines changed

fs/btrfs/ioctl.c

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3160,6 +3160,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)
31603160

31613161
static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
31623162
{
3163+
BTRFS_DEV_LOOKUP_ARGS(args);
31633164
struct inode *inode = file_inode(file);
31643165
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
31653166
struct btrfs_ioctl_vol_args_v2 *vol_args;
@@ -3171,35 +3172,39 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
31713172
if (!capable(CAP_SYS_ADMIN))
31723173
return -EPERM;
31733174

3174-
ret = mnt_want_write_file(file);
3175-
if (ret)
3176-
return ret;
3177-
31783175
vol_args = memdup_user(arg, sizeof(*vol_args));
31793176
if (IS_ERR(vol_args)) {
31803177
ret = PTR_ERR(vol_args);
3181-
goto err_drop;
3178+
goto out;
31823179
}
31833180

31843181
if (vol_args->flags & ~BTRFS_DEVICE_REMOVE_ARGS_MASK) {
31853182
ret = -EOPNOTSUPP;
31863183
goto out;
31873184
}
3185+
31883186
vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
3189-
if (!(vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) &&
3190-
strcmp("cancel", vol_args->name) == 0)
3187+
if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) {
3188+
args.devid = vol_args->devid;
3189+
} else if (!strcmp("cancel", vol_args->name)) {
31913190
cancel = true;
3191+
} else {
3192+
ret = btrfs_get_dev_args_from_path(fs_info, &args, vol_args->name);
3193+
if (ret)
3194+
goto out;
3195+
}
3196+
3197+
ret = mnt_want_write_file(file);
3198+
if (ret)
3199+
goto out;
31923200

31933201
ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_DEV_REMOVE,
31943202
cancel);
31953203
if (ret)
3196-
goto out;
3197-
/* Exclusive operation is now claimed */
3204+
goto err_drop;
31983205

3199-
if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID)
3200-
ret = btrfs_rm_device(fs_info, NULL, vol_args->devid, &bdev, &mode);
3201-
else
3202-
ret = btrfs_rm_device(fs_info, vol_args->name, 0, &bdev, &mode);
3206+
/* Exclusive operation is now claimed */
3207+
ret = btrfs_rm_device(fs_info, &args, &bdev, &mode);
32033208

32043209
btrfs_exclop_finish(fs_info);
32053210

@@ -3211,17 +3216,19 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32113216
btrfs_info(fs_info, "device deleted: %s",
32123217
vol_args->name);
32133218
}
3214-
out:
3215-
kfree(vol_args);
32163219
err_drop:
32173220
mnt_drop_write_file(file);
32183221
if (bdev)
32193222
blkdev_put(bdev, mode);
3223+
out:
3224+
btrfs_put_dev_args_from_path(&args);
3225+
kfree(vol_args);
32203226
return ret;
32213227
}
32223228

32233229
static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
32243230
{
3231+
BTRFS_DEV_LOOKUP_ARGS(args);
32253232
struct inode *inode = file_inode(file);
32263233
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
32273234
struct btrfs_ioctl_vol_args *vol_args;
@@ -3233,32 +3240,38 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
32333240
if (!capable(CAP_SYS_ADMIN))
32343241
return -EPERM;
32353242

3236-
ret = mnt_want_write_file(file);
3237-
if (ret)
3238-
return ret;
3239-
32403243
vol_args = memdup_user(arg, sizeof(*vol_args));
3241-
if (IS_ERR(vol_args)) {
3242-
ret = PTR_ERR(vol_args);
3243-
goto out_drop_write;
3244-
}
3244+
if (IS_ERR(vol_args))
3245+
return PTR_ERR(vol_args);
3246+
32453247
vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
3246-
cancel = (strcmp("cancel", vol_args->name) == 0);
3248+
if (!strcmp("cancel", vol_args->name)) {
3249+
cancel = true;
3250+
} else {
3251+
ret = btrfs_get_dev_args_from_path(fs_info, &args, vol_args->name);
3252+
if (ret)
3253+
goto out;
3254+
}
3255+
3256+
ret = mnt_want_write_file(file);
3257+
if (ret)
3258+
goto out;
32473259

32483260
ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_DEV_REMOVE,
32493261
cancel);
32503262
if (ret == 0) {
3251-
ret = btrfs_rm_device(fs_info, vol_args->name, 0, &bdev, &mode);
3263+
ret = btrfs_rm_device(fs_info, &args, &bdev, &mode);
32523264
if (!ret)
32533265
btrfs_info(fs_info, "disk deleted %s", vol_args->name);
32543266
btrfs_exclop_finish(fs_info);
32553267
}
32563268

3257-
kfree(vol_args);
3258-
out_drop_write:
32593269
mnt_drop_write_file(file);
32603270
if (bdev)
32613271
blkdev_put(bdev, mode);
3272+
out:
3273+
btrfs_put_dev_args_from_path(&args);
3274+
kfree(vol_args);
32623275
return ret;
32633276
}
32643277

fs/btrfs/volumes.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,8 +2076,9 @@ void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
20762076
update_dev_time(bdev);
20772077
}
20782078

2079-
int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
2080-
u64 devid, struct block_device **bdev, fmode_t *mode)
2079+
int btrfs_rm_device(struct btrfs_fs_info *fs_info,
2080+
struct btrfs_dev_lookup_args *args,
2081+
struct block_device **bdev, fmode_t *mode)
20812082
{
20822083
struct btrfs_device *device;
20832084
struct btrfs_fs_devices *cur_devices;
@@ -2096,14 +2097,12 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
20962097
if (ret)
20972098
goto out;
20982099

2099-
device = btrfs_find_device_by_devspec(fs_info, devid, device_path);
2100-
2101-
if (IS_ERR(device)) {
2102-
if (PTR_ERR(device) == -ENOENT &&
2103-
device_path && strcmp(device_path, "missing") == 0)
2100+
device = btrfs_find_device(fs_info->fs_devices, args);
2101+
if (!device) {
2102+
if (args->missing)
21042103
ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND;
21052104
else
2106-
ret = PTR_ERR(device);
2105+
ret = -ENOENT;
21072106
goto out;
21082107
}
21092108

fs/btrfs/volumes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
529529
void btrfs_put_dev_args_from_path(struct btrfs_dev_lookup_args *args);
530530
void btrfs_free_device(struct btrfs_device *device);
531531
int btrfs_rm_device(struct btrfs_fs_info *fs_info,
532-
const char *device_path, u64 devid,
532+
struct btrfs_dev_lookup_args *args,
533533
struct block_device **bdev, fmode_t *mode);
534534
void __exit btrfs_cleanup_fs_uuids(void);
535535
int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len);

0 commit comments

Comments
 (0)