btrfs: qgroup: use qgroup_iterator_nested to in qgroup_update_refcnt()
authorQu Wenruo <wqu@suse.com>
Sat, 2 Sep 2023 00:13:57 +0000 (08:13 +0800)
committerDavid Sterba <dsterba@suse.com>
Thu, 12 Oct 2023 14:44:03 +0000 (16:44 +0200)
The ulist @qgroups is utilized to record all involved qgroups from both
old and new roots inside btrfs_qgroup_account_extent().

Due to the fact that qgroup_update_refcnt() itself is already utilizing
qgroup_iterator, here we have to introduce another list_head,
btrfs_qgroup::nested_iterator, allowing nested iteration.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/qgroup.c
fs/btrfs/qgroup.h

index 9645becee73b489c9d3f67766353268727bcbdd3..cfe2a6ed622900620efbdc0ee38ac365bb4e3a6c 100644 (file)
@@ -209,6 +209,7 @@ static struct btrfs_qgroup *add_qgroup_rb(struct btrfs_fs_info *fs_info,
        INIT_LIST_HEAD(&qgroup->members);
        INIT_LIST_HEAD(&qgroup->dirty);
        INIT_LIST_HEAD(&qgroup->iterator);
+       INIT_LIST_HEAD(&qgroup->nested_iterator);
 
        rb_link_node(&qgroup->node, parent, p);
        rb_insert_color(&qgroup->node, &fs_info->qgroup_tree);
@@ -2417,22 +2418,39 @@ out:
        return ret;
 }
 
+static void qgroup_iterator_nested_add(struct list_head *head, struct btrfs_qgroup *qgroup)
+{
+       if (!list_empty(&qgroup->nested_iterator))
+               return;
+
+       list_add_tail(&qgroup->nested_iterator, head);
+}
+
+static void qgroup_iterator_nested_clean(struct list_head *head)
+{
+       while (!list_empty(head)) {
+               struct btrfs_qgroup *qgroup;
+
+               qgroup = list_first_entry(head, struct btrfs_qgroup, nested_iterator);
+               list_del_init(&qgroup->nested_iterator);
+       }
+}
+
 #define UPDATE_NEW     0
 #define UPDATE_OLD     1
 /*
  * Walk all of the roots that points to the bytenr and adjust their refcnts.
  */
-static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
-                               struct ulist *roots, struct ulist *qgroups,
-                               u64 seq, int update_old)
+static void qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
+                                struct ulist *roots, struct list_head *qgroups,
+                                u64 seq, int update_old)
 {
        struct ulist_node *unode;
        struct ulist_iterator uiter;
        struct btrfs_qgroup *qg;
-       int ret = 0;
 
        if (!roots)
-               return 0;
+               return;
        ULIST_ITER_INIT(&uiter);
        while ((unode = ulist_next(roots, &uiter))) {
                LIST_HEAD(tmp);
@@ -2441,10 +2459,7 @@ static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
                if (!qg)
                        continue;
 
-               ret = ulist_add(qgroups, qg->qgroupid, qgroup_to_aux(qg),
-                               GFP_ATOMIC);
-               if (ret < 0)
-                       return ret;
+               qgroup_iterator_nested_add(qgroups, qg);
                qgroup_iterator_add(&tmp, qg);
                list_for_each_entry(qg, &tmp, iterator) {
                        struct btrfs_qgroup_list *glist;
@@ -2455,17 +2470,12 @@ static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
                                btrfs_qgroup_update_new_refcnt(qg, seq, 1);
 
                        list_for_each_entry(glist, &qg->groups, next_group) {
-                               ret = ulist_add(qgroups, glist->group->qgroupid,
-                                               qgroup_to_aux(glist->group),
-                                               GFP_ATOMIC);
-                               if (ret < 0)
-                                       return ret;
+                               qgroup_iterator_nested_add(qgroups, glist->group);
                                qgroup_iterator_add(&tmp, glist->group);
                        }
                }
                qgroup_iterator_clean(&tmp);
        }
-       return 0;
 }
 
 /*
@@ -2504,22 +2514,16 @@ static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
  * But this time we don't need to consider other things, the codes and logic
  * is easy to understand now.
  */
