Btrfs: rework qgroup accounting
[linux-2.6-block.git] / fs / btrfs / extent-tree.c
index aff579df5f4734507f9ca8ba0dffc920a474397c..343eb10230a199c4db63e5c7ff589a0e219722d5 100644 (file)
@@ -35,6 +35,7 @@
 #include "free-space-cache.h"
 #include "math.h"
 #include "sysfs.h"
+#include "qgroup.h"
 
 #undef SCRAMBLE_DELAYED_REFS
 
@@ -80,7 +81,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                u64 bytenr, u64 num_bytes, u64 parent,
                                u64 root_objectid, u64 owner_objectid,
                                u64 owner_offset, int refs_to_drop,
-                               struct btrfs_delayed_extent_op *extra_op);
+                               struct btrfs_delayed_extent_op *extra_op,
+                               int no_quota);
 static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op,
                                    struct extent_buffer *leaf,
                                    struct btrfs_extent_item *ei);
@@ -93,7 +95,8 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
                                     struct btrfs_root *root,
                                     u64 parent, u64 root_objectid,
                                     u64 flags, struct btrfs_disk_key *key,
-                                    int level, struct btrfs_key *ins);
+                                    int level, struct btrfs_key *ins,
+                                    int no_quota);
 static int do_chunk_alloc(struct btrfs_trans_handle *trans,
                          struct btrfs_root *extent_root, u64 flags,
                          int force);
@@ -1270,7 +1273,7 @@ fail:
 static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans,
                                           struct btrfs_root *root,
                                           struct btrfs_path *path,
-                                          int refs_to_drop)
+                                          int refs_to_drop, int *last_ref)
 {
        struct btrfs_key key;
        struct btrfs_extent_data_ref *ref1 = NULL;
@@ -1306,6 +1309,7 @@ static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans,
 
        if (num_refs == 0) {
                ret = btrfs_del_item(trans, root, path);
+               *last_ref = 1;
        } else {
                if (key.type == BTRFS_EXTENT_DATA_REF_KEY)
                        btrfs_set_extent_data_ref_count(leaf, ref1, num_refs);
@@ -1763,7 +1767,8 @@ void update_inline_extent_backref(struct btrfs_root *root,
                                  struct btrfs_path *path,
                                  struct btrfs_extent_inline_ref *iref,
                                  int refs_to_mod,
-                                 struct btrfs_delayed_extent_op *extent_op)
+                                 struct btrfs_delayed_extent_op *extent_op,
+                                 int *last_ref)
 {
        struct extent_buffer *leaf;
        struct btrfs_extent_item *ei;
@@ -1807,6 +1812,7 @@ void update_inline_extent_backref(struct btrfs_root *root,
                else
                        btrfs_set_shared_data_ref_count(leaf, sref, refs);
        } else {
+               *last_ref = 1;
                size =  btrfs_extent_inline_ref_size(type);
                item_size = btrfs_item_size_nr(leaf, path->slots[0]);
                ptr = (unsigned long)iref;
@@ -1838,7 +1844,7 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
        if (ret == 0) {
                BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID);
                update_inline_extent_backref(root, path, iref,
-                                            refs_to_add, extent_op);
+                                            refs_to_add, extent_op, NULL);
        } else if (ret == -ENOENT) {
                setup_inline_extent_backref(root, path, iref, parent,
                                            root_objectid, owner, offset,
@@ -1871,17 +1877,19 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
                                 struct btrfs_root *root,
                                 struct btrfs_path *path,
                                 struct btrfs_extent_inline_ref *iref,
-                                int refs_to_drop, int is_data)
+                                int refs_to_drop, int is_data, int *last_ref)
 {
        int ret = 0;
 
        BUG_ON(!is_data && refs_to_drop != 1);
        if (iref) {
                update_inline_extent_backref(root, path, iref,
-                                            -refs_to_drop, NULL);
+                                            -refs_to_drop, NULL, last_ref);
        } else if (is_data) {
-               ret = remove_extent_data_ref(trans, root, path, refs_to_drop);
+               ret = remove_extent_data_ref(trans, root, path, refs_to_drop,
+                                            last_ref);
        } else {
+               *last_ref = 1;
                ret = btrfs_del_item(trans, root, path);
        }
        return ret;
@@ -1945,7 +1953,8 @@ static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
 int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                         struct btrfs_root *root,
                         u64 bytenr, u64 num_bytes, u64 parent,
-                        u64 root_objectid, u64 owner, u64 offset, int for_cow)
+                        u64 root_objectid, u64 owner, u64 offset,
+                        int no_quota)
 {
        int ret;
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -1957,12 +1966,12 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                ret = btrfs_add_delayed_tree_ref(fs_info, trans, bytenr,
                                        num_bytes,
                                        parent, root_objectid, (int)owner,
-                                       BTRFS_ADD_DELAYED_REF, NULL, for_cow);
+                                       BTRFS_ADD_DELAYED_REF, NULL, no_quota);
        } else {
                ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr,
                                        num_bytes,
                                        parent, root_objectid, owner, offset,
-                                       BTRFS_ADD_DELAYED_REF, NULL, for_cow);
+                                       BTRFS_ADD_DELAYED_REF, NULL, no_quota);
        }
        return ret;
 }
