diff options
author | David Sterba <dsterba@suse.com> | 2019-08-21 13:12:59 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2019-09-09 08:59:15 -0400 |
commit | 18d0f5c6e16ce762f92ab7879c30ff2e37cd9cef (patch) | |
tree | 6631caf404b4d0fc7215d8d3d0bc9fdcedc984c8 | |
parent | 4b231ae47417d47a6bafab92b452ad629acdacb0 (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.c | 362 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 14 | ||||
-rw-r--r-- | fs/btrfs/send.c | 374 |
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 | ||
5249 | static 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 | |||
5264 | static 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 | */ | ||
5294 | static 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 | |||
5317 | static 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 | */ | ||
5358 | int 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 | |||
5604 | out: | ||
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, | |||
2590 | struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent, | 2590 | struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent, |
2591 | int slot); | 2591 | int slot); |
2592 | 2592 | ||
2593 | enum 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 | }; | ||
2599 | typedef 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); | ||
2604 | int btrfs_compare_trees(struct btrfs_root *left_root, | ||
2605 | struct btrfs_root *right_root, | ||
2606 | btrfs_changed_cb_t cb, void *ctx); | ||
2607 | int btrfs_cow_block(struct btrfs_trans_handle *trans, | 2593 | int 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 | |||
266 | enum 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 | }; | ||
272 | typedef 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 |
264 | static void inconsistent_snapshot_error(struct send_ctx *sctx, | 279 | static 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 | ||
6532 | static 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 | |||
6547 | static 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 | */ | ||
6577 | static 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 | |||
6600 | static 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 | */ | ||
6638 | static 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 | |||
6884 | out: | ||
6885 | btrfs_free_path(left_path); | ||
6886 | btrfs_free_path(right_path); | ||
6887 | kvfree(tmp_buf); | ||
6888 | return ret; | ||
6889 | } | ||
6890 | |||
6517 | static int send_subvol(struct send_ctx *sctx) | 6891 | static int send_subvol(struct send_ctx *sctx) |
6518 | { | 6892 | { |
6519 | int ret; | 6893 | int ret; |