Btrfs: split btrfs_qgroup_account_ref into four functions

The function is separated into a preparation part and the three accounting
steps mentioned in the qgroups documentation. The goal is to make steps two
and three usable by the rescan functionality. A side effect is that the
function is restructured into readable subunits.

Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
This commit is contained in:
Jan Schmidt 2013-04-25 16:04:50 +00:00 committed by Josef Bacik
parent 3c76cd84e0
commit 46b665ceb1

View File

@ -1185,6 +1185,144 @@ int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
return 0;
}
static int qgroup_account_ref_step1(struct btrfs_fs_info *fs_info,
struct ulist *roots, struct ulist *tmp,
u64 seq)
{
struct ulist_node *unode;
struct ulist_iterator uiter;
struct ulist_node *tmp_unode;
struct ulist_iterator tmp_uiter;
struct btrfs_qgroup *qg;
int ret;
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(roots, &uiter))) {
qg = find_qgroup_rb(fs_info, unode->val);
if (!qg)
continue;
ulist_reinit(tmp);
/* XXX id not needed */
ret = ulist_add(tmp, qg->qgroupid,
(u64)(uintptr_t)qg, GFP_ATOMIC);
if (ret < 0)
return ret;
ULIST_ITER_INIT(&tmp_uiter);
while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
struct btrfs_qgroup_list *glist;
qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
if (qg->refcnt < seq)
qg->refcnt = seq + 1;
else
++qg->refcnt;
list_for_each_entry(glist, &qg->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
(u64)(uintptr_t)glist->group,
GFP_ATOMIC);
if (ret < 0)
return ret;
}
}
}
return 0;
}
static int qgroup_account_ref_step2(struct btrfs_fs_info *fs_info,
struct ulist *roots, struct ulist *tmp,
u64 seq, int sgn, u64 num_bytes,
struct btrfs_qgroup *qgroup)
{
struct ulist_node *unode;
struct ulist_iterator uiter;
struct btrfs_qgroup *qg;
struct btrfs_qgroup_list *glist;
int ret;
ulist_reinit(tmp);
ret = ulist_add(tmp, qgroup->qgroupid, (uintptr_t)qgroup, GFP_ATOMIC);
if (ret < 0)
return ret;
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(tmp, &uiter))) {
qg = (struct btrfs_qgroup *)(uintptr_t)unode->aux;
if (qg->refcnt < seq) {
/* not visited by step 1 */
qg->rfer += sgn * num_bytes;
qg->rfer_cmpr += sgn * num_bytes;
if (roots->nnodes == 0) {
qg->excl += sgn * num_bytes;
qg->excl_cmpr += sgn * num_bytes;
}
qgroup_dirty(fs_info, qg);
}
WARN_ON(qg->tag >= seq);
qg->tag = seq;
list_for_each_entry(glist, &qg->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
(uintptr_t)glist->group, GFP_ATOMIC);
if (ret < 0)
return ret;
}
}
return 0;
}
static int qgroup_account_ref_step3(struct btrfs_fs_info *fs_info,
struct ulist *roots, struct ulist *tmp,
u64 seq, int sgn, u64 num_bytes)
{
struct ulist_node *unode;
struct ulist_iterator uiter;
struct btrfs_qgroup *qg;
struct ulist_node *tmp_unode;
struct ulist_iterator tmp_uiter;
int ret;
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(roots, &uiter))) {
qg = find_qgroup_rb(fs_info, unode->val);
if (!qg)
continue;
ulist_reinit(tmp);
ret = ulist_add(tmp, qg->qgroupid, (uintptr_t)qg, GFP_ATOMIC);
if (ret < 0)
return ret;
ULIST_ITER_INIT(&tmp_uiter);
while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
struct btrfs_qgroup_list *glist;
qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
if (qg->tag == seq)
continue;
if (qg->refcnt - seq == roots->nnodes) {
qg->excl -= sgn * num_bytes;
qg->excl_cmpr -= sgn * num_bytes;
qgroup_dirty(fs_info, qg);
}
list_for_each_entry(glist, &qg->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
(uintptr_t)glist->group,
GFP_ATOMIC);
if (ret < 0)
return ret;
}
}
}
return 0;
}
/*
* btrfs_qgroup_account_ref is called for every ref that is added to or deleted
* from the fs. First, all roots referencing the extent are searched, and
@ -1200,10 +1338,8 @@ int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *quota_root;
u64 ref_root;
struct btrfs_qgroup *qgroup;
struct ulist_node *unode;
struct ulist *roots = NULL;
struct ulist *tmp = NULL;
struct ulist_iterator uiter;
u64 seq;
int ret = 0;
int sgn;
@ -1287,119 +1423,26 @@ int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
seq = fs_info->qgroup_seq;
fs_info->qgroup_seq += roots->nnodes + 1; /* max refcnt */
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(roots, &uiter))) {
struct ulist_node *tmp_unode;
struct ulist_iterator tmp_uiter;
struct btrfs_qgroup *qg;
qg = find_qgroup_rb(fs_info, unode->val);
if (!qg)
continue;
ulist_reinit(tmp);
/* XXX id not needed */
ret = ulist_add(tmp, qg->qgroupid,
(u64)(uintptr_t)qg, GFP_ATOMIC);
if (ret < 0)
goto unlock;
ULIST_ITER_INIT(&tmp_uiter);
while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
struct btrfs_qgroup_list *glist;
qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
if (qg->refcnt < seq)
qg->refcnt = seq + 1;
else
++qg->refcnt;
list_for_each_entry(glist, &qg->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
(u64)(uintptr_t)glist->group,
GFP_ATOMIC);
if (ret < 0)
goto unlock;
}
}
}
ret = qgroup_account_ref_step1(fs_info, roots, tmp, seq);
if (ret)
goto unlock;
/*
* step 2: walk from the new root
*/
ulist_reinit(tmp);
ret = ulist_add(tmp, qgroup->qgroupid,
(uintptr_t)qgroup, GFP_ATOMIC);
if (ret < 0)
ret = qgroup_account_ref_step2(fs_info, roots, tmp, seq, sgn,
node->num_bytes, qgroup);
if (ret)
goto unlock;
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(tmp, &uiter))) {
struct btrfs_qgroup *qg;
struct btrfs_qgroup_list *glist;
qg = (struct btrfs_qgroup *)(uintptr_t)unode->aux;
if (qg->refcnt < seq) {
/* not visited by step 1 */
qg->rfer += sgn * node->num_bytes;
qg->rfer_cmpr += sgn * node->num_bytes;
if (roots->nnodes == 0) {
qg->excl += sgn * node->num_bytes;
qg->excl_cmpr += sgn * node->num_bytes;
}
qgroup_dirty(fs_info, qg);
}
WARN_ON(qg->tag >= seq);
qg->tag = seq;
list_for_each_entry(glist, &qg->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
(uintptr_t)glist->group, GFP_ATOMIC);
if (ret < 0)
goto unlock;
}
}
/*
* step 3: walk again from old refs
*/
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(roots, &uiter))) {
struct btrfs_qgroup *qg;
struct ulist_node *tmp_unode;
struct ulist_iterator tmp_uiter;
ret = qgroup_account_ref_step3(fs_info, roots, tmp, seq, sgn,
node->num_bytes);
if (ret)
goto unlock;
qg = find_qgroup_rb(fs_info, unode->val);
if (!qg)
continue;
ulist_reinit(tmp);
ret = ulist_add(tmp, qg->qgroupid,
(uintptr_t)qg, GFP_ATOMIC);
if (ret < 0)
goto unlock;
ULIST_ITER_INIT(&tmp_uiter);
while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
struct btrfs_qgroup_list *glist;
qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
if (qg->tag == seq)
continue;
if (qg->refcnt - seq == roots->nnodes) {
qg->excl -= sgn * node->num_bytes;
qg->excl_cmpr -= sgn * node->num_bytes;
qgroup_dirty(fs_info, qg);
}
list_for_each_entry(glist, &qg->groups, next_group) {
ret = ulist_add(tmp, glist->group->qgroupid,
(uintptr_t)glist->group,
GFP_ATOMIC);
if (ret < 0)
goto unlock;
}
}
}
ret = 0;
unlock:
spin_unlock(&fs_info->qgroup_lock);
ulist_free(roots);