btrfs: qgroup: simple quota auto hierarchy for nested subvolumes
[linux-2.6-microblaze.git] / fs / btrfs / qgroup.c
index 2637d6b..5b3ad2f 100644 (file)
 #include "root-tree.h"
 #include "tree-checker.h"
 
+enum btrfs_qgroup_mode btrfs_qgroup_mode(struct btrfs_fs_info *fs_info)
+{
+       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+               return BTRFS_QGROUP_MODE_DISABLED;
+       if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE)
+               return BTRFS_QGROUP_MODE_SIMPLE;
+       return BTRFS_QGROUP_MODE_FULL;
+}
+
+bool btrfs_qgroup_enabled(struct btrfs_fs_info *fs_info)
+{
+       return btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_DISABLED;
+}
+
+bool btrfs_qgroup_full_accounting(struct btrfs_fs_info *fs_info)
+{
+       return btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_FULL;
+}
+
 /*
  * Helpers to access qgroup reservation
  *
@@ -146,16 +165,6 @@ struct btrfs_qgroup_list {
        struct btrfs_qgroup *member;
 };
 
-static inline u64 qgroup_to_aux(struct btrfs_qgroup *qg)
-{
-       return (u64)(uintptr_t)qg;
-}
-
-static inline struct btrfs_qgroup* unode_aux_to_qgroup(struct ulist_node *n)
-{
-       return (struct btrfs_qgroup *)(uintptr_t)n->aux;
-}
-
 static int
 qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
                   int init_flags);
@@ -180,34 +189,46 @@ static struct btrfs_qgroup *find_qgroup_rb(struct btrfs_fs_info *fs_info,
        return NULL;
 }
 
-/* must be called with qgroup_lock held */
+/*
+ * Add qgroup to the filesystem's qgroup tree.
+ *
+ * Must be called with qgroup_lock held and @prealloc preallocated.
+ *
+ * The control on the lifespan of @prealloc would be transfered to this
+ * function, thus caller should no longer touch @prealloc.
+ */
 static struct btrfs_qgroup *add_qgroup_rb(struct btrfs_fs_info *fs_info,
+                                         struct btrfs_qgroup *prealloc,
                                          u64 qgroupid)
 {
        struct rb_node **p = &fs_info->qgroup_tree.rb_node;
        struct rb_node *parent = NULL;
        struct btrfs_qgroup *qgroup;
 
+       /* Caller must have pre-allocated @prealloc. */
+       ASSERT(prealloc);
+
        while (*p) {
                parent = *p;
                qgroup = rb_entry(parent, struct btrfs_qgroup, node);
 
-               if (qgroup->qgroupid < qgroupid)
+               if (qgroup->qgroupid < qgroupid) {
                        p = &(*p)->rb_left;
-               else if (qgroup->qgroupid > qgroupid)
+               } else if (qgroup->qgroupid > qgroupid) {
                        p = &(*p)->rb_right;
-               else
+               } else {
+                       kfree(prealloc);
                        return qgroup;
+               }
        }
 
-       qgroup = kzalloc(sizeof(*qgroup), GFP_ATOMIC);
-       if (!qgroup)
-               return ERR_PTR(-ENOMEM);
-
+       qgroup = prealloc;
        qgroup->qgroupid = qgroupid;
        INIT_LIST_HEAD(&qgroup->groups);
        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);
@@ -254,27 +275,26 @@ static int del_qgroup_rb(struct btrfs_fs_info *fs_info, u64 qgroupid)
 /*
  * Add relation specified by two qgroups.
  *
- * Must be called with qgroup_lock held.
+ * Must be called with qgroup_lock held, the ownership of @prealloc is
+ * transferred to this function and caller should not touch it anymore.
  *
  * Return: 0        on success
  *         -ENOENT  if one of the qgroups is NULL
  *         <0       other errors
  */
-static int __add_relation_rb(struct btrfs_qgroup *member, struct btrfs_qgroup *parent)
+static int __add_relation_rb(struct btrfs_qgroup_list *prealloc,
+                            struct btrfs_qgroup *member,
+                            struct btrfs_qgroup *parent)
 {
-       struct btrfs_qgroup_list *list;
-
-       if (!member || !parent)
+       if (!member || !parent) {
+               kfree(prealloc);
                return -ENOENT;
+       }
 
-       list = kzalloc(sizeof(*list), GFP_ATOMIC);
-       if (!list)
-               return -ENOMEM;
-
-       list->group = parent;
-       list->member = member;
-       list_add_tail(&list->next_group, &member->groups);
-       list_add_tail(&list->next_member, &parent->members);
+       prealloc->group = parent;
+       prealloc->member = member;
+       list_add_tail(&prealloc->next_group, &member->groups);
+       list_add_tail(&prealloc->next_member, &parent->members);
 
        return 0;
 }
@@ -288,7 +308,9 @@ static int __add_relation_rb(struct btrfs_qgroup *member, struct btrfs_qgroup *p
  *         -ENOENT  if one of the ids does not exist
  *         <0       other errors
  */
-static int add_relation_rb(struct btrfs_fs_info *fs_info, u64 memberid, u64 parentid)
+static int add_relation_rb(struct btrfs_fs_info *fs_info,
+                          struct btrfs_qgroup_list *prealloc,
+                          u64 memberid, u64 parentid)
 {
        struct btrfs_qgroup *member;
        struct btrfs_qgroup *parent;
@@ -296,7 +318,7 @@ static int add_relation_rb(struct btrfs_fs_info *fs_info, u64 memberid, u64 pare
        member = find_qgroup_rb(fs_info, memberid);
        parent = find_qgroup_rb(fs_info, parentid);
 
-       return __add_relation_rb(member, parent);
+       return __add_relation_rb(prealloc, member, parent);
 }
 
 /* Must be called with qgroup_lock held */
@@ -340,6 +362,8 @@ int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid,
 
 static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info)
 {
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
+               return;
        fs_info->qgroup_flags |= (BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT |
                                  BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN |
                                  BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING);
@@ -360,8 +384,9 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
        int ret = 0;
        u64 flags = 0;
        u64 rescan_progress = 0;
+       bool simple;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED)
                return 0;
 
        fs_info->qgroup_ulist = ulist_alloc(GFP_KERNEL);
@@ -411,14 +436,14 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
                                 "old qgroup version, quota disabled");
                                goto out;
                        }
+                       fs_info->qgroup_flags = btrfs_qgroup_status_flags(l, ptr);
+                       simple = (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE);
                        if (btrfs_qgroup_status_generation(l, ptr) !=
-                           fs_info->generation) {
+                           fs_info->generation && !simple) {
                                qgroup_mark_inconsistent(fs_info);
                                btrfs_err(fs_info,
                                        "qgroup generation mismatch, marked as inconsistent");
                        }
-                       fs_info->qgroup_flags = btrfs_qgroup_status_flags(l,
-                                                                         ptr);
                        rescan_progress = btrfs_qgroup_status_rescan(l, ptr);
                        goto next1;
                }
