btrfs: make btrfs_issue_discard return bytes discarded
[cascardo/linux.git] / fs / btrfs / extent-tree.c
index 0ec3acd..16655bb 100644 (file)
@@ -79,11 +79,10 @@ static int update_block_group(struct btrfs_trans_handle *trans,
                              u64 num_bytes, int alloc);
 static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root,
-                               u64 bytenr, u64 num_bytes, u64 parent,
+                               struct btrfs_delayed_ref_node *node, u64 parent,
                                u64 root_objectid, u64 owner_objectid,
                                u64 owner_offset, int refs_to_drop,
-                               struct btrfs_delayed_extent_op *extra_op,
-                               int no_quota);
+                               struct btrfs_delayed_extent_op *extra_op);
 static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op,
                                    struct extent_buffer *leaf,
                                    struct btrfs_extent_item *ei);
@@ -1884,10 +1883,17 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
        return ret;
 }
 
-static int btrfs_issue_discard(struct block_device *bdev,
-                               u64 start, u64 len)
+static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
+                              u64 *discarded_bytes)
 {
-       return blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_NOFS, 0);
+       int ret = 0;
+
+       *discarded_bytes = 0;
+       ret = blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_NOFS, 0);
+       if (!ret)
+               *discarded_bytes = len;
+
+       return ret;
 }
 
 int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
@@ -1908,14 +1914,16 @@ int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
 
 
                for (i = 0; i < bbio->num_stripes; i++, stripe++) {
+                       u64 bytes;
                        if (!stripe->dev->can_discard)
                                continue;
 
                        ret = btrfs_issue_discard(stripe->dev->bdev,
                                                  stripe->physical,
-                                                 stripe->length);
+                                                 stripe->length,
+                                                 &bytes);
                        if (!ret)
-                               discarded_bytes += stripe->length;
+                               discarded_bytes += bytes;
                        else if (ret != -EOPNOTSUPP)
                                break; /* Logic errors or -ENOMEM, or -EIO but I don't know how that could happen JDM */
 
@@ -1967,10 +1975,9 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
 
 static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                                  struct btrfs_root *root,
-                                 u64 bytenr, u64 num_bytes,
+                                 struct btrfs_delayed_ref_node *node,
                                  u64 parent, u64 root_objectid,
                                  u64 owner, u64 offset, int refs_to_add,
-                                 int no_quota,
                                  struct btrfs_delayed_extent_op *extent_op)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -1978,9 +1985,11 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
        struct extent_buffer *leaf;
        struct btrfs_extent_item *item;
        struct btrfs_key key;
+       u64 bytenr = node->bytenr;
+       u64 num_bytes = node->num_bytes;
        u64 refs;
        int ret;
-       enum btrfs_qgroup_operation_type type = BTRFS_QGROUP_OPER_ADD_EXCL;
+       int no_quota = node->no_quota;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -1996,26 +2005,8 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                                           bytenr, num_bytes, parent,
                                           root_objectid, owner, offset,
                                           refs_to_add, extent_op);
-       if ((ret < 0 && ret != -EAGAIN) || (!ret && no_quota))
+       if ((ret < 0 && ret != -EAGAIN) || !ret)
                goto out;
-       /*
-        * Ok we were able to insert an inline extent and it appears to be a new
-        * reference, deal with the qgroup accounting.
-        */
-       if (!ret && !no_quota) {
-               ASSERT(root->fs_info->quota_enabled);
-               leaf = path->nodes[0];
-               btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
-               item = btrfs_item_ptr(leaf, path->slots[0],
-                                     struct btrfs_extent_item);
-               if (btrfs_extent_refs(leaf, item) > (u64)refs_to_add)
-                       type = BTRFS_QGROUP_OPER_ADD_SHARED;
-               btrfs_release_path(path);
-
-               ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid,
-                                             bytenr, num_bytes, type, 0);
-               goto out;
-       }
 
        /*
         * Ok we had -EAGAIN which means we didn't have space to insert and
@@ -2026,8 +2017,6 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
        btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
        refs = btrfs_extent_refs(leaf, item);
-       if (refs)
-               type = BTRFS_QGROUP_OPER_ADD_SHARED;
        btrfs_set_extent_refs(leaf, item, refs + refs_to_add);
        if (extent_op)
                __run_delayed_extent_op(extent_op, leaf, item);
@@ -2035,13 +2024,6 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(leaf);
        btrfs_release_path(path);
 
-       if (!no_quota) {
-               ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid,
-                                             bytenr, num_bytes, type, 0);
-               if (ret)
-                       goto out;
-       }
-
        path->reada = 1;
        path->leave_spinning = 1;
        /* now insert the actual backref */
