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: Jan Schmidt <list.btrfs@jan-o-sch.net>
This commit is contained in:
parent
19956c7e94
commit
d42244a0d3
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user