+static int account_one_extent(struct ulist *roots, u64 bytenr, u64 num_bytes)
+{
+ int ret;
+ u64 id, nr_roots, nr_refs;
+ struct qgroup_count *count;
+ struct ulist *counts = ulist_alloc(0);
+ struct ulist *tmp = ulist_alloc(0);
+ struct ulist_iterator uiter;
+ struct ulist_iterator tmp_uiter;
+ struct ulist_node *unode;
+ struct ulist_node *tmp_unode;
+ struct btrfs_qgroup_list *glist;
+
+ if (!counts || !tmp) {
+ ulist_free(counts);
+ ulist_free(tmp);
+ return ENOMEM;
+ }
+
+ ULIST_ITER_INIT(&uiter);
+ while ((unode = ulist_next(roots, &uiter))) {
+ BUG_ON(unode->val == 0ULL);
+
+ /*
+ * For each root, find their corresponding tracking group and
+ * add it to our qgroups list.
+ */
+ count = find_count(unode->val);
+ if (!count)
+ continue;
+
+ BUG_ON(!is_fstree(unode->val));
+ ret = ulist_add(counts, count->qgroupid, ptr_to_u64(count), 0);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Now we look for parents (and parents of those...). Use a tmp
+ * ulist here to avoid re-walking (and re-incrementing) our
+ * already added items on every loop iteration.
+ */
+ ulist_reinit(tmp);
+ ret = ulist_add(tmp, count->qgroupid, ptr_to_u64(count), 0);
+ if (ret < 0)
+ goto out;
+
+ ULIST_ITER_INIT(&tmp_uiter);
+ while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
+ /* Bump the refcount on a node every time we see it. */
+ count = u64_to_ptr(tmp_unode->aux);
+ update_cur_refcnt(count);
+
+ list_for_each_entry(glist, &count->groups, next_group) {
+ struct qgroup_count *parent;
+ parent = glist->group;
+ id = parent->qgroupid;
+
+ BUG_ON(!count);
+
+ ret = ulist_add(counts, id, ptr_to_u64(parent),
+ 0);
+ if (ret < 0)
+ goto out;
+ ret = ulist_add(tmp, id, ptr_to_u64(parent),
+ 0);
+ if (ret < 0)
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * Now that we have gathered up and counted all the groups, we
+ * can add bytes for this ref.
+ */
+ nr_roots = roots->nnodes;
+ ULIST_ITER_INIT(&uiter);
+ while ((unode = ulist_next(counts, &uiter))) {
+ count = u64_to_ptr(unode->aux);
+
+ nr_refs = group_get_cur_refcnt(count);
+ if (nr_refs) {
+ count->info.referenced += num_bytes;
+ count->info.referenced_compressed += num_bytes;
+
+ if (nr_refs == nr_roots) {
+ count->info.exclusive += num_bytes;
+ count->info.exclusive_compressed += num_bytes;
+ }
+ }
+#ifdef QGROUP_VERIFY_DEBUG
+ printf("account (%llu, %llu), qgroup %llu/%llu, rfer %llu,"
+ " excl %llu, refs %llu, roots %llu\n", bytenr, num_bytes,
+ btrfs_qgroup_level(count->qgroupid),
+ btrfs_qgroup_subvid(count->qgroupid),
+ count->info.referenced, count->info.exclusive, nr_refs,
+ nr_roots);
+#endif
+ }
+
+ inc_qgroup_seq(roots->nnodes);
+ ret = 0;
+out:
+ ulist_free(counts);
+ ulist_free(tmp);
+ return ret;
+}
+