Mutually exclusive ops fixes
cover Mutually exclusive ops fixes
Hello,
This is a set of fixes for mutually exclusive ops stuff added by Stefan as part of the dev-replace patchset. The main one here is the first patch, it fixes a regression in balancing interface. The second patch brings EINVAL error code back. The rest are relatively minor ordering fixes.
Thanks,
Ilya
Ilya Dryomov (5):
Btrfs: bring back balance pause/resume logic
Btrfs: fix "mutually exclusive op is running" error code
Btrfs: fix unlock order in btrfs_ioctl_resize
Btrfs: fix unlock order in btrfs_ioctl_rm_dev
Btrfs: reorder locks and sanity checks in btrfs_ioctl_defrag
fs/btrfs/ioctl.c | 107 ++++++++++++++++++++++++++++++++++++++------------- fs/btrfs/volumes.c | 10 +++- 2 files changed, 86 insertions(+), 31 deletions(-)
Signature · 1 line
1.7.9.1
PATCH 1/5 Btrfs: bring back balance pause/resume logic
Balance pause/resume logic got broken by 5ac00add (went in into 3.8-rc1 as part of dev-replace merge). Offending commit took a stab at making mutually exclusive volume operations (add_dev, rm_dev, resize, balance, replace_dev) not block behind volume_mutex if another such operation is in progress and instead return an error right away. Balancing front-end relied on the blocking behaviour, so the fix is ugly, but short of a complete rework, it's the best we can do.
--- fs/btrfs/ioctl.c | 78 ++++++++++++++++++++++++++++++++++++++++++--------- fs/btrfs/volumes.c | 10 ++++-- 2 files changed, 71 insertions(+), 17 deletions(-)
fs/btrfs/ioctl.c +64 -14
@@ -3441,8 +3441,8 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)struct btrfs_fs_info *fs_info = root->fs_info;struct btrfs_ioctl_balance_args *bargs;struct btrfs_balance_control *bctl; +bool need_unlock;/* for mut. excl. ops lock */ int ret; -int need_to_clear_lock =0 ;if (!capable (CAP_SYS_ADMIN ))return -EPERM ;
@@ -3451,14 +3451,61 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)if (ret)return ret; -mutex_lock (&fs_info->volume_mutex); +again: +if (!atomic_xchg (&fs_info->mutually_exclusive_operation_running,1 )) { +mutex_lock (&fs_info->volume_mutex); +mutex_lock (&fs_info->balance_mutex); + need_unlock = true; + goto locked; + } + +/* +* mut. excl. ops lock is locked. Three possibilites: +* (1) some other op is running +* (2) balance is running +* (3) balance is paused -- special case (think resume) +*/ mutex_lock (&fs_info->balance_mutex); +if (fs_info->balance_ctl) { +/* this is either (2) or (3) */ +if (!atomic_read (&fs_info->balance_running)) { +mutex_unlock (&fs_info->balance_mutex); +if (!mutex_trylock (&fs_info->volume_mutex)) + goto again; +mutex_lock (&fs_info->balance_mutex); + +if (fs_info->balance_ctl && + !atomic_read (&fs_info->balance_running)) { +/* this is (3) */ + need_unlock = false; + goto locked; + } + +mutex_unlock (&fs_info->balance_mutex); +mutex_unlock (&fs_info->volume_mutex); + goto again; + }else { +/* this is (2) */ +mutex_unlock (&fs_info->balance_mutex); + ret = -EINPROGRESS ; + goto out; + } + }else { +/* this is (1) */ +mutex_unlock (&fs_info->balance_mutex); +pr_info ("btrfs: dev add/delete/balance/replace/resize operation in progress\n" ); + ret = -EINVAL ; + goto out; + } + +locked: +BUG_ON (!atomic_read (&fs_info->mutually_exclusive_operation_running));if (arg) { bargs =memdup_user (arg,sizeof (*bargs));if (IS_ERR (bargs)) { ret =PTR_ERR (bargs); - goto out; + goto out_unlock; }if (bargs->flags &BTRFS_BALANCE_RESUME ) {
@@ -3478,13 +3525,10 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) bargs =NULL ; } -if (atomic_xchg (&root->fs_info->mutually_exclusive_operation_running, -1 )) { -pr_info ("btrfs: dev add/delete/balance/replace/resize operation in progress\n" ); +if (fs_info->balance_ctl) { ret = -EINPROGRESS ; goto out_bargs; } - need_to_clear_lock =1 ; bctl =kzalloc (sizeof (*bctl),GFP_NOFS );if (!bctl) {
@@ -3505,11 +3549,17 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) } do_balance: - ret =btrfs_balance (bctl, bargs);/* -* bctl is freed in __cancel_balance or in free_fs_info if -* restriper was paused all the way until unmount +* Ownership of bctl and mutually_exclusive_operation_running +* goes to to btrfs_balance. bctl is freed in __cancel_balance, +* or, if restriper was paused all the way until unmount, in +* free_fs_info. mutually_exclusive_operation_running is +* cleared in __cancel_balance. */ + need_unlock = false; + + ret =btrfs_balance (bctl, bargs); +if (arg) {if (copy_to_user (arg, bargs,sizeof (*bargs))) ret = -EFAULT ;
@@ -3517,12 +3567,12 @@ do_balance: out_bargs:kfree (bargs); -out: -if (need_to_clear_lock) -atomic_set (&root->fs_info->mutually_exclusive_operation_running, -0 ); +out_unlock:mutex_unlock (&fs_info->balance_mutex);mutex_unlock (&fs_info->volume_mutex); +if (need_unlock) +atomic_set (&fs_info->mutually_exclusive_operation_running,0 ); +out:mnt_drop_write_file (file);return ret; }
fs/btrfs/volumes.c +7 -3
@@ -2959,6 +2959,8 @@ static void __cancel_balance(struct btrfs_fs_info *fs_info)unset_balance_control (fs_info); ret =del_balance_item (fs_info->tree_root);BUG_ON (ret); + +atomic_set (&fs_info->mutually_exclusive_operation_running,0 ); } void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock,
@@ -3138,8 +3140,10 @@ int btrfs_balance(struct btrfs_balance_control *bctl, out:if (bctl->flags &BTRFS_BALANCE_RESUME )__cancel_balance (fs_info); -else +else {kfree (bctl); +atomic_set (&fs_info->mutually_exclusive_operation_running,0 ); + }return ret; }
@@ -3156,7 +3160,6 @@ static int balance_kthread(void *data) ret =btrfs_balance (fs_info->balance_ctl,NULL ); } -atomic_set (&fs_info->mutually_exclusive_operation_running,0 );mutex_unlock (&fs_info->balance_mutex);mutex_unlock (&fs_info->volume_mutex);
@@ -3179,7 +3182,6 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info)return 0 ; } -WARN_ON (atomic_xchg (&fs_info->mutually_exclusive_operation_running,1 )); tsk =kthread_run (balance_kthread, fs_info,"btrfs-balance" );if (IS_ERR (tsk))return PTR_ERR (tsk);
@@ -3233,6 +3235,8 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info)btrfs_balance_sys (leaf, item, &disk_bargs);btrfs_disk_balance_args_to_cpu (&bctl->sys, &disk_bargs); +WARN_ON (atomic_xchg (&fs_info->mutually_exclusive_operation_running,1 )); +mutex_lock (&fs_info->volume_mutex);mutex_lock (&fs_info->balance_mutex);
Signature · 1 line
1.7.9.1
PATCH 2/5 Btrfs: fix "mutually exclusive op is running" error code
In reply to Ilya DryomovThe error code that is returned in response to starting a mutually exclusive operation when there is one already running got silently changed from EINVAL to EINPROGRESS by 5ac00add. Returning EINPROGRESS to, say, add_dev, when rm_dev is running is misleading. Furthermore, the operation itself may want to use EINPROGRESS for other purposes.
--- fs/btrfs/ioctl.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
fs/btrfs/ioctl.c +4 -4
@@ -1337,7 +1337,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, 1)) {pr_info ("btrfs: dev add/delete/balance/replace/resize operation in progress\n" );mnt_drop_write_file (file); -return -EINPROGRESS ; +return -EINVAL ; }mutex_lock (&root->fs_info->volume_mutex);
@@ -2193,7 +2193,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)if (atomic_xchg (&root->fs_info->mutually_exclusive_operation_running,1 )) {pr_info ("btrfs: dev add/delete/balance/replace/resize operation in progress\n" ); -return -EINPROGRESS ; +return -EINVAL ; } ret =mnt_want_write_file (file);if (ret) {
@@ -2267,7 +2267,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)if (atomic_xchg (&root->fs_info->mutually_exclusive_operation_running,1 )) {pr_info ("btrfs: dev add/delete/balance/replace/resize operation in progress\n" ); -return -EINPROGRESS ; +return -EINVAL ; }mutex_lock (&root->fs_info->volume_mutex);
@@ -2304,7 +2304,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) 1)) {pr_info ("btrfs: dev add/delete/balance/replace/resize operation in progress\n" );mnt_drop_write_file (file); -return -EINPROGRESS ; +return -EINVAL ; }mutex_lock (&root->fs_info->volume_mutex);
Signature · 1 line
1.7.9.1
PATCH 3/5 Btrfs: fix unlock order in btrfs_ioctl_resize
In reply to Ilya DryomovFix unlock order in btrfs_ioctl_resize().
--- fs/btrfs/ioctl.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
fs/btrfs/ioctl.c +1 -1
@@ -1447,8 +1447,8 @@ out_free:kfree (vol_args); out:mutex_unlock (&root->fs_info->volume_mutex); -mnt_drop_write_file (file);atomic_set (&root->fs_info->mutually_exclusive_operation_running,0 ); +mnt_drop_write_file (file);return ret; }
Signature · 1 line
1.7.9.1
PATCH 4/5 Btrfs: fix unlock order in btrfs_ioctl_rm_dev
In reply to Ilya DryomovFix unlock order in btrfs_ioctl_rm_dev().
--- fs/btrfs/ioctl.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
fs/btrfs/ioctl.c +1 -1
@@ -2320,8 +2320,8 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)kfree (vol_args); out:mutex_unlock (&root->fs_info->volume_mutex); -mnt_drop_write_file (file);atomic_set (&root->fs_info->mutually_exclusive_operation_running,0 ); +mnt_drop_write_file (file);return ret; }
Signature · 1 line
1.7.9.1
Quoted 7 lines · Ilya Dryomov
Hello,
This is a set of fixes for mutually exclusive ops stuff added by Stefan as part of the dev-replace patchset. The main one here is the first patch, it fixes a regression in balancing interface. The second patch brings EINVAL error code back. The rest are relatively minor ordering fixes.
You can pull from
git://github.com/idryomov/btrfs-unstable.git mutex-ops@next-master
top commit 2f68aafa90fedb3cdb80f0a8517a2130c5c035b0
Thanks,
Ilya
PATCH 5/5 Btrfs: reorder locks and sanity checks in btrfs_ioctl_defrag
In reply to Ilya DryomovOperation-specific check (whether subvol is readonly or not) should go after the mutual exclusiveness check.
--- fs/btrfs/ioctl.c | 17 +++++++++-------- 1 files changed, 9 insertions(+), 8 deletions(-)
fs/btrfs/ioctl.c +9 -8
@@ -2187,19 +2187,20 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)struct btrfs_ioctl_defrag_range_args *range;int ret; -if (btrfs_root_readonly (root)) -return -EROFS ; + ret =mnt_want_write_file (file); +if (ret) +return ret;if (atomic_xchg (&root->fs_info->mutually_exclusive_operation_running,1 )) {pr_info ("btrfs: dev add/delete/balance/replace/resize operation in progress\n" ); +mnt_drop_write_file (file);return -EINVAL ; } - ret =mnt_want_write_file (file); -if (ret) { -atomic_set (&root->fs_info->mutually_exclusive_operation_running, -0 ); -return ret; + +if (btrfs_root_readonly (root)) { + ret = -EROFS ; + goto out; }switch (inode->i_mode &S_IFMT ) {
@@ -2251,8 +2252,8 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp) ret = -EINVAL ; } out: -mnt_drop_write_file (file);atomic_set (&root->fs_info->mutually_exclusive_operation_running,0 ); +mnt_drop_write_file (file);return ret; }
Signature · 1 line
1.7.9.1
Quoted 6 lines · Ilya Dryomov
This is a set of fixes for mutually exclusive ops stuff added by Stefan as part of the dev-replace patchset. The main one here is the first patch, it fixes a regression in balancing interface. The second patch brings EINVAL error code back. The rest are relatively minor ordering fixes.
Quoted 6 lines
Ilya Dryomov (5):
Btrfs: bring back balance pause/resume logic
Btrfs: fix "mutually exclusive op is running" error code
Btrfs: fix unlock order inbtrfs_ioctl_resize
Btrfs: fix unlock order inbtrfs_ioctl_rm_dev
Btrfs: reorder locks and sanity checks inbtrfs_ioctl_defrag
1-5