@@ -434,11 +459,14 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
                        qgroup_mark_inconsistent(fs_info);
                }
                if (!qgroup) {
-                       qgroup = add_qgroup_rb(fs_info, found_key.offset);
-                       if (IS_ERR(qgroup)) {
-                               ret = PTR_ERR(qgroup);
+                       struct btrfs_qgroup *prealloc;
+
+                       prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL);
+                       if (!prealloc) {
+                               ret = -ENOMEM;
                                goto out;
                        }
+                       qgroup = add_qgroup_rb(fs_info, prealloc, found_key.offset);
                }
                ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup);
                if (ret < 0)
@@ -489,6 +517,8 @@ next1:
        if (ret)
                goto out;
        while (1) {
+               struct btrfs_qgroup_list *list = NULL;
+
                slot = path->slots[0];
                l = path->nodes[0];
                btrfs_item_key_to_cpu(l, &found_key, slot);
@@ -502,8 +532,14 @@ next1:
                        goto next2;
                }
 
-               ret = add_relation_rb(fs_info, found_key.objectid,
+               list = kzalloc(sizeof(*list), GFP_KERNEL);
+               if (!list) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ret = add_relation_rb(fs_info, list, found_key.objectid,
                                      found_key.offset);
+               list = NULL;
                if (ret == -ENOENT) {
                        btrfs_warn(fs_info,
                                "orphan qgroup relation 0x%llx->0x%llx",
@@ -550,7 +586,7 @@ bool btrfs_check_quota_leak(struct btrfs_fs_info *fs_info)
        struct rb_node *node;
        bool ret = false;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED)
                return ret;
        /*
         * Since we're unmounting, there is no race and no need to grab qgroup
@@ -622,7 +658,7 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
 
        ret = btrfs_insert_empty_item(trans, quota_root, path, &key, 0);
 
-       btrfs_mark_buffer_dirty(path->nodes[0]);
+       btrfs_mark_buffer_dirty(trans, path->nodes[0]);
 
        btrfs_free_path(path);
        return ret;
@@ -700,7 +736,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
        btrfs_set_qgroup_info_excl(leaf, qgroup_info, 0);
        btrfs_set_qgroup_info_excl_cmpr(leaf, qgroup_info, 0);
 
-       btrfs_mark_buffer_dirty(leaf);
+       btrfs_mark_buffer_dirty(trans, leaf);
 
        btrfs_release_path(path);
 
@@ -719,7 +755,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
        btrfs_set_qgroup_limit_rsv_rfer(leaf, qgroup_limit, 0);
        btrfs_set_qgroup_limit_rsv_excl(leaf, qgroup_limit, 0);
 
-       btrfs_mark_buffer_dirty(leaf);
+       btrfs_mark_buffer_dirty(trans, leaf);
 
        ret = 0;
 out:
@@ -808,7 +844,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans,
        btrfs_set_qgroup_limit_rsv_rfer(l, qgroup_limit, qgroup->rsv_rfer);
        btrfs_set_qgroup_limit_rsv_excl(l, qgroup_limit, qgroup->rsv_excl);
 
-       btrfs_mark_buffer_dirty(l);
+       btrfs_mark_buffer_dirty(trans, l);
 
 out:
        btrfs_free_path(path);
@@ -854,7 +890,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans,
        btrfs_set_qgroup_info_excl(l, qgroup_info, qgroup->excl);
        btrfs_set_qgroup_info_excl_cmpr(l, qgroup_info, qgroup->excl_cmpr);
 
-       btrfs_mark_buffer_dirty(l);
+       btrfs_mark_buffer_dirty(trans, l);
 
 out:
        btrfs_free_path(path);
@@ -896,7 +932,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans)
        btrfs_set_qgroup_status_rescan(l, ptr,
                                fs_info->qgroup_rescan_progress.objectid);
 
-       btrfs_mark_buffer_dirty(l);
+       btrfs_mark_buffer_dirty(trans, l);
 
 out:
        btrfs_free_path(path);
@@ -949,7 +985,8 @@ out:
        return ret;
 }
 
-int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
+int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
+                      struct btrfs_ioctl_quota_ctl_args *quota_ctl_args)
 {
        struct btrfs_root *quota_root;
        struct btrfs_root *tree_root = fs_info->tree_root;
@@ -959,8 +996,10 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
        struct btrfs_key key;
        struct btrfs_key found_key;
        struct btrfs_qgroup *qgroup = NULL;
+       struct btrfs_qgroup *prealloc = NULL;
        struct btrfs_trans_handle *trans = NULL;
        struct ulist *ulist = NULL;
+       const bool simple = (quota_ctl_args->cmd == BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA);
        int ret = 0;
        int slot;
 
@@ -1063,13 +1102,16 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
                                 struct btrfs_qgroup_status_item);
        btrfs_set_qgroup_status_generation(leaf, ptr, trans->transid);
        btrfs_set_qgroup_status_version(leaf, ptr, BTRFS_QGROUP_STATUS_VERSION);
-       fs_info->qgroup_flags = BTRFS_QGROUP_STATUS_FLAG_ON |
-                               BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
+       fs_info->qgroup_flags = BTRFS_QGROUP_STATUS_FLAG_ON;
+       if (simple)
+               fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
+       else
+               fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
        btrfs_set_qgroup_status_flags(leaf, ptr, fs_info->qgroup_flags &
                                      BTRFS_QGROUP_STATUS_FLAGS_MASK);
        btrfs_set_qgroup_status_rescan(leaf, ptr, 0);
 
-       btrfs_mark_buffer_dirty(leaf);
+       btrfs_mark_buffer_dirty(trans, leaf);
 
        key.objectid = 0;
        key.type = BTRFS_ROOT_REF_KEY;
@@ -1094,6 +1136,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
                        /* Release locks on tree_root before we access quota_root */
                        btrfs_release_path(path);
 
+                       /* We should not have a stray @prealloc pointer. */
+                       ASSERT(prealloc == NULL);
+                       prealloc = kzalloc(sizeof(*prealloc), GFP_NOFS);
+                       if (!prealloc) {
+                               ret = -ENOMEM;
+                               btrfs_abort_transaction(trans, ret);
+                               goto out_free_path;
+                       }
+
                        ret = add_qgroup_item(trans, quota_root,
                                              found_key.offset);
                        if (ret) {
@@ -1101,7 +1152,8 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
                                goto out_free_path;
                        }
 
-                       qgroup = add_qgroup_rb(fs_info, found_key.offset);
+                       qgroup = add_qgroup_rb(fs_info, prealloc, found_key.offset);
+                       prealloc = NULL;
                        if (IS_ERR(qgroup)) {
                                ret = PTR_ERR(qgroup);
                                btrfs_abort_transaction(trans, ret);
@@ -1144,12 +1196,14 @@ out_add_root:
                goto out_free_path;
        }
 
-       qgroup = add_qgroup_rb(fs_info, BTRFS_FS_TREE_OBJECTID);
-       if (IS_ERR(qgroup)) {
-               ret = PTR_ERR(qgroup);
-               btrfs_abort_transaction(trans, ret);
+       ASSERT(prealloc == NULL);
+       prealloc = kzalloc(sizeof(*prealloc), GFP_NOFS);
+       if (!prealloc) {
+               ret = -ENOMEM;
                goto out_free_path;
        }
+       qgroup = add_qgroup_rb(fs_info, prealloc, BTRFS_FS_TREE_OBJECTID);
+       prealloc = NULL;
        ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup);
        if (ret < 0) {
                btrfs_abort_transaction(trans, ret);
@@ -1180,8 +1234,14 @@ out_add_root:
        spin_lock(&fs_info->qgroup_lock);
        fs_info->quota_root = quota_root;
        set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
+       if (simple)
+               btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA);
        spin_unlock(&fs_info->qgroup_lock);
 
+       /* Skip rescan for simple qgroups. */
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
+               goto out_free_path;
+
        ret = qgroup_rescan_init(fs_info, 0, 1);
        if (!ret) {
                qgroup_rescan_zero_tracking(fs_info);
@@ -1222,6 +1282,39 @@ out:
        else if (trans)
                ret = btrfs_end_transaction(trans);
        ulist_free(ulist);
+       kfree(prealloc);
+       return ret;
+}
+
+/*
+ * It is possible to have outstanding ordered extents which reserved bytes
+ * before we disabled. We need to fully flush delalloc, ordered extents, and a
+ * commit to ensure that we don't leak such reservations, only to have them
+ * come back if we re-enable.
+ *
+ * - enable simple quotas
+ * - reserve space
+ * - release it, store rsv_bytes in OE
+ * - disable quotas
+ * - enable simple quotas (qgroup rsv are all 0)
+ * - OE finishes
+ * - run delayed refs
+ * - free rsv_bytes, resulting in miscounting or even underflow
+ */
+static int flush_reservations(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_trans_handle *trans;
+       int ret;
+
+       ret = btrfs_start_delalloc_roots(fs_info, LONG_MAX, false);
+       if (ret)
+               return ret;
+       btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
+       trans = btrfs_join_transaction(fs_info->tree_root);
+       if (IS_ERR(trans))
+               return PTR_ERR(trans);
+       btrfs_commit_transaction(trans);
+
        return ret;
 }
 
@@ -1269,6 +1362,10 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
        clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
        btrfs_qgroup_wait_for_completion(fs_info, false);
 
+       ret = flush_reservations(fs_info);
+       if (ret)
+               goto out_unlock_cleaner;
+
        /*
         * 1 For the root item
         *
@@ -1295,6 +1392,7 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
        quota_root = fs_info->quota_root;
        fs_info->quota_root = NULL;
        fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON;
+       fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
        fs_info->qgroup_drop_subtree_thres = BTRFS_MAX_LEVEL;
        spin_unlock(&fs_info->qgroup_lock);
 
@@ -1329,7 +1427,8 @@ out:
        if (ret && trans)
                btrfs_end_transaction(trans);
        else if (trans)
-               ret = btrfs_end_transaction(trans);
+               ret = btrfs_commit_transaction(trans);
+out_unlock_cleaner:
        mutex_unlock(&fs_info->cleaner_mutex);
 
        return ret;
@@ -1342,6 +1441,24 @@ static void qgroup_dirty(struct btrfs_fs_info *fs_info,
                list_add(&qgroup->dirty, &fs_info->dirty_qgroups);
 }
 
+static void qgroup_iterator_add(struct list_head *head, struct btrfs_qgroup *qgroup)
+{
+       if (!list_empty(&qgroup->iterator))
+               return;
+
+       list_add_tail(&qgroup->iterator, head);
+}
+
+static void qgroup_iterator_clean(struct list_head *head)
+{
+       while (!list_empty(head)) {
+               struct btrfs_qgroup *qgroup;
+
+               qgroup = list_first_entry(head, struct btrfs_qgroup, iterator);
+               list_del_init(&qgroup->iterator);
+       }
+}
+
 /*
  * The easy accounting, we're updating qgroup relationship whose child qgroup
  * only has exclusive extents.
@@ -1356,14 +1473,12 @@ static void qgroup_dirty(struct btrfs_fs_info *fs_info,
  *
  * Caller should hold fs_info->qgroup_lock.
  */
-static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
-                                   struct ulist *tmp, u64 ref_root,
+static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info, u64 ref_root,
                                    struct btrfs_qgroup *src, int sign)
 {
        struct btrfs_qgroup *qgroup;
-       struct btrfs_qgroup_list *glist;
-       struct ulist_node *unode;
-       struct ulist_iterator uiter;
+       struct btrfs_qgroup *cur;
+       LIST_HEAD(qgroup_list);
        u64 num_bytes = src->excl;
        int ret = 0;
 
@@ -1371,53 +1486,30 @@ static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
        if (!qgroup)
                goto out;
 
-       qgroup->rfer += sign * num_bytes;
-       qgroup->rfer_cmpr += sign * num_bytes;
-
-       WARN_ON(sign < 0 && qgroup->excl < num_bytes);
-       qgroup->excl += sign * num_bytes;
-       qgroup->excl_cmpr += sign * num_bytes;
-
-       if (sign > 0)
-               qgroup_rsv_add_by_qgroup(fs_info, qgroup, src);
-       else
-               qgroup_rsv_release_by_qgroup(fs_info, qgroup, src);
-
-       qgroup_dirty(fs_info, qgroup);
-
-       /* Get all of the parent groups that contain this qgroup */
-       list_for_each_entry(glist, &qgroup->groups, next_group) {
-               ret = ulist_add(tmp, glist->group->qgroupid,
-                               qgroup_to_aux(glist->group), GFP_ATOMIC);
-               if (ret < 0)
-                       goto out;
-       }
+       qgroup_iterator_add(&qgroup_list, qgroup);
+       list_for_each_entry(cur, &qgroup_list, iterator) {
+               struct btrfs_qgroup_list *glist;
 
-       /* Iterate all of the parents and adjust their reference counts */
-       ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(tmp, &uiter))) {
-               qgroup = unode_aux_to_qgroup(unode);
                qgroup->rfer += sign * num_bytes;
                qgroup->rfer_cmpr += sign * num_bytes;
+
                WARN_ON(sign < 0 && qgroup->excl < num_bytes);
                qgroup->excl += sign * num_bytes;
+               qgroup->excl_cmpr += sign * num_bytes;
+
                if (sign > 0)
                        qgroup_rsv_add_by_qgroup(fs_info, qgroup, src);
                else
                        qgroup_rsv_release_by_qgroup(fs_info, qgroup, src);
-               qgroup->excl_cmpr += sign * num_bytes;
                qgroup_dirty(fs_info, qgroup);
 
-               /* Add any parents of the parents */
-               list_for_each_entry(glist, &qgroup->groups, next_group) {
-                       ret = ulist_add(tmp, glist->group->qgroupid,
-                                       qgroup_to_aux(glist->group), GFP_ATOMIC);
-                       if (ret < 0)
-                               goto out;
-               }
+               /* Append parent qgroups to @qgroup_list. */
+               list_for_each_entry(glist, &qgroup->groups, next_group)
+                       qgroup_iterator_add(&qgroup_list, glist->group);
        }
        ret = 0;
 out:
+       qgroup_iterator_clean(&qgroup_list);
        return ret;
 }
 
