Commit 9c8b35b1 authored by Qu Wenruo's avatar Qu Wenruo Committed by Chris Mason

btrfs: quota: Automatically update related qgroups or mark INCONSISTENT flags...

btrfs: quota: Automatically update related qgroups or mark INCONSISTENT flags when assigning/deleting a qgroup relations.

Operation like qgroups assigning/deleting qgroup relations will mostly
cause qgroup data inconsistent, since it needs to do the full rescan to
determine whether shared extents are exclusive or still shared in
parent qgroups.

But there are some exceptions, like qgroup with only exclusive extents
(qgroup->excl == qgroup->rfer), in that case, we only needs to
modify all its parents' excl and rfer.

So this patch adds a quick path for such qgroup in qgroup
assign/remove routine, and if quick path failed, the qgroup status will
be marked INCONSISTENT, and return 1 to info user-land.

BTW since the quick path is much the same of qgroup_excl_accounting(),
so move the core of it to __qgroup_excl_accounting() and reuse it.
Signed-off-by: default avatarQu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: default avatarDongsheng Yang <yangds.fnst@cn.fujitsu.com>
Reviewed-by: default avatarJosef Bacik <jbacik@fb.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent 8ea0ec9e
...@@ -1001,6 +1001,110 @@ static void qgroup_dirty(struct btrfs_fs_info *fs_info, ...@@ -1001,6 +1001,110 @@ static void qgroup_dirty(struct btrfs_fs_info *fs_info,
list_add(&qgroup->dirty, &fs_info->dirty_qgroups); list_add(&qgroup->dirty, &fs_info->dirty_qgroups);
} }
/*
* The easy accounting, if we are adding/removing the only ref for an extent
* then this qgroup and all of the parent qgroups get their refrence and
* exclusive counts adjusted.
*
* Caller should hold fs_info->qgroup_lock.
*/
static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
struct ulist *tmp, u64 ref_root,
u64 num_bytes, int sign)
{
struct btrfs_qgroup *qgroup;
struct btrfs_qgroup_list *glist;
struct ulist_node *unode;
struct ulist_iterator uiter;
int ret = 0;
qgroup = find_qgroup_rb(fs_info, ref_root);
if (!qgroup)
goto out;
qgroup->rfer += sign * num_bytes;
qgroup->rfer_cmpr += sign * num_bytes;
WARN_ON(sign < 0 && qgroup->excl < num_bytes);
qgroup->excl += sign * num_bytes;
qgroup->excl_cmpr += sign * num_bytes;
if (sign > 0)
qgroup->reserved -= num_bytes;
qgroup_dirty(fs_info, qgroup);
/* Get all of the parent groups that contain this qgroup */
list_for_each_entry(glist, &qgroup->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
ptr_to_u64(glist->group), GFP_ATOMIC);
if (ret < 0)
goto out;
}
/* Iterate all of the parents and adjust their reference counts */
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(tmp, &uiter))) {
qgroup = u64_to_ptr(unode->aux);
qgroup->rfer += sign * num_bytes;
qgroup->rfer_cmpr += sign * num_bytes;
WARN_ON(sign < 0 && qgroup->excl < num_bytes);
qgroup->excl += sign * num_bytes;
if (sign > 0)
qgroup->reserved -= num_bytes;
qgroup->excl_cmpr += sign * num_bytes;
qgroup_dirty(fs_info, qgroup);
/* Add any parents of the parents */
list_for_each_entry(glist, &qgroup->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
ptr_to_u64(glist->group), GFP_ATOMIC);
if (ret < 0)
goto out;
}
}
ret = 0;
out:
return ret;
}
/*
* Quick path for updating qgroup with only excl refs.
*
* In that case, just update all parent will be enough.
* Or we needs to do a full rescan.
* Caller should also hold fs_info->qgroup_lock.
*
* Return 0 for quick update, return >0 for need to full rescan
* and mark INCONSISTENT flag.
* Return < 0 for other error.
*/
static int quick_update_accounting(struct btrfs_fs_info *fs_info,
struct ulist *tmp, u64 src, u64 dst,
int sign)
{
struct btrfs_qgroup *qgroup;
int ret = 1;
int err = 0;
qgroup = find_qgroup_rb(fs_info, src);
if (!qgroup)
goto out;
if (qgroup->excl == qgroup->rfer) {
ret = 0;
err = __qgroup_excl_accounting(fs_info, tmp, dst,
qgroup->excl, sign);
if (err < 0) {
ret = err;
goto out;
}
}
out:
if (ret)
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
return ret;
}
int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 src, u64 dst) struct btrfs_fs_info *fs_info, u64 src, u64 dst)
{ {
...@@ -1008,8 +1112,13 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, ...@@ -1008,8 +1112,13 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
struct btrfs_qgroup *parent; struct btrfs_qgroup *parent;
struct btrfs_qgroup *member; struct btrfs_qgroup *member;
struct btrfs_qgroup_list *list; struct btrfs_qgroup_list *list;
struct ulist *tmp;
int ret = 0; int ret = 0;
tmp = ulist_alloc(GFP_NOFS);
if (!tmp)
return -ENOMEM;
/* Check the level of src and dst first */ /* Check the level of src and dst first */
if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst)) if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst))
return -EINVAL; return -EINVAL;
...@@ -1047,9 +1156,15 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, ...@@ -1047,9 +1156,15 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
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);
if (ret < 0) {
spin_unlock(&fs_info->qgroup_lock);
goto out;
}
ret = quick_update_accounting(fs_info, tmp, src, dst, 1);
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
out: out:
mutex_unlock(&fs_info->qgroup_ioctl_lock); mutex_unlock(&fs_info->qgroup_ioctl_lock);
ulist_free(tmp);
return ret; return ret;
} }
...@@ -1060,9 +1175,14 @@ int __del_qgroup_relation(struct btrfs_trans_handle *trans, ...@@ -1060,9 +1175,14 @@ int __del_qgroup_relation(struct btrfs_trans_handle *trans,
struct btrfs_qgroup *parent; struct btrfs_qgroup *parent;
struct btrfs_qgroup *member; struct btrfs_qgroup *member;
struct btrfs_qgroup_list *list; struct btrfs_qgroup_list *list;
struct ulist *tmp;
int ret = 0; int ret = 0;
int err; int err;
tmp = ulist_alloc(GFP_NOFS);
if (!tmp)
return -ENOMEM;
quota_root = fs_info->quota_root; quota_root = fs_info->quota_root;
if (!quota_root) { if (!quota_root) {
ret = -EINVAL; ret = -EINVAL;
...@@ -1091,8 +1211,10 @@ int __del_qgroup_relation(struct btrfs_trans_handle *trans, ...@@ -1091,8 +1211,10 @@ int __del_qgroup_relation(struct btrfs_trans_handle *trans,
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
del_relation_rb(fs_info, src, dst); del_relation_rb(fs_info, src, dst);
ret = quick_update_accounting(fs_info, tmp, src, dst, -1);
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
out: out:
ulist_free(tmp);
return ret; return ret;
} }
...@@ -1400,19 +1522,10 @@ int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans, ...@@ -1400,19 +1522,10 @@ int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
/*
* The easy accounting, if we are adding/removing the only ref for an extent
* then this qgroup and all of the parent qgroups get their refrence and
* exclusive counts adjusted.
*/
static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
struct btrfs_qgroup_operation *oper) struct btrfs_qgroup_operation *oper)
{ {
struct btrfs_qgroup *qgroup;
struct ulist *tmp; struct ulist *tmp;
struct btrfs_qgroup_list *glist;
struct ulist_node *unode;
struct ulist_iterator uiter;
int sign = 0; int sign = 0;
int ret = 0; int ret = 0;
...@@ -1423,9 +1536,7 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, ...@@ -1423,9 +1536,7 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
if (!fs_info->quota_root) if (!fs_info->quota_root)
goto out; goto out;
qgroup = find_qgroup_rb(fs_info, oper->ref_root);
if (!qgroup)
goto out;
switch (oper->type) { switch (oper->type) {
case BTRFS_QGROUP_OPER_ADD_EXCL: case BTRFS_QGROUP_OPER_ADD_EXCL:
sign = 1; sign = 1;
...@@ -1436,47 +1547,8 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, ...@@ -1436,47 +1547,8 @@ static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
default: default:
ASSERT(0); ASSERT(0);
} }
qgroup->rfer += sign * oper->num_bytes; ret = __qgroup_excl_accounting(fs_info, tmp, oper->ref_root,
qgroup->rfer_cmpr += sign * oper->num_bytes; oper->num_bytes, sign);
WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes);
qgroup->excl += sign * oper->num_bytes;
qgroup->excl_cmpr += sign * oper->num_bytes;
if (sign > 0)
qgroup->reserved -= oper->num_bytes;
qgroup_dirty(fs_info, qgroup);
/* Get all of the parent groups that contain this qgroup */
list_for_each_entry(glist, &qgroup->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
ptr_to_u64(glist->group), GFP_ATOMIC);
if (ret < 0)
goto out;
}
/* Iterate all of the parents and adjust their reference counts */
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(tmp, &uiter))) {
qgroup = u64_to_ptr(unode->aux);
qgroup->rfer += sign * oper->num_bytes;
qgroup->rfer_cmpr += sign * oper->num_bytes;
WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes);
qgroup->excl += sign * oper->num_bytes;
if (sign > 0)
qgroup->reserved -= oper->num_bytes;
qgroup->excl_cmpr += sign * oper->num_bytes;
qgroup_dirty(fs_info, qgroup);
/* Add any parents of the parents */
list_for_each_entry(glist, &qgroup->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
ptr_to_u64(glist->group), GFP_ATOMIC);
if (ret < 0)
goto out;
}
}
ret = 0;
out: out:
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
ulist_free(tmp); ulist_free(tmp);
...@@ -2732,10 +2804,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) ...@@ -2732,10 +2804,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
ret = update_qgroup_status_item(trans, fs_info, fs_info->quota_root); ret = update_qgroup_status_item(trans, fs_info, fs_info->quota_root);
if (ret < 0) { if (ret < 0) {
err = ret; err = ret;
btrfs_err(fs_info, "fail to update qgroup status: %d\n", btrfs_err(fs_info, "fail to update qgroup status: %d\n", err);
err);
btrfs_abort_transaction(trans, fs_info->quota_root, err);
goto done;
} }
btrfs_end_transaction(trans, fs_info->quota_root); btrfs_end_transaction(trans, fs_info->quota_root);
......
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