Commit c8c42864 authored by Chris Mason's avatar Chris Mason
Browse files

Btrfs: break up btrfs_search_slot into smaller pieces



btrfs_search_slot was doing too many things at once.  This breaks
it up into more reasonable units.

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 04018de5
Loading
Loading
Loading
Loading
+131 −90
Original line number Diff line number Diff line
@@ -1244,7 +1244,7 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans,
 * readahead one full node of leaves, finding things that are close
 * to the block in 'slot', and triggering ra on them.
 */
static noinline void reada_for_search(struct btrfs_root *root,
static void reada_for_search(struct btrfs_root *root,
			     struct btrfs_path *path,
			     int level, int slot, u64 objectid)
{
@@ -1446,6 +1446,117 @@ noinline void btrfs_unlock_up_safe(struct btrfs_path *path, int level)
	}
}

/*
 * helper function for btrfs_search_slot.  The goal is to find a block
 * in cache without setting the path to blocking.  If we find the block
 * we return zero and the path is unchanged.
 *
 * If we can't find the block, we set the path blocking and do some
 * reada.  -EAGAIN is returned and the search must be repeated.
 */
static int
read_block_for_search(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root, struct btrfs_path *p,
		       struct extent_buffer **eb_ret, int level, int slot,
		       struct btrfs_key *key)
{
	u64 blocknr;
	u64 gen;
	u32 blocksize;
	struct extent_buffer *b = *eb_ret;
	struct extent_buffer *tmp;

	blocknr = btrfs_node_blockptr(b, slot);
	gen = btrfs_node_ptr_generation(b, slot);
	blocksize = btrfs_level_size(root, level - 1);

	tmp = btrfs_find_tree_block(root, blocknr, blocksize);
	if (tmp && btrfs_buffer_uptodate(tmp, gen)) {
		*eb_ret = tmp;
		return 0;
	}

	/*
	 * reduce lock contention at high levels
	 * of the btree by dropping locks before
	 * we read.
	 */
	btrfs_release_path(NULL, p);
	if (tmp)
		free_extent_buffer(tmp);
	if (p->reada)
		reada_for_search(root, p, level, slot, key->objectid);

	tmp = read_tree_block(root, blocknr, blocksize, gen);
	if (tmp)
		free_extent_buffer(tmp);
	return -EAGAIN;
}

/*
 * helper function for btrfs_search_slot.  This does all of the checks
 * for node-level blocks and does any balancing required based on
 * the ins_len.
 *
 * If no extra work was required, zero is returned.  If we had to
 * drop the path, -EAGAIN is returned and btrfs_search_slot must
 * start over
 */
static int
setup_nodes_for_search(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root, struct btrfs_path *p,
		       struct extent_buffer *b, int level, int ins_len)
{
	int ret;
	if ((p->search_for_split || ins_len > 0) && btrfs_header_nritems(b) >=
	    BTRFS_NODEPTRS_PER_BLOCK(root) - 3) {
		int sret;

		sret = reada_for_balance(root, p, level);
		if (sret)
			goto again;

		btrfs_set_path_blocking(p);
		sret = split_node(trans, root, p, level);
		btrfs_clear_path_blocking(p, NULL);

		BUG_ON(sret > 0);
		if (sret) {
			ret = sret;
			goto done;
		}
		b = p->nodes[level];
	} else if (ins_len < 0 && btrfs_header_nritems(b) <
		   BTRFS_NODEPTRS_PER_BLOCK(root) / 4) {
		int sret;

		sret = reada_for_balance(root, p, level);
		if (sret)
			goto again;

		btrfs_set_path_blocking(p);
		sret = balance_level(trans, root, p, level);
		btrfs_clear_path_blocking(p, NULL);

		if (sret) {
			ret = sret;
			goto done;
		}
		b = p->nodes[level];
		if (!b) {
			btrfs_release_path(NULL, p);
			goto again;
		}
		BUG_ON(btrfs_header_nritems(b) == 1);
	}
	return 0;

again:
	ret = -EAGAIN;
done:
	return ret;
}

