aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ctree.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r--fs/btrfs/ctree.c133
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 */
5494static 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 */
5521int 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)) {
5542search:
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;
5581cow_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
5619out:
5620 if (ret)
5621 btrfs_release_path(path);
5622
5623 return ret;
5624}
5625
5493int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, 5626int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path,
5494 u64 time_seq) 5627 u64 time_seq)
5495{ 5628{