@@ -2087,17 +2069,15 @@ static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
                                                 ref->objectid, ref->offset,
                                                 &ins, node->ref_mod);
        } else if (node->action == BTRFS_ADD_DELAYED_REF) {
-               ret = __btrfs_inc_extent_ref(trans, root, node->bytenr,
-                                            node->num_bytes, parent,
+               ret = __btrfs_inc_extent_ref(trans, root, node, parent,
                                             ref_root, ref->objectid,
                                             ref->offset, node->ref_mod,
-                                            node->no_quota, extent_op);
+                                            extent_op);
        } else if (node->action == BTRFS_DROP_DELAYED_REF) {
-               ret = __btrfs_free_extent(trans, root, node->bytenr,
-                                         node->num_bytes, parent,
+               ret = __btrfs_free_extent(trans, root, node, parent,
                                          ref_root, ref->objectid,
                                          ref->offset, node->ref_mod,
-                                         extent_op, node->no_quota);
+                                         extent_op);
        } else {
                BUG();
        }
@@ -2255,15 +2235,14 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
                                                ref->level, &ins,
                                                node->no_quota);
        } else if (node->action == BTRFS_ADD_DELAYED_REF) {
-               ret = __btrfs_inc_extent_ref(trans, root, node->bytenr,
-                                            node->num_bytes, parent, ref_root,
-                                            ref->level, 0, 1, node->no_quota,
+               ret = __btrfs_inc_extent_ref(trans, root, node,
+                                            parent, ref_root,
+                                            ref->level, 0, 1,
                                             extent_op);
        } else if (node->action == BTRFS_DROP_DELAYED_REF) {
-               ret = __btrfs_free_extent(trans, root, node->bytenr,
-                                         node->num_bytes, parent, ref_root,
-                                         ref->level, 0, 1, extent_op,
-                                         node->no_quota);
+               ret = __btrfs_free_extent(trans, root, node,
+                                         parent, ref_root,
+                                         ref->level, 0, 1, extent_op);
        } else {
                BUG();
        }
@@ -2323,28 +2302,27 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
        return ret;
 }
 
-static noinline struct btrfs_delayed_ref_node *
+static inline struct btrfs_delayed_ref_node *
 select_delayed_ref(struct btrfs_delayed_ref_head *head)
 {
-       struct rb_node *node;
-       struct btrfs_delayed_ref_node *ref, *last = NULL;;
+       struct btrfs_delayed_ref_node *ref;
+
+       if (list_empty(&head->ref_list))
+               return NULL;
 
        /*
-        * select delayed ref of type BTRFS_ADD_DELAYED_REF first.
-        * this prevents ref count from going down to zero when
-        * there still are pending delayed ref.
+        * Select a delayed ref of type BTRFS_ADD_DELAYED_REF first.
+        * This is to prevent a ref count from going down to zero, which deletes
+        * the extent item from the extent tree, when there still are references
+        * to add, which would fail because they would not find the extent item.
         */
-       node = rb_first(&head->ref_root);
-       while (node) {
-               ref = rb_entry(node, struct btrfs_delayed_ref_node,
-                               rb_node);
+       list_for_each_entry(ref, &head->ref_list, list) {
                if (ref->action == BTRFS_ADD_DELAYED_REF)
                        return ref;
-               else if (last == NULL)
-                       last = ref;
-               node = rb_next(node);
        }
-       return last;
+
+       return list_entry(head->ref_list.next, struct btrfs_delayed_ref_node,
+                         list);
 }
 
 /*
@@ -2396,16 +2374,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
                        }
                }
 
-               /*
-                * We need to try and merge add/drops of the same ref since we
-                * can run into issues with relocate dropping the implicit ref
-                * and then it being added back again before the drop can
-                * finish.  If we merged anything we need to re-loop so we can
-                * get a good ref.
-                */
                spin_lock(&locked_ref->lock);
-               btrfs_merge_delayed_refs(trans, fs_info, delayed_refs,
-                                        locked_ref);
 
                /*
                 * locked_ref is the head node, so we have to go one
@@ -2482,7 +2451,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
                        spin_unlock(&locked_ref->lock);
                        spin_lock(&delayed_refs->lock);
                        spin_lock(&locked_ref->lock);
-                       if (rb_first(&locked_ref->ref_root) ||
+                       if (!list_empty(&locked_ref->ref_list) ||
                            locked_ref->extent_op) {
                                spin_unlock(&locked_ref->lock);
                                spin_unlock(&delayed_refs->lock);
@@ -2496,7 +2465,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
                } else {
                        actual_count++;
                        ref->in_tree = 0;
-                       rb_erase(&ref->rb_node, &locked_ref->ref_root);
+                       list_del(&ref->list);
                }
                atomic_dec(&delayed_refs->num_entries);
 
@@ -2864,9 +2833,6 @@ again:
                goto again;
        }
 out:
-       ret = btrfs_delayed_qgroup_accounting(trans, root->fs_info);
-       if (ret)
-               return ret;
        assert_qgroups_uptodate(trans);
        return 0;
 }
@@ -2905,7 +2871,6 @@ static noinline int check_delayed_ref(struct btrfs_trans_handle *trans,
        struct btrfs_delayed_ref_node *ref;
        struct btrfs_delayed_data_ref *data_ref;
        struct btrfs_delayed_ref_root *delayed_refs;
-       struct rb_node *node;
        int ret = 0;
 
        delayed_refs = &trans->transaction->delayed_refs;
@@ -2934,11 +2899,7 @@ static noinline int check_delayed_ref(struct btrfs_trans_handle *trans,
        spin_unlock(&delayed_refs->lock);
 
        spin_lock(&head->lock);
-       node = rb_first(&head->ref_root);
-       while (node) {
-               ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
-               node = rb_next(node);
-
+       list_for_each_entry(ref, &head->ref_list, list) {
                /* If it's a shared ref we know a cross reference exists */
                if (ref->type != BTRFS_EXTENT_DATA_REF_KEY) {
                        ret = 1;
@@ -3693,7 +3654,8 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
                found->disk_total += total_bytes * factor;
                found->bytes_used += bytes_used;
                found->disk_used += bytes_used * factor;
-               found->full = 0;
+               if (total_bytes > 0)
+                       found->full = 0;
                spin_unlock(&found->lock);
                *space_info = found;
                return 0;
@@ -3721,7 +3683,10 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        found->bytes_reserved = 0;
        found->bytes_readonly = 0;
        found->bytes_may_use = 0;
-       found->full = 0;
+       if (total_bytes > 0)
+               found->full = 0;
+       else
+               found->full = 1;
        found->force_alloc = CHUNK_ALLOC_NO_FORCE;
        found->chunk_alloc = 0;
        found->flush = 0;
@@ -3975,6 +3940,9 @@ commit_trans:
                    !atomic_read(&root->fs_info->open_ioctl_trans)) {
                        need_commit--;
 
+                       if (need_commit > 0)
+                               btrfs_wait_ordered_roots(fs_info, -1);
+
                        trans = btrfs_join_transaction(root);
                        if (IS_ERR(trans))
                                return PTR_ERR(trans);
@@ -4088,7 +4056,7 @@ static int should_alloc_chunk(struct btrfs_root *root,
        return 1;
 }
 
-static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type)
+static u64 get_profile_num_devs(struct btrfs_root *root, u64 type)
 {
        u64 num_dev;
 
@@ -4102,24 +4070,43 @@ static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type)
        else
                num_dev = 1;    /* DUP or single */
 
-       /* metadata for updaing devices and chunk tree */
-       return btrfs_calc_trans_metadata_size(root, num_dev + 1);
+       return num_dev;
 }
 
