btrfs: move leaf search logic out of btrfs_search_slot()

There's quite a significant amount of code for doing the key search for a
leaf at btrfs_search_slot(), with a couple labels and gotos in it, plus
btrfs_search_slot() is already big enough.

So move the logic that does the key search on a leaf into a new helper
function. This makes it better organized, removing the need for the labels
and the gotos, as well as reducing the indentation level and the size of
btrfs_search_slot().

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Filipe Manana 2021-12-02 10:30:38 +00:00 committed by David Sterba
parent e5e1c1741b
commit 109324cfda

View File

@ -1701,6 +1701,132 @@ static inline int search_for_key_slot(struct extent_buffer *eb,
return generic_bin_search(eb, search_low_slot, key, slot);
}
static int search_leaf(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
const struct btrfs_key *key,
struct btrfs_path *path,
int ins_len,
int prev_cmp)
{
struct extent_buffer *leaf = path->nodes[0];
int leaf_free_space = -1;
int search_low_slot = 0;
int ret;
bool do_bin_search = true;
/*
* If we are doing an insertion, the leaf has enough free space and the
* destination slot for the key is not slot 0, then we can unlock our
* write lock on the parent, and any other upper nodes, before doing the
* binary search on the leaf (with search_for_key_slot()), allowing other
* tasks to lock the parent and any other upper nodes.
*/
if (ins_len > 0) {
/*
* Cache the leaf free space, since we will need it later and it
* will not change until then.
*/
leaf_free_space = btrfs_leaf_free_space(leaf);
/*
* !path->locks[1] means we have a single node tree, the leaf is
* the root of the tree.
*/
if (path->locks[1] && leaf_free_space >= ins_len) {
struct btrfs_disk_key first_key;
ASSERT(btrfs_header_nritems(leaf) > 0);
btrfs_item_key(leaf, &first_key, 0);
/*
* Doing the extra comparison with the first key is cheap,
* taking into account that the first key is very likely
* already in a cache line because it immediately follows
* the extent buffer's header and we have recently accessed
* the header's level field.
*/
ret = comp_keys(&first_key, key);
if (ret < 0) {
/*
* The first key is smaller than the key we want
* to insert, so we are safe to unlock all upper
* nodes and we have to do the binary search.
*
* We do use btrfs_unlock_up_safe() and not
* unlock_up() because the later does not unlock
* nodes with a slot of 0 - we can safely unlock
* any node even if its slot is 0 since in this
* case the key does not end up at slot 0 of the
* leaf and there's no need to split the leaf.
*/
btrfs_unlock_up_safe(path, 1);
search_low_slot = 1;
} else {
/*
* The first key is >= then the key we want to
* insert, so we can skip the binary search as
* the target key will be at slot 0.
*
* We can not unlock upper nodes when the key is
* less than the first key, because we will need
* to update the key at slot 0 of the parent node
* and possibly of other upper nodes too.
* If the key matches the first key, then we can
* unlock all the upper nodes, using
* btrfs_unlock_up_safe() instead of unlock_up()
* as stated above.
*/
if (ret == 0)
btrfs_unlock_up_safe(path, 1);
/*
* ret is already 0 or 1, matching the result of
* a btrfs_bin_search() call, so there is no need
* to adjust it.
*/
do_bin_search = false;
path->slots[0] = 0;
}
}
}
if (do_bin_search) {
ret = search_for_key_slot(leaf, search_low_slot, key,
prev_cmp, &path->slots[0]);
if (ret < 0)
return ret;
}
if (ins_len > 0) {
/*
* Item key already exists. In this case, if we are allowed to
* insert the item (for example, in dir_item case, item key
* collision is allowed), it will be merged with the original
* item. Only the item size grows, no new btrfs item will be
* added. If search_for_extension is not set, ins_len already
* accounts the size btrfs_item, deduct it here so leaf space
* check will be correct.
*/
if (ret == 0 && !path->search_for_extension) {
ASSERT(ins_len >= sizeof(struct btrfs_item));
ins_len -= sizeof(struct btrfs_item);
}
ASSERT(leaf_free_space >= 0);
if (leaf_free_space < ins_len) {
int err;
err = split_leaf(trans, root, key, path, ins_len,
(ret == 0));
BUG_ON(err > 0);
if (err)
ret = err;
}
}
return ret;
}
/*
* btrfs_search_slot - look for a key in a tree and perform necessary
* modifications to preserve tree invariants.
@ -1862,124 +1988,10 @@ cow_done:
}
if (level == 0) {
int leaf_free_space = 0;
int search_low_slot = 0;
/*
* If we are doing an insertion, the leaf has enough free
* space and the destination slot for the key is not slot
* 0, then we can unlock our write lock on the parent, and
* any other upper nodes, before doing the binary search
* on the leaf (with search_for_key_slot()), allowing other
* tasks to lock the parent and any other upper nodes.
*/
if (ins_len > 0) {
struct btrfs_disk_key first_key;
/*
* Cache the leaf free space, since we will need it
* later and it will not change until then.
*/
leaf_free_space = btrfs_leaf_free_space(b);
/*
* !p->locks[1] means we have a single node tree,
* the leaf is the root of the tree.
*/
if (!p->locks[1] || leaf_free_space < ins_len)
goto leaf_search;
ASSERT(btrfs_header_nritems(b) > 0);
btrfs_item_key(b, &first_key, 0);
/*
* Doing the extra comparison with the first key
* is cheap, taking into account that the first
* key is very likely already in a cache line
* because it immediately follows the extent
* buffer's header and we have recently accessed
* the header's level field.
*/
ret = comp_keys(&first_key, key);
if (ret < 0) {
/*
* The first key is smaller than the key
* we want to insert, so we are safe to
* unlock all upper nodes and we have to
* do the binary search.
*
* We do use btrfs_unlock_up_safe() and
* not unlock_up() because the later does
* not unlock nodes with a slot of 0.
* We can safely unlock any node even if
* its slot is 0 since in this case the
* key does not end up at slot 0 of the
* leaf and there's also no need to split
* the leaf.
*/
btrfs_unlock_up_safe(p, 1);
search_low_slot = 1;
} else {
/*
* The first key is >= then the key we
* want to insert, so we can skip the
* binary search as the target key will
* be at slot 0.
*
* We can not unlock upper nodes when
* the key is less than the first key,
* because we will need to update the key
* at slot 0 of the parent node and
* possibly of other upper nodes too.
* If the key matches the first key, then
* we can unlock all the upper nodes,
* using btrfs_unlock_up_safe() instead
* of unlock_up() as stated above.
*/
if (ret == 0)
btrfs_unlock_up_safe(p, 1);
slot = 0;
/*
* ret is already 0 or 1, matching the
* result of a btrfs_bin_search() call,
* so there is no need to adjust it.
*/
goto skip_leaf_search;
}
}
leaf_search:
ret = search_for_key_slot(b, search_low_slot, key,
prev_cmp, &slot);
if (ret < 0)
goto done;
skip_leaf_search:
p->slots[level] = slot;
/*
* Item key already exists. In this case, if we are
* allowed to insert the item (for example, in dir_item
* case, item key collision is allowed), it will be
* merged with the original item. Only the item size
* grows, no new btrfs item will be added. If
* search_for_extension is not set, ins_len already
* accounts the size btrfs_item, deduct it here so leaf
* space check will be correct.
*/
if (ret == 0 && ins_len > 0 && !p->search_for_extension) {
ASSERT(ins_len >= sizeof(struct btrfs_item));
ins_len -= sizeof(struct btrfs_item);
}
if (ins_len > 0 && leaf_free_space < ins_len) {
if (ins_len > 0)
ASSERT(write_lock_level >= 1);
err = split_leaf(trans, root, key,
p, ins_len, ret == 0);
BUG_ON(err > 0);
if (err) {
ret = err;
goto done;
}
}
ret = search_leaf(trans, root, key, p, ins_len, prev_cmp);
if (!p->search_for_split)
unlock_up(p, level, lowest_unlock,
min_write_lock_level, NULL);