Skip to content

Commit f75534a

Browse files
josefbacikgregkh
authored andcommitted
btrfs: use btrfs_get_dev_args_from_path in dev removal ioctls
[ Upstream commit 1a15eb7 ] 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+ #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+ #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]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 321a818 commit f75534a

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
@@ -3218,6 +3218,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)
32183218

32193219
static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32203220
{
3221+
BTRFS_DEV_LOOKUP_ARGS(args);
32213222
struct inode *inode = file_inode(file);
32223223
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
32233224
struct btrfs_ioctl_vol_args_v2 *vol_args;
@@ -3229,35 +3230,39 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32293230
if (!capable(CAP_SYS_ADMIN))
32303231
return -EPERM;
32313232

3232-
ret = mnt_want_write_file(file);
3233-
if (ret)
3234-
return ret;
3235-
32363233
vol_args = memdup_user(arg, sizeof(*vol_args));
32373234
if (IS_ERR(vol_args)) {
32383235
ret = PTR_ERR(vol_args);
3239-
goto err_drop;
3236+
goto out;
32403237
}
32413238

32423239
if (vol_args->flags & ~BTRFS_DEVICE_REMOVE_ARGS_MASK) {
32433240
ret = -EOPNOTSUPP;
32443241
goto out;
32453242
}
3243+
32463244
vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0';
3247-
if (!(vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) &&
3248-
strcmp("cancel", vol_args->name) == 0)
3245+
if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) {
3246+
args.devid = vol_args->devid;
3247+
} else if (!strcmp("cancel", vol_args->name)) {
32493248
cancel = true;
3249+
} else {
3250+
ret = btrfs_get_dev_args_from_path(fs_info, &args, vol_args->name);
3251+
if (ret)
3252+
goto out;
3253+
}
3254+
3255+
ret = mnt_want_write_file(file);
3256+
if (ret)
3257+
goto out;
32503258

32513259
ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_DEV_REMOVE,
32523260
cancel);
32533261
if (ret)
3254-
goto out;
3255-
/* Exclusive operation is now claimed */
3262+
goto err_drop;
32563263

3257-
if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID)
3258-
ret = btrfs_rm_device(fs_info, NULL, vol_args->devid, &bdev, &mode);
3259-
else
3260-
ret = btrfs_rm_device(fs_info, vol_args->name, 0, &bdev, &mode);
3264+
/* Exclusive operation is now claimed */
3265+
ret = btrfs_rm_device(fs_info, &args, &bdev, &mode);
32613266

32623267
btrfs_exclop_finish(fs_info);
32633268

@@ -3269,17 +3274,19 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg)
32693274
btrfs_info(fs_info, "device deleted: %s",
32703275
vol_args->name);
32713276
}
3272-
out:
3273-
kfree(vol_args);
32743277
err_drop:
32753278
mnt_drop_write_file(file);
32763279
if (bdev)
32773280
blkdev_put(bdev, mode);
3281+
out:
3282+
btrfs_put_dev_args_from_path(&args);
3283+
kfree(vol_args);
32783284
return ret;
32793285
}
32803286

32813287
static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
32823288
{
3289+
BTRFS_DEV_LOOKUP_ARGS(args);
32833290
struct inode *inode = file_inode(file);
32843291
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
32853292
struct btrfs_ioctl_vol_args *vol_args;
@@ -3291,32 +3298,38 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
32913298
if (!capable(CAP_SYS_ADMIN))
32923299
return -EPERM;
32933300

3294-
ret = mnt_want_write_file(file);
3295-
if (ret)
3296-
return ret;
3297-
32983301
vol_args = memdup_user(arg, sizeof(*vol_args));
3299-
if (IS_ERR(vol_args)) {
3300-
ret = PTR_ERR(vol_args);
3301-
goto out_drop_write;
3302-
}
3302+
if (IS_ERR(vol_args))
3303+
return PTR_ERR(vol_args);
3304+
33033305
vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
3304-
cancel = (strcmp("cancel", vol_args->name) == 0);
3306+
if (!strcmp("cancel", vol_args->name)) {
3307+
cancel = true;
3308+
} else {
3309+
ret = btrfs_get_dev_args_from_path(fs_info, &args, vol_args->name);
3310+
if (ret)
3311+
goto out;
3312+
}
3313+
3314+
ret = mnt_want_write_file(file);
3315+
if (ret)
3316+
goto out;
33053317

33063318
ret = exclop_start_or_cancel_reloc(fs_info, BTRFS_EXCLOP_DEV_REMOVE,
33073319
cancel);
33083320
if (ret == 0) {
3309-
ret = btrfs_rm_device(fs_info, vol_args->name, 0, &bdev, &mode);
3321+
ret = btrfs_rm_device(fs_info, &args, &bdev, &mode);
33103322
if (!ret)
33113323
btrfs_info(fs_info, "disk deleted %s", vol_args->name);
33123324
btrfs_exclop_finish(fs_info);
33133325
}
33143326

3315-
kfree(vol_args);
3316-
out_drop_write:
33173327
mnt_drop_write_file(file);
33183328
if (bdev)
33193329
blkdev_put(bdev, mode);
3330+
out:
3331+
btrfs_put_dev_args_from_path(&args);
3332+
kfree(vol_args);
33203333
return ret;
33213334
}
33223335

fs/btrfs/volumes.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,8 +2120,9 @@ void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
21202120
update_dev_time(device_path);
21212121
}
21222122

2123-
int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
2124-
u64 devid, struct block_device **bdev, fmode_t *mode)
2123+
int btrfs_rm_device(struct btrfs_fs_info *fs_info,
2124+
struct btrfs_dev_lookup_args *args,
2125+
struct block_device **bdev, fmode_t *mode)
21252126
{
21262127
struct btrfs_device *device;
21272128
struct btrfs_fs_devices *cur_devices;
@@ -2140,14 +2141,12 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
21402141
if (ret)
21412142
goto out;
21422143

2143-
device = btrfs_find_device_by_devspec(fs_info, devid, device_path);
2144-
2145-
if (IS_ERR(device)) {
2146-
if (PTR_ERR(device) == -ENOENT &&
2147-
device_path && strcmp(device_path, "missing") == 0)
2144+
device = btrfs_find_device(fs_info->fs_devices, args);
2145+
if (!device) {
2146+
if (args->missing)
21482147
ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND;
21492148
else
2150-
ret = PTR_ERR(device);
2149+
ret = -ENOENT;
21512150
goto out;
21522151
}
21532152

fs/btrfs/volumes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
496496
void btrfs_put_dev_args_from_path(struct btrfs_dev_lookup_args *args);
497497
void btrfs_free_device(struct btrfs_device *device);
498498
int btrfs_rm_device(struct btrfs_fs_info *fs_info,
499-
const char *device_path, u64 devid,
499+
struct btrfs_dev_lookup_args *args,
500500
struct block_device **bdev, fmode_t *mode);
501501
void __exit btrfs_cleanup_fs_uuids(void);
502502
int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len);

0 commit comments

Comments
 (0)