btrfs: qgroup: Add new qgroup calculation function
authorQu Wenruo <quwenruo@cn.fujitsu.com>
Thu, 16 Apr 2015 07:37:33 +0000 (15:37 +0800)
committerChris Mason <clm@fb.com>
Wed, 10 Jun 2015 16:25:49 +0000 (09:25 -0700)
btrfs_qgroup_account_extents().

The new btrfs_qgroup_account_extents() function should be called in
btrfs_commit_transaction() and it will update all the qgroup according
to delayed_ref_root->dirty_extent_root.

The new function can handle both normal operation during
commit_transaction() or in rescan in a unified method with clearer
logic.

Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/extent-tree.h [new file with mode: 0644]
fs/btrfs/qgroup.c
fs/btrfs/qgroup.h

diff --git a/fs/btrfs/extent-tree.h b/fs/btrfs/extent-tree.h
new file mode 100644 (file)
index 0000000..e69de29
index 7b18fff558ca22b90c14aa626f1ac0cbaff1555e..607c6ca349559acab9df20952fc3202a70a26aac 100644 (file)
@@ -2455,6 +2455,122 @@ static int btrfs_qgroup_account(struct btrfs_trans_handle *trans,
        return ret;
 }
 
+static int
+btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans,
+                           struct btrfs_fs_info *fs_info,
+                           u64 bytenr, u64 num_bytes,
+                           struct ulist *old_roots, struct ulist *new_roots)
+{
+       struct ulist *qgroups = NULL;
+       struct ulist *tmp = NULL;
+       u64 seq;
+       u64 nr_new_roots = 0;
+       u64 nr_old_roots = 0;
+       int ret = 0;
+
+       if (new_roots)
+               nr_new_roots = new_roots->nnodes;
+       if (old_roots)
+               nr_old_roots = old_roots->nnodes;
+
+       if (!fs_info->quota_enabled)
+               goto out_free;
+       BUG_ON(!fs_info->quota_root);
+
+       qgroups = ulist_alloc(GFP_NOFS);
+       if (!qgroups) {
+               ret = -ENOMEM;
+               goto out_free;
+       }
+       tmp = ulist_alloc(GFP_NOFS);
+       if (!tmp) {
+               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) {
+                       mutex_unlock(&fs_info->qgroup_rescan_lock);
+                       ret = 0;
+                       goto out_free;
+               }
+       }
+       mutex_unlock(&fs_info->qgroup_rescan_lock);
+
+       spin_lock(&fs_info->qgroup_lock);
+       seq = fs_info->qgroup_seq;
+
+       /* Update old refcnts using old_roots */
+       ret = qgroup_update_refcnt(fs_info, old_roots, tmp, qgroups, seq,
+                                  UPDATE_OLD);
+       if (ret < 0)
+               goto out;
+
+       /* Update new refcnts using new_roots */
+       ret = qgroup_update_refcnt(fs_info, new_roots, tmp, qgroups, seq,
+                                  UPDATE_NEW);
+       if (ret < 0)
+               goto out;
+
+       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(tmp);
+       ulist_free(qgroups);
+       ulist_free(old_roots);
+       ulist_free(new_roots);
+       return ret;
+}
+
+int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans,
+                                struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_qgroup_extent_record *record;
+       struct btrfs_delayed_ref_root *delayed_refs;
+       struct ulist *new_roots = NULL;
+       struct rb_node *node;
+       int ret = 0;
+
+       delayed_refs = &trans->transaction->delayed_refs;
+       while ((node = rb_first(&delayed_refs->dirty_extent_root))) {
+               record = rb_entry(node, struct btrfs_qgroup_extent_record,
+                                 node);
+
+               if (!ret) {
+                       /*
+                        * Use (u64)-1 as time_seq to do special search, which
+                        * doesn't lock tree or delayed_refs and search current
+                        * root. It's safe inside commit_transaction().
+                        */
+                       ret = btrfs_find_all_roots(trans, fs_info,
+                                       record->bytenr, (u64)-1, &new_roots);
+                       if (ret < 0)
+                               goto cleanup;
+                       ret = btrfs_qgroup_account_extent(trans, fs_info,
+                                       record->bytenr, record->num_bytes,
+                                       record->old_roots, new_roots);
+                       record->old_roots = NULL;
+                       new_roots = NULL;
+               }
+cleanup:
+               ulist_free(record->old_roots);
+               ulist_free(new_roots);
+               new_roots = NULL;
+               rb_erase(node, &delayed_refs->dirty_extent_root);
+               kfree(record);
+
+       }
+       return ret;
+}
+
 /*
  * Needs to be called everytime we run delayed refs, even if there is an error
  * in order to cleanup outstanding operations.
index 6fe249f078ac7520e1d57278707d446365e3da38..cb703f859af6339a31cbfad39b15fd8a70d000bc 100644 (file)
@@ -103,6 +103,8 @@ int btrfs_qgroup_prepare_account_extents(struct btrfs_trans_handle *trans,
 struct btrfs_qgroup_extent_record
 *btrfs_qgroup_insert_dirty_extent(struct btrfs_delayed_ref_root *delayed_refs,
                                  struct btrfs_qgroup_extent_record *record);
+int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans,
+                                struct btrfs_fs_info *fs_info);
 int btrfs_delayed_qgroup_accounting(struct btrfs_trans_handle *trans,
                                    struct btrfs_fs_info *fs_info);
 void btrfs_remove_qgroup_operation(struct btrfs_trans_handle *trans,