diff options
Diffstat (limited to 'fs/btrfs/ctree.c')
| -rw-r--r-- | fs/btrfs/ctree.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 0c5c28ff794f..e8b32641ea90 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
| @@ -5490,6 +5490,139 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) | |||
| 5490 | return btrfs_next_old_leaf(root, path, 0); | 5490 | return btrfs_next_old_leaf(root, path, 0); |
| 5491 | } | 5491 | } |
| 5492 | 5492 | ||
| 5493 | /* Release the path up to but not including the given level */ | ||
| 5494 | static void btrfs_release_level(struct btrfs_path *path, int level) | ||
| 5495 | { | ||
| 5496 | int i; | ||
| 5497 | |||
| 5498 | for (i = 0; i < level; i++) { | ||
| 5499 | path->slots[i] = 0; | ||
| 5500 | if (!path->nodes[i]) | ||
| 5501 | continue; | ||
| 5502 | if (path->locks[i]) { | ||
| 5503 | btrfs_tree_unlock_rw(path->nodes[i], path->locks[i]); | ||
| 5504 | path->locks[i] = 0; | ||
| 5505 | } | ||
| 5506 | free_extent_buffer(path->nodes[i]); | ||
| 5507 | path->nodes[i] = NULL; | ||
| 5508 | } | ||
| 5509 | } | ||
| 5510 | |||
| 5511 | /* | ||
| 5512 | * This function assumes 2 things | ||
| 5513 | * | ||
| 5514 | * 1) You are using path->keep_locks | ||
| 5515 | * 2) You are not inserting items. | ||
| 5516 | * | ||
| 5517 | * If either of these are not true do not use this function. If you need a next | ||
| 5518 | * leaf with either of these not being true then this function can be easily | ||
| 5519 | * adapted to do that, but at the moment these are the limitations. | ||
| 5520 | */ | ||
| 5521 | int btrfs_next_leaf_write(struct btrfs_trans_handle *trans, | ||
| 5522 | struct btrfs_root *root, struct btrfs_path *path, | ||
| 5523 | int del) | ||
| 5524 | { | ||
| 5525 | struct extent_buffer *b; | ||
| 5526 | struct btrfs_key key; | ||
| 5527 | u32 nritems; | ||
| 5528 | int level = 1; | ||
| 5529 | int slot; | ||
| 5530 | int ret = 1; | ||
| 5531 | int write_lock_level = BTRFS_MAX_LEVEL; | ||
| 5532 | int ins_len = del ? -1 : 0; | ||
| 5533 | |||
| 5534 | WARN_ON(!(path->keep_locks || path->really_keep_locks)); | ||
| 5535 | |||
| 5536 | nritems = btrfs_header_nritems(path->nodes[0]); | ||
| 5537 | btrfs_item_key_to_cpu(path->nodes[0], &key, nritems - 1); | ||
| 5538 | |||
| 5539 | while (path->nodes[level]) { | ||
| 5540 | nritems = btrfs_header_nritems(path->nodes[level]); | ||
| 5541 | if (!(path->locks[level] & BTRFS_WRITE_LOCK)) { | ||
| 5542 | search: | ||
| 5543 | btrfs_release_path(path); | ||
| 5544 | ret = btrfs_search_slot(trans, root, &key, path, | ||
| 5545 | ins_len, 1); | ||
| 5546 | if (ret < 0) | ||
| 5547 | goto out; | ||
| 5548 | level = 1; | ||
| 5549 | continue; | ||
| 5550 | } | ||
| 5551 | |||
| 5552 | if (path->slots[level] >= nritems - 1) { | ||
| 5553 | level++; | ||
| 5554 | continue; | ||
| 5555 | } | ||
| 5556 | |||
| 5557 | btrfs_release_level(path, level); | ||
| 5558 | break; | ||
| 5559 | } | ||
| 5560 | |||
| 5561 | if (!path->nodes[level]) { | ||
| 5562 | ret = 1; | ||
| 5563 | goto out; | ||
| 5564 | } | ||
| 5565 | |||
| 5566 | path->slots[level]++; | ||
| 5567 | b = path->nodes[level]; | ||
| 5568 | |||
| 5569 | while (b) { | ||
| 5570 | level = btrfs_header_level(b); | ||
| 5571 | |||
| 5572 | if (!should_cow_block(trans, root, b)) | ||
| 5573 | goto cow_done; | ||
| 5574 | |||
| 5575 | btrfs_set_path_blocking(path); | ||
| 5576 | ret = btrfs_cow_block(trans, root, b, | ||
| 5577 | path->nodes[level + 1], | ||
| 5578 | path->slots[level + 1], &b); | ||
| 5579 | if (ret) | ||
| 5580 | goto out; | ||
| 5581 | cow_done: | ||
| 5582 | path->nodes[level] = b; | ||
| 5583 | btrfs_clear_path_blocking(path, NULL, 0); | ||
| 5584 | if (level != 0) { | ||
| 5585 | ret = setup_nodes_for_search(trans, root, path, b, | ||
| 5586 | level, ins_len, | ||
| 5587 | &write_lock_level); | ||
| 5588 | if (ret == -EAGAIN) | ||
| 5589 | goto search; | ||
| 5590 | if (ret) | ||
| 5591 | goto out; | ||
| 5592 | |||
| 5593 | b = path->nodes[level]; | ||
| 5594 | slot = path->slots[level]; | ||
| 5595 | |||
| 5596 | ret = read_block_for_search(trans, root, path, | ||
| 5597 | &b, level, slot, &key, 0); | ||
| 5598 | if (ret == -EAGAIN) | ||
| 5599 | goto search; | ||
| 5600 | if (ret) | ||
| 5601 | goto out; | ||
| 5602 | level = btrfs_header_level(b); | ||
| 5603 | if (!btrfs_try_tree_write_lock(b)) { | ||
| 5604 | btrfs_set_path_blocking(path); | ||
| 5605 | btrfs_tree_lock(b); | ||
| 5606 | btrfs_clear_path_blocking(path, b, | ||
| 5607 | BTRFS_WRITE_LOCK); | ||
| 5608 | } | ||
| 5609 | path->locks[level] = BTRFS_WRITE_LOCK; | ||
| 5610 | path->nodes[level] = b; | ||
| 5611 | path->slots[level] = 0; | ||
| 5612 | } else { | ||
| 5613 | path->slots[level] = 0; | ||
| 5614 | ret = 0; | ||
| 5615 | break; | ||
| 5616 | } | ||
| 5617 | } | ||
| 5618 | |||
| 5619 | out: | ||
| 5620 | if (ret) | ||
| 5621 | btrfs_release_path(path); | ||
| 5622 | |||
| 5623 | return ret; | ||
| 5624 | } | ||
| 5625 | |||
| 5493 | int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, | 5626 | int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, |
| 5494 | u64 time_seq) | 5627 | u64 time_seq) |
| 5495 | { | 5628 | { |