@@ -1972,31 +1981,64 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                                  u64 bytenr, u64 num_bytes,
                                  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;
        struct btrfs_path *path;
        struct extent_buffer *leaf;
        struct btrfs_extent_item *item;
+       struct btrfs_key key;
        u64 refs;
        int ret;
+       enum btrfs_qgroup_operation_type type = BTRFS_QGROUP_OPER_ADD_EXCL;
 
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
+       if (!is_fstree(root_objectid) || !root->fs_info->quota_enabled)
+               no_quota = 1;
+
        path->reada = 1;
        path->leave_spinning = 1;
        /* this will setup the path even if it fails to insert the back ref */
-       ret = insert_inline_extent_backref(trans, root->fs_info->extent_root,
-                                          path, bytenr, num_bytes, parent,
+       ret = insert_inline_extent_backref(trans, fs_info->extent_root, path,
+                                          bytenr, num_bytes, parent,
                                           root_objectid, owner, offset,
                                           refs_to_add, extent_op);
-       if (ret != -EAGAIN)
+       if ((ret < 0 && ret != -EAGAIN) || (!ret && no_quota))
                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
+        * inline extent ref, so just update the reference count and add a
+        * normal backref.
+        */
        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);
        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);
@@ -2004,9 +2046,15 @@ 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 */
        ret = insert_extent_backref(trans, root->fs_info->extent_root,
                                    path, bytenr, parent, root_objectid,
@@ -2040,8 +2088,7 @@ static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
 
        if (node->type == BTRFS_SHARED_DATA_REF_KEY)
                parent = ref->parent;
-       else
-               ref_root = ref->root;
+       ref_root = ref->root;
 
        if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
                if (extent_op)
@@ -2055,13 +2102,13 @@ static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
                                             node->num_bytes, parent,
                                             ref_root, ref->objectid,
                                             ref->offset, node->ref_mod,
-                                            extent_op);
+                                            node->no_quota, 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->objectid,
                                          ref->offset, node->ref_mod,
-                                         extent_op);
+                                         extent_op, node->no_quota);
        } else {
                BUG();
        }
@@ -2198,8 +2245,7 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
 
        if (node->type == BTRFS_SHARED_BLOCK_REF_KEY)
                parent = ref->parent;
-       else
-               ref_root = ref->root;
+       ref_root = ref->root;
 
        ins.objectid = node->bytenr;
        if (skinny_metadata) {
@@ -2217,15 +2263,18 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
                                                parent, ref_root,
                                                extent_op->flags_to_set,
                                                &extent_op->key,
-                                               ref->level, &ins);
+                                               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, extent_op);
+                                            ref->level, 0, 1, node->no_quota,
+                                            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);
+                                         ref->level, 0, 1, extent_op,
+                                         node->no_quota);
        } else {
                BUG();
        }
