diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/extent-tree.c | 82 |
1 files changed, 53 insertions, 29 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 53026806ae9e..4aedbff36b8f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -4911,6 +4911,7 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans, | |||
4911 | u64 bytenr; | 4911 | u64 bytenr; |
4912 | u64 generation; | 4912 | u64 generation; |
4913 | u64 refs; | 4913 | u64 refs; |
4914 | u64 flags; | ||
4914 | u64 last = 0; | 4915 | u64 last = 0; |
4915 | u32 nritems; | 4916 | u32 nritems; |
4916 | u32 blocksize; | 4917 | u32 blocksize; |
@@ -4948,15 +4949,19 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans, | |||
4948 | generation <= root->root_key.offset) | 4949 | generation <= root->root_key.offset) |
4949 | continue; | 4950 | continue; |
4950 | 4951 | ||
4952 | /* We don't lock the tree block, it's OK to be racy here */ | ||
4953 | ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize, | ||
4954 | &refs, &flags); | ||
4955 | BUG_ON(ret); | ||
4956 | BUG_ON(refs == 0); | ||
4957 | |||
4951 | if (wc->stage == DROP_REFERENCE) { | 4958 | if (wc->stage == DROP_REFERENCE) { |
4952 | ret = btrfs_lookup_extent_info(trans, root, | ||
4953 | bytenr, blocksize, | ||
4954 | &refs, NULL); | ||
4955 | BUG_ON(ret); | ||
4956 | BUG_ON(refs == 0); | ||
4957 | if (refs == 1) | 4959 | if (refs == 1) |
4958 | goto reada; | 4960 | goto reada; |
4959 | 4961 | ||
4962 | if (wc->level == 1 && | ||
4963 | (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) | ||
4964 | continue; | ||
4960 | if (!wc->update_ref || | 4965 | if (!wc->update_ref || |
4961 | generation <= root->root_key.offset) | 4966 | generation <= root->root_key.offset) |
4962 | continue; | 4967 | continue; |
@@ -4965,6 +4970,10 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans, | |||
4965 | &wc->update_progress); | 4970 | &wc->update_progress); |
4966 | if (ret < 0) | 4971 | if (ret < 0) |
4967 | continue; | 4972 | continue; |
4973 | } else { | ||
4974 | if (wc->level == 1 && | ||
4975 | (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) | ||
4976 | continue; | ||
4968 | } | 4977 | } |
4969 | reada: | 4978 | reada: |
4970 | ret = readahead_tree_block(root, bytenr, blocksize, | 4979 | ret = readahead_tree_block(root, bytenr, blocksize, |
@@ -4988,7 +4997,7 @@ reada: | |||
4988 | static noinline int walk_down_proc(struct btrfs_trans_handle *trans, | 4997 | static noinline int walk_down_proc(struct btrfs_trans_handle *trans, |
4989 | struct btrfs_root *root, | 4998 | struct btrfs_root *root, |
4990 | struct btrfs_path *path, | 4999 | struct btrfs_path *path, |
4991 | struct walk_control *wc) | 5000 | struct walk_control *wc, int lookup_info) |
4992 | { | 5001 | { |
4993 | int level = wc->level; | 5002 | int level = wc->level; |
4994 | struct extent_buffer *eb = path->nodes[level]; | 5003 | struct extent_buffer *eb = path->nodes[level]; |
@@ -5003,8 +5012,9 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, | |||
5003 | * when reference count of tree block is 1, it won't increase | 5012 | * when reference count of tree block is 1, it won't increase |
5004 | * again. once full backref flag is set, we never clear it. | 5013 | * again. once full backref flag is set, we never clear it. |
5005 | */ | 5014 | */ |
5006 | if ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) || | 5015 | if (lookup_info && |
5007 | (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag))) { | 5016 | ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) || |
5017 | (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag)))) { | ||
5008 | BUG_ON(!path->locks[level]); | 5018 | BUG_ON(!path->locks[level]); |
5009 | ret = btrfs_lookup_extent_info(trans, root, | 5019 | ret = btrfs_lookup_extent_info(trans, root, |
5010 | eb->start, eb->len, | 5020 | eb->start, eb->len, |
@@ -5065,7 +5075,7 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, | |||
5065 | static noinline int do_walk_down(struct btrfs_trans_handle *trans, | 5075 | static noinline int do_walk_down(struct btrfs_trans_handle *trans, |
5066 | struct btrfs_root *root, | 5076 | struct btrfs_root *root, |
5067 | struct btrfs_path *path, | 5077 | struct btrfs_path *path, |
5068 | struct walk_control *wc) | 5078 | struct walk_control *wc, int *lookup_info) |
5069 | { | 5079 | { |
5070 | u64 bytenr; | 5080 | u64 bytenr; |
5071 | u64 generation; | 5081 | u64 generation; |
@@ -5085,8 +5095,10 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, | |||
5085 | * for the subtree | 5095 | * for the subtree |
5086 | */ | 5096 | */ |
5087 | if (wc->stage == UPDATE_BACKREF && | 5097 | if (wc->stage == UPDATE_BACKREF && |
5088 | generation <= root->root_key.offset) | 5098 | generation <= root->root_key.offset) { |
5099 | *lookup_info = 1; | ||
5089 | return 1; | 5100 | return 1; |
5101 | } | ||
5090 | 5102 | ||
5091 | bytenr = btrfs_node_blockptr(path->nodes[level], path->slots[level]); | 5103 | bytenr = btrfs_node_blockptr(path->nodes[level], path->slots[level]); |
5092 | blocksize = btrfs_level_size(root, level - 1); | 5104 | blocksize = btrfs_level_size(root, level - 1); |
@@ -5099,14 +5111,19 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, | |||
5099 | btrfs_tree_lock(next); | 5111 | btrfs_tree_lock(next); |
5100 | btrfs_set_lock_blocking(next); | 5112 | btrfs_set_lock_blocking(next); |
5101 | 5113 | ||
5102 | if (wc->stage == DROP_REFERENCE) { | 5114 | ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize, |
5103 | ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize, | 5115 | &wc->refs[level - 1], |
5104 | &wc->refs[level - 1], | 5116 | &wc->flags[level - 1]); |
5105 | &wc->flags[level - 1]); | 5117 | BUG_ON(ret); |
5106 | BUG_ON(ret); | 5118 | BUG_ON(wc->refs[level - 1] == 0); |
5107 | BUG_ON(wc->refs[level - 1] == 0); | 5119 | *lookup_info = 0; |
5108 | 5120 | ||
5121 | if (wc->stage == DROP_REFERENCE) { | ||
5109 | if (wc->refs[level - 1] > 1) { | 5122 | if (wc->refs[level - 1] > 1) { |
5123 | if (level == 1 && | ||
5124 | (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF)) | ||
5125 | goto skip; | ||
5126 | |||
5110 | if (!wc->update_ref || | 5127 | if (!wc->update_ref || |
5111 | generation <= root->root_key.offset) | 5128 | generation <= root->root_key.offset) |
5112 | goto skip; | 5129 | goto skip; |
@@ -5120,12 +5137,17 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, | |||
5120 | wc->stage = UPDATE_BACKREF; | 5137 | wc->stage = UPDATE_BACKREF; |
5121 | wc->shared_level = level - 1; | 5138 | wc->shared_level = level - 1; |
5122 | } | 5139 | } |
5140 | } else { | ||
5141 | if (level == 1 && | ||
5142 | (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF)) | ||
5143 | goto skip; | ||
5123 | } | 5144 | } |
5124 | 5145 | ||
5125 | if (!btrfs_buffer_uptodate(next, generation)) { | 5146 | if (!btrfs_buffer_uptodate(next, generation)) { |
5126 | btrfs_tree_unlock(next); | 5147 | btrfs_tree_unlock(next); |
5127 | free_extent_buffer(next); | 5148 | free_extent_buffer(next); |
5128 | next = NULL; | 5149 | next = NULL; |
5150 | *lookup_info = 1; | ||
5129 | } | 5151 | } |
5130 | 5152 | ||
5131 | if (!next) { | 5153 | if (!next) { |
@@ -5148,21 +5170,22 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, | |||
5148 | skip: | 5170 | skip: |
5149 | wc->refs[level - 1] = 0; | 5171 | wc->refs[level - 1] = 0; |
5150 | wc->flags[level - 1] = 0; | 5172 | wc->flags[level - 1] = 0; |
5173 | if (wc->stage == DROP_REFERENCE) { | ||
5174 | if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) { | ||
5175 | parent = path->nodes[level]->start; | ||
5176 | } else { | ||
5177 | BUG_ON(root->root_key.objectid != | ||
5178 | btrfs_header_owner(path->nodes[level])); | ||
5179 | parent = 0; | ||
5180 | } | ||
5151 | 5181 | ||
5152 | if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) { | 5182 | ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent, |
5153 | parent = path->nodes[level]->start; | 5183 | root->root_key.objectid, level - 1, 0); |
5154 | } else { | 5184 | BUG_ON(ret); |
5155 | BUG_ON(root->root_key.objectid != | ||
5156 | btrfs_header_owner(path->nodes[level])); | ||
5157 | parent = 0; | ||
5158 | } | 5185 | } |
5159 | |||
5160 | ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent, | ||
5161 | root->root_key.objectid, level - 1, 0); | ||
5162 | BUG_ON(ret); | ||
5163 | |||
5164 | btrfs_tree_unlock(next); | 5186 | btrfs_tree_unlock(next); |
5165 | free_extent_buffer(next); | 5187 | free_extent_buffer(next); |
5188 | *lookup_info = 1; | ||
5166 | return 1; | 5189 | return 1; |
5167 | } | 5190 | } |
5168 | 5191 | ||
@@ -5276,6 +5299,7 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans, | |||
5276 | struct walk_control *wc) | 5299 | struct walk_control *wc) |
5277 | { | 5300 | { |
5278 | int level = wc->level; | 5301 | int level = wc->level; |
5302 | int lookup_info = 1; | ||
5279 | int ret; | 5303 | int ret; |
5280 | 5304 | ||
5281 | while (level >= 0) { | 5305 | while (level >= 0) { |
@@ -5283,14 +5307,14 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans, | |||
5283 | btrfs_header_nritems(path->nodes[level])) | 5307 | btrfs_header_nritems(path->nodes[level])) |
5284 | break; | 5308 | break; |
5285 | 5309 | ||
5286 | ret = walk_down_proc(trans, root, path, wc); | 5310 | ret = walk_down_proc(trans, root, path, wc, lookup_info); |
5287 | if (ret > 0) | 5311 | if (ret > 0) |
5288 | break; | 5312 | break; |
5289 | 5313 | ||
5290 | if (level == 0) | 5314 | if (level == 0) |
5291 | break; | 5315 | break; |
5292 | 5316 | ||
5293 | ret = do_walk_down(trans, root, path, wc); | 5317 | ret = do_walk_down(trans, root, path, wc, &lookup_info); |
5294 | if (ret > 0) { | 5318 | if (ret > 0) { |
5295 | path->slots[level]++; | 5319 | path->slots[level]++; |
5296 | continue; | 5320 | continue; |