/*
 * look for key in the tree.  path is filled in with nodes along the way
 * if key is found, we return zero and you can find the item in the leaf
@@ -1464,16 +1575,11 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
		      ins_len, int cow)
{
	struct extent_buffer *b;
	struct extent_buffer *tmp;
	int slot;
	int ret;
	int level;
	int should_reada = p->reada;
	int lowest_unlock = 1;
	int blocksize;
	u8 lowest_level = 0;
	u64 blocknr;
	u64 gen;

	lowest_level = p->lowest_level;
	WARN_ON(lowest_level && ins_len > 0);
@@ -1502,7 +1608,11 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
		if (cow) {
			int wret;

			/* is a cow on this block not required */
			/*
			 * if we don't really need to cow this block
			 * then we don't want to set the path blocking,
			 * so we test it here
			 */
			if (btrfs_header_generation(b) == trans->transid &&
			    btrfs_header_owner(b) == root->root_key.objectid &&
			    !btrfs_header_flag(b, BTRFS_HEADER_FLAG_WRITTEN)) {
@@ -1557,51 +1667,15 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
			if (ret && slot > 0)
				slot -= 1;
			p->slots[level] = slot;
			if ((p->search_for_split || ins_len > 0) &&
			    btrfs_header_nritems(b) >=
			    BTRFS_NODEPTRS_PER_BLOCK(root) - 3) {
				int sret;

				sret = reada_for_balance(root, p, level);
				if (sret)
			ret = setup_nodes_for_search(trans, root, p, b, level,
						     ins_len);
			if (ret == -EAGAIN)
				goto again;

				btrfs_set_path_blocking(p);
				sret = split_node(trans, root, p, level);
				btrfs_clear_path_blocking(p, NULL);

				BUG_ON(sret > 0);
				if (sret) {
					ret = sret;
			else if (ret)
				goto done;
				}
			b = p->nodes[level];
			slot = p->slots[level];
			} else if (ins_len < 0 &&
				   btrfs_header_nritems(b) <
				   BTRFS_NODEPTRS_PER_BLOCK(root) / 4) {
				int sret;

				sret = reada_for_balance(root, p, level);
				if (sret)
					goto again;

				btrfs_set_path_blocking(p);
				sret = balance_level(trans, root, p, level);
				btrfs_clear_path_blocking(p, NULL);

				if (sret) {
					ret = sret;
					goto done;
				}
				b = p->nodes[level];
				if (!b) {
					btrfs_release_path(NULL, p);
					goto again;
				}
				slot = p->slots[level];
				BUG_ON(btrfs_header_nritems(b) == 1);
			}
			unlock_up(p, level, lowest_unlock);

			/* this is only true while dropping a snapshot */
@@ -1610,44 +1684,11 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
				goto done;
			}

			blocknr = btrfs_node_blockptr(b, slot);
			gen = btrfs_node_ptr_generation(b, slot);
			blocksize = btrfs_level_size(root, level - 1);

			tmp = btrfs_find_tree_block(root, blocknr, blocksize);
			if (tmp && btrfs_buffer_uptodate(tmp, gen)) {
				b = tmp;
			} else {
				/*
				 * reduce lock contention at high levels
				 * of the btree by dropping locks before
				 * we read.
				 */
				if (level > 0) {
					btrfs_release_path(NULL, p);
					if (tmp)
						free_extent_buffer(tmp);
					if (should_reada)
						reada_for_search(root, p,
								 level, slot,
								 key->objectid);

					tmp = read_tree_block(root, blocknr,
							 blocksize, gen);
					if (tmp)
						free_extent_buffer(tmp);
			ret = read_block_for_search(trans, root, p,
						    &b, level, slot, key);
			if (ret == -EAGAIN)
				goto again;
				} else {
					btrfs_set_path_blocking(p);
					if (tmp)
						free_extent_buffer(tmp);
					if (should_reada)
						reada_for_search(root, p,
								 level, slot,
								 key->objectid);
					b = read_node_slot(root, b, slot);
				}
			}

			if (!p->skip_locking) {
				int lret;