|
@@ -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;
|
|
|
-
|
|
|
- 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);
|
|
|
- }
|
|
|
+ ret = qgroup_account_ref_step3(fs_info, roots, tmp, seq, sgn,
|
|
|
+ node->num_bytes);
|
|
|
+ if (ret)
|
|
|
+ goto unlock;
|
|
|
|
|
|
- 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);
|