aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2011-09-26 13:56:12 -0400
committerJosef Bacik <josef@redhat.com>2011-10-19 15:12:47 -0400
commit462d6fac8960a3ba797927adfcbd29d503eb16fd (patch)
tree82e72468a746b55d09246db8272823c2907633e1 /fs
parentef3be45722317f8c2fb0e861065df0c3830ff9ac (diff)
Btrfs: introduce convert_extent_bit
If I have a range where I know a certain bit is and I want to set it to another bit the only option I have is to call set and then clear bit, which will result in 2 tree searches. This is inefficient, so introduce convert_extent_bit which will go through and set the bit I want and clear the old bit I don't want. Thanks, Signed-off-by: Josef Bacik <josef@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/extent_io.c188
-rw-r--r--fs/btrfs/extent_io.h2
2 files changed, 190 insertions, 0 deletions
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 7d5e55632809..0ada0b700b44 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -894,6 +894,194 @@ search_again:
894 goto again; 894 goto again;
895} 895}
896 896
897/**
898 * convert_extent - convert all bits in a given range from one bit to another
899 * @tree: the io tree to search
900 * @start: the start offset in bytes
901 * @end: the end offset in bytes (inclusive)
902 * @bits: the bits to set in this range
903 * @clear_bits: the bits to clear in this range
904 * @mask: the allocation mask
905 *
906 * This will go through and set bits for the given range. If any states exist
907 * already in this range they are set with the given bit and cleared of the
908 * clear_bits. This is only meant to be used by things that are mergeable, ie
909 * converting from say DELALLOC to DIRTY. This is not meant to be used with
910 * boundary bits like LOCK.
911 */
912int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
913 int bits, int clear_bits, gfp_t mask)
914{
915 struct extent_state *state;
916 struct extent_state *prealloc = NULL;
917 struct rb_node *node;
918 int err = 0;
919 u64 last_start;
920 u64 last_end;
921
922again:
923 if (!prealloc && (mask & __GFP_WAIT)) {
924 prealloc = alloc_extent_state(mask);
925 if (!prealloc)
926 return -ENOMEM;
927 }
928
929 spin_lock(&tree->lock);
930 /*
931 * this search will find all the extents that end after
932 * our range starts.
933 */
934 node = tree_search(tree, start);
935 if (!node) {
936 prealloc = alloc_extent_state_atomic(prealloc);
937 if (!prealloc)
938 return -ENOMEM;
939 err = insert_state(tree, prealloc, start, end, &bits);
940 prealloc = NULL;
941 BUG_ON(err == -EEXIST);
942 goto out;
943 }
944 state = rb_entry(node, struct extent_state, rb_node);
945hit_next:
946 last_start = state->start;
947 last_end = state->end;
948
949 /*
950 * | ---- desired range ---- |
951 * | state |
952 *
953 * Just lock what we found and keep going
954 */
955 if (state->start == start && state->end <= end) {
956 struct rb_node *next_node;
957
958 set_state_bits(tree, state, &bits);
959 clear_state_bit(tree, state, &clear_bits, 0);
960
961 merge_state(tree, state);
962 if (last_end == (u64)-1)
963 goto out;
964
965 start = last_end + 1;
966 next_node = rb_next(&state->rb_node);
967 if (next_node && start < end && prealloc && !need_resched()) {
968 state = rb_entry(next_node, struct extent_state,
969 rb_node);
970 if (state->start == start)
971 goto hit_next;
972 }
973 goto search_again;
974 }
975
976 /*
977 * | ---- desired range ---- |
978 * | state |
979 * or
980 * | ------------- state -------------- |
981 *
982 * We need to split the extent we found, and may flip bits on
983 * second half.
984 *
985 * If the extent we found extends past our
986 * range, we just split and search again. It'll get split
987 * again the next time though.
988 *
989 * If the extent we found is inside our range, we set the
990 * desired bit on it.
991 */
992 if (state->start < start) {
993 prealloc = alloc_extent_state_atomic(prealloc);
994 if (!prealloc)
995 return -ENOMEM;
996 err = split_state(tree, state, prealloc, start);
997 BUG_ON(err == -EEXIST);
998 prealloc = NULL;
999 if (err)
1000 goto out;
1001 if (state->end <= end) {
1002 set_state_bits(tree, state, &bits);
1003 clear_state_bit(tree, state, &clear_bits, 0);
1004 merge_state(tree, state);
1005 if (last_end == (u64)-1)
1006 goto out;
1007 start = last_end + 1;
1008 }
1009 goto search_again;
1010 }
1011 /*
1012 * | ---- desired range ---- |
1013 * | state | or | state |
1014 *
1015 * There's a hole, we need to insert something in it and
1016 * ignore the extent we found.
1017 */
1018 if (state->start > start) {
1019 u64 this_end;
1020 if (end < last_start)
1021 this_end = end;
1022 else
1023 this_end = last_start - 1;
1024
1025 prealloc = alloc_extent_state_atomic(prealloc);
1026 if (!prealloc)
1027 return -ENOMEM;
1028
1029 /*
1030 * Avoid to free 'prealloc' if it can be merged with
1031 * the later extent.
1032 */
1033 err = insert_state(tree, prealloc, start, this_end,
1034 &bits);
1035 BUG_ON(err == -EEXIST);
1036 if (err) {
1037 free_extent_state(prealloc);
1038 prealloc = NULL;
1039 goto out;
1040 }
1041 prealloc = NULL;
1042 start = this_end + 1;
1043 goto search_again;
1044 }
1045 /*
1046 * | ---- desired range ---- |
1047 * | state |
1048 * We need to split the extent, and set the bit
1049 * on the first half
1050 */
1051 if (state->start <= end && state->end > end) {
1052 prealloc = alloc_extent_state_atomic(prealloc);
1053 if (!prealloc)
1054 return -ENOMEM;
1055
1056 err = split_state(tree, state, prealloc, end + 1);
1057 BUG_ON(err == -EEXIST);
1058
1059 set_state_bits(tree, prealloc, &bits);
1060 clear_state_bit(tree, prealloc, &clear_bits, 0);
1061
1062 merge_state(tree, prealloc);
1063 prealloc = NULL;
1064 goto out;
1065 }
1066
1067 goto search_again;
1068
1069out:
1070 spin_unlock(&tree->lock);
1071 if (prealloc)
1072 free_extent_state(prealloc);
1073
1074 return err;
1075
1076search_again:
1077 if (start > end)
1078 goto out;
1079 spin_unlock(&tree->lock);
1080 if (mask & __GFP_WAIT)
1081 cond_resched();
1082 goto again;
1083}
1084
897/* wrappers around set/clear extent bit */ 1085/* wrappers around set/clear extent bit */
898int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, 1086int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
899 gfp_t mask) 1087 gfp_t mask)
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 7b2f0c3e7929..cea445dcd806 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -214,6 +214,8 @@ int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
214 gfp_t mask); 214 gfp_t mask);
215int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, 215int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
216 gfp_t mask); 216 gfp_t mask);
217int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
218 int bits, int clear_bits, gfp_t mask);
217int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end, 219int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end,
218 struct extent_state **cached_state, gfp_t mask); 220 struct extent_state **cached_state, gfp_t mask);
219int find_first_extent_bit(struct extent_io_tree *tree, u64 start, 221int find_first_extent_bit(struct extent_io_tree *tree, u64 start,