From: Wang Shilong <wangshilong1991@gmail.com>
To: Josef Bacik <jbacik@fb.com>
Cc: <linux-btrfs@vger.kernel.org>
Subject: Re: [PATCH 2/3] Btrfs: rework qgroup accounting
Date: Sat, 21 Dec 2013 16:01:58 +0800 [thread overview]
Message-ID: <D8610409-5BF3-4150-A0DF-DC9985169540@gmail.com> (raw)
In-Reply-To: <1387400849-7274-3-git-send-email-jbacik@fb.com>
Hi Josef,
I compie btrfs-next in my 32-bit machine, i get the following warnings:
fs/btrfs/qgroup.c: In function ‘qgroup_excl_accounting’:
fs/btrfs/qgroup.c:1503:12: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
qgroup = (struct btrfs_qgroup *)unode->aux;
^
fs/btrfs/qgroup.c: In function ‘qgroup_calc_old_refcnt’:
fs/btrfs/qgroup.c:1571:9: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
qg = (struct btrfs_qgroup *)tmp_unode->aux;
^
fs/btrfs/qgroup.c: In function ‘qgroup_account_deleted_refs’:
fs/btrfs/qgroup.c:1665:8: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
qg = (struct btrfs_qgroup *)unode->aux;
^
fs/btrfs/qgroup.c: In function ‘qgroup_calc_new_refcnt’:
fs/btrfs/qgroup.c:1705:8: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
qg = (struct btrfs_qgroup *)unode->aux;
^
fs/btrfs/qgroup.c: In function ‘qgroup_adjust_counters’:
fs/btrfs/qgroup.c:1767:8: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
qg = (struct btrfs_qgroup *)unode->aux;
this patch is newly added into btrfs-next, so i think it is better that you fix these warnings locally .^_^
Thanks,
Wang
> Currently qgroups account for space by intercepting delayed ref updates to fs
> trees. It does this by adding sequence numbers to delayed ref updates so that
> it can figure out how the tree looked before the update so we can adjust the
> counters properly. The problem with this is that it does not allow delayed refs
> to be merged, so if you say are defragging an extent with 5k snapshots pointing
> to it we will thrash the delayed ref lock because we need to go back and
> manually merge these things together. Instead we want to process quota changes
> when we know they are going to happen, like when we first allocate an extent, we
> free a reference for an extent, we add new references etc. This patch does this
> and removes the reliance on the delayed ref sequence number. Thanks,
>
> Signed-off-by: Josef Bacik <jbacik@fb.com>
> ---
> fs/btrfs/backref.c | 19 +-
> fs/btrfs/backref.h | 5 +-
> fs/btrfs/ctree.h | 51 ++-
> fs/btrfs/delayed-ref.c | 10 -
> fs/btrfs/delayed-ref.h | 19 -
> fs/btrfs/disk-io.c | 3 +
> fs/btrfs/extent-tree.c | 457 ++++++++++++++++----
> fs/btrfs/qgroup.c | 1075 +++++++++++++++++++++++++++++++++++++-----------
> fs/btrfs/transaction.c | 51 ++-
> 9 files changed, 1312 insertions(+), 378 deletions(-)
>
> diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
> index 6a3f7f5..9ee3030 100644
> --- a/fs/btrfs/backref.c
> +++ b/fs/btrfs/backref.c
> @@ -817,7 +817,8 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
> static int find_parent_nodes(struct btrfs_trans_handle *trans,
> struct btrfs_fs_info *fs_info, u64 bytenr,
> u64 time_seq, struct ulist *refs,
> - struct ulist *roots, const u64 *extent_item_pos)
> + struct ulist *roots, const u64 *extent_item_pos,
> + int search_commit_root)
> {
> struct btrfs_key key;
> struct btrfs_path *path;
> @@ -842,7 +843,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
> path = btrfs_alloc_path();
> if (!path)
> return -ENOMEM;
> - if (!trans)
> + if (search_commit_root)
> path->search_commit_root = 1;
>
> /*
> @@ -1046,7 +1047,8 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
> }
>
> ret = find_parent_nodes(trans, fs_info, bytenr,
> - time_seq, *leafs, tmp, extent_item_pos);
> + time_seq, *leafs, tmp, extent_item_pos,
> + (trans == NULL));
> ulist_free(tmp);
>
> if (ret < 0 && ret != -ENOENT) {
> @@ -1071,8 +1073,9 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
> * returns 0 on success, < 0 on error.
> */
> int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> - struct btrfs_fs_info *fs_info, u64 bytenr,
> - u64 time_seq, struct ulist **roots)
> + struct btrfs_fs_info *fs_info, u64 bytenr,
> + u64 time_seq, struct ulist **roots,
> + int search_commit_root)
> {
> struct ulist *tmp;
> struct ulist_node *node = NULL;
> @@ -1091,7 +1094,8 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> ULIST_ITER_INIT(&uiter);
> while (1) {
> ret = find_parent_nodes(trans, fs_info, bytenr,
> - time_seq, tmp, *roots, NULL);
> + time_seq, tmp, *roots, NULL,
> + search_commit_root);
> if (ret < 0 && ret != -ENOENT) {
> ulist_free(tmp);
> ulist_free(*roots);
> @@ -1497,7 +1501,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
> ULIST_ITER_INIT(&ref_uiter);
> while (!ret && (ref_node = ulist_next(refs, &ref_uiter))) {
> ret = btrfs_find_all_roots(trans, fs_info, ref_node->val,
> - tree_mod_seq_elem.seq, &roots);
> + tree_mod_seq_elem.seq, &roots,
> + search_commit_root);
> if (ret)
> break;
> ULIST_ITER_INIT(&root_uiter);
> diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h
> index a910b27..0ad87c8 100644
> --- a/fs/btrfs/backref.h
> +++ b/fs/btrfs/backref.h
> @@ -55,8 +55,9 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
> int paths_from_inode(u64 inum, struct inode_fs_paths *ipath);
>
> int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> - struct btrfs_fs_info *fs_info, u64 bytenr,
> - u64 time_seq, struct ulist **roots);
> + struct btrfs_fs_info *fs_info, u64 bytenr,
> + u64 time_seq, struct ulist **roots,
> + int search_commit_root);
> char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
> u32 name_len, unsigned long name_off,
> struct extent_buffer *eb_in, u64 parent,
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 8b3fd61..944c916 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -1289,6 +1289,32 @@ enum btrfs_orphan_cleanup_state {
> ORPHAN_CLEANUP_DONE = 2,
> };
>
> +/*
> + * A description of the operations, all of these operations only happen when we
> + * are adding the 1st reference for that subvolume in the case of adding space
> + * or on the last reference delete in the case of subtraction.
> + *
> + * BTRFS_QGROUP_OPER_ADD_EXCL: adding bytes where this subvolume is the only
> + * one pointing at the bytes we are adding. This is called on the first
> + * allocation.
> + *
> + * BTRFS_QGROUP_OPER_ADD_SHARED: adding bytes where this bytenr is going to be
> + * shared between subvols. This is called on the creation of a ref that already
> + * has refs from a different subvolume, so basically reflink.
> + *
> + * BTRFS_QGROUP_OPER_SUB_EXCL: removing bytes where this subvolume is the only
> + * one referencing the range.
> + *
> + * BTRFS_QGROUP_OPER_SUB_SHARED: removing bytes where this subvolume shares with
> + * refs with other subvolumes.
> + */
> +enum btrfs_qgroup_operation_type {
> + BTRFS_QGROUP_OPER_ADD_EXCL,
> + BTRFS_QGROUP_OPER_ADD_SHARED,
> + BTRFS_QGROUP_OPER_SUB_EXCL,
> + BTRFS_QGROUP_OPER_SUB_SHARED,
> +};
> +
> /* used by the raid56 code to lock stripes for read/modify/write */
> struct btrfs_stripe_hash {
> struct list_head hash_list;
> @@ -1629,7 +1655,10 @@ struct btrfs_fs_info {
>
> /* holds configuration and tracking. Protected by qgroup_lock */
> struct rb_root qgroup_tree;
> + struct rb_root qgroup_op_tree;
> spinlock_t qgroup_lock;
> + spinlock_t qgroup_op_lock;
> + atomic_t qgroup_op_seq;
>
> /*
> * used to avoid frequently calling ulist_alloc()/ulist_free()
> @@ -3323,12 +3352,10 @@ int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans,
> struct btrfs_fs_info *fs_info);
> int __get_raid_index(u64 flags);
> int lock_ref(struct btrfs_fs_info *fs_info, u64 root_objectid, u64 bytenr,
> - u64 num_bytes, int for_cow,
> - struct btrfs_block_group_cache **block_group,
> + u64 num_bytes, struct btrfs_block_group_cache **block_group,
> struct extent_state **cached_state);
> int unlock_ref(struct btrfs_fs_info *fs_info, u64 root_objectid, u64 bytenr,
> - u64 num_bytes, int for_cow,
> - struct btrfs_block_group_cache *block_group,
> + u64 num_bytes, struct btrfs_block_group_cache *block_group,
> struct extent_state **cached_state);
> /* ctree.c */
> int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
> @@ -4031,12 +4058,16 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info);
> void btrfs_free_qgroup_config(struct btrfs_fs_info *fs_info);
> struct btrfs_delayed_extent_op;
> int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
> - struct btrfs_delayed_ref_node *node,
> - struct btrfs_delayed_extent_op *extent_op);
> -int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
> - struct btrfs_fs_info *fs_info,
> - struct btrfs_delayed_ref_node *node,
> - struct btrfs_delayed_extent_op *extent_op);
> + struct btrfs_fs_info *fs_info, u64 ref_root,
> + u64 bytenr, u64 num_bytes, u64 parent,
> + enum btrfs_qgroup_operation_type type);
> +int btrfs_delayed_qgroup_accounting(struct btrfs_trans_handle *trans,
> + struct btrfs_fs_info *fs_info);
> +void btrfs_remove_last_qgroup_operation(struct btrfs_trans_handle *trans,
> + struct btrfs_fs_info *fs_info,
> + u64 ref_root);
> +int btrfs_qgroup_add_shared_ref(struct btrfs_trans_handle *trans, u64 ref_root,
> + u64 parent);
> int btrfs_run_qgroups(struct btrfs_trans_handle *trans,
> struct btrfs_fs_info *fs_info);
> int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
> diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
> index ee1c29d..d5a601e 100644
> --- a/fs/btrfs/delayed-ref.c
> +++ b/fs/btrfs/delayed-ref.c
> @@ -681,9 +681,6 @@ static noinline void add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
> ref->is_head = 0;
> ref->in_tree = 1;
> ref->for_cow = for_cow;
> -
> - if (need_ref_seq(for_cow, ref_root))
> - seq = btrfs_get_tree_mod_seq(fs_info, &trans->delayed_ref_elem);
> ref->seq = seq;
>
> full_ref = btrfs_delayed_node_to_tree_ref(ref);
> @@ -741,9 +738,6 @@ static noinline void add_delayed_data_ref(struct btrfs_fs_info *fs_info,
> ref->is_head = 0;
> ref->in_tree = 1;
> ref->for_cow = for_cow;
> -
> - if (need_ref_seq(for_cow, ref_root))
> - seq = btrfs_get_tree_mod_seq(fs_info, &trans->delayed_ref_elem);
> ref->seq = seq;
>
> full_ref = btrfs_delayed_node_to_data_ref(ref);
> @@ -817,8 +811,6 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
> num_bytes, parent, ref_root, level, action,
> for_cow);
> spin_unlock(&delayed_refs->lock);
> - if (need_ref_seq(for_cow, ref_root))
> - btrfs_qgroup_record_ref(trans, &ref->node, extent_op);
>
> return 0;
> }
> @@ -865,8 +857,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
> num_bytes, parent, ref_root, owner, offset,
> action, for_cow);
> spin_unlock(&delayed_refs->lock);
> - if (need_ref_seq(for_cow, ref_root))
> - btrfs_qgroup_record_ref(trans, &ref->node, extent_op);
>
> return 0;
> }
> diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h
> index db71a37..b747180 100644
> --- a/fs/btrfs/delayed-ref.h
> +++ b/fs/btrfs/delayed-ref.h
> @@ -241,25 +241,6 @@ int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info,
> u64 seq);
>
> /*
> - * delayed refs with a ref_seq > 0 must be held back during backref walking.
> - * this only applies to items in one of the fs-trees. for_cow items never need
> - * to be held back, so they won't get a ref_seq number.
> - */
> -static inline int need_ref_seq(int for_cow, u64 rootid)
> -{
> - if (for_cow)
> - return 0;
> -
> - if (rootid == BTRFS_FS_TREE_OBJECTID)
> - return 1;
> -
> - if ((s64)rootid >= (s64)BTRFS_FIRST_FREE_OBJECTID)
> - return 1;
> -
> - return 0;
> -}
> -
> -/*
> * a node might live in a head or a regular ref, this lets you
> * test for the proper type to use.
> */
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index 4a1871c..3eb27b9 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -2151,6 +2151,7 @@ int open_ctree(struct super_block *sb,
> spin_lock_init(&fs_info->free_chunk_lock);
> spin_lock_init(&fs_info->tree_mod_seq_lock);
> spin_lock_init(&fs_info->super_lock);
> + spin_lock_init(&fs_info->qgroup_op_lock);
> spin_lock_init(&fs_info->buffer_lock);
> rwlock_init(&fs_info->tree_mod_log_lock);
> mutex_init(&fs_info->reloc_mutex);
> @@ -2175,6 +2176,7 @@ int open_ctree(struct super_block *sb,
> atomic_set(&fs_info->async_submit_draining, 0);
> atomic_set(&fs_info->nr_async_bios, 0);
> atomic_set(&fs_info->defrag_running, 0);
> + atomic_set(&fs_info->qgroup_op_seq, 0);
> atomic64_set(&fs_info->tree_mod_seq, 0);
> fs_info->sb = sb;
> fs_info->max_inline = 8192 * 1024;
> @@ -2282,6 +2284,7 @@ int open_ctree(struct super_block *sb,
> spin_lock_init(&fs_info->qgroup_lock);
> mutex_init(&fs_info->qgroup_ioctl_lock);
> fs_info->qgroup_tree = RB_ROOT;
> + fs_info->qgroup_op_tree = RB_ROOT;
> INIT_LIST_HEAD(&fs_info->dirty_qgroups);
> fs_info->qgroup_seq = 1;
> fs_info->quota_enabled = 0;
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index 03b536c..71673d6 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -680,7 +680,6 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(
> * @root_objectid: the root objectid that we are modifying for this extent.
> * @bytenr: the byte we are modifying the reference for
> * @num_bytes: the number of bytes we are locking.
> - * @for_cow: if this operation is for cow then we don't need to lock
> * @block_group: we will store the block group we looked up so that the unlock
> * doesn't have to do another search.
> * @cached_state: this is for caching our location so when we unlock we don't
> @@ -689,14 +688,13 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(
> * This can return -ENOMEM if we cannot allocate our extent state.
> */
> int lock_ref(struct btrfs_fs_info *fs_info, u64 root_objectid, u64 bytenr,
> - u64 num_bytes, int for_cow,
> - struct btrfs_block_group_cache **block_group,
> + u64 num_bytes, struct btrfs_block_group_cache **block_group,
> struct extent_state **cached_state)
> {
> struct btrfs_block_group_cache *cache;
> int ret;
>
> - if (!fs_info->quota_enabled || !need_ref_seq(for_cow, root_objectid))
> + if (!fs_info->quota_enabled || !is_fstree(root_objectid))
> return 0;
>
> cache = btrfs_lookup_block_group(fs_info, bytenr);
> @@ -721,7 +719,6 @@ int lock_ref(struct btrfs_fs_info *fs_info, u64 root_objectid, u64 bytenr,
> * @root_objectid: the root objectid that we are modifying for this extent.
> * @bytenr: the byte we are modifying the reference for
> * @num_bytes: the number of bytes we are locking.
> - * @for_cow: if this ref update is for cow we didn't take the lock.
> * @block_group: the block_group we got from lock_ref.
> * @cached_state: this is for caching our location so when we unlock we don't
> * have to do a tree search.
> @@ -729,13 +726,12 @@ int lock_ref(struct btrfs_fs_info *fs_info, u64 root_objectid, u64 bytenr,
> * This can return -ENOMEM if we fail to allocate an extent state.
> */
> int unlock_ref(struct btrfs_fs_info *fs_info, u64 root_objectid, u64 bytenr,
> - u64 num_bytes, int for_cow,
> - struct btrfs_block_group_cache *block_group,
> + u64 num_bytes, struct btrfs_block_group_cache *block_group,
> struct extent_state **cached_state)
> {
> int ret;
>
> - if (!fs_info->quota_enabled || !need_ref_seq(for_cow, root_objectid))
> + if (!fs_info->quota_enabled || !is_fstree(root_objectid))
> return 0;
>
> ret = unlock_extent_cached(&block_group->ref_lock, bytenr,
> @@ -1342,7 +1338,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;
> @@ -1378,6 +1374,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);
> @@ -1533,6 +1530,193 @@ static int find_next_key(struct btrfs_path *path, int level,
> }
>
> /*
> + * Look at an inline extent backref to see if one of the references matches the
> + * root we are looking for.
> + */
> +static int find_root_in_inline_backref(struct btrfs_trans_handle *trans,
> + struct btrfs_path *path, u64 ref_root,
> + int slot, int add_shared,
> + int *refs_to_add, int *shared_refs)
> +{
> + struct extent_buffer *leaf = path->nodes[0];
> + struct btrfs_extent_item *ei;
> + struct btrfs_extent_data_ref *dref;
> + struct btrfs_extent_inline_ref *iref;
> + struct btrfs_key key;
> + u64 item_size;
> + u64 flags;
> + u64 root;
> + u64 refs;
> + u64 parent;
> + unsigned long ptr;
> + unsigned long end;
> + int type;
> + int ret = 0;
> + bool found = false;
> +
> + item_size = btrfs_item_size_nr(leaf, slot);
> +#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
> + /* We really shouldn't have V0 references with an fs that has quotas. */
> + if (item_size < sizeof(*ei))
> + return -EINVAL;
> +#endif
> + btrfs_item_key_to_cpu(leaf, &key, slot);
> + ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
> + flags = btrfs_extent_flags(leaf, ei);
> +
> + ptr = (unsigned long)(ei + 1);
> + end = (unsigned long)ei + item_size;
> +
> + if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK &&
> + key.type == BTRFS_EXTENT_ITEM_KEY) {
> + ptr += sizeof(struct btrfs_tree_block_info);
> + ASSERT(ptr <= end);
> + }
> +
> + while (!found && ptr < end) {
> + iref = (struct btrfs_extent_inline_ref *)ptr;
> + type = btrfs_extent_inline_ref_type(leaf, iref);
> +
> + switch (type) {
> + case BTRFS_EXTENT_DATA_REF_KEY:
> + dref = (struct btrfs_extent_data_ref *)(&iref->offset);
> + root = btrfs_extent_data_ref_root(leaf, dref);
> + if (root != ref_root)
> + break;
> + refs = btrfs_extent_data_ref_count(leaf, dref);
> + if (refs > (u64)(*refs_to_add))
> + found = true;
> + else
> + *refs_to_add -= (int)refs;
> + break;
> + case BTRFS_TREE_BLOCK_REF_KEY:
> + if (btrfs_extent_inline_ref_offset(leaf, iref) !=
> + ref_root)
> + break;
> + if (*refs_to_add == 0)
> + found = true;
> + (*refs_to_add)--;
> + break;
> + case BTRFS_SHARED_DATA_REF_KEY:
> + case BTRFS_SHARED_BLOCK_REF_KEY:
> + (*shared_refs)++;
> + if (!add_shared)
> + break;
> + parent = btrfs_extent_inline_ref_offset(leaf, iref);
> + ret = btrfs_qgroup_add_shared_ref(trans, ref_root,
> + parent);
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> + if (ret || found)
> + break;
> + ptr += btrfs_extent_inline_ref_size(type);
> + }
> + if (found)
> + ret = 1;
> + return ret;
> +}
> +
> +/*
> + * This will look for the given root ref from path. This assumes that path is
> + * pointing at the extent item for the extent you want to check.
> + */
> +static int root_has_ref(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, struct btrfs_path *path,
> + u64 bytenr, u64 ref_root, int can_search_forward,
> + int add_shared, int refs_to_add, int *shared_refs)
> +{
> + struct extent_buffer *leaf = path->nodes[0];
> + struct btrfs_extent_data_ref *ref;
> + struct btrfs_key key;
> + u64 refs;
> + u64 found_root;
> + int slot = path->slots[0];
> + int ret = 0;
> + bool found = false;
> +
> + root = root->fs_info->extent_root;
> +
> + /* Return 1 so we don't do any of the accounting */
> + if (!root->fs_info->quota_enabled || !is_fstree(ref_root))
> + return 1;
> +
> + while (!found) {
> + btrfs_item_key_to_cpu(leaf, &key, slot);
> + if (key.objectid != bytenr)
> + break;
> + if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY)
> + goto next;
> + switch (key.type) {
> + case BTRFS_EXTENT_ITEM_KEY:
> + case BTRFS_METADATA_ITEM_KEY:
> + ret = find_root_in_inline_backref(trans, path,
> + ref_root, slot,
> + add_shared,
> + &refs_to_add,
> + shared_refs);
> + if (ret > 0)
> + found = true;
> + break;
> + case BTRFS_EXTENT_DATA_REF_KEY:
> + ref = btrfs_item_ptr(leaf, slot,
> + struct btrfs_extent_data_ref);
> + found_root = btrfs_extent_data_ref_root(leaf, ref);
> + if (found_root != ref_root)
> + break;
> + refs = btrfs_extent_data_ref_count(leaf, ref);
> + if (refs > (u64)refs_to_add)
> + found = true;
> + else
> + refs_to_add -= (int)refs;
> + break;
> + case BTRFS_TREE_BLOCK_REF_KEY:
> + if (key.offset != ref_root)
> + break;
> + if (!refs_to_add)
> + found = true;
> + refs_to_add--;
> + break;
> + case BTRFS_SHARED_DATA_REF_KEY:
> + case BTRFS_SHARED_BLOCK_REF_KEY:
> + (*shared_refs)++;
> + if (!add_shared)
> + break;
> + ret = btrfs_qgroup_add_shared_ref(trans, ref_root,
> + key.offset);
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> + if (ret || found)
> + break;
> +next:
> + slot++;
> + if (slot >= btrfs_header_nritems(leaf)) {
> + if (!can_search_forward) {
> + ret = -EAGAIN;
> + break;
> + }
> + ret = btrfs_next_leaf(root, path);
> + if (ret < 0)
> + break;
> + if (ret > 0) {
> + ret = 0;
> + break;
> + }
> + leaf = path->nodes[0];
> + slot = 0;
> + }
> + }
> + if (found)
> + ret = 1;
> + return ret;
> +}
> +
> +/*
> * look for inline back ref. if back ref is found, *ref_ret is set
> * to the address of inline back ref, and 0 is returned.
> *
> @@ -1834,7 +2018,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;
> @@ -1878,6 +2063,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;
> @@ -1898,9 +2084,11 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
> u64 bytenr, u64 num_bytes, u64 parent,
> u64 root_objectid, u64 owner,
> u64 offset, int refs_to_add,
> - struct btrfs_delayed_extent_op *extent_op)
> + struct btrfs_delayed_extent_op *extent_op,
> + int *new_ref)
> {
> struct btrfs_extent_inline_ref *iref;
> + int shared_refs = 0;
> int ret;
>
> ret = lookup_inline_extent_backref(trans, root, path, &iref,
> @@ -1909,8 +2097,33 @@ 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) {
> + /*
> + * We want to quickly see if we are adding a ref for a root that
> + * already holds a ref on this backref, so we'll search forward
> + * as much as we can to see if that's the case. This is to keep
> + * us from searching again if we don't have to, otherwise we'll
> + * have to do this whole search over again to make sure we
> + * really don't have another guy. Keep in mind this does
> + * nothing if quotas aren't enabled.
> + */
> + ret = root_has_ref(trans, root, path, bytenr, root_objectid, 0,
> + 0, 0, &shared_refs);
> + if (ret <= 0) {
> + /*
> + * If we got an error back from root_has_ref or we
> + * tripped over a shared ref then we need to make sure
> + * the caller re-calls root_has_ref to either verify
> + * this root is not already pointing at this bytenr or
> + * in the case of shared_refs we add the shared refs we
> + * find.
> + */
> + if (shared_refs || ret < 0)
> + *new_ref = 2;
> + else
> + *new_ref = 1;
> + }
> setup_inline_extent_backref(root, path, iref, parent,
> root_objectid, owner, offset,
> refs_to_add, extent_op);
> @@ -1942,17 +2155,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;
> @@ -2045,11 +2260,16 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
> u64 owner, u64 offset, int refs_to_add,
> 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 new_ref = 0;
> + int shared_refs = 0;
> int ret;
> + enum btrfs_qgroup_operation_type type = BTRFS_QGROUP_OPER_ADD_EXCL;
>
> path = btrfs_alloc_path();
> if (!path)
> @@ -2058,16 +2278,75 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
> 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)
> + refs_to_add, extent_op, &new_ref);
> + if ((ret < 0 && ret != -EAGAIN) || (!ret && !new_ref))
> + 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 && new_ref) {
> + int shared_refs = 0;
> +
> + 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;
> +
> + /* Parent doesn't matter for add operations */
> + ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid,
> + bytenr, num_bytes, 0, type);
> + if (ret < 0)
> + goto out;
> +
> + /* There are no shared refs, so we can just exit now. */
> + if (new_ref == 1)
> + goto out;
> + btrfs_release_path(path);
> + path->leave_spinning = 0;
> + ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key,
> + path, 0, 0);
> + if (ret < 0)
> + goto out;
> + ASSERT(ret == 0);
> +
> + /*
> + * Have to pass the refs we added since we will have added this
> + * backref already so we needto make sure there wasn't any other
> + * refs for this root.
> + */
> + ret = root_has_ref(trans, root, path, bytenr, root_objectid,
> + 1, 1, refs_to_add, &shared_refs);
> + if (ret < 0)
> + goto out;
> + /*
> + * Upon a second search we found our root referencing this
> + * bytenr already.
> + */
> + if (ret > 0)
> + btrfs_remove_last_qgroup_operation(trans, fs_info,
> + root_objectid);
> + ret = 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);
> @@ -2075,9 +2354,37 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
> btrfs_mark_buffer_dirty(leaf);
> btrfs_release_path(path);
>
> + /*
> + * Do qgroup accounting. We go ahead and add the ref first in case
> + * there are no matches, since in the other case we'd have to run
> + * root_has_ref twice, once without add_shared set and then again with
> + * add_shared set if there were any shared refs.
> + */
> + if (is_fstree(root_objectid) && fs_info->quota_enabled) {
> + ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid,
> + bytenr, num_bytes, 0, type);
> + if (ret)
> + goto out;
> + path->leave_spinning = 0;
> + ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key,
> + path, 0, 0);
> + if (ret < 0)
> + goto out;
> + ASSERT(ret == 0);
> +
> + ret = root_has_ref(trans, root, path, bytenr, root_objectid,
> + 1, 1, 0, &shared_refs);
> + if (ret < 0)
> + goto out;
> + if (ret > 0)
> + btrfs_remove_last_qgroup_operation(trans, fs_info,
> + root_objectid);
> + btrfs_release_path(path);
> + ret = 0;
> + }
> +
> 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,
> @@ -2114,11 +2421,10 @@ 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;
>
> ret = lock_ref(root->fs_info, ref->root, node->bytenr, node->num_bytes,
> - node->for_cow, &block_group, &cached_state);
> + &block_group, &cached_state);
> if (ret)
> return ret;
> if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
> @@ -2144,8 +2450,7 @@ static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
> BUG();
> }
> err = unlock_ref(root->fs_info, ref->root, node->bytenr,
> - node->num_bytes, node->for_cow, block_group,
> - &cached_state);
> + node->num_bytes, block_group, &cached_state);
> return ret ? ret : err;
> }
>
> @@ -2282,8 +2587,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) {
> @@ -2295,7 +2599,7 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
> }
>
> ret = lock_ref(root->fs_info, ref->root, node->bytenr, node->num_bytes,
> - node->for_cow, &block_group, &cached_state);
> + &block_group, &cached_state);
> if (ret)
> return ret;
> BUG_ON(node->ref_mod != 1);
> @@ -2318,8 +2622,7 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
> BUG();
> }
> err = unlock_ref(root->fs_info, ref->root, node->bytenr,
> - node->num_bytes, node->for_cow, block_group,
> - &cached_state);
> + node->num_bytes, block_group, &cached_state);
> return ret ? ret : err;
> }
>
> @@ -2633,42 +2936,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 int refs_newer(struct btrfs_delayed_ref_root *delayed_refs, int seq,
> int count)
> {
> @@ -2754,8 +3021,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;
> INIT_LIST_HEAD(&cluster);
> if (count == 0) {
> @@ -2829,6 +3094,7 @@ again:
> btrfs_abort_transaction(trans, root, ret);
> atomic_dec(&delayed_refs->procs_running_refs);
> wake_up(&delayed_refs->wait);
> + btrfs_delayed_qgroup_accounting(trans, root->fs_info);
> return ret;
> }
>
> @@ -2910,6 +3176,9 @@ out:
> wake_up(&delayed_refs->wait);
>
> spin_unlock(&delayed_refs->lock);
> + ret = btrfs_delayed_qgroup_accounting(trans, root->fs_info);
> + if (ret)
> + return ret;
> assert_qgroups_uptodate(trans);
> return 0;
> }
> @@ -5796,6 +6065,8 @@ 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);
>
> @@ -5846,7 +6117,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;
> @@ -5970,6 +6241,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);
> /*
> @@ -5985,7 +6257,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;
> @@ -6006,6 +6278,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) {
> @@ -6028,6 +6301,35 @@ 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 && info->quota_enabled &&
> + is_fstree(root_objectid)) {
> + int shared_refs = 0;
> +
> + ret = btrfs_qgroup_record_ref(trans, info, root_objectid,
> + bytenr, num_bytes, parent, type);
> + /*
> + * If we deleted the extent item then we can just bail without
> + * having to look for more extents.
> + */
> + if (ret < 0 || type == BTRFS_QGROUP_OPER_SUB_EXCL)
> + goto out;
> + path->leave_spinning = 0;
> + ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
> + if (ret < 0)
> + goto out;
> + ASSERT(ret == 0);
> + ret = root_has_ref(trans, root, path, bytenr, root_objectid, 1,
> + 0, 0, &shared_refs);
> + if (ret < 0)
> + goto out;
> + if (ret > 0)
> + btrfs_remove_last_qgroup_operation(trans, info,
> + root_objectid);
> + ret = 0;
> + }
> out:
> btrfs_free_path(path);
> return ret;
> @@ -6899,6 +7201,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, 0,
> + BTRFS_QGROUP_OPER_ADD_EXCL);
> + 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",
> @@ -6923,6 +7232,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);
>
> @@ -6956,6 +7266,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);
> @@ -6977,6 +7288,12 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
> btrfs_mark_buffer_dirty(leaf);
> btrfs_free_path(path);
>
> + ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid,
> + ins->objectid, num_bytes, 0,
> + BTRFS_QGROUP_OPER_ADD_EXCL);
> + 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",
> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
> index bd0b058..1ecc528 100644
> --- a/fs/btrfs/qgroup.c
> +++ b/fs/btrfs/qgroup.c
> @@ -84,8 +84,8 @@ struct btrfs_qgroup {
> /*
> * temp variables for accounting operations
> */
> - u64 tag;
> - u64 refcnt;
> + u64 old_refcnt;
> + u64 new_refcnt;
> };
>
> /*
> @@ -98,6 +98,23 @@ struct btrfs_qgroup_list {
> struct btrfs_qgroup *member;
> };
>
> +struct qgroup_shared_ref {
> + u64 parent;
> + u64 ref_root;
> + struct list_head list;
> +};
> +
> +struct btrfs_qgroup_operation {
> + u64 ref_root;
> + u64 bytenr;
> + u64 num_bytes;
> + u64 seq;
> + enum btrfs_qgroup_operation_type type;
> + struct rb_node n;
> + struct list_head list;
> + struct list_head shared_refs;
> +};
> +
> static int
> qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
> int init_flags);
> @@ -1174,33 +1191,322 @@ out:
> mutex_unlock(&fs_info->qgroup_ioctl_lock);
> return ret;
> }
> +static int comp_oper(struct btrfs_qgroup_operation *oper1,
> + struct btrfs_qgroup_operation *oper2)
> +{
> + if (oper1->bytenr < oper2->bytenr)
> + return -1;
> + if (oper1->bytenr > oper2->bytenr)
> + return 1;
> + if (oper1->seq < oper2->seq)
> + return -1;
> + if (oper1->seq > oper2->seq)
> + return -1;
> + if (oper1->ref_root < oper2->ref_root)
> + return -1;
> + if (oper1->ref_root > oper2->ref_root)
> + return 1;
> + if (oper1->type < oper2->type)
> + return -1;
> + if (oper1->type > oper2->type)
> + return 1;
> + return 0;
> +}
> +
> +/* Looks up the first operation for the given bytenr. */
> +static struct btrfs_qgroup_operation *
> +lookup_first_oper(struct btrfs_fs_info *fs_info, u64 bytenr)
> +{
> + struct rb_node *n;
> + struct btrfs_qgroup_operation *oper, *prev = NULL;
> +
> + spin_lock(&fs_info->qgroup_op_lock);
> + n = fs_info->qgroup_op_tree.rb_node;
> + while (n) {
> + oper = rb_entry(n, struct btrfs_qgroup_operation, n);
> + if (oper->bytenr < bytenr) {
> + n = n->rb_right;
> + } else if (oper->bytenr > bytenr) {
> + n = n->rb_left;
> + } else {
> + prev = oper;
> + n = n->rb_left;
> + }
> + }
> + spin_unlock(&fs_info->qgroup_op_lock);
> + return prev;
> +}
> +
> +static int insert_qgroup_oper(struct btrfs_fs_info *fs_info,
> + struct btrfs_qgroup_operation *oper)
> +{
> + struct rb_node **p;
> + struct rb_node *parent = NULL;
> + struct btrfs_qgroup_operation *cur;
> + int cmp;
> +
> + spin_lock(&fs_info->qgroup_op_lock);
> + p = &fs_info->qgroup_op_tree.rb_node;
> + while (*p) {
> + parent = *p;
> + cur = rb_entry(parent, struct btrfs_qgroup_operation, n);
> + cmp = comp_oper(cur, oper);
> + if (cmp < 0) {
> + p = &(*p)->rb_right;
> + } else if (cmp) {
> + p = &(*p)->rb_left;
> + } else {
> + spin_unlock(&fs_info->qgroup_op_lock);
> + return -EEXIST;
> + }
> + }
> + rb_link_node(&oper->n, parent, p);
> + rb_insert_color(&oper->n, &fs_info->qgroup_op_tree);
> + spin_unlock(&fs_info->qgroup_op_lock);
> + return 0;
> +}
>
> /*
> - * btrfs_qgroup_record_ref is called when the ref is added or deleted. it puts
> - * the modification into a list that's later used by btrfs_end_transaction to
> - * pass the recorded modifications on to btrfs_qgroup_account_ref.
> + * Record a quota operation for processing later on.
> + * @trans: the transaction we are adding the delayed op to.
> + * @fs_info: the fs_info for this fs.
> + * @ref_root: the root of the reference we are acting on,
> + * @num_bytes: the number of bytes in the reference.
> + * @parent: if we are removing a shared ref then this will be set.
> + * @type: the type of operation this is.
> + *
> + * We just add it to our trans qgroup_ref_list and carry on and process these
> + * operations in order at some later point. If the reference root isn't a fs
> + * root then we don't bother with doing anything.
> + *
> + * MUST BE HOLDING THE REF LOCK.
> */
> int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
> - struct btrfs_delayed_ref_node *node,
> - struct btrfs_delayed_extent_op *extent_op)
> + struct btrfs_fs_info *fs_info, u64 ref_root,
> + u64 bytenr, u64 num_bytes, u64 parent,
> + enum btrfs_qgroup_operation_type type)
> {
> - struct qgroup_update *u;
> + struct btrfs_qgroup_operation *oper;
> + int ret;
>
> - BUG_ON(!trans->delayed_ref_elem.seq);
> - u = kmalloc(sizeof(*u), GFP_NOFS);
> - if (!u)
> + if (!is_fstree(ref_root) || !fs_info->quota_enabled)
> + return 0;
> +
> + oper = kmalloc(sizeof(*oper), GFP_NOFS);
> + if (!oper)
> return -ENOMEM;
>
> - u->node = node;
> - u->extent_op = extent_op;
> - list_add_tail(&u->list, &trans->qgroup_ref_list);
> + oper->ref_root = ref_root;
> + oper->bytenr = bytenr;
> + oper->num_bytes = num_bytes;
> + oper->type = type;
> + oper->seq = atomic_inc_return(&fs_info->qgroup_op_seq);
> + INIT_LIST_HEAD(&oper->shared_refs);
> + ret = insert_qgroup_oper(fs_info, oper);
> + if (ret) {
> + /* Shouldn't happen so have an assert for developers */
> + ASSERT(0);
> + kfree(oper);
> + return ret;
> + }
> + list_add_tail(&oper->list, &trans->qgroup_ref_list);
>
> + /*
> + * If we are removing a shared extent ref we could possibly have another
> + * operation outstanding that needs to lookup this shared ref, so we
> + * need to go and look if there exists such a person and update their
> + * shared ref dependancy with the root ref so it knows if it needs to do
> + * anything. We are relying on the ref lock to protect the actual
> + * operation here.
> + */
> + if (unlikely(parent) && (type == BTRFS_QGROUP_OPER_SUB_EXCL ||
> + type == BTRFS_QGROUP_OPER_SUB_SHARED)) {
> + u64 seq = oper->seq;
> + struct qgroup_shared_ref *ref;
> + struct rb_node *n;
> +
> + oper = lookup_first_oper(fs_info, bytenr);
> + if (!oper)
> + return 0;
> + do {
> + list_for_each_entry(ref, &oper->shared_refs, list) {
> + if (ref->parent == parent)
> + ref->ref_root = ref_root;
> + }
> + spin_lock(&fs_info->qgroup_op_lock);
> + n = rb_next(&oper->n);
> + if (n) {
> + oper = rb_entry(n,
> + struct btrfs_qgroup_operation,
> + n);
> + if (oper->bytenr != bytenr || oper->seq >= seq)
> + n = NULL;
> + }
> + spin_unlock(&fs_info->qgroup_op_lock);
> + } while (n);
> + }
> return 0;
> }
>
> -static int qgroup_account_ref_step1(struct btrfs_fs_info *fs_info,
> - struct ulist *roots, struct ulist *tmp,
> - u64 seq)
> +/*
> + * Remove the last qgroup operation that was added.
> + * @trans: the trans handle.
> + * @fs_info: the fs_info for this file system.
> + * @ref_root: the root for the reference.
> + *
> + * Sometimes we may pre-emptively add an operation only to find after further
> + * investigation that we no longer need it, so this will pop the last guy off
> + * the list and free him up.
> + */
> +void btrfs_remove_last_qgroup_operation(struct btrfs_trans_handle *trans,
> + struct btrfs_fs_info *fs_info,
> + u64 ref_root)
> +{
> + struct btrfs_qgroup_operation *oper;
> +
> + if (!is_fstree(ref_root))
> + return;
> +
> + oper = list_entry(trans->qgroup_ref_list.prev,
> + struct btrfs_qgroup_operation, list);
> + ASSERT(oper->ref_root == ref_root);
> + list_del_init(&oper->list);
> + while (!list_empty(&oper->shared_refs)) {
> + struct qgroup_shared_ref *ref;
> + ref = list_first_entry(&oper->shared_refs,
> + struct qgroup_shared_ref, list);
> + list_del_init(&ref->list);
> + kfree(ref);
> + }
> + spin_lock(&fs_info->qgroup_op_lock);
> + rb_erase(&oper->n, &fs_info->qgroup_op_tree);
> + spin_unlock(&fs_info->qgroup_op_lock);
> + kfree(oper);
> +}
> +
> +/*
> + * Record a shared ref in the most recent qgroup operation added.
> + * @trans: the trans handle for this operation.
> + * @ref_root: the ref_root this operation was for, this is to make sure we
> + * actually need to do anything at all. Use the same ref_root we called
> + * btrfs_qgroup_record_ref with.
> + * @parent: the parent of the shared ref.
> + *
> + * This is meant to be called directly after calling btrfs_qgroup_record_ref
> + * for the ref we care about, it does no searching to make sure we're on the
> + * right qgroup operation.
> + *
> + * MUST BE HOLDING THE REF LOCK.
> + */
> +int btrfs_qgroup_add_shared_ref(struct btrfs_trans_handle *trans, u64 ref_root,
> + u64 parent)
> +{
> + struct qgroup_shared_ref *ref;
> + struct btrfs_qgroup_operation *oper;
> + int ret = 0;
> +
> + if (!is_fstree(ref_root))
> + return 0;
> + ref = kmalloc(sizeof(*ref), GFP_NOFS);
> + if (!ref)
> + return -ENOMEM;
> + ref->parent = parent;
> + ref->ref_root = 0;
> + ASSERT(!list_empty(&trans->qgroup_ref_list));
> + oper = list_entry(trans->qgroup_ref_list.prev,
> + struct btrfs_qgroup_operation, list);
> + list_add_tail(&ref->list, &oper->shared_refs);
> + return ret;
> +}
> +
> +/*
> + * The easy accounting, if we are adding/removing the only ref for an extent
> + * then this qgroup and all of the parent qgroups get their refrence and
> + * exclusive counts adjusted.
> + */
> +static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
> + struct btrfs_qgroup_operation *oper)
> +{
> + struct btrfs_qgroup *qgroup;
> + struct ulist *tmp;
> + struct btrfs_qgroup_list *glist;
> + struct ulist_node *unode;
> + struct ulist_iterator uiter;
> + int sign = 0;
> + int ret = 0;
> +
> + tmp = ulist_alloc(GFP_NOFS);
> + if (!tmp)
> + return -ENOMEM;
> +
> + spin_lock(&fs_info->qgroup_lock);
> + if (!fs_info->quota_root)
> + goto out;
> + qgroup = find_qgroup_rb(fs_info, oper->ref_root);
> + if (!qgroup)
> + goto out;
> + switch (oper->type) {
> + case BTRFS_QGROUP_OPER_ADD_EXCL:
> + sign = 1;
> + break;
> + case BTRFS_QGROUP_OPER_SUB_EXCL:
> + sign = -1;
> + break;
> + default:
> + ASSERT(0);
> + }
> + qgroup->rfer += sign * oper->num_bytes;
> + qgroup->rfer_cmpr += sign * oper->num_bytes;
> +
> + WARN_ON(sign < 0 && qgroup->excl < oper->num_bytes);
> + qgroup->excl += sign * oper->num_bytes;
> + qgroup->excl_cmpr += sign * oper->num_bytes;
> +
> + 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, (u64)glist->group,
> + GFP_ATOMIC);
> + if (ret < 0)
> + goto out;
> + }
> +
> + /* Iterate all of the parents and adjust their reference counts */
> + ULIST_ITER_INIT(&uiter);
> + while ((unode = ulist_next(tmp, &uiter))) {
> + qgroup = (struct btrfs_qgroup *)unode->aux;
> + qgroup->rfer += sign * oper->num_bytes;
> + qgroup->rfer_cmpr += sign * oper->num_bytes;
> + qgroup->excl += sign * oper->num_bytes;
> + if (sign < 0)
> + WARN_ON(qgroup->excl < oper->num_bytes);
> + qgroup->excl_cmpr += sign * oper->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,
> + (u64)glist->group, GFP_ATOMIC);
> + if (ret < 0)
> + goto out;
> + }
> + }
> + ret = 0;
> +out:
> + spin_unlock(&fs_info->qgroup_lock);
> + ulist_free(tmp);
> + return ret;
> +}
> +
> +/*
> + * Walk all of the roots that pointed to our bytenr and adjust their refcnts as
> + * properly.
> + */
> +static int qgroup_calc_old_refcnt(struct btrfs_fs_info *fs_info,
> + u64 root_to_skip, struct ulist *roots,
> + struct ulist *qgroups, u64 seq,
> + int *old_roots, int rescan)
> {
> struct ulist_node *unode;
> struct ulist_iterator uiter;
> @@ -1211,256 +1517,557 @@ static int qgroup_account_ref_step1(struct btrfs_fs_info *fs_info,
>
> ULIST_ITER_INIT(&uiter);
> while ((unode = ulist_next(roots, &uiter))) {
> + /* We don't count our current root here */
> + if (unode->val == root_to_skip)
> + continue;
> qg = find_qgroup_rb(fs_info, unode->val);
> if (!qg)
> continue;
> + /*
> + * We could have a pending removal of this same ref so we may
> + * not have actually found our ref root when doing
> + * btrfs_find_all_roots, so we need to keep track of how many
> + * old roots we find in case we removed ours and added a
> + * different one at the same time. I don't think this could
> + * happen in practice but that sort of thinking leads to pain
> + * and suffering and to the dark side.
> + */
> + (*old_roots)++;
>
> - ulist_reinit(tmp);
> - /* XXX id not needed */
> - ret = ulist_add(tmp, qg->qgroupid,
> - (u64)(uintptr_t)qg, GFP_ATOMIC);
> + ulist_reinit(qgroups);
> + ret = ulist_add(qgroups, qg->qgroupid, (u64)qg, GFP_ATOMIC);
> if (ret < 0)
> return ret;
> ULIST_ITER_INIT(&tmp_uiter);
> - while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
> + while ((tmp_unode = ulist_next(qgroups, &tmp_uiter))) {
> struct btrfs_qgroup_list *glist;
>
> - qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
> - if (qg->refcnt < seq)
> - qg->refcnt = seq + 1;
> + qg = (struct btrfs_qgroup *)tmp_unode->aux;
> + /*
> + * We use this sequence number to keep from having to
> + * run the whole list and 0 out the refcnt every time.
> + * We basically use sequnce as the known 0 count and
> + * then add 1 everytime we see a qgroup. This is how we
> + * get how many of the roots actually point up to the
> + * upper level qgroups in order to determine exclusive
> + * counts.
> + *
> + * For rescan we want to set old_refcnt to seq so our
> + * exclusive calculations end up correct.
> + */
> + if (rescan)
> + qg->old_refcnt = seq;
> + else if (qg->old_refcnt < seq)
> + qg->old_refcnt = seq + 1;
> else
> - ++qg->refcnt;
> + qg->old_refcnt++;
>
> + if (qg->new_refcnt < seq)
> + qg->new_refcnt = seq + 1;
> + else
> + qg->new_refcnt++;
> list_for_each_entry(glist, &qg->groups, next_group) {
> - ret = ulist_add(tmp, glist->group->qgroupid,
> - (u64)(uintptr_t)glist->group,
> - GFP_ATOMIC);
> + ret = ulist_add(qgroups, glist->group->qgroupid,
> + (u64)glist->group, GFP_ATOMIC);
> if (ret < 0)
> return ret;
> }
> }
> }
> + return 0;
> +}
> +
> +/*
> + * We need to walk forward in our operation tree and account for any roots that
> + * were deleted after we made this operation.
> + */
> +static int qgroup_account_deleted_refs(struct btrfs_fs_info *fs_info,
> + struct btrfs_qgroup_operation *oper,
> + struct ulist *qgroups, u64 seq,
> + int *old_roots)
> +{
> + struct ulist_node *unode;
> + struct ulist_iterator uiter;
> + struct btrfs_qgroup *qg;
> + struct btrfs_qgroup_operation *tmp;
> + struct rb_node *n;
> + int ret;
> +
> + ulist_reinit(qgroups);
> +
> + /*
> + * We only walk forward in the tree since we're only interested in
> + * removals that happened _after_ our operation.
> + */
> + spin_lock(&fs_info->qgroup_op_lock);
> + n = rb_next(&oper->n);
> + spin_unlock(&fs_info->qgroup_op_lock);
> + if (!n)
> + return 0;
> + tmp = rb_entry(n, struct btrfs_qgroup_operation, n);
> + while (tmp->bytenr == oper->bytenr) {
> + /*
> + * If it's not a removal we don't care, additions work out
> + * properly with our refcnt tracking.
> + */
> + if (tmp->type != BTRFS_QGROUP_OPER_SUB_SHARED &&
> + tmp->type != BTRFS_QGROUP_OPER_SUB_EXCL)
> + goto next;
> + qg = find_qgroup_rb(fs_info, oper->ref_root);
> + if (!qg)
> + goto next;
> + ret = ulist_add(qgroups, qg->qgroupid, (u64)qg, GFP_ATOMIC);
> + if (ret)
> + return ret;
> + (*old_roots)++;
> +next:
> + spin_lock(&fs_info->qgroup_op_lock);
> + n = rb_next(&tmp->n);
> + spin_unlock(&fs_info->qgroup_op_lock);
> + if (!n)
> + break;
> + tmp = rb_entry(n, struct btrfs_qgroup_operation, n);
> + }
> +
> + /* Ok now process the qgroups we found */
> + ULIST_ITER_INIT(&uiter);
> + while ((unode = ulist_next(qgroups, &uiter))) {
> + struct btrfs_qgroup_list *glist;
>
> + qg = (struct btrfs_qgroup *)unode->aux;
> + if (qg->old_refcnt < seq)
> + qg->old_refcnt = seq + 1;
> + else
> + qg->old_refcnt++;
> + if (qg->new_refcnt < seq)
> + qg->new_refcnt = seq + 1;
> + else
> + qg->new_refcnt++;
> + list_for_each_entry(glist, &qg->groups, next_group) {
> + ret = ulist_add(qgroups, glist->group->qgroupid,
> + (u64)glist->group, GFP_ATOMIC);
> + if (ret < 0)
> + return ret;
> + }
> + }
> return 0;
> }
>
> -static int qgroup_account_ref_step2(struct btrfs_fs_info *fs_info,
> - struct ulist *roots, struct ulist *tmp,
> - u64 seq, int sgn, u64 num_bytes,
> - struct btrfs_qgroup *qgroup)
> +/* Add refcnt for the newly added reference. */
> +static int qgroup_calc_new_refcnt(struct btrfs_fs_info *fs_info,
> + struct btrfs_qgroup_operation *oper,
> + struct btrfs_qgroup *qgroup,
> + struct ulist *roots, struct ulist *qgroups,
> + u64 seq)
> {
> struct ulist_node *unode;
> struct ulist_iterator uiter;
> struct btrfs_qgroup *qg;
> - struct btrfs_qgroup_list *glist;
> int ret;
>
> - ulist_reinit(tmp);
> - ret = ulist_add(tmp, qgroup->qgroupid, (uintptr_t)qgroup, GFP_ATOMIC);
> + ulist_reinit(qgroups);
> + ret = ulist_add(qgroups, qgroup->qgroupid, (u64)qgroup, GFP_ATOMIC);
> if (ret < 0)
> return ret;
> -
> ULIST_ITER_INIT(&uiter);
> - while ((unode = ulist_next(tmp, &uiter))) {
> - qg = (struct btrfs_qgroup *)(uintptr_t)unode->aux;
> - if (qg->refcnt < seq) {
> - /* not visited by step 1 */
> - qg->rfer += sgn * num_bytes;
> - qg->rfer_cmpr += sgn * num_bytes;
> - if (roots->nnodes == 0) {
> - qg->excl += sgn * num_bytes;
> - qg->excl_cmpr += sgn * num_bytes;
> - }
> - qgroup_dirty(fs_info, qg);
> - }
> - WARN_ON(qg->tag >= seq);
> - qg->tag = seq;
> + while ((unode = ulist_next(qgroups, &uiter))) {
> + struct btrfs_qgroup_list *glist;
>
> + qg = (struct btrfs_qgroup *)unode->aux;
> + if (oper->type == BTRFS_QGROUP_OPER_ADD_SHARED) {
> + if (qg->new_refcnt < seq)
> + qg->new_refcnt = seq + 1;
> + else
> + qg->new_refcnt++;
> + } else {
> + if (qg->old_refcnt < seq)
> + qg->old_refcnt = seq + 1;
> + else
> + qg->old_refcnt++;
> + }
> list_for_each_entry(glist, &qg->groups, next_group) {
> - ret = ulist_add(tmp, glist->group->qgroupid,
> - (uintptr_t)glist->group, GFP_ATOMIC);
> + ret = ulist_add(qgroups, glist->group->qgroupid,
> + (u64)glist->group, GFP_ATOMIC);
> if (ret < 0)
> return ret;
> }
> }
> -
> return 0;
> }
>
> -static int qgroup_account_ref_step3(struct btrfs_fs_info *fs_info,
> - struct ulist *roots, struct ulist *tmp,
> - u64 seq, int sgn, u64 num_bytes)
> +/*
> + * This adjusts the counters for all referenced qgroups if need be.
> + */
> +static int qgroup_adjust_counters(struct btrfs_fs_info *fs_info,
> + u64 root_to_skip, u64 num_bytes,
> + struct ulist *roots, struct ulist *qgroups,
> + u64 seq, int old_roots, int new_roots, int rescan)
> {
> struct ulist_node *unode;
> struct ulist_iterator uiter;
> struct btrfs_qgroup *qg;
> - struct ulist_node *tmp_unode;
> - struct ulist_iterator tmp_uiter;
> + u64 cur_new_count, cur_old_count;
> int ret;
>
> ULIST_ITER_INIT(&uiter);
> while ((unode = ulist_next(roots, &uiter))) {
> + struct btrfs_qgroup_list *glist;
> +
> + if (unode->val == root_to_skip)
> + continue;
> qg = find_qgroup_rb(fs_info, unode->val);
> if (!qg)
> continue;
>
> - ulist_reinit(tmp);
> - ret = ulist_add(tmp, qg->qgroupid, (uintptr_t)qg, GFP_ATOMIC);
> + ret = ulist_add(qgroups, qg->qgroupid, (u64)qg, GFP_ATOMIC);
> if (ret < 0)
> return ret;
> + list_for_each_entry(glist, &qg->groups, next_group) {
> + ret = ulist_add(qgroups, glist->group->qgroupid,
> + (u64)glist->group, GFP_ATOMIC);
> + if (ret < 0)
> + return ret;
> + }
> + }
>
> - ULIST_ITER_INIT(&tmp_uiter);
> - while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
> - struct btrfs_qgroup_list *glist;
> + ULIST_ITER_INIT(&uiter);
> + while ((unode = ulist_next(qgroups, &uiter))) {
> + bool dirty = false;
>
> - qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
> - if (qg->tag == seq)
> - continue;
> + qg = (struct btrfs_qgroup *)unode->aux;
> + /*
> + * Wasn't referenced before but is now, add to the reference
> + * counters.
> + */
> + if (qg->old_refcnt <= seq && qg->new_refcnt > seq) {
> + qg->rfer += num_bytes;
> + qg->rfer_cmpr += num_bytes;
> + dirty = true;
> + }
>
> - if (qg->refcnt - seq == roots->nnodes) {
> - qg->excl -= sgn * num_bytes;
> - qg->excl_cmpr -= sgn * num_bytes;
> - qgroup_dirty(fs_info, qg);
> - }
> + /*
> + * Was referenced before but isn't now, subtract from the
> + * reference counters.
> + */
> + if (qg->old_refcnt > seq && qg->new_refcnt <= seq) {
> + qg->rfer -= num_bytes;
> + qg->rfer_cmpr -= num_bytes;
> + dirty = true;
> + }
>
> - list_for_each_entry(glist, &qg->groups, next_group) {
> - ret = ulist_add(tmp, glist->group->qgroupid,
> - (uintptr_t)glist->group,
> - GFP_ATOMIC);
> - if (ret < 0)
> - return ret;
> - }
> + cur_old_count = qg->old_refcnt - seq;
> + cur_new_count = qg->new_refcnt - seq;
> +
> + /*
> + * If our refcount was the same as the roots previously but our
> + * new count isn't the same as the number of roots now then we
> + * went from having a exclusive reference on this range to not.
> + */
> + if (old_roots && cur_old_count == old_roots &&
> + cur_new_count != new_roots) {
> + qg->excl -= num_bytes;
> + qg->excl_cmpr -= num_bytes;
> + dirty = true;
> }
> - }
>
> + /*
> + * If we didn't reference all the roots before but now we do we
> + * have an exclusive reference to this range.
> + */
> + if ((!old_roots || (old_roots && cur_old_count != old_roots))
> + && cur_new_count == new_roots) {
> + qg->excl += num_bytes;
> + qg->excl_cmpr += num_bytes;
> + dirty = true;
> + }
> +
> + if (dirty)
> + qgroup_dirty(fs_info, qg);
> + }
> return 0;
> }
>
> /*
> - * btrfs_qgroup_account_ref is called for every ref that is added to or deleted
> - * from the fs. First, all roots referencing the extent are searched, and
> - * then the space is accounted accordingly to the different roots. The
> - * accounting algorithm works in 3 steps documented inline.
> + * If we added or removed a reference for a root and there were shared refs
> + * remaining then we need to go through and see if any of those refs lead back
> + * to our same root and if so we don't need to do anything with this operation.
> */
> -int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
> - struct btrfs_fs_info *fs_info,
> - struct btrfs_delayed_ref_node *node,
> - struct btrfs_delayed_extent_op *extent_op)
> +static int check_shared_refs(struct btrfs_fs_info *fs_info,
> + struct btrfs_qgroup_operation *oper)
> {
> - struct btrfs_root *quota_root;
> - u64 ref_root;
> - struct btrfs_qgroup *qgroup;
> struct ulist *roots = NULL;
> - u64 seq;
> + struct ulist_node *unode;
> + struct ulist_iterator uiter;
> + struct qgroup_shared_ref *ref;
> int ret = 0;
> - int sgn;
>
> - if (!fs_info->quota_enabled)
> - return 0;
> + /* Now process all of our shared ref dependancies */
> + while (!list_empty(&oper->shared_refs)) {
> + ref = list_first_entry(&oper->shared_refs,
> + struct qgroup_shared_ref, list);
> + if (ret) {
> + list_del_init(&ref->list);
> + kfree(ref);
> + continue;
> + }
> + if (ref->ref_root) {
> + if (ref->ref_root == oper->ref_root)
> + ret = 1;
> + goto next;
> + }
>
> - BUG_ON(!fs_info->quota_root);
> + ret = btrfs_find_all_roots(NULL, fs_info, ref->parent, 0,
> + &roots, 0);
> + if (ret < 0)
> + goto next;
>
> - if (node->type == BTRFS_TREE_BLOCK_REF_KEY ||
> - node->type == BTRFS_SHARED_BLOCK_REF_KEY) {
> - struct btrfs_delayed_tree_ref *ref;
> - ref = btrfs_delayed_node_to_tree_ref(node);
> - ref_root = ref->root;
> - } else if (node->type == BTRFS_EXTENT_DATA_REF_KEY ||
> - node->type == BTRFS_SHARED_DATA_REF_KEY) {
> - struct btrfs_delayed_data_ref *ref;
> - ref = btrfs_delayed_node_to_data_ref(node);
> - ref_root = ref->root;
> - } else {
> - BUG();
> + ULIST_ITER_INIT(&uiter);
> + while ((unode = ulist_next(roots, &uiter))) {
> + if (unode->val == oper->ref_root) {
> + ret = 1;
> + break;
> + }
> + }
> + ulist_free(roots);
> + roots = NULL;
> +next:
> + list_del_init(&ref->list);
> + kfree(ref);
> }
>
> - if (!is_fstree(ref_root)) {
> - /*
> - * non-fs-trees are not being accounted
> - */
> - return 0;
> - }
> + return ret;
> +}
>
> - switch (node->action) {
> - case BTRFS_ADD_DELAYED_REF:
> - case BTRFS_ADD_DELAYED_EXTENT:
> - sgn = 1;
> - seq = btrfs_tree_mod_seq_prev(node->seq);
> - break;
> - case BTRFS_DROP_DELAYED_REF:
> - sgn = -1;
> - seq = node->seq;
> - break;
> - case BTRFS_UPDATE_DELAYED_HEAD:
> - return 0;
> - default:
> - BUG();
> - }
> +/*
> + * If we share a reference across multiple roots then we may need to adjust
> + * various qgroups referenced and exclusive counters. The basic premise is this
> + *
> + * 1) We have seq to represent a 0 count. Instead of looping through all of the
> + * qgroups and resetting their refcount to 0 we just constantly bump this
> + * sequence number to act as the base reference count. This means that if
> + * anybody is equal to or below this sequence they were never referenced. We
> + * jack this sequence up by the number of roots we found each time in order to
> + * make sure we don't have any overlap.
> + *
> + * 2) We first search all the roots that reference the area _except_ the root
> + * we're acting on currently. This makes up the old_refcnt of all the qgroups
> + * before.
> + *
> + * 3) We walk all of the qgroups referenced by the root we are currently acting
> + * on, and will either adjust old_refcnt in the case of a removal or the
> + * new_refcnt in the case of an addition.
> + *
> + * 4) Finally we walk all the qgroups that are referenced by this range
> + * including the root we are acting on currently. We will adjust the counters
> + * based on the number of roots we had and will have after this operation.
> + *
> + * Take this example as an illustration
> + *
> + * [qgroup 1/0]
> + * / | \
> + * [qg 0/0] [qg 0/1] [qg 0/2]
> + * \ | /
> + * [ extent ]
> + *
> + * Say we are adding a reference that is covered by qg 0/0. The first step
> + * would give a refcnt of 1 to qg 0/1 and 0/2 and a refcnt of 2 to qg 1/0 with
> + * old_roots being 2. Because it is adding new_roots will be 1. We then go
> + * through qg 0/0 which will get the new_refcnt set to 1 and add 1 to qg 1/0's
> + * new_refcnt, bringing it to 3. We then walk through all of the qgroups, we
> + * notice that the old refcnt for qg 0/0 < the new refcnt, so we added a
> + * reference and thus must add the size to the referenced bytes. Everything
> + * else is the same so nothing else changes.
> + */
> +static int qgroup_shared_accounting(struct btrfs_fs_info *fs_info,
> + struct btrfs_qgroup_operation *oper)
> +{
> + struct ulist *roots = NULL;
> + struct ulist *qgroups;
> + struct btrfs_qgroup *qgroup;
> + u64 seq;
> + int old_roots = 0;
> + int new_roots = 0;
> + int ret = 0;
>
> - mutex_lock(&fs_info->qgroup_rescan_lock);
> - if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
> - if (fs_info->qgroup_rescan_progress.objectid <= node->bytenr) {
> - mutex_unlock(&fs_info->qgroup_rescan_lock);
> + if (unlikely(!list_empty(&oper->shared_refs))) {
> + ret = check_shared_refs(fs_info, oper);
> + if (ret < 0)
> + return ret;
> + if (ret)
> return 0;
> - }
> }
> - mutex_unlock(&fs_info->qgroup_rescan_lock);
>
> - /*
> - * the delayed ref sequence number we pass depends on the direction of
> - * the operation. for add operations, we pass
> - * tree_mod_log_prev_seq(node->seq) to skip
> - * the delayed ref's current sequence number, because we need the state
> - * of the tree before the add operation. for delete operations, we pass
> - * (node->seq) to include the delayed ref's current sequence number,
> - * because we need the state of the tree after the delete operation.
> - */
> - ret = btrfs_find_all_roots(trans, fs_info, node->bytenr, seq, &roots);
> - if (ret < 0)
> - return ret;
> + qgroups = ulist_alloc(GFP_NOFS);
> + if (!qgroups)
> + return -ENOMEM;
>
> + ret = btrfs_find_all_roots(NULL, fs_info, oper->bytenr, 0, &roots, 0);
> + if (ret < 0) {
> + ulist_free(qgroups);
> + return ret;
> + }
> spin_lock(&fs_info->qgroup_lock);
> -
> - quota_root = fs_info->quota_root;
> - if (!quota_root)
> - goto unlock;
> -
> - qgroup = find_qgroup_rb(fs_info, ref_root);
> + qgroup = find_qgroup_rb(fs_info, oper->ref_root);
> if (!qgroup)
> - goto unlock;
> + goto out;
> + seq = fs_info->qgroup_seq;
>
> /*
> - * step 1: for each old ref, visit all nodes once and inc refcnt
> + * So roots is the list of all the roots currently pointing at the
> + * bytenr, including the ref we are adding if we are adding, or not if
> + * we are removing a ref. So we pass in the ref_root to skip that root
> + * in our calculations. We set old_refnct and new_refcnt cause who the
> + * hell knows what everything looked like before, and it doesn't matter
> + * except...
> */
> - ulist_reinit(fs_info->qgroup_ulist);
> - seq = fs_info->qgroup_seq;
> - fs_info->qgroup_seq += roots->nnodes + 1; /* max refcnt */
> + ret = qgroup_calc_old_refcnt(fs_info, oper->ref_root, roots, qgroups,
> + seq, &old_roots, 0);
> + if (ret < 0)
> + goto out;
>
> - ret = qgroup_account_ref_step1(fs_info, roots, fs_info->qgroup_ulist,
> - seq);
> - if (ret)
> - goto unlock;
> + /*
> + * ...in the case of removals. If we had a removal before we got around
> + * to processing this operation then we need to find that guy and count
> + * his references as if they really existed so we don't end up screwing
> + * up the exclusive counts. Then whenever we go to process the delete
> + * everything will be grand and we can account for whatever exclusive
> + * changes need to be made there. We also have to pass in old_roots so
> + * we have an accurate count of the roots as it pertains to this
> + * operations view of the world.
> + */
> + ret = qgroup_account_deleted_refs(fs_info, oper, qgroups, seq,
> + &old_roots);
> + if (ret < 0)
> + goto out;
>
> /*
> - * step 2: walk from the new root
> + * We are adding our root, need to adjust up the number of roots,
> + * otherwise old_roots is the number of roots we want.
> */
> - ret = qgroup_account_ref_step2(fs_info, roots, fs_info->qgroup_ulist,
> - seq, sgn, node->num_bytes, qgroup);
> - if (ret)
> - goto unlock;
> + if (oper->type == BTRFS_QGROUP_OPER_ADD_SHARED) {
> + new_roots = old_roots + 1;
> + } else {
> + new_roots = old_roots;
> + old_roots++;
> + }
> + fs_info->qgroup_seq += old_roots + 1;
>
> /*
> - * step 3: walk again from old refs
> + * Now adjust the refcounts of the qgroups that care about this
> + * reference, either the old_count in the case of removal or new_count
> + * in the case of an addition.
> */
> - ret = qgroup_account_ref_step3(fs_info, roots, fs_info->qgroup_ulist,
> - seq, sgn, node->num_bytes);
> - if (ret)
> - goto unlock;
> + ret = qgroup_calc_new_refcnt(fs_info, oper, qgroup, roots, qgroups,
> + seq);
> + if (ret < 0)
> + goto out;
>
> -unlock:
> + /* We are doing this in case we have a pending delete */
> + ulist_reinit(qgroups);
> + ret = ulist_add(qgroups, qgroup->qgroupid, (u64)qgroup, GFP_ATOMIC);
> + if (ret < 0)
> + goto out;
> + ret = 0;
> +
> + /*
> + * And now the magic happens, bless Arne for having a pretty elegant
> + * solution for this.
> + */
> + qgroup_adjust_counters(fs_info, oper->ref_root, oper->num_bytes,
> + roots, qgroups, seq, old_roots, new_roots, 0);
> +out:
> spin_unlock(&fs_info->qgroup_lock);
> + ulist_free(qgroups);
> ulist_free(roots);
> + return ret;
> +}
> +
> +/*
> + * btrfs_qgroup_account_ref is called for every ref that is added to or deleted
> + * from the fs. First, all roots referencing the extent are searched, and
> + * then the space is accounted accordingly to the different roots. The
> + * accounting algorithm works in 3 steps documented inline.
> + */
> +static int btrfs_qgroup_account(struct btrfs_trans_handle *trans,
> + struct btrfs_fs_info *fs_info,
> + struct btrfs_qgroup_operation *oper)
> +{
> + struct btrfs_block_group_cache *cache;
> + struct extent_state *cached_state = NULL;
> + int ret = 0;
> + int err;
> +
> + if (!fs_info->quota_enabled)
> + return 0;
> +
> + BUG_ON(!fs_info->quota_root);
> +
> + mutex_lock(&fs_info->qgroup_rescan_lock);
> + if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
> + if (fs_info->qgroup_rescan_progress.objectid <= oper->bytenr) {
> + mutex_unlock(&fs_info->qgroup_rescan_lock);
> + return 0;
> + }
> + }
> + mutex_unlock(&fs_info->qgroup_rescan_lock);
> +
> + ASSERT(is_fstree(oper->ref_root));
> +
> + ret = lock_ref(fs_info, oper->ref_root, oper->bytenr, oper->num_bytes,
> + &cache, &cached_state);
> + if (ret)
> + return ret;
> +
> + switch (oper->type) {
> + case BTRFS_QGROUP_OPER_ADD_EXCL:
> + case BTRFS_QGROUP_OPER_SUB_EXCL:
> + ret = qgroup_excl_accounting(fs_info, oper);
> + break;
> + case BTRFS_QGROUP_OPER_ADD_SHARED:
> + case BTRFS_QGROUP_OPER_SUB_SHARED:
> + ret = qgroup_shared_accounting(fs_info, oper);
> + break;
> + default:
> + ASSERT(0);
> + }
> + err = unlock_ref(fs_info, oper->ref_root, oper->bytenr,
> + oper->num_bytes, cache, &cached_state);
> + return ret ? ret : err;
> +}
> +
> +/*
> + * Needs to be called everytime we run delayed refs, even if there is an error
> + * in order to cleanup outstanding operations.
> + */
> +int btrfs_delayed_qgroup_accounting(struct btrfs_trans_handle *trans,
> + struct btrfs_fs_info *fs_info)
> +{
> + struct btrfs_qgroup_operation *oper;
> + struct qgroup_shared_ref *ref;
> + int ret = 0;
>
> + while (!list_empty(&trans->qgroup_ref_list)) {
> + oper = list_first_entry(&trans->qgroup_ref_list,
> + struct btrfs_qgroup_operation, list);
> + list_del_init(&oper->list);
> + if (!ret || !trans->aborted)
> + ret = btrfs_qgroup_account(trans, fs_info, oper);
> + /*
> + * If there was an error we could have things still on our
> + * shared refs list.
> + */
> + while (!list_empty(&oper->shared_refs)) {
> + ASSERT(ret || trans->aborted);
> + ref = list_first_entry(&oper->shared_refs,
> + struct qgroup_shared_ref, list);
> + list_del_init(&ref->list);
> + kfree(ref);
> + }
> + spin_lock(&fs_info->qgroup_op_lock);
> + rb_erase(&oper->n, &fs_info->qgroup_op_tree);
> + spin_unlock(&fs_info->qgroup_op_lock);
> + kfree(oper);
> + }
> return ret;
> }
>
> @@ -1629,8 +2236,16 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
> srcgroup = find_qgroup_rb(fs_info, srcid);
> if (!srcgroup)
> goto unlock;
> - dstgroup->rfer = srcgroup->rfer - level_size;
> - dstgroup->rfer_cmpr = srcgroup->rfer_cmpr - level_size;
> +
> + /*
> + * We call inherit after we clone the root in order to make sure
> + * our counts don't go crazy, so at this point the only
> + * difference between the two roots should be the root node.
> + */
> + dstgroup->rfer = srcgroup->rfer;
> + dstgroup->rfer_cmpr = srcgroup->rfer_cmpr;
> + dstgroup->excl = level_size;
> + dstgroup->excl_cmpr = level_size;
> srcgroup->excl = level_size;
> srcgroup->excl_cmpr = level_size;
> qgroup_dirty(fs_info, dstgroup);
> @@ -1850,11 +2465,13 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
> struct extent_buffer *scratch_leaf)
> {
> struct btrfs_key found;
> + struct btrfs_block_group_cache *cache;
> + struct extent_state *cached_state = NULL;
> struct ulist *roots = NULL;
> - struct ulist_node *unode;
> - struct ulist_iterator uiter;
> struct seq_list tree_mod_seq_elem = {};
> + u64 num_bytes;
> u64 seq;
> + int new_roots;
> int slot;
> int ret;
>
> @@ -1896,78 +2513,68 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
>
> for (; slot < btrfs_header_nritems(scratch_leaf); ++slot) {
> btrfs_item_key_to_cpu(scratch_leaf, &found, slot);
> - if (found.type != BTRFS_EXTENT_ITEM_KEY)
> + if (found.type != BTRFS_EXTENT_ITEM_KEY &&
> + found.type != BTRFS_METADATA_ITEM_KEY)
> continue;
> - ret = btrfs_find_all_roots(trans, fs_info, found.objectid,
> - tree_mod_seq_elem.seq, &roots);
> - if (ret < 0)
> +
> + if (found.type == BTRFS_METADATA_ITEM_KEY)
> + num_bytes = fs_info->extent_root->leafsize;
> + else
> + num_bytes = found.offset;
> +
> + /*
> + * lock_ref checks to make sure we really need to lock by
> + * checking the ref root root we are modifying the ref for, so
> + * just use the fs tree objectid.
> + */
> + ret = lock_ref(fs_info, BTRFS_FS_TREE_OBJECTID, found.objectid,
> + num_bytes, &cache, &cached_state);
> + if (ret)
> goto out;
> + ret = btrfs_find_all_roots(NULL, fs_info, found.objectid, 0,
> + &roots, 0);
> + if (ret < 0) {
> + unlock_ref(fs_info, BTRFS_FS_TREE_OBJECTID,
> + found.objectid, num_bytes, cache,
> + &cached_state);
> + goto out;
> + }
> spin_lock(&fs_info->qgroup_lock);
> seq = fs_info->qgroup_seq;
> - fs_info->qgroup_seq += roots->nnodes + 1; /* max refcnt */
> + fs_info->qgroup_seq += roots->nnodes + 1;
>
> - ret = qgroup_account_ref_step1(fs_info, roots, tmp, seq);
> - if (ret) {
> + ulist_reinit(tmp);
> + new_roots = 0;
> + ret = qgroup_calc_old_refcnt(fs_info, 0, roots, tmp, seq,
> + &new_roots, 1);
> + if (ret < 0) {
> spin_unlock(&fs_info->qgroup_lock);
> ulist_free(roots);
> + unlock_ref(fs_info, BTRFS_FS_TREE_OBJECTID,
> + found.objectid, num_bytes, cache,
> + &cached_state);
> goto out;
> }
>
> - /*
> - * step2 of btrfs_qgroup_account_ref works from a single root,
> - * we're doing all at once here.
> - */
> ulist_reinit(tmp);
> - ULIST_ITER_INIT(&uiter);
> - while ((unode = ulist_next(roots, &uiter))) {
> - struct btrfs_qgroup *qg;
> -
> - qg = find_qgroup_rb(fs_info, unode->val);
> - if (!qg)
> - continue;
> -
> - ret = ulist_add(tmp, qg->qgroupid, (uintptr_t)qg,
> - GFP_ATOMIC);
> - if (ret < 0) {
> - spin_unlock(&fs_info->qgroup_lock);
> - ulist_free(roots);
> - goto out;
> - }
> - }
> -
> - /* this loop is similar to step 2 of btrfs_qgroup_account_ref */
> - ULIST_ITER_INIT(&uiter);
> - while ((unode = ulist_next(tmp, &uiter))) {
> - struct btrfs_qgroup *qg;
> - struct btrfs_qgroup_list *glist;
> -
> - qg = (struct btrfs_qgroup *)(uintptr_t) unode->aux;
> - qg->rfer += found.offset;
> - qg->rfer_cmpr += found.offset;
> - WARN_ON(qg->tag >= seq);
> - if (qg->refcnt - seq == roots->nnodes) {
> - qg->excl += found.offset;
> - qg->excl_cmpr += found.offset;
> - }
> - qgroup_dirty(fs_info, qg);
> -
> - list_for_each_entry(glist, &qg->groups, next_group) {
> - ret = ulist_add(tmp, glist->group->qgroupid,
> - (uintptr_t)glist->group,
> - GFP_ATOMIC);
> - if (ret < 0) {
> - spin_unlock(&fs_info->qgroup_lock);
> - ulist_free(roots);
> - goto out;
> - }
> - }
> + ret = qgroup_adjust_counters(fs_info, 0, num_bytes, roots, tmp,
> + seq, 0, new_roots, 1);
> + if (ret < 0) {
> + spin_unlock(&fs_info->qgroup_lock);
> + ulist_free(roots);
> + unlock_ref(fs_info, BTRFS_FS_TREE_OBJECTID,
> + found.objectid, num_bytes, cache,
> + &cached_state);
> + goto out;
> }
> -
> spin_unlock(&fs_info->qgroup_lock);
> ulist_free(roots);
> - ret = 0;
> + ret = unlock_ref(fs_info, BTRFS_FS_TREE_OBJECTID,
> + found.objectid, num_bytes, cache,
> + &cached_state);
> + if (ret)
> + break;
> }
> -
> out:
> btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
>
> diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
> index 026f1fe..503cb46 100644
> --- a/fs/btrfs/transaction.c
> +++ b/fs/btrfs/transaction.c
> @@ -692,23 +692,9 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
> return 0;
> }
>
> - /*
> - * do the qgroup accounting as early as possible
> - */
> - err = btrfs_delayed_refs_qgroup_accounting(trans, info);
> -
> btrfs_trans_release_metadata(trans, root);
> trans->block_rsv = NULL;
>
> - if (trans->qgroup_reserved) {
> - /*
> - * the same root has to be passed here between start_transaction
> - * and end_transaction. Subvolume quota depends on this.
> - */
> - btrfs_qgroup_free(trans->root, trans->qgroup_reserved);
> - trans->qgroup_reserved = 0;
> - }
> -
> if (!list_empty(&trans->new_bgs))
> btrfs_create_pending_block_groups(trans, root);
>
> @@ -719,6 +705,15 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
> btrfs_run_delayed_refs(trans, root, cur);
> }
>
> + if (trans->qgroup_reserved) {
> + /*
> + * the same root has to be passed here between start_transaction
> + * and end_transaction. Subvolume quota depends on this.
> + */
> + btrfs_qgroup_free(trans->root, trans->qgroup_reserved);
> + trans->qgroup_reserved = 0;
> + }
> +
> btrfs_trans_release_metadata(trans, root);
> trans->block_rsv = NULL;
>
> @@ -1176,12 +1171,6 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
> goto no_free_objectid;
> }
>
> - pending->error = btrfs_qgroup_inherit(trans, fs_info,
> - root->root_key.objectid,
> - objectid, pending->inherit);
> - if (pending->error)
> - goto no_free_objectid;
> -
> key.objectid = objectid;
> key.offset = (u64)-1;
> key.type = BTRFS_ROOT_ITEM_KEY;
> @@ -1278,6 +1267,22 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
> goto fail;
> }
>
> + /*
> + * We need to flush delayed refs in order to make sure all of our quota
> + * operations have been done before we call btrfs_qgroup_inherit.
> + */
> + ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
> + if (ret) {
> + btrfs_abort_transaction(trans, root, ret);
> + goto fail;
> + }
> +
> + pending->error = btrfs_qgroup_inherit(trans, fs_info,
> + root->root_key.objectid,
> + objectid, pending->inherit);
> + if (pending->error)
> + goto no_free_objectid;
> +
> /* see comments in should_cow_block() */
> root->force_cow = 1;
> smp_wmb();
> @@ -1607,12 +1612,6 @@ static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans,
> * them now so that they hinder processing of more delayed refs
> * as little as possible.
> */
> - if (ret) {
> - btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info);
> - return ret;
> - }
> -
> - ret = btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info);
> if (ret)
> return ret;
>
> --
> 1.8.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2013-12-21 8:02 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-12-18 21:07 Rework qgroup accounting Josef Bacik
2013-12-18 21:07 ` [PATCH 1/3] Btrfs: introduce lock_ref/unlock_ref Josef Bacik
2013-12-19 4:01 ` Dave Chinner
2013-12-19 14:37 ` Josef Bacik
2013-12-18 21:07 ` [PATCH 2/3] Btrfs: rework qgroup accounting Josef Bacik
2013-12-21 8:01 ` Wang Shilong [this message]
2013-12-21 14:13 ` Josef Bacik
2013-12-21 8:56 ` Wang Shilong
2013-12-21 14:14 ` Josef Bacik
2014-01-07 16:43 ` Josef Bacik
2014-01-08 14:33 ` David Sterba
2014-01-08 14:42 ` Josef Bacik
2013-12-18 21:07 ` [PATCH 3/3] Btrfs: add sanity tests for new qgroup accounting code Josef Bacik
2013-12-19 2:00 ` Rework qgroup accounting Liu Bo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=D8610409-5BF3-4150-A0DF-DC9985169540@gmail.com \
--to=wangshilong1991@gmail.com \
--cc=jbacik@fb.com \
--cc=linux-btrfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).