Commit d42244a0 authored by Jan Schmidt's avatar Jan Schmidt

Btrfs: resolve tree mod log locking issue in btrfs_next_leaf

With the tree mod log, we may end up with two roots (the current root and a
rewinded version of it) both pointing to two leaves, l1 and l2, of which l2
had already been cow-ed in the current transaction. If we don't rewind any
tree blocks, we cannot have two roots both pointing to an already cowed tree
block.

Now there is btrfs_next_leaf, which has a leaf locked and wants a lock on
the next (right) leaf. And there is push_leaf_left, which has a (cowed!)
leaf locked and wants a lock on the previous (left) leaf.

In order to solve this dead lock situation, we use try_lock in
btrfs_next_leaf (only in case it's called with a tree mod log time_seq
paramter) and if we fail to get a lock on the next leaf, we give up our lock
on the current leaf and retry from the very beginning.
Signed-off-by: default avatarJan Schmidt <list.btrfs@jan-o-sch.net>
parent 19956c7e
...@@ -5119,6 +5119,18 @@ int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, ...@@ -5119,6 +5119,18 @@ int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path,
if (!path->skip_locking) { if (!path->skip_locking) {
ret = btrfs_try_tree_read_lock(next); ret = btrfs_try_tree_read_lock(next);
if (!ret && time_seq) {
/*
* If we don't get the lock, we may be racing
* with push_leaf_left, holding that lock while
* itself waiting for the leaf we've currently
* locked. To solve this situation, we give up
* on our lock and cycle.
*/
btrfs_release_path(path);
cond_resched();
goto again;
}
if (!ret) { if (!ret) {
btrfs_set_path_blocking(path); btrfs_set_path_blocking(path);
btrfs_tree_read_lock(next); btrfs_tree_read_lock(next);
......
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