@@ -2573,42 +2622,6 @@ static u64 find_middle(struct rb_root *root)
 }
 #endif
 
-int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans,
-                                        struct btrfs_fs_info *fs_info)
-{
-       struct qgroup_update *qgroup_update;
-       int ret = 0;
-
-       if (list_empty(&trans->qgroup_ref_list) !=
-           !trans->delayed_ref_elem.seq) {
-               /* list without seq or seq without list */
-               btrfs_err(fs_info,
-                       "qgroup accounting update error, list is%s empty, seq is %#x.%x",
-                       list_empty(&trans->qgroup_ref_list) ? "" : " not",
-                       (u32)(trans->delayed_ref_elem.seq >> 32),
-                       (u32)trans->delayed_ref_elem.seq);
-               BUG();
-       }
-
-       if (!trans->delayed_ref_elem.seq)
-               return 0;
-
-       while (!list_empty(&trans->qgroup_ref_list)) {
-               qgroup_update = list_first_entry(&trans->qgroup_ref_list,
-                                                struct qgroup_update, list);
-               list_del(&qgroup_update->list);
-               if (!ret)
-                       ret = btrfs_qgroup_account_ref(
-                                       trans, fs_info, qgroup_update->node,
-                                       qgroup_update->extent_op);
-               kfree(qgroup_update);
-       }
-
-       btrfs_put_tree_mod_seq(fs_info, &trans->delayed_ref_elem);
-
-       return ret;
-}
-
 static inline u64 heads_to_leaves(struct btrfs_root *root, u64 heads)
 {
        u64 num_bytes;
@@ -2697,8 +2710,6 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
        if (root == root->fs_info->extent_root)
                root = root->fs_info->tree_root;
 
-       btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info);
-
        delayed_refs = &trans->transaction->delayed_refs;
        if (count == 0) {
                count = atomic_read(&delayed_refs->num_entries) * 2;
@@ -2757,6 +2768,9 @@ again:
                goto again;
        }
 out:
+       ret = btrfs_delayed_qgroup_accounting(trans, root->fs_info);
+       if (ret)
+               return ret;
        assert_qgroups_uptodate(trans);
        return 0;
 }
@@ -2963,7 +2977,7 @@ out:
 static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           struct extent_buffer *buf,
-                          int full_backref, int inc, int for_cow)
+                          int full_backref, int inc, int no_quota)
 {
        u64 bytenr;
        u64 num_bytes;
@@ -3013,7 +3027,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
                        key.offset -= btrfs_file_extent_offset(buf, fi);
                        ret = process_func(trans, root, bytenr, num_bytes,
                                           parent, ref_root, key.objectid,
-                                          key.offset, for_cow);
+                                          key.offset, no_quota);
                        if (ret)
                                goto fail;
                } else {
@@ -3021,7 +3035,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
                        num_bytes = btrfs_level_size(root, level - 1);
                        ret = process_func(trans, root, bytenr, num_bytes,
                                           parent, ref_root, level - 1, 0,
-                                          for_cow);
+                                          no_quota);
                        if (ret)
                                goto fail;
                }
@@ -3032,15 +3046,15 @@ fail:
 }
 
 int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                 struct extent_buffer *buf, int full_backref, int for_cow)
+                 struct extent_buffer *buf, int full_backref, int no_quota)
 {
-       return __btrfs_mod_ref(trans, root, buf, full_backref, 1, for_cow);
+       return __btrfs_mod_ref(trans, root, buf, full_backref, 1, no_quota);
 }
 
 int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                 struct extent_buffer *buf, int full_backref, int for_cow)
