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

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
Loading
Loading
Loading
Loading
+88 −81
Original line number Diff line number Diff line
@@ -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,
static int handle_one_tree_block(struct backref_cache *cache,
				 struct btrfs_path *path,
				 struct btrfs_backref_iter *iter,
				 struct btrfs_key *node_key,
					int level, u64 bytenr)
				 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) {
			err = ret;
		if (ret < 0)
			goto out;
	}
	ret = 0;
	cur->checked = 1;
	WARN_ON(exist);
out:
	btrfs_backref_iter_release(iter);
	return ret;
}
	if (ret < 0) {
		err = 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;
	}
	ret = 0;
	btrfs_backref_iter_release(iter);

	cur->checked = 1;
	WARN_ON(exist);
	node = alloc_backref_node(cache, bytenr, level);
	if (!node) {
		err = -ENOMEM;
		goto out;
	}

	/* 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,
	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];
		goto again;
		}
	} while (edge);

	/*
	 * everything goes well, connect backref nodes and insert backref nodes