diff options
author | Yan Zheng <zheng.yan@oracle.com> | 2008-07-30 09:26:11 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:05 -0400 |
commit | f321e4910398cf7922265d269fb17fd26f312571 (patch) | |
tree | 8430f004991351e48a4b3f9441fe0cbbcf70eddb /fs/btrfs/extent-tree.c | |
parent | 3bf10418675cb424724b5cb9d7725b234defe1fd (diff) |
Btrfs: Update and fix mount -o nodatacow
To check whether a given file extent is referenced by multiple snapshots, the
checker walks down the fs tree through dead root and checks all tree blocks in
the path.
We can easily detect whether a given tree block is directly referenced by other
snapshot. We can also detect any indirect reference from other snapshot by
checking reference's generation. The checker can always detect multiple
references, but can't reliably detect cases of single reference. So btrfs may
do file data cow even there is only one reference.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
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, |