-static int qgroup_update_counters(struct btrfs_fs_info *fs_info,
-                                 struct ulist *qgroups,
-                                 u64 nr_old_roots,
-                                 u64 nr_new_roots,
-                                 u64 num_bytes, u64 seq)
+static void qgroup_update_counters(struct btrfs_fs_info *fs_info,
+                                  struct list_head *qgroups, u64 nr_old_roots,
+                                  u64 nr_new_roots, u64 num_bytes, u64 seq)
 {
-       struct ulist_node *unode;
-       struct ulist_iterator uiter;
        struct btrfs_qgroup *qg;
-       u64 cur_new_count, cur_old_count;
 
-       ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(qgroups, &uiter))) {
+       list_for_each_entry(qg, qgroups, nested_iterator) {
+               u64 cur_new_count, cur_old_count;
                bool dirty = false;
 
-               qg = unode_aux_to_qgroup(unode);
                cur_old_count = btrfs_qgroup_get_old_refcnt(qg, seq);
                cur_new_count = btrfs_qgroup_get_new_refcnt(qg, seq);
 
@@ -2590,7 +2594,6 @@ static int qgroup_update_counters(struct btrfs_fs_info *fs_info,
                if (dirty)
                        qgroup_dirty(fs_info, qg);
        }
-       return 0;
 }
 
 /*
@@ -2627,7 +2630,7 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
                                struct ulist *new_roots)
 {
        struct btrfs_fs_info *fs_info = trans->fs_info;
-       struct ulist *qgroups = NULL;
+       LIST_HEAD(qgroups);
        u64 seq;
        u64 nr_new_roots = 0;
        u64 nr_old_roots = 0;
@@ -2661,11 +2664,6 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
        trace_btrfs_qgroup_account_extent(fs_info, trans->transid, bytenr,
                                        num_bytes, nr_old_roots, nr_new_roots);
 
-       qgroups = ulist_alloc(GFP_NOFS);
-       if (!qgroups) {
-               ret = -ENOMEM;
-               goto out_free;
-       }
        mutex_lock(&fs_info->qgroup_rescan_lock);
        if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
                if (fs_info->qgroup_rescan_progress.objectid <= bytenr) {
@@ -2680,26 +2678,21 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
        seq = fs_info->qgroup_seq;
 
        /* Update old refcnts using old_roots */
-       ret = qgroup_update_refcnt(fs_info, old_roots, qgroups, seq, UPDATE_OLD);
-       if (ret < 0)
-               goto out;
+       qgroup_update_refcnt(fs_info, old_roots, &qgroups, seq, UPDATE_OLD);
 
        /* Update new refcnts using new_roots */
-       ret = qgroup_update_refcnt(fs_info, new_roots, qgroups, seq, UPDATE_NEW);
-       if (ret < 0)
-               goto out;
+       qgroup_update_refcnt(fs_info, new_roots, &qgroups, seq, UPDATE_NEW);
 
-       qgroup_update_counters(fs_info, qgroups, nr_old_roots, nr_new_roots,
+       qgroup_update_counters(fs_info, &qgroups, nr_old_roots, nr_new_roots,
                               num_bytes, seq);
 
        /*
         * Bump qgroup_seq to avoid seq overlap
         */
        fs_info->qgroup_seq += max(nr_old_roots, nr_new_roots) + 1;
-out:
        spin_unlock(&fs_info->qgroup_lock);
 out_free:
-       ulist_free(qgroups);
+       qgroup_iterator_nested_clean(&qgroups);
        ulist_free(old_roots);
        ulist_free(new_roots);
        return ret;
index 6a43618580b6bf3ca5ba1324199220e8e2647b02..b4416d5be47d0ab09035fe3c7fa631e2829a6b65 100644 (file)
@@ -229,6 +229,24 @@ struct btrfs_qgroup {
         * finished.
         */
        struct list_head iterator;
+
+       /*
+        * For nested iterator usage.
+        *
+        * Here we support at most one level of nested iterator calls like:
+        *
+        *      LIST_HEAD(all_qgroups);
+        *      {
+        *              LIST_HEAD(local_qgroups);
+        *              qgroup_iterator_add(local_qgroups, qg);
+        *              qgroup_iterator_nested_add(all_qgroups, qg);
+        *              do_some_work(local_qgroups);
+        *              qgroup_iterator_clean(local_qgroups);
+        *      }
+        *      do_some_work(all_qgroups);
+        *      qgroup_iterator_nested_clean(all_qgroups);
+        */
+       struct list_head nested_iterator;
        struct rb_node node;      /* tree of qgroups */
 
        /*