-static void check_system_chunk(struct btrfs_trans_handle *trans,
-                              struct btrfs_root *root, u64 type)
+/*
+ * If @is_allocation is true, reserve space in the system space info necessary
+ * for allocating a chunk, otherwise if it's false, reserve space necessary for
+ * removing a chunk.
+ */
+void check_system_chunk(struct btrfs_trans_handle *trans,
+                       struct btrfs_root *root,
+                       u64 type)
 {
        struct btrfs_space_info *info;
        u64 left;
        u64 thresh;
+       int ret = 0;
+       u64 num_devs;
+
+       /*
+        * Needed because we can end up allocating a system chunk and for an
+        * atomic and race free space reservation in the chunk block reserve.
+        */
+       ASSERT(mutex_is_locked(&root->fs_info->chunk_mutex));
 
        info = __find_space_info(root->fs_info, BTRFS_BLOCK_GROUP_SYSTEM);
        spin_lock(&info->lock);
        left = info->total_bytes - info->bytes_used - info->bytes_pinned -
-               info->bytes_reserved - info->bytes_readonly;
+               info->bytes_reserved - info->bytes_readonly -
+               info->bytes_may_use;
        spin_unlock(&info->lock);
 
-       thresh = get_system_chunk_thresh(root, type);
+       num_devs = get_profile_num_devs(root, type);
+
+       /* num_devs device items to update and 1 chunk item to add or remove */
+       thresh = btrfs_calc_trunc_metadata_size(root, num_devs) +
+               btrfs_calc_trans_metadata_size(root, 1);
+
        if (left < thresh && btrfs_test_opt(root, ENOSPC_DEBUG)) {
                btrfs_info(root->fs_info, "left=%llu, need=%llu, flags=%llu",
                        left, thresh, type);
@@ -4130,7 +4117,21 @@ static void check_system_chunk(struct btrfs_trans_handle *trans,
                u64 flags;
 
                flags = btrfs_get_alloc_profile(root->fs_info->chunk_root, 0);
-               btrfs_alloc_chunk(trans, root, flags);
+               /*
+                * Ignore failure to create system chunk. We might end up not
+                * needing it, as we might not need to COW all nodes/leafs from
+                * the paths we visit in the chunk tree (they were already COWed
+                * or created in the current transaction for example).
+                */
+               ret = btrfs_alloc_chunk(trans, root, flags);
+       }
+
+       if (!ret) {
+               ret = btrfs_block_rsv_add(root->fs_info->chunk_root,
+                                         &root->fs_info->chunk_block_rsv,
+                                         thresh, BTRFS_RESERVE_NO_FLUSH);
+               if (!ret)
+                       trans->chunk_bytes_reserved += thresh;
        }
 }
 
@@ -4235,6 +4236,24 @@ out:
        space_info->chunk_alloc = 0;
        spin_unlock(&space_info->lock);
        mutex_unlock(&fs_info->chunk_mutex);
+       /*
+        * When we allocate a new chunk we reserve space in the chunk block
+        * reserve to make sure we can COW nodes/leafs in the chunk tree or
+        * add new nodes/leafs to it if we end up needing to do it when
+        * inserting the chunk item and updating device items as part of the
+        * second phase of chunk allocation, performed by
+        * btrfs_finish_chunk_alloc(). So make sure we don't accumulate a
+        * large number of new block groups to create in our transaction
+        * handle's new_bgs list to avoid exhausting the chunk block reserve
+        * in extreme cases - like having a single transaction create many new
+        * block groups when starting to write out the free space caches of all
+        * the block groups that were made dirty during the lifetime of the
+        * transaction.
+        */
+       if (trans->chunk_bytes_reserved >= (2 * 1024 * 1024ull)) {
+               btrfs_create_pending_block_groups(trans, trans->root);
+               btrfs_trans_release_chunk_metadata(trans);
+       }
        return ret;
 }
 
@@ -5188,6 +5207,24 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans,
        trans->bytes_reserved = 0;
 }
 
+/*
+ * To be called after all the new block groups attached to the transaction
+ * handle have been created (btrfs_create_pending_block_groups()).
+ */
+void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans)
+{
+       struct btrfs_fs_info *fs_info = trans->root->fs_info;
+
+       if (!trans->chunk_bytes_reserved)
+               return;
+
+       WARN_ON_ONCE(!list_empty(&trans->new_bgs));
+
+       block_rsv_release_bytes(fs_info, &fs_info->chunk_block_rsv, NULL,
+                               trans->chunk_bytes_reserved);
+       trans->chunk_bytes_reserved = 0;
+}
+
 /* Can only return 0 or -ENOSPC */
 int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans,
                                  struct inode *inode)
@@ -6092,11 +6129,10 @@ static void add_pinned_bytes(struct btrfs_fs_info *fs_info, u64 num_bytes,
 
 static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root,
-                               u64 bytenr, u64 num_bytes, u64 parent,
+                               struct btrfs_delayed_ref_node *node, u64 parent,
                                u64 root_objectid, u64 owner_objectid,
                                u64 owner_offset, int refs_to_drop,
-                               struct btrfs_delayed_extent_op *extent_op,
-                               int no_quota)
+                               struct btrfs_delayed_extent_op *extent_op)
 {
        struct btrfs_key key;
        struct btrfs_path *path;
@@ -6110,10 +6146,12 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        int extent_slot = 0;
        int found_extent = 0;
        int num_to_del = 1;
+       int no_quota = node->no_quota;
        u32 item_size;
        u64 refs;
+       u64 bytenr = node->bytenr;
+       u64 num_bytes = node->num_bytes;
        int last_ref = 0;
-       enum btrfs_qgroup_operation_type type = BTRFS_QGROUP_OPER_SUB_EXCL;
        bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
                                                 SKINNY_METADATA);
 
