Commit e7d571c7 authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba

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: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 0304f2d8
...@@ -957,77 +957,31 @@ static int handle_indirect_tree_backref(struct backref_cache *cache, ...@@ -957,77 +957,31 @@ static int handle_indirect_tree_backref(struct backref_cache *cache,
return ret; return ret;
} }
/* static int handle_one_tree_block(struct backref_cache *cache,
* build backref tree for a given tree block. root of the backref tree struct btrfs_path *path,
* corresponds the tree block, leaves of the backref tree correspond struct btrfs_backref_iter *iter,
* roots of b-trees that reference the tree block. struct btrfs_key *node_key,
* struct backref_node *cur)
* 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)
{ {
struct btrfs_backref_iter *iter; struct btrfs_fs_info *fs_info = cache->fs_info;
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 backref_edge *edge; struct backref_edge *edge;
struct rb_node *rb_node; struct backref_node *exist;
int cowonly;
int ret; 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); ret = btrfs_backref_iter_start(iter, cur->bytenr);
if (ret < 0) { if (ret < 0)
err = ret; return ret;
goto out;
}
/* /*
* We skip the first btrfs_tree_block_info, as we don't use the key * 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 * stored in it, but fetch it from the tree block
*/ */
if (btrfs_backref_has_tree_block_info(iter)) { if (btrfs_backref_has_tree_block_info(iter)) {
ret = btrfs_backref_iter_next(iter); ret = btrfs_backref_iter_next(iter);
if (ret < 0) { if (ret < 0)
err = ret;
goto out; goto out;
}
/* No extra backref? This means the tree block is corrupted */ /* No extra backref? This means the tree block is corrupted */
if (ret > 0) { if (ret > 0) {
err = -EUCLEAN; ret = -EUCLEAN;
goto out; goto out;
} }
} }
...@@ -1070,7 +1024,7 @@ struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -1070,7 +1024,7 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
type = btrfs_get_extent_inline_ref_type(eb, iref, type = btrfs_get_extent_inline_ref_type(eb, iref,
BTRFS_REF_TYPE_BLOCK); BTRFS_REF_TYPE_BLOCK);
if (type == BTRFS_REF_TYPE_INVALID) { if (type == BTRFS_REF_TYPE_INVALID) {
err = -EUCLEAN; ret = -EUCLEAN;
goto out; goto out;
} }
key.type = type; key.type = type;
...@@ -1096,16 +1050,13 @@ struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -1096,16 +1050,13 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
/* SHARED_BLOCK_REF means key.offset is the parent bytenr */ /* SHARED_BLOCK_REF means key.offset is the parent bytenr */
if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) { if (key.type == BTRFS_SHARED_BLOCK_REF_KEY) {
ret = handle_direct_tree_backref(cache, &key, cur); ret = handle_direct_tree_backref(cache, &key, cur);
if (ret < 0) { if (ret < 0)
err = ret;
goto out; goto out;
}
continue; continue;
} else if (unlikely(key.type == BTRFS_EXTENT_REF_V0_KEY)) { } else if (unlikely(key.type == BTRFS_EXTENT_REF_V0_KEY)) {
err = -EINVAL; ret = -EINVAL;
btrfs_print_v0_err(rc->extent_root->fs_info); btrfs_print_v0_err(fs_info);
btrfs_handle_fs_error(rc->extent_root->fs_info, err, btrfs_handle_fs_error(fs_info, ret, NULL);
NULL);
goto out; goto out;
} else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) { } else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) {
continue; continue;
...@@ -1118,30 +1069,86 @@ struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -1118,30 +1069,86 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
*/ */
ret = handle_indirect_tree_backref(cache, path, &key, node_key, ret = handle_indirect_tree_backref(cache, path, &key, node_key,
cur); cur);
if (ret < 0) { if (ret < 0)
err = ret;
goto out; goto out;
}
}
if (ret < 0) {
err = ret;
goto out;
} }
ret = 0; ret = 0;
btrfs_backref_iter_release(iter);
cur->checked = 1; cur->checked = 1;
WARN_ON(exist); WARN_ON(exist);
out:
btrfs_backref_iter_release(iter);
return ret;
}
/* the pending list isn't empty, take the first block to process */ /*
if (!list_empty(&cache->pending_edge)) { * Build backref tree for a given tree block. Root of the backref tree
edge = list_first_entry(&cache->pending_edge, * corresponds the tree block, leaves of the backref tree correspond roots of
struct backref_edge, list[UPPER]); * b-trees that reference the tree block.
list_del_init(&edge->list[UPPER]); *
cur = edge->node[UPPER]; * The basic idea of this function is check backrefs of a given block to find
goto again; * 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;
}
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 * everything goes well, connect backref nodes and insert backref nodes
* into the cache. * into the cache.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment