btrfs: reloc: remove the open-coded goto loop for breadth-first search
build_backref_tree() uses "goto again;" to implement a breadth-first search to build backref cache. This patch will extract most of its work into a wrapper, handle_one_tree_block(), and use a do {} while() loop to implement the same thing. Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
0304f2d8cc
commit
e7d571c7b0
|
@ -957,77 +957,31 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* build backref tree for a given tree block. root of the backref tree
|
||||
* corresponds the tree block, leaves of the backref tree correspond
|
||||
* roots of b-trees that reference the tree block.
|
||||
*
|
||||
* the basic idea of this function is check backrefs of a given block
|
||||
* to find upper level blocks that reference the block, and then check
|
||||
* backrefs of these upper level blocks recursively. the recursion stop
|
||||
* when tree root is reached or backrefs for the block is cached.
|
||||
*
|
||||
* NOTE: if we find backrefs for a block are cached, we know backrefs
|
||||
* for all upper level blocks that directly/indirectly reference the
|
||||
* block are also cached.
|
||||
*/
|
||||
static noinline_for_stack
|
||||
struct backref_node *build_backref_tree(struct reloc_control *rc,
|
||||
struct btrfs_key *node_key,
|
||||
int level, u64 bytenr)
|
||||
static int handle_one_tree_block(struct backref_cache *cache,
|
||||
struct btrfs_path *path,
|
||||
struct btrfs_backref_iter *iter,
|
||||
struct btrfs_key *node_key,
|
||||
struct backref_node *cur)
|
||||
{
|
||||
struct btrfs_backref_iter *iter;
|
||||
struct backref_cache *cache = &rc->backref_cache;
|
||||
/* For searching parent of TREE_BLOCK_REF */
|
||||
struct btrfs_path *path;
|
||||
struct backref_node *cur;
|
||||
struct backref_node *upper;
|
||||
struct backref_node *lower;
|
||||
struct backref_node *node = NULL;
|
||||
struct backref_node *exist = NULL;
|
||||
struct btrfs_fs_info *fs_info = cache->fs_info;
|
||||
struct backref_edge *edge;
|
||||
struct rb_node *rb_node;
|
||||
int cowonly;
|
||||
struct backref_node *exist;
|
||||
int ret;
|
||||
int err = 0;
|
||||
|
||||
iter = btrfs_backref_iter_alloc(rc->extent_root->fs_info, GFP_NOFS);
|
||||
if (!iter)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
path = btrfs_alloc_path();
|
||||
if (!path) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
node = alloc_backref_node(cache, bytenr, level);
|
||||
if (!node) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
node->lowest = 1;
|
||||
cur = node;
|
||||
again:
|
||||
ret = btrfs_backref_iter_start(iter, cur->bytenr);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* We skip the first btrfs_tree_block_info, as we don't use the key
|
||||
* stored in it, but fetch it from the tree block
|
||||
*/
|
||||
if (btrfs_backref_has_tree_block_info(iter)) {
|
||||
ret = btrfs_backref_iter_next(iter);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
/* No extra backref? This means the tree block is corrupted */
|
||||
if (ret > 0) {
|
||||
err = -EUCLEAN;
|
||||
ret = -EUCLEAN;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -1070,7 +1024,7 @@ again:
|
|||
type = btrfs_get_extent_inline_ref_type(eb, iref,
|
||||
BTRFS_REF_TYPE_BLOCK);
|
||||
if (type == BTRFS_REF_TYPE_INVALID) {
|
||||
err = -EUCLEAN;
|
||||
ret = -EUCLEAN;
|
||||
goto out;
|
||||
}
|
||||
key.type = type;
|
||||
|
@ -1096,16 +1050,13 @@ again:
|
|||
/* SHARED_BLOCK_REF means key.offset is the parent bytenr */
|
||||
if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
|
||||
ret = handle_direct_tree_backref(cache, &key, cur);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
continue;
|
||||
} else if (unlikely(key.type == BTRFS_EXTENT_REF_V0_KEY)) {
|
||||
err = -EINVAL;
|
||||
btrfs_print_v0_err(rc->extent_root->fs_info);
|
||||
btrfs_handle_fs_error(rc->extent_root->fs_info, err,
|
||||
NULL);
|
||||
ret = -EINVAL;
|
||||
btrfs_print_v0_err(fs_info);
|
||||
btrfs_handle_fs_error(fs_info, ret, NULL);
|
||||
goto out;
|
||||
} else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) {
|
||||
continue;
|
||||
|
@ -1118,29 +1069,85 @@ again:
|
|||
*/
|
||||
ret = handle_indirect_tree_backref(cache, path, &key, node_key,
|
||||
cur);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
cur->checked = 1;
|
||||
WARN_ON(exist);
|
||||
out:
|
||||
btrfs_backref_iter_release(iter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build backref tree for a given tree block. Root of the backref tree
|
||||
* corresponds the tree block, leaves of the backref tree correspond roots of
|
||||
* b-trees that reference the tree block.
|
||||
*
|
||||
* The basic idea of this function is check backrefs of a given block to find
|
||||
* upper level blocks that reference the block, and then check backrefs of
|
||||
* these upper level blocks recursively. The recursion stops when tree root is
|
||||
* reached or backrefs for the block is cached.
|
||||
*
|
||||
* NOTE: if we find that backrefs for a block are cached, we know backrefs for
|
||||
* all upper level blocks that directly/indirectly reference the block are also
|
||||
* cached.
|
||||
*/
|
||||
static noinline_for_stack struct backref_node *build_backref_tree(
|
||||
struct reloc_control *rc, struct btrfs_key *node_key,
|
||||
int level, u64 bytenr)
|
||||
{
|
||||
struct btrfs_backref_iter *iter;
|
||||
struct backref_cache *cache = &rc->backref_cache;
|
||||
/* For searching parent of TREE_BLOCK_REF */
|
||||
struct btrfs_path *path;
|
||||
struct backref_node *cur;
|
||||
struct backref_node *upper;
|
||||
struct backref_node *lower;
|
||||
struct backref_node *node = NULL;
|
||||
struct backref_edge *edge;
|
||||
struct rb_node *rb_node;
|
||||
int cowonly;
|
||||
int ret;
|
||||
int err = 0;
|
||||
|
||||
iter = btrfs_backref_iter_alloc(rc->extent_root->fs_info, GFP_NOFS);
|
||||
if (!iter)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
path = btrfs_alloc_path();
|
||||
if (!path) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
node = alloc_backref_node(cache, bytenr, level);
|
||||
if (!node) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
node->lowest = 1;
|
||||
cur = node;
|
||||
|
||||
/* Breadth-first search to build backref cache */
|
||||
do {
|
||||
ret = handle_one_tree_block(cache, path, iter, node_key, cur);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
btrfs_backref_iter_release(iter);
|
||||
|
||||
cur->checked = 1;
|
||||
WARN_ON(exist);
|
||||
|
||||
/* the pending list isn't empty, take the first block to process */
|
||||
if (!list_empty(&cache->pending_edge)) {
|
||||
edge = list_first_entry(&cache->pending_edge,
|
||||
struct backref_edge, list[UPPER]);
|
||||
list_del_init(&edge->list[UPPER]);
|
||||
cur = edge->node[UPPER];
|
||||
goto again;
|
||||
}
|
||||
edge = list_first_entry_or_null(&cache->pending_edge,
|
||||
struct backref_edge, list[UPPER]);
|
||||
/*
|
||||
* The pending list isn't empty, take the first block to
|
||||
* process
|
||||
*/
|
||||
if (edge) {
|
||||
list_del_init(&edge->list[UPPER]);
|
||||
cur = edge->node[UPPER];
|
||||
}
|
||||
} while (edge);
|
||||
|
||||
/*
|
||||
* everything goes well, connect backref nodes and insert backref nodes
|
||||
|
|
Loading…
Reference in New Issue