@@ -1434,8 +1526,7 @@ out:
  * Return < 0 for other error.
  */
 static int quick_update_accounting(struct btrfs_fs_info *fs_info,
-                                  struct ulist *tmp, u64 src, u64 dst,
-                                  int sign)
+                                  u64 src, u64 dst, int sign)
 {
        struct btrfs_qgroup *qgroup;
        int ret = 1;
@@ -1446,8 +1537,7 @@ static int quick_update_accounting(struct btrfs_fs_info *fs_info,
                goto out;
        if (qgroup->excl == qgroup->rfer) {
                ret = 0;
-               err = __qgroup_excl_accounting(fs_info, tmp, dst,
-                                              qgroup, sign);
+               err = __qgroup_excl_accounting(fs_info, dst, qgroup, sign);
                if (err < 0) {
                        ret = err;
                        goto out;
@@ -1459,28 +1549,19 @@ out:
        return ret;
 }
 
-int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
-                             u64 dst)
+int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst)
 {
        struct btrfs_fs_info *fs_info = trans->fs_info;
        struct btrfs_qgroup *parent;
        struct btrfs_qgroup *member;
        struct btrfs_qgroup_list *list;
-       struct ulist *tmp;
-       unsigned int nofs_flag;
+       struct btrfs_qgroup_list *prealloc = NULL;
        int ret = 0;
 
        /* Check the level of src and dst first */
        if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst))
                return -EINVAL;
 
