diff options
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r-- | fs/btrfs/extent-tree.c | 202 |
1 files changed, 126 insertions, 76 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6290cf41d647..fe1ddbd2bfd6 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -802,70 +802,57 @@ out: | |||
802 | return 0; | 802 | return 0; |
803 | } | 803 | } |
804 | 804 | ||
805 | u32 btrfs_count_snapshots_in_path(struct btrfs_root *root, | 805 | |
806 | struct btrfs_path *count_path, | 806 | static int get_reference_status(struct btrfs_root *root, u64 bytenr, |
807 | u64 expected_owner, | 807 | u64 parent_gen, u64 ref_objectid, |
808 | u64 first_extent) | 808 | u64 *min_generation, u32 *ref_count) |
809 | { | 809 | { |
810 | struct btrfs_root *extent_root = root->fs_info->extent_root; | 810 | struct btrfs_root *extent_root = root->fs_info->extent_root; |
811 | struct btrfs_path *path; | 811 | struct btrfs_path *path; |
812 | u64 bytenr; | 812 | struct extent_buffer *leaf; |
813 | u64 found_objectid; | 813 | struct btrfs_extent_ref *ref_item; |
814 | u64 found_owner; | 814 | struct btrfs_key key; |
815 | struct btrfs_key found_key; | ||
815 | u64 root_objectid = root->root_key.objectid; | 816 | u64 root_objectid = root->root_key.objectid; |
816 | u32 total_count = 0; | 817 | u64 ref_generation; |
817 | u32 extent_refs; | ||
818 | u32 cur_count; | ||
819 | u32 nritems; | 818 | u32 nritems; |
820 | int ret; | 819 | int ret; |
821 | struct btrfs_key key; | ||
822 | struct btrfs_key found_key; | ||
823 | struct extent_buffer *l; | ||
824 | struct btrfs_extent_item *item; | ||
825 | struct btrfs_extent_ref *ref_item; | ||
826 | int level = -1; | ||
827 | 820 | ||
828 | /* FIXME, needs locking */ | ||
829 | BUG(); | ||
830 | |||
831 | mutex_lock(&root->fs_info->alloc_mutex); | ||
832 | path = btrfs_alloc_path(); | ||
833 | again: | ||
834 | if (level == -1) | ||
835 | bytenr = first_extent; | ||
836 | else | ||
837 | bytenr = count_path->nodes[level]->start; | ||
838 | |||
839 | cur_count = 0; | ||
840 | key.objectid = bytenr; | 821 | key.objectid = bytenr; |
841 | key.offset = 0; | 822 | key.offset = 0; |
823 | key.type = BTRFS_EXTENT_ITEM_KEY; | ||
842 | 824 | ||
843 | btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); | 825 | path = btrfs_alloc_path(); |
826 | mutex_lock(&root->fs_info->alloc_mutex); | ||
844 | ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); | 827 | ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); |
845 | if (ret < 0) | 828 | if (ret < 0) |
846 | goto out; | 829 | goto out; |
847 | BUG_ON(ret == 0); | 830 | BUG_ON(ret == 0); |
848 | 831 | ||
849 | l = path->nodes[0]; | 832 | leaf = path->nodes[0]; |
850 | btrfs_item_key_to_cpu(l, &found_key, path->slots[0]); | 833 | btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); |
851 | 834 | ||
852 | if (found_key.objectid != bytenr || | 835 | if (found_key.objectid != bytenr || |
853 | found_key.type != BTRFS_EXTENT_ITEM_KEY) { | 836 | found_key.type != BTRFS_EXTENT_ITEM_KEY) { |
837 | ret = 1; | ||
854 | goto out; | 838 | goto out; |
855 | } | 839 | } |
856 | 840 | ||
857 | item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item); | 841 | *ref_count = 0; |
858 | extent_refs = btrfs_extent_refs(l, item); | 842 | *min_generation = (u64)-1; |
843 | |||
859 | while (1) { | 844 | while (1) { |
860 | l = path->nodes[0]; | 845 | leaf = path->nodes[0]; |
861 | nritems = btrfs_header_nritems(l); | 846 | nritems = btrfs_header_nritems(leaf); |
862 | if (path->slots[0] >= nritems) { | 847 | if (path->slots[0] >= nritems) { |
863 | ret = btrfs_next_leaf(extent_root, path); | 848 | ret = btrfs_next_leaf(extent_root, path); |
849 | if (ret < 0) | ||
850 | goto out; | ||
864 | if (ret == 0) | 851 | if (ret == 0) |
865 | continue; | 852 | continue; |
866 | break; | 853 | break; |
867 | } | 854 | } |
868 | btrfs_item_key_to_cpu(l, &found_key, path->slots[0]); | 855 | btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); |
869 | if (found_key.objectid != bytenr) | 856 | if (found_key.objectid != bytenr) |
870 | break; | 857 | break; |
871 | 858 | ||
@@ -874,57 +861,120 @@ again: | |||
874 | continue; | 861 | continue; |
875 | } | 862 | } |
876 | 863 | ||
877 | cur_count++; | 864 | ref_item = btrfs_item_ptr(leaf, path->slots[0], |
878 | ref_item = btrfs_item_ptr(l, path->slots[0], | ||
879 | struct btrfs_extent_ref); | 865 | struct btrfs_extent_ref); |
880 | found_objectid = btrfs_ref_root(l, ref_item); | 866 | ref_generation = btrfs_ref_generation(leaf, ref_item); |
881 | 867 | /* | |
882 | if (found_objectid != root_objectid) { | 868 | * For (parent_gen > 0 && parent_gen > ref_gen): |
883 | total_count = 2; | 869 | * |
884 | goto out; | 870 | * we reach here through the oldest root, therefore |
885 | } | 871 | * all other reference from same snapshot should have |
886 | if (level == -1) { | 872 | * a larger generation. |
887 | found_owner = btrfs_ref_objectid(l, ref_item); | 873 | */ |
888 | if (found_owner != expected_owner) { | 874 | if ((root_objectid != btrfs_ref_root(leaf, ref_item)) || |
889 | total_count = 2; | 875 | (parent_gen > 0 && parent_gen > ref_generation) || |
890 | goto out; | 876 | (ref_objectid >= BTRFS_FIRST_FREE_OBJECTID && |
891 | } | 877 | ref_objectid != btrfs_ref_objectid(leaf, ref_item))) { |
892 | /* | 878 | if (ref_count) |
893 | * nasty. we don't count a reference held by | 879 | *ref_count = 2; |
894 | * the running transaction. This allows nodatacow | 880 | break; |
895 | * to avoid cow most of the time | ||
896 | */ | ||
897 | if (found_owner >= BTRFS_FIRST_FREE_OBJECTID && | ||
898 | btrfs_ref_generation(l, ref_item) == | ||
899 | root->fs_info->generation) { | ||
900 | extent_refs--; | ||
901 | } | ||
902 | } | 881 | } |
903 | total_count = 1; | 882 | |
883 | *ref_count = 1; | ||
884 | if (*min_generation > ref_generation) | ||
885 | *min_generation = ref_generation; | ||
886 | |||
904 | path->slots[0]++; | 887 | path->slots[0]++; |
905 | } | 888 | } |
906 | /* | 889 | ret = 0; |
907 | * if there is more than one reference against a data extent, | 890 | out: |
908 | * we have to assume the other ref is another snapshot | 891 | mutex_unlock(&root->fs_info->alloc_mutex); |
909 | */ | 892 | btrfs_free_path(path); |
910 | if (level == -1 && extent_refs > 1) { | 893 | return ret; |
911 | total_count = 2; | 894 | } |
895 | |||
896 | int btrfs_cross_ref_exists(struct btrfs_root *root, | ||
897 | struct btrfs_key *key, u64 bytenr) | ||
898 | { | ||
899 | struct btrfs_trans_handle *trans; | ||
900 | struct btrfs_root *old_root; | ||
901 | struct btrfs_path *path = NULL; | ||
902 | struct extent_buffer *eb; | ||
903 | struct btrfs_file_extent_item *item; | ||
904 | u64 ref_generation; | ||
905 | u64 min_generation; | ||
906 | u64 extent_start; | ||
907 | u32 ref_count; | ||
908 | int level; | ||
909 | int ret; | ||
910 | |||
911 | BUG_ON(key->type != BTRFS_EXTENT_DATA_KEY); | ||
912 | ret = get_reference_status(root, bytenr, 0, key->objectid, | ||
913 | &min_generation, &ref_count); | ||
914 | if (ret) | ||
915 | return ret; | ||
916 | |||
917 | if (ref_count != 1) | ||
918 | return 1; | ||
919 | |||
920 | trans = btrfs_start_transaction(root, 0); | ||
921 | old_root = root->dirty_root->root; | ||
922 | ref_generation = old_root->root_key.offset; | ||
923 | |||
924 | /* all references are created in running transaction */ | ||
925 | if (min_generation > ref_generation) { | ||
926 | ret = 0; | ||
912 | goto out; | 927 | goto out; |
913 | } | 928 | } |
914 | if (cur_count == 0) { | 929 | |
915 | total_count = 0; | 930 | path = btrfs_alloc_path(); |
931 | if (!path) { | ||
932 | ret = -ENOMEM; | ||
916 | goto out; | 933 | goto out; |
917 | } | 934 | } |
918 | if (level >= 0 && root->node == count_path->nodes[level]) | 935 | |
936 | path->skip_locking = 1; | ||
937 | /* if no item found, the extent is referenced by other snapshot */ | ||
938 | ret = btrfs_search_slot(NULL, old_root, key, path, 0, 0); | ||
939 | if (ret) | ||
919 | goto out; | 940 | goto out; |
920 | level++; | ||
921 | btrfs_release_path(root, path); | ||
922 | goto again; | ||
923 | 941 | ||
942 | eb = path->nodes[0]; | ||
943 | item = btrfs_item_ptr(eb, path->slots[0], | ||
944 | struct btrfs_file_extent_item); | ||
945 | if (btrfs_file_extent_type(eb, item) != BTRFS_FILE_EXTENT_REG || | ||
946 | btrfs_file_extent_disk_bytenr(eb, item) != bytenr) { | ||
947 | ret = 1; | ||
948 | goto out; | ||
949 | } | ||
950 | |||
951 | for (level = BTRFS_MAX_LEVEL - 1; level >= -1; level--) { | ||
952 | if (level >= 0) { | ||
953 | eb = path->nodes[level]; | ||
954 | if (!eb) | ||
955 | continue; | ||
956 | extent_start = eb->start; | ||
957 | } else | ||
958 | extent_start = bytenr; | ||
959 | |||
960 | ret = get_reference_status(root, extent_start, ref_generation, | ||
961 | 0, &min_generation, &ref_count); | ||
962 | if (ret) | ||
963 | goto out; | ||
964 | |||
965 | if (ref_count != 1) { | ||
966 | ret = 1; | ||
967 | goto out; | ||
968 | } | ||
969 | if (level >= 0) | ||
970 | ref_generation = btrfs_header_generation(eb); | ||
971 | } | ||
972 | ret = 0; | ||
924 | out: | 973 | out: |
925 | btrfs_free_path(path); | 974 | if (path) |
926 | mutex_unlock(&root->fs_info->alloc_mutex); | 975 | btrfs_free_path(path); |
927 | return total_count; | 976 | btrfs_end_transaction(trans, root); |
977 | return ret; | ||
928 | } | 978 | } |
929 | 979 | ||
930 | int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | 980 | int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, |