+                 struct extent_buffer *buf, int full_backref, int no_quota)
 {
-       return __btrfs_mod_ref(trans, root, buf, full_backref, 0, for_cow);
+       return __btrfs_mod_ref(trans, root, buf, full_backref, 0, no_quota);
 }
 
 static int write_one_cache_group(struct btrfs_trans_handle *trans,
@@ -5723,7 +5737,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                u64 bytenr, u64 num_bytes, u64 parent,
                                u64 root_objectid, u64 owner_objectid,
                                u64 owner_offset, int refs_to_drop,
-                               struct btrfs_delayed_extent_op *extent_op)
+                               struct btrfs_delayed_extent_op *extent_op,
+                               int no_quota)
 {
        struct btrfs_key key;
        struct btrfs_path *path;
@@ -5739,9 +5754,14 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        int num_to_del = 1;
        u32 item_size;
        u64 refs;
+       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);
 
+       if (!info->quota_enabled || !is_fstree(root_objectid))
+               no_quota = 1;
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -5789,7 +5809,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        BUG_ON(iref);
                        ret = remove_extent_backref(trans, extent_root, path,
                                                    NULL, refs_to_drop,
-                                                   is_data);
+                                                   is_data, &last_ref);
                        if (ret) {
                                btrfs_abort_transaction(trans, extent_root, ret);
                                goto out;
@@ -5916,6 +5936,7 @@ 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);
                /*
@@ -5931,7 +5952,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                if (found_extent) {
                        ret = remove_extent_backref(trans, extent_root, path,
                                                    iref, refs_to_drop,
-                                                   is_data);
+                                                   is_data, &last_ref);
                        if (ret) {
                                btrfs_abort_transaction(trans, extent_root, ret);
                                goto out;
@@ -5952,6 +5973,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        }
                }
 
+               last_ref = 1;
                ret = btrfs_del_items(trans, extent_root, path, path->slots[0],
                                      num_to_del);
                if (ret) {
@@ -5974,6 +5996,20 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        goto out;
                }
        }
+       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;
@@ -6110,7 +6146,7 @@ out:
 /* Can return -ENOMEM */
 int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                      u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid,
-                     u64 owner, u64 offset, int for_cow)
+                     u64 owner, u64 offset, int no_quota)
 {
        int ret;
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -6130,13 +6166,13 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                ret = btrfs_add_delayed_tree_ref(fs_info, trans, bytenr,
                                        num_bytes,
                                        parent, root_objectid, (int)owner,
-                                       BTRFS_DROP_DELAYED_REF, NULL, for_cow);
+                                       BTRFS_DROP_DELAYED_REF, NULL, no_quota);
        } else {
                ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr,
                                                num_bytes,
                                                parent, root_objectid, owner,
                                                offset, BTRFS_DROP_DELAYED_REF,
-                                               NULL, for_cow);
+                                               NULL, no_quota);
        }
        return ret;
 }
@@ -6842,6 +6878,13 @@ 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(root, ins->objectid, ins->offset, 1);
        if (ret) { /* -ENOENT, logic error */
                btrfs_err(fs_info, "update block group failed for %llu %llu",
@@ -6856,7 +6899,8 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
                                     struct btrfs_root *root,
                                     u64 parent, u64 root_objectid,
                                     u64 flags, struct btrfs_disk_key *key,
-                                    int level, struct btrfs_key *ins)
+                                    int level, struct btrfs_key *ins,
+                                    int no_quota)
 {
        int ret;
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -6866,6 +6910,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        struct btrfs_path *path;
        struct extent_buffer *leaf;
        u32 size = sizeof(*extent_item) + sizeof(*iref);
+       u64 num_bytes = ins->offset;
        bool skinny_metadata = btrfs_fs_incompat(root->fs_info,
                                                 SKINNY_METADATA);
 
@@ -6899,6 +6944,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
 
        if (skinny_metadata) {
                iref = (struct btrfs_extent_inline_ref *)(extent_item + 1);
+               num_bytes = root->leafsize;
        } else {
                block_info = (struct btrfs_tree_block_info *)(extent_item + 1);
                btrfs_set_tree_block_key(leaf, block_info, key);
@@ -6920,6 +6966,14 @@ 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(root, ins->objectid, root->leafsize, 1);
        if (ret) { /* -ENOENT, logic error */
                btrfs_err(fs_info, "update block group failed for %llu %llu",