On Tue, Sep 11, 2018 at 01:38:14PM +0800, Qu Wenruo wrote:
> Introduce new function, qgroup_trace_new_subtree_blocks(), to iterate
> all new tree blocks in a reloc tree.
> So that qgroup could skip unrelated tree blocks during balance, which
> should hugely speedup balance speed when quota is enabled.
> 
> The function qgroup_trace_new_subtree_blocks() itself only cares about
> new tree blocks in reloc tree.
> 
> All its main works are:
> 
> 1) Read out tree blocks according to parent pointers
> 
> 2) Do recursive depth-first search
>    Will call the same function on all its children tree blocks, with
>    search level set to current level -1.
>    And will also skip all children whose generation is smaller than
>    @last_snapshot.
> 
> 3) Call qgroup_trace_extent_swap() to trace tree blocks
> 
> So although we have parameter list related to source file tree, it's not
> used at all, but only passed to qgroup_trace_extent_swap().
> Thus despite the tree read code, the core should be pretty short and all
> about recursive depth-first search.
> 
> Signed-off-by: Qu Wenruo <w...@suse.com>
> ---
>  fs/btrfs/qgroup.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 114 insertions(+)
> 
> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
> index 5155fb42ce79..0702953d70a7 100644
> --- a/fs/btrfs/qgroup.c
> +++ b/fs/btrfs/qgroup.c
> @@ -1839,6 +1839,120 @@ static int qgroup_trace_extent_swap(struct 
> btrfs_trans_handle* trans,
>       return ret;
>  }
>  
> +/*
> + * Helper function to do recursive generation aware depth-first search, to
> + * locate all new tree blocks in a subtree of tree reloc tree.
> + *
> + * E.g. (OO = Old tree blocks, NN = New tree blocks, whose gen == 
> last_snapshot)
> + *       Tree reloc tree
> + * L2         NN (a)
> + *          /    \
> + * L1    OO        NN (b)
> + *      /  \      /  \
> + * L0  OO  OO    OO  NN
> + *               (c) (d)
> + * If we pass:
> + * @dst_path = [ nodes[1] = NN(b), nodes[0] = NULL ],
> + * @cur_level = 1
> + * @root_level = 1
> + *
> + * We will iterate through tree blocks NN(b), NN(d) and info qgroup to trace
> + * above tree blocks along with their counter parts in file tree.
> + * While during search, old tree blocsk OO(c) will be skiped as tree block 
> swap
> + * won't affect OO(c).
> + */
> +static int qgroup_trace_new_subtree_blocks(struct btrfs_trans_handle* trans,
> +                                        struct extent_buffer *src_eb,
> +                                        struct btrfs_path *dst_path,
> +                                        int cur_level, int root_level,
> +                                        u64 last_snapshot)
> +{
> +     struct btrfs_fs_info *fs_info = trans->fs_info;
> +     struct extent_buffer *eb;
> +     bool need_cleanup = false;
> +     int ret = 0;
> +     int i;

This function could be called recursively based on the value of
cur_level so please add some sanity checks so this does not accidentally
escape. It needs to be a plain if/return, not just an assert or bug-on.

> +
> +     /* Read the tree block if needed */
> +     if (dst_path->nodes[cur_level] == NULL) {
> +             struct btrfs_key first_key;
> +             int parent_slot;
> +             u64 child_gen;
> +             u64 child_bytenr;
> +
> +             /*
> +              * We need to get child blockptr/gen from parent before
> +              * we can read it.
> +               */
> +             eb = dst_path->nodes[cur_level + 1];
> +             parent_slot = dst_path->slots[cur_level + 1];
> +             child_bytenr = btrfs_node_blockptr(eb, parent_slot);
> +             child_gen = btrfs_node_ptr_generation(eb, parent_slot);
> +             btrfs_node_key_to_cpu(eb, &first_key, parent_slot);
> +
> +             /* This node is old, no need to trace */
> +             if (child_gen < last_snapshot)
> +                     goto out;
> +
> +             eb = read_tree_block(fs_info, child_bytenr, child_gen,
> +                                  cur_level, &first_key);
> +             if (IS_ERR(eb)) {
> +                     ret = PTR_ERR(eb);
> +                     goto out;
> +             } else if (!extent_buffer_uptodate(eb)) {
> +                     free_extent_buffer(eb);
> +                     ret = -EIO;
> +                     goto out;
> +             }
> +
> +             dst_path->nodes[cur_level] = eb;
> +             dst_path->slots[cur_level] = 0;
> +
> +             btrfs_tree_read_lock(eb);
> +             btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
> +             dst_path->locks[cur_level] = BTRFS_READ_LOCK_BLOCKING;
> +             need_cleanup = true;
> +     }
> +
> +     /* Now record this tree block and its counter part for qgroups */
> +     ret = qgroup_trace_extent_swap(trans, src_eb, dst_path, cur_level,
> +                                    root_level);
> +     if (ret < 0)
> +             goto cleanup;
> +
> +     eb = dst_path->nodes[cur_level];
> +
> +     if (cur_level > 0) {
> +             /* Iterate all children tree blocks */
> +             for (i = 0; i < btrfs_header_nritems(eb); i++) {
> +                     /* Skip old tree blocks as they won't be swapped */
> +                     if (btrfs_node_ptr_generation(eb, i) < last_snapshot)
> +                             continue;
> +                     dst_path->slots[cur_level] = i;
> +
> +                     /* Recursive call (at most 7 times) */
> +                     ret = qgroup_trace_new_subtree_blocks(trans, src_eb,
> +                                     dst_path, cur_level - 1, root_level,
> +                                     last_snapshot);
> +                     if (ret < 0)
> +                             goto cleanup;
> +             }
> +     }
> +
> +cleanup:
> +     if (need_cleanup) {
> +             /* Clean up */
> +             btrfs_tree_unlock_rw(dst_path->nodes[cur_level],
> +                                  dst_path->locks[cur_level]);
> +             free_extent_buffer(dst_path->nodes[cur_level]);
> +             dst_path->nodes[cur_level] = NULL;
> +             dst_path->slots[cur_level] = 0;
> +             dst_path->locks[cur_level] = 0;
> +     }
> +out:
> +     return ret;
> +}
> +
>  int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
>                              struct extent_buffer *root_eb,
>                              u64 root_gen, int root_level)
> -- 
> 2.18.0

Reply via email to