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 /fs/btrfs/send.c | |
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>
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r-- | fs/btrfs/send.c | 374 |
1 files changed, 374 insertions, 0 deletions
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; |