summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2019-08-21 13:12:59 -0400
committerDavid Sterba <dsterba@suse.com>2019-09-09 08:59:15 -0400
commit18d0f5c6e16ce762f92ab7879c30ff2e37cd9cef (patch)
tree6631caf404b4d0fc7215d8d3d0bc9fdcedc984c8
parent4b231ae47417d47a6bafab92b452ad629acdacb0 (diff)
btrfs: move functions for tree compare to send.c
Send is the only user of tree_compare, we can move it there along with the other helpers and definitions. Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/ctree.c362
-rw-r--r--fs/btrfs/ctree.h14
-rw-r--r--fs/btrfs/send.c374
3 files changed, 374 insertions, 376 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 3b585f3e4d11..fbf94e28fba8 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -5246,368 +5246,6 @@ out:
5246 return ret; 5246 return ret;
5247} 5247}
5248 5248
5249static int tree_move_down(struct btrfs_path *path, int *level)
5250{
5251 struct extent_buffer *eb;
5252
5253 BUG_ON(*level == 0);
5254 eb = btrfs_read_node_slot(path->nodes[*level], path->slots[*level]);
5255 if (IS_ERR(eb))
5256 return PTR_ERR(eb);
5257
5258 path->nodes[*level - 1] = eb;
5259 path->slots[*level - 1] = 0;
5260 (*level)--;
5261 return 0;
5262}
5263
5264static int tree_move_next_or_upnext(struct btrfs_path *path,
5265 int *level, int root_level)
5266{
5267 int ret = 0;
5268 int nritems;
5269 nritems = btrfs_header_nritems(path->nodes[*level]);
5270
5271 path->slots[*level]++;
5272
5273 while (path->slots[*level] >= nritems) {
5274 if (*level == root_level)
5275 return -1;
5276
5277 /* move upnext */
5278 path->slots[*level] = 0;
5279 free_extent_buffer(path->nodes[*level]);
5280 path->nodes[*level] = NULL;
5281 (*level)++;
5282 path->slots[*level]++;
5283
5284 nritems = btrfs_header_nritems(path->nodes[*level]);
5285 ret = 1;
5286 }
5287 return ret;
5288}
5289
5290/*
5291 * Returns 1 if it had to move up and next. 0 is returned if it moved only next
5292 * or down.
5293 */
5294static int tree_advance(struct btrfs_path *path,
5295 int *level, int root_level,
5296 int allow_down,
5297 struct btrfs_key *key)
5298{
5299 int ret;
5300
5301 if (*level == 0 || !allow_down) {
5302 ret = tree_move_next_or_upnext(path, level, root_level);
5303 } else {
5304 ret = tree_move_down(path, level);
5305 }
5306 if (ret >= 0) {
5307 if (*level == 0)
5308 btrfs_item_key_to_cpu(path->nodes[*level], key,
5309 path->slots[*level]);
5310 else
5311 btrfs_node_key_to_cpu(path->nodes[*level], key,
5312 path->slots[*level]);
5313 }
5314 return ret;
5315}
5316
5317static int tree_compare_item(struct btrfs_path *left_path,
5318 struct btrfs_path *right_path,
5319 char *tmp_buf)
5320{
5321 int cmp;
5322 int len1, len2;
5323 unsigned long off1, off2;
5324
5325 len1 = btrfs_item_size_nr(left_path->nodes[0], left_path->slots[0]);
5326 len2 = btrfs_item_size_nr(right_path->nodes[0], right_path->slots[0]);
5327 if (len1 != len2)
5328 return 1;
5329
5330 off1 = btrfs_item_ptr_offset(left_path->nodes[0], left_path->slots[0]);
5331 off2 = btrfs_item_ptr_offset(right_path->nodes[0],
5332 right_path->slots[0]);
5333
5334 read_extent_buffer(left_path->nodes[0], tmp_buf, off1, len1);
5335
5336 cmp = memcmp_extent_buffer(right_path->nodes[0], tmp_buf, off2, len1);
5337 if (cmp)
5338 return 1;
5339 return 0;
5340}
5341
5342#define ADVANCE 1
5343#define ADVANCE_ONLY_NEXT -1
5344
5345/*
5346 * This function compares two trees and calls the provided callback for
5347 * every changed/new/deleted item it finds.
5348 * If shared tree blocks are encountered, whole subtrees are skipped, making
5349 * the compare pretty fast on snapshotted subvolumes.
5350 *
5351 * This currently works on commit roots only. As commit roots are read only,
5352 * we don't do any locking. The commit roots are protected with transactions.
5353 * Transactions are ended and rejoined when a commit is tried in between.
5354 *
5355 * This function checks for modifications done to the trees while comparing.
5356 * If it detects a change, it aborts immediately.
5357 */
5358int btrfs_compare_trees(struct btrfs_root *left_root,
5359 struct btrfs_root *right_root,
5360 btrfs_changed_cb_t changed_cb, void *ctx)
5361{
5362 struct btrfs_fs_info *fs_info = left_root->fs_info;
5363 int ret;
5364 int cmp;
5365 struct btrfs_path *left_path = NULL;
5366 struct btrfs_path *right_path = NULL;
5367 struct btrfs_key left_key;
5368 struct btrfs_key right_key;
5369 char *tmp_buf = NULL;
5370 int left_root_level;
5371 int right_root_level;
5372 int left_level;
5373 int right_level;
5374 int left_end_reached;
5375 int right_end_reached;
5376 int advance_left;
5377 int advance_right;
5378 u64 left_blockptr;
5379 u64 right_blockptr;
5380 u64 left_gen;
5381 u64 right_gen;
5382
5383 left_path = btrfs_alloc_path();
5384 if (!left_path) {
5385 ret = -ENOMEM;
5386 goto out;
5387 }
5388 right_path = btrfs_alloc_path();
5389 if (!right_path) {
5390 ret = -ENOMEM;
5391 goto out;
5392 }
5393
5394 tmp_buf = kvmalloc(fs_info->nodesize, GFP_KERNEL);
5395 if (!tmp_buf) {
5396 ret = -ENOMEM;
5397 goto out;
5398 }
5399
5400 left_path->search_commit_root = 1;
5401 left_path->skip_locking = 1;
5402 right_path->search_commit_root = 1;
5403 right_path->skip_locking = 1;
5404
5405 /*
5406 * Strategy: Go to the first items of both trees. Then do
5407 *
5408 * If both trees are at level 0
5409 * Compare keys of current items
5410 * If left < right treat left item as new, advance left tree
5411 * and repeat
5412 * If left > right treat right item as deleted, advance right tree
5413 * and repeat
5414 * If left == right do deep compare of items, treat as changed if
5415 * needed, advance both trees and repeat
5416 * If both trees are at the same level but not at level 0
5417 * Compare keys of current nodes/leafs
5418 * If left < right advance left tree and repeat
5419 * If left > right advance right tree and repeat
5420 * If left == right compare blockptrs of the next nodes/leafs
5421 * If they match advance both trees but stay at the same level
5422 * and repeat
5423 * If they don't match advance both trees while allowing to go
5424 * deeper and repeat
5425 * If tree levels are different
5426 * Advance the tree that needs it and repeat
5427 *
5428 * Advancing a tree means:
5429 * If we are at level 0, try to go to the next slot. If that's not
5430 * possible, go one level up and repeat. Stop when we found a level
5431 * where we could go to the next slot. We may at this point be on a
5432 * node or a leaf.
5433 *
5434 * If we are not at level 0 and not on shared tree blocks, go one
5435 * level deeper.
5436 *
5437 * If we are not at level 0 and on shared tree blocks, go one slot to
5438 * the right if possible or go up and right.
5439 */
5440
5441 down_read(&fs_info->commit_root_sem);
5442 left_level = btrfs_header_level(left_root->commit_root);
5443 left_root_level = left_level;
5444 left_path->nodes[left_level] =
5445 btrfs_clone_extent_buffer(left_root->commit_root);
5446 if (!left_path->nodes[left_level]) {
5447 up_read(&fs_info->commit_root_sem);
5448 ret = -ENOMEM;
5449 goto out;
5450 }
5451
5452 right_level = btrfs_header_level(right_root->commit_root);
5453 right_root_level = right_level;
5454 right_path->nodes[right_level] =
5455 btrfs_clone_extent_buffer(right_root->commit_root);
5456 if (!right_path->nodes[right_level]) {
5457 up_read(&fs_info->commit_root_sem);
5458 ret = -ENOMEM;
5459 goto out;
5460 }
5461 up_read(&fs_info->commit_root_sem);
5462
5463 if (left_level == 0)
5464 btrfs_item_key_to_cpu(left_path->nodes[left_level],
5465 &left_key, left_path->slots[left_level]);
5466 else
5467 btrfs_node_key_to_cpu(left_path->nodes[left_level],
5468 &left_key, left_path->slots[left_level]);
5469 if (right_level == 0)
5470 btrfs_item_key_to_cpu(right_path->nodes[right_level],
5471 &right_key, right_path->slots[right_level]);
5472 else
5473 btrfs_node_key_to_cpu(right_path->nodes[right_level],
5474 &right_key, right_path->slots[right_level]);
5475
5476 left_end_reached = right_end_reached = 0;
5477 advance_left = advance_right = 0;
5478
5479 while (1) {
5480 if (advance_left && !left_end_reached) {
5481 ret = tree_advance(left_path, &left_level,
5482 left_root_level,
5483 advance_left != ADVANCE_ONLY_NEXT,
5484 &left_key);
5485 if (ret == -1)
5486 left_end_reached = ADVANCE;
5487 else if (ret < 0)
5488 goto out;
5489 advance_left = 0;
5490 }
5491 if (advance_right && !right_end_reached) {
5492 ret = tree_advance(right_path, &right_level,
5493 right_root_level,
5494 advance_right != ADVANCE_ONLY_NEXT,
5495 &right_key);
5496 if (ret == -1)
5497 right_end_reached = ADVANCE;
5498 else if (ret < 0)
5499 goto out;
5500 advance_right = 0;
5501 }
5502
5503 if (left_end_reached && right_end_reached) {
5504 ret = 0;
5505 goto out;
5506 } else if (left_end_reached) {
5507 if (right_level == 0) {
5508 ret = changed_cb(left_path, right_path,
5509 &right_key,
5510 BTRFS_COMPARE_TREE_DELETED,
5511 ctx);
5512 if (ret < 0)
5513 goto out;
5514 }
5515 advance_right = ADVANCE;
5516 continue;
5517 } else if (right_end_reached) {
5518 if (left_level == 0) {
5519 ret = changed_cb(left_path, right_path,
5520 &left_key,
5521 BTRFS_COMPARE_TREE_NEW,
5522 ctx);
5523 if (ret < 0)
5524 goto out;
5525 }
5526 advance_left = ADVANCE;
5527 continue;
5528 }
5529
5530 if (left_level == 0 && right_level == 0) {
5531 cmp = btrfs_comp_cpu_keys(&left_key, &right_key);
5532 if (cmp < 0) {
5533 ret = changed_cb(left_path, right_path,
5534 &left_key,
5535 BTRFS_COMPARE_TREE_NEW,
5536 ctx);
5537 if (ret < 0)
5538 goto out;
5539 advance_left = ADVANCE;
5540 } else if (cmp > 0) {
5541 ret = changed_cb(left_path, right_path,
5542 &right_key,
5543 BTRFS_COMPARE_TREE_DELETED,
5544 ctx);
5545 if (ret < 0)
5546 goto out;
5547 advance_right = ADVANCE;
5548 } else {
5549 enum btrfs_compare_tree_result result;
5550
5551 WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
5552 ret = tree_compare_item(left_path, right_path,
5553 tmp_buf);
5554 if (ret)
5555 result = BTRFS_COMPARE_TREE_CHANGED;
5556 else
5557 result = BTRFS_COMPARE_TREE_SAME;
5558 ret = changed_cb(left_path, right_path,
5559 &left_key, result, ctx);
5560 if (ret < 0)
5561 goto out;
5562 advance_left = ADVANCE;
5563 advance_right = ADVANCE;
5564 }
5565 } else if (left_level == right_level) {
5566 cmp = btrfs_comp_cpu_keys(&left_key, &right_key);
5567 if (cmp < 0) {
5568 advance_left = ADVANCE;
5569 } else if (cmp > 0) {
5570 advance_right = ADVANCE;
5571 } else {
5572 left_blockptr = btrfs_node_blockptr(
5573 left_path->nodes[left_level],
5574 left_path->slots[left_level]);
5575 right_blockptr = btrfs_node_blockptr(
5576 right_path->nodes[right_level],
5577 right_path->slots[right_level]);
5578 left_gen = btrfs_node_ptr_generation(
5579 left_path->nodes[left_level],
5580 left_path->slots[left_level]);
5581 right_gen = btrfs_node_ptr_generation(
5582 right_path->nodes[right_level],
5583 right_path->slots[right_level]);
5584 if (left_blockptr == right_blockptr &&
5585 left_gen == right_gen) {
5586 /*
5587 * As we're on a shared block, don't
5588 * allow to go deeper.
5589 */
5590 advance_left = ADVANCE_ONLY_NEXT;
5591 advance_right = ADVANCE_ONLY_NEXT;
5592 } else {
5593 advance_left = ADVANCE;
5594 advance_right = ADVANCE;
5595 }
5596 }
5597 } else if (left_level < right_level) {
5598 advance_right = ADVANCE;
5599 } else {
5600 advance_left = ADVANCE;
5601 }
5602 }
5603
5604out:
5605 btrfs_free_path(left_path);
5606 btrfs_free_path(right_path);
5607 kvfree(tmp_buf);
5608 return ret;
5609}
5610
5611/* 5249/*
5612 * this is similar to btrfs_next_leaf, but does not try to preserve 5250 * this is similar to btrfs_next_leaf, but does not try to preserve
5613 * and fixup the path. It looks for and returns the next key in the 5251 * and fixup the path. It looks for and returns the next key in the
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index dc465df47b32..17cd88521ad2 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2590,20 +2590,6 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
2590struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent, 2590struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent,
2591 int slot); 2591 int slot);
2592 2592
2593enum btrfs_compare_tree_result {
2594 BTRFS_COMPARE_TREE_NEW,
2595 BTRFS_COMPARE_TREE_DELETED,
2596 BTRFS_COMPARE_TREE_CHANGED,
2597 BTRFS_COMPARE_TREE_SAME,
2598};
2599typedef int (*btrfs_changed_cb_t)(struct btrfs_path *left_path,
2600 struct btrfs_path *right_path,
2601 struct btrfs_key *key,
2602 enum btrfs_compare_tree_result result,
2603 void *ctx);
2604int btrfs_compare_trees(struct btrfs_root *left_root,
2605 struct btrfs_root *right_root,
2606 btrfs_changed_cb_t cb, void *ctx);
2607int btrfs_cow_block(struct btrfs_trans_handle *trans, 2593int btrfs_cow_block(struct btrfs_trans_handle *trans,
2608 struct btrfs_root *root, struct extent_buffer *buf, 2594 struct btrfs_root *root, struct extent_buffer *buf,
2609 struct extent_buffer *parent, int parent_slot, 2595 struct extent_buffer *parent, int parent_slot,
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index c3c0c064c25d..f856d6ca3771 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -260,6 +260,21 @@ struct name_cache_entry {
260 char name[]; 260 char name[];
261}; 261};
262 262
263#define ADVANCE 1
264#define ADVANCE_ONLY_NEXT -1
265
266enum btrfs_compare_tree_result {
267 BTRFS_COMPARE_TREE_NEW,
268 BTRFS_COMPARE_TREE_DELETED,
269 BTRFS_COMPARE_TREE_CHANGED,
270 BTRFS_COMPARE_TREE_SAME,
271};
272typedef int (*btrfs_changed_cb_t)(struct btrfs_path *left_path,
273 struct btrfs_path *right_path,
274 struct btrfs_key *key,
275 enum btrfs_compare_tree_result result,
276 void *ctx);
277
263__cold 278__cold
264static void inconsistent_snapshot_error(struct send_ctx *sctx, 279static void inconsistent_snapshot_error(struct send_ctx *sctx,
265 enum btrfs_compare_tree_result result, 280 enum btrfs_compare_tree_result result,
@@ -6514,6 +6529,365 @@ out:
6514 return ret; 6529 return ret;
6515} 6530}
6516 6531
6532static int tree_move_down(struct btrfs_path *path, int *level)
6533{
6534 struct extent_buffer *eb;
6535
6536 BUG_ON(*level == 0);
6537 eb = btrfs_read_node_slot(path->nodes[*level], path->slots[*level]);
6538 if (IS_ERR(eb))
6539 return PTR_ERR(eb);
6540
6541 path->nodes[*level - 1] = eb;
6542 path->slots[*level - 1] = 0;
6543 (*level)--;
6544 return 0;
6545}
6546
6547static int tree_move_next_or_upnext(struct btrfs_path *path,
6548 int *level, int root_level)
6549{
6550 int ret = 0;
6551 int nritems;
6552 nritems = btrfs_header_nritems(path->nodes[*level]);
6553
6554 path->slots[*level]++;
6555
6556 while (path->slots[*level] >= nritems) {
6557 if (*level == root_level)
6558 return -1;
6559
6560 /* move upnext */
6561 path->slots[*level] = 0;
6562 free_extent_buffer(path->nodes[*level]);
6563 path->nodes[*level] = NULL;
6564 (*level)++;
6565 path->slots[*level]++;
6566
6567 nritems = btrfs_header_nritems(path->nodes[*level]);
6568 ret = 1;
6569 }
6570 return ret;
6571}
6572
6573/*
6574 * Returns 1 if it had to move up and next. 0 is returned if it moved only next
6575 * or down.
6576 */
6577static int tree_advance(struct btrfs_path *path,
6578 int *level, int root_level,
6579 int allow_down,
6580 struct btrfs_key *key)
6581{
6582 int ret;
6583
6584 if (*level == 0 || !allow_down) {
6585 ret = tree_move_next_or_upnext(path, level, root_level);
6586 } else {
6587 ret = tree_move_down(path, level);
6588 }
6589 if (ret >= 0) {
6590 if (*level == 0)
6591 btrfs_item_key_to_cpu(path->nodes[*level], key,
6592 path->slots[*level]);
6593 else
6594 btrfs_node_key_to_cpu(path->nodes[*level], key,
6595 path->slots[*level]);
6596 }
6597 return ret;
6598}
6599
6600static int tree_compare_item(struct btrfs_path *left_path,
6601 struct btrfs_path *right_path,
6602 char *tmp_buf)
6603{
6604 int cmp;
6605 int len1, len2;
6606 unsigned long off1, off2;
6607
6608 len1 = btrfs_item_size_nr(left_path->nodes[0], left_path->slots[0]);
6609 len2 = btrfs_item_size_nr(right_path->nodes[0], right_path->slots[0]);
6610 if (len1 != len2)
6611 return 1;
6612
6613 off1 = btrfs_item_ptr_offset(left_path->nodes[0], left_path->slots[0]);
6614 off2 = btrfs_item_ptr_offset(right_path->nodes[0],
6615 right_path->slots[0]);
6616
6617 read_extent_buffer(left_path->nodes[0], tmp_buf, off1, len1);
6618
6619 cmp = memcmp_extent_buffer(right_path->nodes[0], tmp_buf, off2, len1);
6620 if (cmp)
6621 return 1;
6622 return 0;
6623}
6624
6625/*
6626 * This function compares two trees and calls the provided callback for
6627 * every changed/new/deleted item it finds.
6628 * If shared tree blocks are encountered, whole subtrees are skipped, making
6629 * the compare pretty fast on snapshotted subvolumes.
6630 *
6631 * This currently works on commit roots only. As commit roots are read only,
6632 * we don't do any locking. The commit roots are protected with transactions.
6633 * Transactions are ended and rejoined when a commit is tried in between.
6634 *
6635 * This function checks for modifications done to the trees while comparing.
6636 * If it detects a change, it aborts immediately.
6637 */
6638static int btrfs_compare_trees(struct btrfs_root *left_root,
6639 struct btrfs_root *right_root,
6640 btrfs_changed_cb_t changed_cb, void *ctx)
6641{
6642 struct btrfs_fs_info *fs_info = left_root->fs_info;
6643 int ret;
6644 int cmp;
6645 struct btrfs_path *left_path = NULL;
6646 struct btrfs_path *right_path = NULL;
6647 struct btrfs_key left_key;
6648 struct btrfs_key right_key;
6649 char *tmp_buf = NULL;
6650 int left_root_level;
6651 int right_root_level;
6652 int left_level;
6653 int right_level;
6654 int left_end_reached;
6655 int right_end_reached;
6656 int advance_left;
6657 int advance_right;
6658 u64 left_blockptr;
6659 u64 right_blockptr;
6660 u64 left_gen;
6661 u64 right_gen;
6662
6663 left_path = btrfs_alloc_path();
6664 if (!left_path) {
6665 ret = -ENOMEM;
6666 goto out;
6667 }
6668 right_path = btrfs_alloc_path();
6669 if (!right_path) {
6670 ret = -ENOMEM;
6671 goto out;
6672 }
6673
6674 tmp_buf = kvmalloc(fs_info->nodesize, GFP_KERNEL);
6675 if (!tmp_buf) {
6676 ret = -ENOMEM;
6677 goto out;
6678 }
6679
6680 left_path->search_commit_root = 1;
6681 left_path->skip_locking = 1;
6682 right_path->search_commit_root = 1;
6683 right_path->skip_locking = 1;
6684
6685 /*
6686 * Strategy: Go to the first items of both trees. Then do
6687 *
6688 * If both trees are at level 0
6689 * Compare keys of current items
6690 * If left < right treat left item as new, advance left tree
6691 * and repeat
6692 * If left > right treat right item as deleted, advance right tree
6693 * and repeat
6694 * If left == right do deep compare of items, treat as changed if
6695 * needed, advance both trees and repeat
6696 * If both trees are at the same level but not at level 0
6697 * Compare keys of current nodes/leafs
6698 * If left < right advance left tree and repeat
6699 * If left > right advance right tree and repeat
6700 * If left == right compare blockptrs of the next nodes/leafs
6701 * If they match advance both trees but stay at the same level
6702 * and repeat
6703 * If they don't match advance both trees while allowing to go
6704 * deeper and repeat
6705 * If tree levels are different
6706 * Advance the tree that needs it and repeat
6707 *
6708 * Advancing a tree means:
6709 * If we are at level 0, try to go to the next slot. If that's not
6710 * possible, go one level up and repeat. Stop when we found a level
6711 * where we could go to the next slot. We may at this point be on a
6712 * node or a leaf.
6713 *
6714 * If we are not at level 0 and not on shared tree blocks, go one
6715 * level deeper.
6716 *
6717 * If we are not at level 0 and on shared tree blocks, go one slot to
6718 * the right if possible or go up and right.
6719 */
6720
6721 down_read(&fs_info->commit_root_sem);
6722 left_level = btrfs_header_level(left_root->commit_root);
6723 left_root_level = left_level;
6724 left_path->nodes[left_level] =
6725 btrfs_clone_extent_buffer(left_root->commit_root);
6726 if (!left_path->nodes[left_level]) {
6727 up_read(&fs_info->commit_root_sem);
6728 ret = -ENOMEM;
6729 goto out;
6730 }
6731
6732 right_level = btrfs_header_level(right_root->commit_root);
6733 right_root_level = right_level;
6734 right_path->nodes[right_level] =
6735 btrfs_clone_extent_buffer(right_root->commit_root);
6736 if (!right_path->nodes[right_level]) {
6737 up_read(&fs_info->commit_root_sem);
6738 ret = -ENOMEM;
6739 goto out;
6740 }
6741 up_read(&fs_info->commit_root_sem);
6742
6743 if (left_level == 0)
6744 btrfs_item_key_to_cpu(left_path->nodes[left_level],
6745 &left_key, left_path->slots[left_level]);
6746 else
6747 btrfs_node_key_to_cpu(left_path->nodes[left_level],
6748 &left_key, left_path->slots[left_level]);
6749 if (right_level == 0)
6750 btrfs_item_key_to_cpu(right_path->nodes[right_level],
6751 &right_key, right_path->slots[right_level]);
6752 else
6753 btrfs_node_key_to_cpu(right_path->nodes[right_level],
6754 &right_key, right_path->slots[right_level]);
6755
6756 left_end_reached = right_end_reached = 0;
6757 advance_left = advance_right = 0;
6758
6759 while (1) {
6760 if (advance_left && !left_end_reached) {
6761 ret = tree_advance(left_path, &left_level,
6762 left_root_level,
6763 advance_left != ADVANCE_ONLY_NEXT,
6764 &left_key);
6765 if (ret == -1)
6766 left_end_reached = ADVANCE;
6767 else if (ret < 0)
6768 goto out;
6769 advance_left = 0;
6770 }
6771 if (advance_right && !right_end_reached) {
6772 ret = tree_advance(right_path, &right_level,
6773 right_root_level,
6774 advance_right != ADVANCE_ONLY_NEXT,
6775 &right_key);
6776 if (ret == -1)
6777 right_end_reached = ADVANCE;
6778 else if (ret < 0)
6779 goto out;
6780 advance_right = 0;
6781 }
6782
6783 if (left_end_reached && right_end_reached) {
6784 ret = 0;
6785 goto out;
6786 } else if (left_end_reached) {
6787 if (right_level == 0) {
6788 ret = changed_cb(left_path, right_path,
6789 &right_key,
6790 BTRFS_COMPARE_TREE_DELETED,
6791 ctx);
6792 if (ret < 0)
6793 goto out;
6794 }
6795 advance_right = ADVANCE;
6796 continue;
6797 } else if (right_end_reached) {
6798 if (left_level == 0) {
6799 ret = changed_cb(left_path, right_path,
6800 &left_key,
6801 BTRFS_COMPARE_TREE_NEW,
6802 ctx);
6803 if (ret < 0)
6804 goto out;
6805 }
6806 advance_left = ADVANCE;
6807 continue;
6808 }
6809
6810 if (left_level == 0 && right_level == 0) {
6811 cmp = btrfs_comp_cpu_keys(&left_key, &right_key);
6812 if (cmp < 0) {
6813 ret = changed_cb(left_path, right_path,
6814 &left_key,
6815 BTRFS_COMPARE_TREE_NEW,
6816 ctx);
6817 if (ret < 0)
6818 goto out;
6819 advance_left = ADVANCE;
6820 } else if (cmp > 0) {
6821 ret = changed_cb(left_path, right_path,
6822 &right_key,
6823 BTRFS_COMPARE_TREE_DELETED,
6824 ctx);
6825 if (ret < 0)
6826 goto out;
6827 advance_right = ADVANCE;
6828 } else {
6829 enum btrfs_compare_tree_result result;
6830
6831 WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
6832 ret = tree_compare_item(left_path, right_path,
6833 tmp_buf);
6834 if (ret)
6835 result = BTRFS_COMPARE_TREE_CHANGED;
6836 else
6837 result = BTRFS_COMPARE_TREE_SAME;
6838 ret = changed_cb(left_path, right_path,
6839 &left_key, result, ctx);
6840 if (ret < 0)
6841 goto out;
6842 advance_left = ADVANCE;
6843 advance_right = ADVANCE;
6844 }
6845 } else if (left_level == right_level) {
6846 cmp = btrfs_comp_cpu_keys(&left_key, &right_key);
6847 if (cmp < 0) {
6848 advance_left = ADVANCE;
6849 } else if (cmp > 0) {
6850 advance_right = ADVANCE;
6851 } else {
6852 left_blockptr = btrfs_node_blockptr(
6853 left_path->nodes[left_level],
6854 left_path->slots[left_level]);
6855 right_blockptr = btrfs_node_blockptr(
6856 right_path->nodes[right_level],
6857 right_path->slots[right_level]);
6858 left_gen = btrfs_node_ptr_generation(
6859 left_path->nodes[left_level],
6860 left_path->slots[left_level]);
6861 right_gen = btrfs_node_ptr_generation(
6862 right_path->nodes[right_level],
6863 right_path->slots[right_level]);
6864 if (left_blockptr == right_blockptr &&
6865 left_gen == right_gen) {
6866 /*
6867 * As we're on a shared block, don't
6868 * allow to go deeper.
6869 */
6870 advance_left = ADVANCE_ONLY_NEXT;
6871 advance_right = ADVANCE_ONLY_NEXT;
6872 } else {
6873 advance_left = ADVANCE;
6874 advance_right = ADVANCE;
6875 }
6876 }
6877 } else if (left_level < right_level) {
6878 advance_right = ADVANCE;
6879 } else {
6880 advance_left = ADVANCE;
6881 }
6882 }
6883
6884out:
6885 btrfs_free_path(left_path);
6886 btrfs_free_path(right_path);
6887 kvfree(tmp_buf);
6888 return ret;
6889}
6890
6517static int send_subvol(struct send_ctx *sctx) 6891static int send_subvol(struct send_ctx *sctx)
6518{ 6892{
6519 int ret; 6893 int ret;