@@ -6294,7 +6332,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        refs -= refs_to_drop;
 
        if (refs > 0) {
-               type = BTRFS_QGROUP_OPER_SUB_SHARED;
                if (extent_op)
                        __run_delayed_extent_op(extent_op, leaf, ei);
                /*
@@ -6356,18 +6393,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        }
        btrfs_release_path(path);
 
-       /* Deal with the quota accounting */
-       if (!ret && last_ref && !no_quota) {
-               int mod_seq = 0;
-
-               if (owner_objectid >= BTRFS_FIRST_FREE_OBJECTID &&
-                   type == BTRFS_QGROUP_OPER_SUB_SHARED)
-                       mod_seq = 1;
-
-               ret = btrfs_qgroup_record_ref(trans, info, root_objectid,
-                                             bytenr, num_bytes, type,
-                                             mod_seq);
-       }
 out:
        btrfs_free_path(path);
        return ret;
@@ -6393,7 +6418,7 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
                goto out_delayed_unlock;
 
        spin_lock(&head->lock);
-       if (rb_first(&head->ref_root))
+       if (!list_empty(&head->ref_list))
                goto out;
 
        if (head->extent_op) {
@@ -7303,13 +7328,6 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(path->nodes[0]);
        btrfs_free_path(path);
 
-       /* Always set parent to 0 here since its exclusive anyway. */
-       ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid,
-                                     ins->objectid, ins->offset,
-                                     BTRFS_QGROUP_OPER_ADD_EXCL, 0);
-       if (ret)
-               return ret;
-
        ret = update_block_group(trans, root, ins->objectid, ins->offset, 1);
        if (ret) { /* -ENOENT, logic error */
                btrfs_err(fs_info, "update block group failed for %llu %llu",
@@ -7391,14 +7409,6 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(leaf);
        btrfs_free_path(path);
 
-       if (!no_quota) {
-               ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid,
-                                             ins->objectid, num_bytes,
-                                             BTRFS_QGROUP_OPER_ADD_EXCL, 0);
-               if (ret)
-                       return ret;
-       }
-
        ret = update_block_group(trans, root, ins->objectid, root->nodesize,
                                 1);
        if (ret) { /* -ENOENT, logic error */
@@ -7755,12 +7765,18 @@ reada:
        wc->reada_slot = slot;
 }
 
+/*
+ * TODO: Modify related function to add related node/leaf to dirty_extent_root,
+ * for later qgroup accounting.
+ *
+ * Current, this function does nothing.
+ */
 static int account_leaf_items(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
                              struct extent_buffer *eb)
 {
        int nr = btrfs_header_nritems(eb);
-       int i, extent_type, ret;
+       int i, extent_type;
        struct btrfs_key key;
        struct btrfs_file_extent_item *fi;
        u64 bytenr, num_bytes;
@@ -7783,13 +7799,6 @@ static int account_leaf_items(struct btrfs_trans_handle *trans,
                        continue;
 
                num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
-
-               ret = btrfs_qgroup_record_ref(trans, root->fs_info,
-                                             root->objectid,
-                                             bytenr, num_bytes,
-                                             BTRFS_QGROUP_OPER_SUB_SUBTREE, 0);
-               if (ret)
-                       return ret;
        }
        return 0;
 }
@@ -7858,6 +7867,8 @@ static int adjust_slots_upwards(struct btrfs_root *root,
 
 /*
  * root_eb is the subtree root and is locked before this function is called.
+ * TODO: Modify this function to mark all (including complete shared node)
+ * to dirty_extent_root to allow it get accounted in qgroup.
  */
 static int account_shared_subtree(struct btrfs_trans_handle *trans,
                                  struct btrfs_root *root,
@@ -7920,7 +7931,11 @@ walk_down:
                        child_gen = btrfs_node_ptr_generation(eb, parent_slot);
 
                        eb = read_tree_block(root, child_bytenr, child_gen);
-                       if (!eb || !extent_buffer_uptodate(eb)) {
+                       if (IS_ERR(eb)) {
+                               ret = PTR_ERR(eb);
+                               goto out;
+                       } else if (!extent_buffer_uptodate(eb)) {
+                               free_extent_buffer(eb);
                                ret = -EIO;
                                goto out;
                        }
@@ -7931,16 +7946,6 @@ walk_down:
                        btrfs_tree_read_lock(eb);
                        btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
                        path->locks[level] = BTRFS_READ_LOCK_BLOCKING;
-
-                       ret = btrfs_qgroup_record_ref(trans, root->fs_info,
-                                               root->objectid,
-                                               child_bytenr,
-                                               root->nodesize,
-                                               BTRFS_QGROUP_OPER_SUB_SUBTREE,
-                                               0);
-                       if (ret)
-                               goto out;
-
                }
 
                if (level == 0) {
@@ -8151,7 +8156,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
                if (reada && level == 1)
                        reada_walk_down(trans, root, wc, path);
                next = read_tree_block(root, bytenr, generation);
-               if (!next || !extent_buffer_uptodate(next)) {
+               if (IS_ERR(next)) {
+                       return PTR_ERR(next);
+               } else if (!extent_buffer_uptodate(next)) {
                        free_extent_buffer(next);
                        return -EIO;
                }
@@ -8533,24 +8540,6 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
                                goto out_end_trans;
                        }
 
-                       /*
-                        * Qgroup update accounting is run from
-                        * delayed ref handling. This usually works
-                        * out because delayed refs are normally the
-                        * only way qgroup updates are added. However,
-                        * we may have added updates during our tree
-                        * walk so run qgroups here to make sure we
-                        * don't lose any updates.
-                        */
-                       ret = btrfs_delayed_qgroup_accounting(trans,
-                                                             root->fs_info);
-                       if (ret)
-                               printk_ratelimited(KERN_ERR "BTRFS: Failure %d "
-                                                  "running qgroup updates "
-                                                  "during snapshot delete. "
-                                                  "Quota is out of sync, "
-                                                  "rescan required.\n", ret);
-
                        btrfs_end_transaction_throttle(trans, tree_root);
                        if (!for_reloc && btrfs_need_cleaner_sleep(root)) {
                                pr_debug("BTRFS: drop snapshot early exit\n");
@@ -8604,14 +8593,6 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        }
        root_dropped = true;
 out_end_trans:
-       ret = btrfs_delayed_qgroup_accounting(trans, tree_root->fs_info);
-       if (ret)
-               printk_ratelimited(KERN_ERR "BTRFS: Failure %d "
-                                  "running qgroup updates "
-                                  "during snapshot delete. "
-                                  "Quota is out of sync, "
-                                  "rescan required.\n", ret);
-
        btrfs_end_transaction_throttle(trans, tree_root);
 out_free:
        kfree(wc);
@@ -9562,6 +9543,19 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
 
        free_excluded_extents(root, cache);
 
+       /*
+        * Call to ensure the corresponding space_info object is created and
+        * assigned to our block group, but don't update its counters just yet.
+        * We want our bg to be added to the rbtree with its ->space_info set.
+        */
+       ret = update_space_info(root->fs_info, cache->flags, 0, 0,
+                               &cache->space_info);
+       if (ret) {
+               btrfs_remove_free_space_cache(cache);
+               btrfs_put_block_group(cache);
+               return ret;
+       }
+
        ret = btrfs_add_block_group_cache(root->fs_info, cache);
        if (ret) {
                btrfs_remove_free_space_cache(cache);
@@ -9569,6 +9563,10 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
                return ret;
        }
 
+       /*
+        * Now that our block group has its ->space_info set and is inserted in
+        * the rbtree, update the space info's counters.
+        */
        ret = update_space_info(root->fs_info, cache->flags, size, bytes_used,
                                &cache->space_info);
        if (ret) {
@@ -9931,6 +9929,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
                }
                spin_unlock(&fs_info->unused_bgs_lock);
 
+               mutex_lock(&root->fs_info->delete_unused_bgs_mutex);
+
                /* Don't want to race with allocators so take the groups_sem */
                down_write(&space_info->groups_sem);
                spin_lock(&block_group->lock);
@@ -10025,6 +10025,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
 end_trans:
                btrfs_end_transaction(trans, root);
 next:
+               mutex_unlock(&root->fs_info->delete_unused_bgs_mutex);
                btrfs_put_block_group(block_group);
                spin_lock(&fs_info->unused_bgs_lock);
        }