Commit 857bc13f authored by Josef Bacik's avatar Josef Bacik Committed by David Sterba

btrfs: implement a nowait option for tree searches

For NOWAIT IOCBs we'll need a way to tell search to not wait on locks
or anything.  Accomplish this by adding a path->nowait flag that will
use trylocks and skip reading of metadata, returning -EAGAIN in either
of these cases.  For now we only need this for reads, so only the read
side is handled.  Add an ASSERT() to catch anybody trying to use this
for writes so they know they'll have to implement the write side.
Reviewed-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarStefan Roesch <shr@fb.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 611df5d6
...@@ -1447,6 +1447,11 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p, ...@@ -1447,6 +1447,11 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p,
return 0; return 0;
} }
if (p->nowait) {
free_extent_buffer(tmp);
return -EAGAIN;
}
if (unlock_up) if (unlock_up)
btrfs_unlock_up_safe(p, level + 1); btrfs_unlock_up_safe(p, level + 1);
...@@ -1467,6 +1472,8 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p, ...@@ -1467,6 +1472,8 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p,
ret = -EAGAIN; ret = -EAGAIN;
goto out; goto out;
} else if (p->nowait) {
return -EAGAIN;
} }
if (unlock_up) { if (unlock_up) {
...@@ -1634,7 +1641,13 @@ static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root, ...@@ -1634,7 +1641,13 @@ static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root,
* We don't know the level of the root node until we actually * We don't know the level of the root node until we actually
* have it read locked * have it read locked
*/ */
if (p->nowait) {
b = btrfs_try_read_lock_root_node(root);
if (IS_ERR(b))
return b;
} else {
b = btrfs_read_lock_root_node(root); b = btrfs_read_lock_root_node(root);
}
level = btrfs_header_level(b); level = btrfs_header_level(b);
if (level > write_lock_level) if (level > write_lock_level)
goto out; goto out;
...@@ -1910,6 +1923,13 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -1910,6 +1923,13 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root,
WARN_ON(p->nodes[0] != NULL); WARN_ON(p->nodes[0] != NULL);
BUG_ON(!cow && ins_len); BUG_ON(!cow && ins_len);
/*
* For now only allow nowait for read only operations. There's no
* strict reason why we can't, we just only need it for reads so it's
* only implemented for reads.
*/
ASSERT(!p->nowait || !cow);
if (ins_len < 0) { if (ins_len < 0) {
lowest_unlock = 2; lowest_unlock = 2;
...@@ -1936,8 +1956,13 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -1936,8 +1956,13 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root,
if (p->need_commit_sem) { if (p->need_commit_sem) {
ASSERT(p->search_commit_root); ASSERT(p->search_commit_root);
if (p->nowait) {
if (!down_read_trylock(&fs_info->commit_root_sem))
return -EAGAIN;
} else {
down_read(&fs_info->commit_root_sem); down_read(&fs_info->commit_root_sem);
} }
}
again: again:
prev_cmp = -1; prev_cmp = -1;
...@@ -2081,8 +2106,16 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -2081,8 +2106,16 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root,
if (level <= write_lock_level) { if (level <= write_lock_level) {
btrfs_tree_lock(b); btrfs_tree_lock(b);
p->locks[level] = BTRFS_WRITE_LOCK; p->locks[level] = BTRFS_WRITE_LOCK;
} else {
if (p->nowait) {
if (!btrfs_try_tree_read_lock(b)) {
free_extent_buffer(b);
ret = -EAGAIN;
goto done;
}
} else { } else {
btrfs_tree_read_lock(b); btrfs_tree_read_lock(b);
}
p->locks[level] = BTRFS_READ_LOCK; p->locks[level] = BTRFS_READ_LOCK;
} }
p->nodes[level] = b; p->nodes[level] = b;
......
...@@ -443,6 +443,8 @@ struct btrfs_path { ...@@ -443,6 +443,8 @@ struct btrfs_path {
* header (ie. sizeof(struct btrfs_item) is not included). * header (ie. sizeof(struct btrfs_item) is not included).
*/ */
unsigned int search_for_extension:1; unsigned int search_for_extension:1;
/* Stop search if any locks need to be taken (for read) */
unsigned int nowait:1;
}; };
struct btrfs_dev_replace { struct btrfs_dev_replace {
......
...@@ -285,6 +285,31 @@ struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root) ...@@ -285,6 +285,31 @@ struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root)
return eb; return eb;
} }
/*
* Loop around taking references on and locking the root node of the tree in
* nowait mode until we end up with a lock on the root node or returning to
* avoid blocking.
*
* Return: root extent buffer with read lock held or -EAGAIN.
*/
struct extent_buffer *btrfs_try_read_lock_root_node(struct btrfs_root *root)
{
struct extent_buffer *eb;
while (1) {
eb = btrfs_root_node(root);
if (!btrfs_try_tree_read_lock(eb)) {
free_extent_buffer(eb);
return ERR_PTR(-EAGAIN);
}
if (eb == root->node)
break;
btrfs_tree_read_unlock(eb);
free_extent_buffer(eb);
}
return eb;
}
/* /*
* DREW locks * DREW locks
* ========== * ==========
......
...@@ -94,6 +94,7 @@ int btrfs_try_tree_read_lock(struct extent_buffer *eb); ...@@ -94,6 +94,7 @@ int btrfs_try_tree_read_lock(struct extent_buffer *eb);
int btrfs_try_tree_write_lock(struct extent_buffer *eb); int btrfs_try_tree_write_lock(struct extent_buffer *eb);
struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root); struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root);
struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root); struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root);
struct extent_buffer *btrfs_try_read_lock_root_node(struct btrfs_root *root);
#ifdef CONFIG_BTRFS_DEBUG #ifdef CONFIG_BTRFS_DEBUG
static inline void btrfs_assert_tree_write_locked(struct extent_buffer *eb) static inline void btrfs_assert_tree_write_locked(struct extent_buffer *eb)
......
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