-       /* We hold a transaction handle open, must do a NOFS allocation. */
-       nofs_flag = memalloc_nofs_save();
-       tmp = ulist_alloc(GFP_KERNEL);
-       memalloc_nofs_restore(nofs_flag);
-       if (!tmp)
-               return -ENOMEM;
-
        mutex_lock(&fs_info->qgroup_ioctl_lock);
        if (!fs_info->quota_root) {
                ret = -ENOTCONN;
@@ -1501,6 +1582,11 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
                }
        }
 
+       prealloc = kzalloc(sizeof(*list), GFP_NOFS);
+       if (!prealloc) {
+               ret = -ENOMEM;
+               goto out;
+       }
        ret = add_qgroup_relation_item(trans, src, dst);
        if (ret)
                goto out;
@@ -1512,16 +1598,17 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
        }
 
        spin_lock(&fs_info->qgroup_lock);
-       ret = __add_relation_rb(member, parent);
+       ret = __add_relation_rb(prealloc, member, parent);
+       prealloc = NULL;
        if (ret < 0) {
                spin_unlock(&fs_info->qgroup_lock);
                goto out;
        }
-       ret = quick_update_accounting(fs_info, tmp, src, dst, 1);
+       ret = quick_update_accounting(fs_info, src, dst, 1);
        spin_unlock(&fs_info->qgroup_lock);
 out:
+       kfree(prealloc);
        mutex_unlock(&fs_info->qgroup_ioctl_lock);
-       ulist_free(tmp);
        return ret;
 }
 
@@ -1532,19 +1619,10 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
        struct btrfs_qgroup *parent;
        struct btrfs_qgroup *member;
        struct btrfs_qgroup_list *list;
-       struct ulist *tmp;
        bool found = false;
-       unsigned int nofs_flag;
        int ret = 0;
        int ret2;
 
-       /* We hold a transaction handle open, must do a NOFS allocation. */
-       nofs_flag = memalloc_nofs_save();
-       tmp = ulist_alloc(GFP_KERNEL);
-       memalloc_nofs_restore(nofs_flag);
-       if (!tmp)
-               return -ENOMEM;
-
        if (!fs_info->quota_root) {
                ret = -ENOTCONN;
                goto out;
@@ -1582,11 +1660,10 @@ delete_item:
        if (found) {
                spin_lock(&fs_info->qgroup_lock);
                del_relation_rb(fs_info, src, dst);
-               ret = quick_update_accounting(fs_info, tmp, src, dst, -1);
+               ret = quick_update_accounting(fs_info, src, dst, -1);
                spin_unlock(&fs_info->qgroup_lock);
        }
 out:
-       ulist_free(tmp);
        return ret;
 }
 
@@ -1608,8 +1685,12 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
        struct btrfs_fs_info *fs_info = trans->fs_info;
        struct btrfs_root *quota_root;
        struct btrfs_qgroup *qgroup;
+       struct btrfs_qgroup *prealloc = NULL;
        int ret = 0;
 
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED)
+               return 0;
+
        mutex_lock(&fs_info->qgroup_ioctl_lock);
        if (!fs_info->quota_root) {
                ret = -ENOTCONN;
@@ -1622,21 +1703,25 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
                goto out;
        }
 
+       prealloc = kzalloc(sizeof(*prealloc), GFP_NOFS);
+       if (!prealloc) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
        ret = add_qgroup_item(trans, quota_root, qgroupid);
        if (ret)
                goto out;
 
        spin_lock(&fs_info->qgroup_lock);
-       qgroup = add_qgroup_rb(fs_info, qgroupid);
+       qgroup = add_qgroup_rb(fs_info, prealloc, qgroupid);
        spin_unlock(&fs_info->qgroup_lock);
+       prealloc = NULL;
 
-       if (IS_ERR(qgroup)) {
-               ret = PTR_ERR(qgroup);
-               goto out;
-       }
        ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup);
 out:
        mutex_unlock(&fs_info->qgroup_ioctl_lock);
+       kfree(prealloc);
        return ret;
 }
 
@@ -1771,6 +1856,17 @@ out:
        return ret;
 }
 
+/*
+ * Inform qgroup to trace one dirty extent, its info is recorded in @record.
+ * So qgroup can account it at transaction committing time.
+ *
+ * No lock version, caller must acquire delayed ref lock and allocated memory,
+ * then call btrfs_qgroup_trace_extent_post() after exiting lock context.
+ *
+ * Return 0 for success insert
+ * Return >0 for existing record, caller can free @record safely.
+ * Error is not possible
+ */
 int btrfs_qgroup_trace_extent_nolock(struct btrfs_fs_info *fs_info,
                                struct btrfs_delayed_ref_root *delayed_refs,
                                struct btrfs_qgroup_extent_record *record)
@@ -1780,6 +1876,9 @@ int btrfs_qgroup_trace_extent_nolock(struct btrfs_fs_info *fs_info,
        struct btrfs_qgroup_extent_record *entry;
        u64 bytenr = record->bytenr;
 
+       if (!btrfs_qgroup_full_accounting(fs_info))
+               return 0;
+
        lockdep_assert_held(&delayed_refs->lock);
        trace_btrfs_qgroup_trace_extent(fs_info, record);
 
@@ -1806,12 +1905,35 @@ int btrfs_qgroup_trace_extent_nolock(struct btrfs_fs_info *fs_info,
        return 0;
 }
 
+/*
+ * Post handler after qgroup_trace_extent_nolock().
+ *
+ * NOTE: Current qgroup does the expensive backref walk at transaction
+ * committing time with TRANS_STATE_COMMIT_DOING, this blocks incoming
+ * new transaction.
+ * This is designed to allow btrfs_find_all_roots() to get correct new_roots
+ * result.
+ *
+ * However for old_roots there is no need to do backref walk at that time,
+ * since we search commit roots to walk backref and result will always be
+ * correct.
+ *
+ * Due to the nature of no lock version, we can't do backref there.
+ * So we must call btrfs_qgroup_trace_extent_post() after exiting
+ * spinlock context.
+ *
+ * TODO: If we can fix and prove btrfs_find_all_roots() can get correct result
+ * using current root, then we can move all expensive backref walk out of
+ * transaction committing, but not now as qgroup accounting will be wrong again.
+ */
 int btrfs_qgroup_trace_extent_post(struct btrfs_trans_handle *trans,
                                   struct btrfs_qgroup_extent_record *qrecord)
 {
        struct btrfs_backref_walk_ctx ctx = { 0 };
        int ret;
 
+       if (!btrfs_qgroup_full_accounting(trans->fs_info))
+               return 0;
        /*
         * We are always called in a context where we are already holding a
         * transaction handle. Often we are called when adding a data delayed
@@ -1859,6 +1981,19 @@ int btrfs_qgroup_trace_extent_post(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+/*
+ * Inform qgroup to trace one dirty extent, specified by @bytenr and
+ * @num_bytes.
+ * So qgroup can account it at commit trans time.
+ *
+ * Better encapsulated version, with memory allocation and backref walk for
+ * commit roots.
+ * So this can sleep.
+ *
+ * Return 0 if the operation is done.
+ * Return <0 for error, like memory allocation failure or invalid parameter
+ * (NULL trans)
+ */
 int btrfs_qgroup_trace_extent(struct btrfs_trans_handle *trans, u64 bytenr,
                              u64 num_bytes)
 {
@@ -1867,8 +2002,7 @@ int btrfs_qgroup_trace_extent(struct btrfs_trans_handle *trans, u64 bytenr,
        struct btrfs_delayed_ref_root *delayed_refs;
        int ret;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)
-           || bytenr == 0 || num_bytes == 0)
+       if (!btrfs_qgroup_full_accounting(fs_info) || bytenr == 0 || num_bytes == 0)
                return 0;
        record = kzalloc(sizeof(*record), GFP_NOFS);
        if (!record)
@@ -1889,6 +2023,12 @@ int btrfs_qgroup_trace_extent(struct btrfs_trans_handle *trans, u64 bytenr,
        return btrfs_qgroup_trace_extent_post(trans, record);
 }
 
+/*
+ * Inform qgroup to trace all leaf items of data
+ *
+ * Return 0 for success
+ * Return <0 for error(ENOMEM)
+ */
 int btrfs_qgroup_trace_leaf_items(struct btrfs_trans_handle *trans,
                                  struct extent_buffer *eb)
 {
@@ -1900,7 +2040,7 @@ int btrfs_qgroup_trace_leaf_items(struct btrfs_trans_handle *trans,
        u64 bytenr, num_bytes;
 
        /* We can be called directly from walk_up_proc() */
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (!btrfs_qgroup_full_accounting(fs_info))
                return 0;
 
        for (i = 0; i < nr; i++) {
@@ -2276,7 +2416,7 @@ static int qgroup_trace_subtree_swap(struct btrfs_trans_handle *trans,
        int level;
        int ret;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (!btrfs_qgroup_full_accounting(fs_info))
                return 0;
 
        /* Wrong parameter order */
@@ -2319,6 +2459,16 @@ out:
        return ret;
 }
 
+/*
+ * Inform qgroup to trace a whole subtree, including all its child tree
+ * blocks and data.
+ * The root tree block is specified by @root_eb.
+ *
+ * Normally used by relocation(tree block swap) and subvolume deletion.
+ *
+ * Return 0 for success
+ * Return <0 for error(ENOMEM or tree search error)
+ */
 int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
                               struct extent_buffer *root_eb,
                               u64 root_gen, int root_level)
@@ -2333,7 +2483,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
        BUG_ON(root_level < 0 || root_level >= BTRFS_MAX_LEVEL);
        BUG_ON(root_eb == NULL);
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (!btrfs_qgroup_full_accounting(fs_info))
                return 0;
 
        spin_lock(&fs_info->qgroup_lock);
@@ -2445,62 +2595,64 @@ 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 *tmp,
-                               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 ulist_node *tmp_unode;
-       struct ulist_iterator tmp_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);
+
                qg = find_qgroup_rb(fs_info, unode->val);
                if (!qg)
                        continue;
 
-               ulist_reinit(tmp);
-               ret = ulist_add(qgroups, qg->qgroupid, qgroup_to_aux(qg),
-                               GFP_ATOMIC);
-               if (ret < 0)
-                       return ret;
-               ret = ulist_add(tmp, qg->qgroupid, qgroup_to_aux(qg), GFP_ATOMIC);
-               if (ret < 0)
-                       return ret;
-               ULIST_ITER_INIT(&tmp_uiter);
-               while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
+               qgroup_iterator_nested_add(qgroups, qg);
+               qgroup_iterator_add(&tmp, qg);
+               list_for_each_entry(qg, &tmp, iterator) {
                        struct btrfs_qgroup_list *glist;
 
-                       qg = unode_aux_to_qgroup(tmp_unode);
                        if (update_old)
                                btrfs_qgroup_update_old_refcnt(qg, seq, 1);
                        else
                                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;
-                               ret = ulist_add(tmp, 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;
 }
 
 /*
@@ -2539,22 +2691,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);
 
@@ -2625,7 +2771,6 @@ static int qgroup_update_counters(struct btrfs_fs_info *fs_info,
                if (dirty)
                        qgroup_dirty(fs_info, qg);
        }
-       return 0;
 }
 
 /*
@@ -2662,8 +2807,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;
-       struct ulist *tmp = NULL;
+       LIST_HEAD(qgroups);
        u64 seq;
        u64 nr_new_roots = 0;
        u64 nr_old_roots = 0;
@@ -2673,7 +2817,7 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
         * If quotas get disabled meanwhile, the resources need to be freed and
         * we can't just exit here.
         */
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) ||
+       if (!btrfs_qgroup_full_accounting(fs_info) ||
            fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING)
                goto out_free;
 
@@ -2697,17 +2841,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;
-       }
-       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) {
@@ -2722,29 +2855,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, tmp, 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, tmp, 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(tmp);
-       ulist_free(qgroups);
+       qgroup_iterator_nested_clean(&qgroups);
        ulist_free(old_roots);
        ulist_free(new_roots);
        return ret;
@@ -2761,6 +2886,9 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
        u64 qgroup_to_skip;
        int ret = 0;
 
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
+               return 0;
+
        delayed_refs = &trans->transaction->delayed_refs;
        qgroup_to_skip = delayed_refs->qgroup_to_skip;
        while ((node = rb_first(&delayed_refs->dirty_extent_root))) {
@@ -2876,7 +3004,7 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans)
                        qgroup_mark_inconsistent(fs_info);
                spin_lock(&fs_info->qgroup_lock);
        }
-       if (test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (btrfs_qgroup_enabled(fs_info))
                fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_ON;
        else
                fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON;
@@ -2889,6 +3017,47 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans)
        return ret;
 }
 
+static int qgroup_auto_inherit(struct btrfs_fs_info *fs_info,
+                              u64 inode_rootid,
+                              struct btrfs_qgroup_inherit **inherit)
+{
+       int i = 0;
+       u64 num_qgroups = 0;
+       struct btrfs_qgroup *inode_qg;
+       struct btrfs_qgroup_list *qg_list;
+       struct btrfs_qgroup_inherit *res;
+       size_t struct_sz;
+       u64 *qgids;
+
+       if (*inherit)
+               return -EEXIST;
+
+       inode_qg = find_qgroup_rb(fs_info, inode_rootid);
+       if (!inode_qg)
+               return -ENOENT;
+
+       num_qgroups = list_count_nodes(&inode_qg->groups);
+
+       if (!num_qgroups)
+               return 0;
+
+       struct_sz = struct_size(res, qgroups, num_qgroups);
+       if (struct_sz == SIZE_MAX)
+               return -ERANGE;
+
+       res = kzalloc(struct_sz, GFP_NOFS);
+       if (!res)
+               return -ENOMEM;
+       res->num_qgroups = num_qgroups;
+       qgids = res->qgroups;
+
+       list_for_each_entry(qg_list, &inode_qg->groups, next_group)
+               qgids[i] = qg_list->group->qgroupid;
+
+       *inherit = res;
+       return 0;
+}
+
 /*
  * Copy the accounting information between qgroups. This is necessary
  * when a snapshot or a subvolume is created. Throwing an error will
@@ -2896,7 +3065,8 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans)
  * when a readonly fs is a reasonable outcome.
  */
 int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
-                        u64 objectid, struct btrfs_qgroup_inherit *inherit)
+                        u64 objectid, u64 inode_rootid,
+                        struct btrfs_qgroup_inherit *inherit)
 {
        int ret = 0;
        int i;
@@ -2906,10 +3076,17 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
        struct btrfs_root *quota_root;
        struct btrfs_qgroup *srcgroup;
        struct btrfs_qgroup *dstgroup;
+       struct btrfs_qgroup *prealloc;
+       struct btrfs_qgroup_list **qlist_prealloc = NULL;
+       bool free_inherit = false;
        bool need_rescan = false;
        u32 level_size = 0;
        u64 nums;
 
+       prealloc = kzalloc(sizeof(*prealloc), GFP_NOFS);
+       if (!prealloc)
+               return -ENOMEM;
+
        /*
         * There are only two callers of this function.
         *
@@ -2929,7 +3106,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
 
        if (!committing)
                mutex_lock(&fs_info->qgroup_ioctl_lock);
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (!btrfs_qgroup_enabled(fs_info))
                goto out;
 
        quota_root = fs_info->quota_root;
@@ -2938,6 +3115,13 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
                goto out;
        }
 
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE && !inherit) {
+               ret = qgroup_auto_inherit(fs_info, inode_rootid, &inherit);
+               if (ret)
+                       goto out;
+               free_inherit = true;
+       }
+
        if (inherit) {
                i_qgroups = (u64 *)(inherit + 1);
                nums = inherit->num_qgroups + 2 * inherit->num_ref_copies +
@@ -2982,16 +3166,28 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
                                goto out;
                }
                ret = 0;
-       }
 
+               qlist_prealloc = kcalloc(inherit->num_qgroups,
+                                        sizeof(struct btrfs_qgroup_list *),
+                                        GFP_NOFS);
+               if (!qlist_prealloc) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               for (int i = 0; i < inherit->num_qgroups; i++) {
+                       qlist_prealloc[i] = kzalloc(sizeof(struct btrfs_qgroup_list),
+                                                   GFP_NOFS);
+                       if (!qlist_prealloc[i]) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+               }
+       }
 
        spin_lock(&fs_info->qgroup_lock);
 
-       dstgroup = add_qgroup_rb(fs_info, objectid);
-       if (IS_ERR(dstgroup)) {
-               ret = PTR_ERR(dstgroup);
-               goto unlock;
-       }
+       dstgroup = add_qgroup_rb(fs_info, prealloc, objectid);
+       prealloc = NULL;
 
        if (inherit && inherit->flags & BTRFS_QGROUP_INHERIT_SET_LIMITS) {
                dstgroup->lim_flags = inherit->lim.flags;
@@ -3003,7 +3199,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
                qgroup_dirty(fs_info, dstgroup);
        }
 
-       if (srcid) {
+       if (srcid && btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_FULL) {
                srcgroup = find_qgroup_rb(fs_info, srcid);
                if (!srcgroup)
                        goto unlock;
@@ -3038,7 +3234,9 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
        i_qgroups = (u64 *)(inherit + 1);
        for (i = 0; i < inherit->num_qgroups; ++i) {
                if (*i_qgroups) {
-                       ret = add_relation_rb(fs_info, objectid, *i_qgroups);
+                       ret = add_relation_rb(fs_info, qlist_prealloc[i], objectid,
+                                             *i_qgroups);
+                       qlist_prealloc[i] = NULL;
                        if (ret)
                                goto unlock;
                }
@@ -3102,6 +3300,14 @@ out:
                mutex_unlock(&fs_info->qgroup_ioctl_lock);
        if (need_rescan)
                qgroup_mark_inconsistent(fs_info);
+       if (qlist_prealloc) {
+               for (int i = 0; i < inherit->num_qgroups; i++)
+                       kfree(qlist_prealloc[i]);
+               kfree(qlist_prealloc);
+       }
+       if (free_inherit)
+               kfree(inherit);
+       kfree(prealloc);
        return ret;
 }
 
@@ -3125,8 +3331,7 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce,
        struct btrfs_fs_info *fs_info = root->fs_info;
        u64 ref_root = root->root_key.objectid;
        int ret = 0;
-       struct ulist_node *unode;
-       struct ulist_iterator uiter;
+       LIST_HEAD(qgroup_list);
 
        if (!is_fstree(ref_root))
                return 0;
@@ -3146,49 +3351,28 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce,
        if (!qgroup)
                goto out;
 
-       /*
-        * in a first step, we check all affected qgroups if any limits would
-        * be exceeded
-        */
-       ulist_reinit(fs_info->qgroup_ulist);
-       ret = ulist_add(fs_info->qgroup_ulist, qgroup->qgroupid,
-                       qgroup_to_aux(qgroup), GFP_ATOMIC);
-       if (ret < 0)
-               goto out;
-       ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
-               struct btrfs_qgroup *qg;
+       qgroup_iterator_add(&qgroup_list, qgroup);
+       list_for_each_entry(qgroup, &qgroup_list, iterator) {
                struct btrfs_qgroup_list *glist;
 
-               qg = unode_aux_to_qgroup(unode);
-
-               if (enforce && !qgroup_check_limits(qg, num_bytes)) {
+               if (enforce && !qgroup_check_limits(qgroup, num_bytes)) {
                        ret = -EDQUOT;
                        goto out;
                }
 
-               list_for_each_entry(glist, &qg->groups, next_group) {
-                       ret = ulist_add(fs_info->qgroup_ulist,
-                                       glist->group->qgroupid,
-                                       qgroup_to_aux(glist->group), GFP_ATOMIC);
-                       if (ret < 0)
-                               goto out;
-               }
+               list_for_each_entry(glist, &qgroup->groups, next_group)
+                       qgroup_iterator_add(&qgroup_list, glist->group);
        }
+
        ret = 0;
        /*
         * no limits exceeded, now record the reservation into all qgroups
         */
-       ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
-               struct btrfs_qgroup *qg;
-
-               qg = unode_aux_to_qgroup(unode);
-
-               qgroup_rsv_add(fs_info, qg, num_bytes, type);
-       }
+       list_for_each_entry(qgroup, &qgroup_list, iterator)
+               qgroup_rsv_add(fs_info, qgroup, num_bytes, type);
 
 out:
+       qgroup_iterator_clean(&qgroup_list);
        spin_unlock(&fs_info->qgroup_lock);
        return ret;
 }
