Commit f2f6ed3d authored by Wang Shilong's avatar Wang Shilong Committed by Josef Bacik

Btrfs: introduce a mutex lock for btrfs quota operations

The original code has one spin_lock 'qgroup_lock' to protect quota
configurations in memory. If we want to add a BTRFS_QGROUP_INFO_KEY,
it will be added to Btree firstly, and then update configurations in
memory,however, a race condition may happen between these operations.
For example:
	->add_qgroup_info_item()
		->add_qgroup_rb()

For the above case, del_qgroup_info_item() may happen just before
add_qgroup_rb().

What's worse, when we want to add a qgroup relation:
	->add_qgroup_relation_item()
		->add_qgroup_relations()

We don't have any checks whether 'src' and 'dst' exist before
add_qgroup_relation_item(), a race condition can also happen for
the above case.

To avoid race condition and have all the necessary checks, we introduce
a mutex lock 'qgroup_ioctl_lock', and we make all the user change operations
protected by the mutex lock.
Signed-off-by: default avatarWang Shilong <wangsl-fnst@cn.fujitsu.com>
Reviewed-by: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent 7708f029
...@@ -1583,6 +1583,9 @@ struct btrfs_fs_info { ...@@ -1583,6 +1583,9 @@ struct btrfs_fs_info {
struct rb_root qgroup_tree; struct rb_root qgroup_tree;
spinlock_t qgroup_lock; spinlock_t qgroup_lock;
/* protect user change for quota operations */
struct mutex qgroup_ioctl_lock;
/* list of dirty qgroups to be written at next commit */ /* list of dirty qgroups to be written at next commit */
struct list_head dirty_qgroups; struct list_head dirty_qgroups;
......
...@@ -2213,6 +2213,7 @@ int open_ctree(struct super_block *sb, ...@@ -2213,6 +2213,7 @@ int open_ctree(struct super_block *sb,
mutex_init(&fs_info->dev_replace.lock); mutex_init(&fs_info->dev_replace.lock);
spin_lock_init(&fs_info->qgroup_lock); spin_lock_init(&fs_info->qgroup_lock);
mutex_init(&fs_info->qgroup_ioctl_lock);
fs_info->qgroup_tree = RB_ROOT; fs_info->qgroup_tree = RB_ROOT;
INIT_LIST_HEAD(&fs_info->dirty_qgroups); INIT_LIST_HEAD(&fs_info->dirty_qgroups);
fs_info->qgroup_seq = 1; fs_info->qgroup_seq = 1;
......
...@@ -791,6 +791,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans, ...@@ -791,6 +791,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
int ret = 0; int ret = 0;
int slot; int slot;
mutex_lock(&fs_info->qgroup_ioctl_lock);
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
if (fs_info->quota_root) { if (fs_info->quota_root) {
fs_info->pending_quota_state = 1; fs_info->pending_quota_state = 1;
...@@ -900,6 +901,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans, ...@@ -900,6 +901,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
kfree(quota_root); kfree(quota_root);
} }
out: out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
...@@ -910,10 +912,11 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans, ...@@ -910,10 +912,11 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
struct btrfs_root *quota_root; struct btrfs_root *quota_root;
int ret = 0; int ret = 0;
mutex_lock(&fs_info->qgroup_ioctl_lock);
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
if (!fs_info->quota_root) { if (!fs_info->quota_root) {
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
return 0; goto out;
} }
fs_info->quota_enabled = 0; fs_info->quota_enabled = 0;
fs_info->pending_quota_state = 0; fs_info->pending_quota_state = 0;
...@@ -922,8 +925,10 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans, ...@@ -922,8 +925,10 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
btrfs_free_qgroup_config(fs_info); btrfs_free_qgroup_config(fs_info);
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
if (!quota_root) if (!quota_root) {
return -EINVAL; ret = -EINVAL;
goto out;
}
ret = btrfs_clean_quota_tree(trans, quota_root); ret = btrfs_clean_quota_tree(trans, quota_root);
if (ret) if (ret)
...@@ -944,6 +949,7 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans, ...@@ -944,6 +949,7 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
free_extent_buffer(quota_root->commit_root); free_extent_buffer(quota_root->commit_root);
kfree(quota_root); kfree(quota_root);
out: out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
...@@ -959,24 +965,28 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, ...@@ -959,24 +965,28 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
struct btrfs_root *quota_root; struct btrfs_root *quota_root;
int ret = 0; int ret = 0;
mutex_lock(&fs_info->qgroup_ioctl_lock);
quota_root = fs_info->quota_root; quota_root = fs_info->quota_root;
if (!quota_root) if (!quota_root) {
return -EINVAL; ret = -EINVAL;
goto out;
}
ret = add_qgroup_relation_item(trans, quota_root, src, dst); ret = add_qgroup_relation_item(trans, quota_root, src, dst);
if (ret) if (ret)
return ret; goto out;
ret = add_qgroup_relation_item(trans, quota_root, dst, src); ret = add_qgroup_relation_item(trans, quota_root, dst, src);
if (ret) { if (ret) {
del_qgroup_relation_item(trans, quota_root, src, dst); del_qgroup_relation_item(trans, quota_root, src, dst);
return ret; goto out;
} }
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
ret = add_relation_rb(quota_root->fs_info, src, dst); ret = add_relation_rb(quota_root->fs_info, src, dst);
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
...@@ -987,9 +997,12 @@ int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans, ...@@ -987,9 +997,12 @@ int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
int ret = 0; int ret = 0;
int err; int err;
mutex_lock(&fs_info->qgroup_ioctl_lock);
quota_root = fs_info->quota_root; quota_root = fs_info->quota_root;
if (!quota_root) if (!quota_root) {
return -EINVAL; ret = -EINVAL;
goto out;
}
ret = del_qgroup_relation_item(trans, quota_root, src, dst); ret = del_qgroup_relation_item(trans, quota_root, src, dst);
err = del_qgroup_relation_item(trans, quota_root, dst, src); err = del_qgroup_relation_item(trans, quota_root, dst, src);
...@@ -1000,7 +1013,8 @@ int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans, ...@@ -1000,7 +1013,8 @@ int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
del_relation_rb(fs_info, src, dst); del_relation_rb(fs_info, src, dst);
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
...@@ -1011,9 +1025,12 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, ...@@ -1011,9 +1025,12 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans,
struct btrfs_qgroup *qgroup; struct btrfs_qgroup *qgroup;
int ret = 0; int ret = 0;
mutex_lock(&fs_info->qgroup_ioctl_lock);
quota_root = fs_info->quota_root; quota_root = fs_info->quota_root;
if (!quota_root) if (!quota_root) {
return -EINVAL; ret = -EINVAL;
goto out;
}
ret = add_qgroup_item(trans, quota_root, qgroupid); ret = add_qgroup_item(trans, quota_root, qgroupid);
...@@ -1023,7 +1040,8 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, ...@@ -1023,7 +1040,8 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans,
if (IS_ERR(qgroup)) if (IS_ERR(qgroup))
ret = PTR_ERR(qgroup); ret = PTR_ERR(qgroup);
out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
...@@ -1034,9 +1052,12 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, ...@@ -1034,9 +1052,12 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans,
struct btrfs_qgroup *qgroup; struct btrfs_qgroup *qgroup;
int ret = 0; int ret = 0;
mutex_lock(&fs_info->qgroup_ioctl_lock);
quota_root = fs_info->quota_root; quota_root = fs_info->quota_root;
if (!quota_root) if (!quota_root) {
return -EINVAL; ret = -EINVAL;
goto out;
}
/* check if there are no relations to this qgroup */ /* check if there are no relations to this qgroup */
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
...@@ -1044,7 +1065,8 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, ...@@ -1044,7 +1065,8 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans,
if (qgroup) { if (qgroup) {
if (!list_empty(&qgroup->groups) || !list_empty(&qgroup->members)) { if (!list_empty(&qgroup->groups) || !list_empty(&qgroup->members)) {
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
return -EBUSY; ret = -EBUSY;
goto out;
} }
} }
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
...@@ -1054,7 +1076,8 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, ...@@ -1054,7 +1076,8 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans,
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
del_qgroup_rb(quota_root->fs_info, qgroupid); del_qgroup_rb(quota_root->fs_info, qgroupid);
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
...@@ -1062,12 +1085,16 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, ...@@ -1062,12 +1085,16 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 qgroupid, struct btrfs_fs_info *fs_info, u64 qgroupid,
struct btrfs_qgroup_limit *limit) struct btrfs_qgroup_limit *limit)
{ {
struct btrfs_root *quota_root = fs_info->quota_root; struct btrfs_root *quota_root;
struct btrfs_qgroup *qgroup; struct btrfs_qgroup *qgroup;
int ret = 0; int ret = 0;
if (!quota_root) mutex_lock(&fs_info->qgroup_ioctl_lock);
return -EINVAL; quota_root = fs_info->quota_root;
if (!quota_root) {
ret = -EINVAL;
goto out;
}
ret = update_qgroup_limit_item(trans, quota_root, qgroupid, ret = update_qgroup_limit_item(trans, quota_root, qgroupid,
limit->flags, limit->max_rfer, limit->flags, limit->max_rfer,
...@@ -1094,7 +1121,8 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, ...@@ -1094,7 +1121,8 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans,
unlock: unlock:
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
...@@ -1392,11 +1420,14 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, ...@@ -1392,11 +1420,14 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
struct btrfs_qgroup *dstgroup; struct btrfs_qgroup *dstgroup;
u32 level_size = 0; u32 level_size = 0;
mutex_lock(&fs_info->qgroup_ioctl_lock);
if (!fs_info->quota_enabled) if (!fs_info->quota_enabled)
return 0; goto out;
if (!quota_root) if (!quota_root) {
return -EINVAL; ret = -EINVAL;
goto out;
}
/* /*
* create a tracking group for the subvol itself * create a tracking group for the subvol itself
...@@ -1523,6 +1554,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, ...@@ -1523,6 +1554,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
unlock: unlock:
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
out: out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment