diff options
-rw-r--r-- | fs/btrfs/ctree.c | 39 |
1 files changed, 36 insertions, 3 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index a99f1c2a710d..fedf8b9f03a2 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -1469,6 +1469,7 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
1469 | u32 blocksize; | 1469 | u32 blocksize; |
1470 | struct extent_buffer *b = *eb_ret; | 1470 | struct extent_buffer *b = *eb_ret; |
1471 | struct extent_buffer *tmp; | 1471 | struct extent_buffer *tmp; |
1472 | int ret; | ||
1472 | 1473 | ||
1473 | blocknr = btrfs_node_blockptr(b, slot); | 1474 | blocknr = btrfs_node_blockptr(b, slot); |
1474 | gen = btrfs_node_ptr_generation(b, slot); | 1475 | gen = btrfs_node_ptr_generation(b, slot); |
@@ -1476,6 +1477,10 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
1476 | 1477 | ||
1477 | tmp = btrfs_find_tree_block(root, blocknr, blocksize); | 1478 | tmp = btrfs_find_tree_block(root, blocknr, blocksize); |
1478 | if (tmp && btrfs_buffer_uptodate(tmp, gen)) { | 1479 | if (tmp && btrfs_buffer_uptodate(tmp, gen)) { |
1480 | /* | ||
1481 | * we found an up to date block without sleeping, return | ||
1482 | * right away | ||
1483 | */ | ||
1479 | *eb_ret = tmp; | 1484 | *eb_ret = tmp; |
1480 | return 0; | 1485 | return 0; |
1481 | } | 1486 | } |
@@ -1483,7 +1488,9 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
1483 | /* | 1488 | /* |
1484 | * reduce lock contention at high levels | 1489 | * reduce lock contention at high levels |
1485 | * of the btree by dropping locks before | 1490 | * of the btree by dropping locks before |
1486 | * we read. | 1491 | * we read. Don't release the lock on the current |
1492 | * level because we need to walk this node to figure | ||
1493 | * out which blocks to read. | ||
1487 | */ | 1494 | */ |
1488 | btrfs_unlock_up_safe(p, level + 1); | 1495 | btrfs_unlock_up_safe(p, level + 1); |
1489 | btrfs_set_path_blocking(p); | 1496 | btrfs_set_path_blocking(p); |
@@ -1494,10 +1501,21 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
1494 | reada_for_search(root, p, level, slot, key->objectid); | 1501 | reada_for_search(root, p, level, slot, key->objectid); |
1495 | 1502 | ||
1496 | btrfs_release_path(NULL, p); | 1503 | btrfs_release_path(NULL, p); |
1504 | |||
1505 | ret = -EAGAIN; | ||
1497 | tmp = read_tree_block(root, blocknr, blocksize, gen); | 1506 | tmp = read_tree_block(root, blocknr, blocksize, gen); |
1498 | if (tmp) | 1507 | if (tmp) { |
1508 | /* | ||
1509 | * If the read above didn't mark this buffer up to date, | ||
1510 | * it will never end up being up to date. Set ret to EIO now | ||
1511 | * and give up so that our caller doesn't loop forever | ||
1512 | * on our EAGAINs. | ||
1513 | */ | ||
1514 | if (!btrfs_buffer_uptodate(tmp, 0)) | ||
1515 | ret = -EIO; | ||
1499 | free_extent_buffer(tmp); | 1516 | free_extent_buffer(tmp); |
1500 | return -EAGAIN; | 1517 | } |
1518 | return ret; | ||
1501 | } | 1519 | } |
1502 | 1520 | ||
1503 | /* | 1521 | /* |
@@ -1696,6 +1714,9 @@ cow_done: | |||
1696 | if (ret == -EAGAIN) | 1714 | if (ret == -EAGAIN) |
1697 | goto again; | 1715 | goto again; |
1698 | 1716 | ||
1717 | if (ret == -EIO) | ||
1718 | goto done; | ||
1719 | |||
1699 | if (!p->skip_locking) { | 1720 | if (!p->skip_locking) { |
1700 | int lret; | 1721 | int lret; |
1701 | 1722 | ||
@@ -1738,6 +1759,8 @@ done: | |||
1738 | */ | 1759 | */ |
1739 | if (!p->leave_spinning) | 1760 | if (!p->leave_spinning) |
1740 | btrfs_set_path_blocking(p); | 1761 | btrfs_set_path_blocking(p); |
1762 | if (ret < 0) | ||
1763 | btrfs_release_path(root, p); | ||
1741 | return ret; | 1764 | return ret; |
1742 | } | 1765 | } |
1743 | 1766 | ||
@@ -4212,6 +4235,11 @@ again: | |||
4212 | if (ret == -EAGAIN) | 4235 | if (ret == -EAGAIN) |
4213 | goto again; | 4236 | goto again; |
4214 | 4237 | ||
4238 | if (ret < 0) { | ||
4239 | btrfs_release_path(root, path); | ||
4240 | goto done; | ||
4241 | } | ||
4242 | |||
4215 | if (!path->skip_locking) { | 4243 | if (!path->skip_locking) { |
4216 | ret = btrfs_try_spin_lock(next); | 4244 | ret = btrfs_try_spin_lock(next); |
4217 | if (!ret) { | 4245 | if (!ret) { |
@@ -4246,6 +4274,11 @@ again: | |||
4246 | if (ret == -EAGAIN) | 4274 | if (ret == -EAGAIN) |
4247 | goto again; | 4275 | goto again; |
4248 | 4276 | ||
4277 | if (ret < 0) { | ||
4278 | btrfs_release_path(root, path); | ||
4279 | goto done; | ||
4280 | } | ||
4281 | |||
4249 | if (!path->skip_locking) { | 4282 | if (!path->skip_locking) { |
4250 | btrfs_assert_tree_locked(path->nodes[level]); | 4283 | btrfs_assert_tree_locked(path->nodes[level]); |
4251 | ret = btrfs_try_spin_lock(next); | 4284 | ret = btrfs_try_spin_lock(next); |