@@ -3207,9 +3391,7 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info,
                               enum btrfs_qgroup_rsv_type type)
 {
        struct btrfs_qgroup *qgroup;
-       struct ulist_node *unode;
-       struct ulist_iterator uiter;
-       int ret = 0;
+       LIST_HEAD(qgroup_list);
 
        if (!is_fstree(ref_root))
                return;
@@ -3237,30 +3419,17 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info,
                 */
                num_bytes = qgroup->rsv.values[type];
 
-       ulist_reinit(fs_info->qgroup_ulist);
-       ret = ulist_add(fs_info->qgroup_ulist, qgroup->qgroupid,
-                       qgroup_to_aux(qgroup), GFP_ATOMIC);
-       if (ret < 0)
-               goto out;
-       ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
-               struct btrfs_qgroup *qg;
+       qgroup_iterator_add(&qgroup_list, qgroup);
+       list_for_each_entry(qgroup, &qgroup_list, iterator) {
                struct btrfs_qgroup_list *glist;
 
-               qg = unode_aux_to_qgroup(unode);
-
-               qgroup_rsv_release(fs_info, qg, num_bytes, type);
-
-               list_for_each_entry(glist, &qg->groups, next_group) {
-                       ret = ulist_add(fs_info->qgroup_ulist,
-                                       glist->group->qgroupid,
-                                       qgroup_to_aux(glist->group), GFP_ATOMIC);
-                       if (ret < 0)
-                               goto out;
+               qgroup_rsv_release(fs_info, qgroup, num_bytes, type);
+               list_for_each_entry(glist, &qgroup->groups, next_group) {
+                       qgroup_iterator_add(&qgroup_list, glist->group);
                }
        }
-
 out:
+       qgroup_iterator_clean(&qgroup_list);
        spin_unlock(&fs_info->qgroup_lock);
 }
 
@@ -3295,6 +3464,9 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
        int slot;
        int ret;
 
+       if (!btrfs_qgroup_full_accounting(fs_info))
+               return 1;
+
        mutex_lock(&fs_info->qgroup_rescan_lock);
        extent_root = btrfs_extent_root(fs_info,
                                fs_info->qgroup_rescan_progress.objectid);
@@ -3375,10 +3547,15 @@ out:
 
 static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
 {
-       return btrfs_fs_closing(fs_info) ||
-               test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state) ||
-               !test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) ||
-                         fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN;
+       if (btrfs_fs_closing(fs_info))
+               return true;
+       if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
+               return true;
+       if (!btrfs_qgroup_enabled(fs_info))
+               return true;
+       if (fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN)
+               return true;
+       return false;
 }
 
 static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
@@ -3392,6 +3569,9 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
        bool stopped = false;
        bool did_leaf_rescans = false;
 
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
+               return;
+
        path = btrfs_alloc_path();
        if (!path)
                goto out;
@@ -3495,6 +3675,11 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
 {
        int ret = 0;
 
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) {
+               btrfs_warn(fs_info, "qgroup rescan init failed, running in simple mode");
+               return -EINVAL;
+       }
+
        if (!init_flags) {
                /* we're resuming qgroup rescan at mount time */
                if (!(fs_info->qgroup_flags &
@@ -3525,7 +3710,7 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
                        btrfs_warn(fs_info,
                        "qgroup rescan init failed, qgroup is not enabled");
                        ret = -EINVAL;
-               } else if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) {
+               } else if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED) {
                        /* Quota disable is in progress */
                        ret = -EBUSY;
                }
@@ -3546,7 +3731,7 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
        mutex_unlock(&fs_info->qgroup_rescan_lock);
 
        btrfs_init_work(&fs_info->qgroup_rescan_work,
-                       btrfs_qgroup_rescan_worker, NULL, NULL);
+                       btrfs_qgroup_rescan_worker, NULL);
        return 0;
 }
 
@@ -3590,15 +3775,16 @@ btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
         * going to clear all tracking information for a clean start.
         */
 
-       trans = btrfs_join_transaction(fs_info->fs_root);
-       if (IS_ERR(trans)) {
+       trans = btrfs_attach_transaction_barrier(fs_info->fs_root);
+       if (IS_ERR(trans) && trans != ERR_PTR(-ENOENT)) {
                fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
                return PTR_ERR(trans);
-       }
-       ret = btrfs_commit_transaction(trans);
-       if (ret) {
-               fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
-               return ret;
+       } else if (trans != ERR_PTR(-ENOENT)) {
+               ret = btrfs_commit_transaction(trans);
+               if (ret) {
+                       fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+                       return ret;
+               }
        }
 
        qgroup_rescan_zero_tracking(fs_info);
@@ -3757,9 +3943,11 @@ static int try_flush_qgroup(struct btrfs_root *root)
                goto out;
        btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1);
 
-       trans = btrfs_join_transaction(root);
+       trans = btrfs_attach_transaction_barrier(root);
        if (IS_ERR(trans)) {
                ret = PTR_ERR(trans);
+               if (ret == -ENOENT)
+                       ret = 0;
                goto out;
        }
 
@@ -3781,7 +3969,7 @@ static int qgroup_reserve_data(struct btrfs_inode *inode,
        u64 to_reserve;
        int ret;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &root->fs_info->flags) ||
+       if (btrfs_qgroup_mode(root->fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
            !is_fstree(root->root_key.objectid) || len == 0)
                return 0;
 
@@ -3913,8 +4101,12 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
        int trace_op = QGROUP_RELEASE;
        int ret;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &inode->root->fs_info->flags))
-               return 0;
+       if (btrfs_qgroup_mode(inode->root->fs_info) == BTRFS_QGROUP_MODE_DISABLED) {
+               extent_changeset_init(&changeset);
+               return clear_record_extent_bits(&inode->io_tree, start,
+                                               start + len - 1,
+                                               EXTENT_QGROUP_RESERVED, &changeset);
+       }
 
        /* In release case, we shouldn't have @reserved */
        WARN_ON(!free && reserved);
@@ -4024,7 +4216,7 @@ int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
        struct btrfs_fs_info *fs_info = root->fs_info;
        int ret;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) ||
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
            !is_fstree(root->root_key.objectid) || num_bytes == 0)
                return 0;
 
@@ -4061,11 +4253,15 @@ int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
        return btrfs_qgroup_reserve_meta(root, num_bytes, type, enforce);
 }
 
+/*
+ * Per-transaction meta reservation should be all freed at transaction commit
+ * time
+ */
 void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) ||
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
            !is_fstree(root->root_key.objectid))
                return;
 
@@ -4081,7 +4277,7 @@ void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes,
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) ||
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
            !is_fstree(root->root_key.objectid))
                return;
 
@@ -4101,9 +4297,7 @@ static void qgroup_convert_meta(struct btrfs_fs_info *fs_info, u64 ref_root,
                                int num_bytes)
 {
        struct btrfs_qgroup *qgroup;
-       struct ulist_node *unode;
-       struct ulist_iterator uiter;
-       int ret = 0;
+       LIST_HEAD(qgroup_list);
 
        if (num_bytes == 0)
                return;
@@ -4114,39 +4308,35 @@ static void qgroup_convert_meta(struct btrfs_fs_info *fs_info, u64 ref_root,
        qgroup = find_qgroup_rb(fs_info, ref_root);
        if (!qgroup)
                goto out;
-       ulist_reinit(fs_info->qgroup_ulist);
-       ret = ulist_add(fs_info->qgroup_ulist, qgroup->qgroupid,
-                      qgroup_to_aux(qgroup), GFP_ATOMIC);
-       if (ret < 0)
-               goto out;
-       ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
-               struct btrfs_qgroup *qg;
-               struct btrfs_qgroup_list *glist;
 
-               qg = unode_aux_to_qgroup(unode);
+       qgroup_iterator_add(&qgroup_list, qgroup);
+       list_for_each_entry(qgroup, &qgroup_list, iterator) {
+               struct btrfs_qgroup_list *glist;
 
-               qgroup_rsv_release(fs_info, qg, num_bytes,
+               qgroup_rsv_release(fs_info, qgroup, num_bytes,
                                BTRFS_QGROUP_RSV_META_PREALLOC);
-               qgroup_rsv_add(fs_info, qg, num_bytes,
+               qgroup_rsv_add(fs_info, qgroup, num_bytes,
                                BTRFS_QGROUP_RSV_META_PERTRANS);
-               list_for_each_entry(glist, &qg->groups, next_group) {
-                       ret = ulist_add(fs_info->qgroup_ulist,
-                                       glist->group->qgroupid,
-                                       qgroup_to_aux(glist->group), GFP_ATOMIC);
-                       if (ret < 0)
-                               goto out;
-               }
+
+               list_for_each_entry(glist, &qgroup->groups, next_group)
+                       qgroup_iterator_add(&qgroup_list, glist->group);
        }
 out:
+       qgroup_iterator_clean(&qgroup_list);
        spin_unlock(&fs_info->qgroup_lock);
 }
 
+/*
+ * Convert @num_bytes of META_PREALLOCATED reservation to META_PERTRANS.
+ *
+ * This is called when preallocated meta reservation needs to be used.
+ * Normally after btrfs_join_transaction() call.
+ */
 void btrfs_qgroup_convert_reserved_meta(struct btrfs_root *root, int num_bytes)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) ||
+       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
            !is_fstree(root->root_key.objectid))
                return;
        /* Same as btrfs_qgroup_free_meta_prealloc() */
@@ -4254,7 +4444,7 @@ int btrfs_qgroup_add_swapped_blocks(struct btrfs_trans_handle *trans,
        int level = btrfs_header_level(subvol_parent) - 1;
        int ret = 0;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (!btrfs_qgroup_full_accounting(fs_info))
                return 0;
 
        if (btrfs_node_ptr_generation(subvol_parent, subvol_slot) >
@@ -4364,7 +4554,7 @@ int btrfs_qgroup_trace_subtree_after_cow(struct btrfs_trans_handle *trans,
        int ret = 0;
        int i;
 
-       if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
+       if (!btrfs_qgroup_full_accounting(fs_info))
                return 0;
        if (!is_fstree(root->root_key.objectid) || !root->reloc_root)
                return 0;
@@ -4447,3 +4637,49 @@ void btrfs_qgroup_destroy_extent_records(struct btrfs_transaction *trans)
        }
        *root = RB_ROOT;
 }
+
+int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
+                             struct btrfs_squota_delta *delta)
+{
+       int ret;
+       struct btrfs_qgroup *qgroup;
+       struct btrfs_qgroup *qg;
+       LIST_HEAD(qgroup_list);
+       u64 root = delta->root;
+       u64 num_bytes = delta->num_bytes;
+       const int sign = (delta->is_inc ? 1 : -1);
+
+       if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE)
+               return 0;
+
+       if (!is_fstree(root))
+               return 0;
+
+       spin_lock(&fs_info->qgroup_lock);
+       qgroup = find_qgroup_rb(fs_info, root);
+       if (!qgroup) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       ret = 0;
+       qgroup_iterator_add(&qgroup_list, qgroup);
+       list_for_each_entry(qg, &qgroup_list, iterator) {
+               struct btrfs_qgroup_list *glist;
+
+               qg->excl += num_bytes * sign;
+               qg->rfer += num_bytes * sign;
+               qgroup_dirty(fs_info, qg);
+
+               list_for_each_entry(glist, &qg->groups, next_group)
+                       qgroup_iterator_add(&qgroup_list, glist->group);
+       }
+       qgroup_iterator_clean(&qgroup_list);
+
+out:
+       spin_unlock(&fs_info->qgroup_lock);
+       if (!ret && delta->rsv_bytes)
+               btrfs_qgroup_free_refroot(fs_info, root, delta->rsv_bytes,
+                                         BTRFS_QGROUP_RSV_DATA);
+       return ret;
+}