diff options
| -rw-r--r-- | fs/btrfs/ctree.c | 425 | ||||
| -rw-r--r-- | fs/btrfs/ctree.h | 15 |
2 files changed, 440 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index c82a9e4a953e..4c10fd19d481 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
| @@ -5005,6 +5005,431 @@ out: | |||
| 5005 | return ret; | 5005 | return ret; |
| 5006 | } | 5006 | } |
| 5007 | 5007 | ||
| 5008 | static void tree_move_down(struct btrfs_root *root, | ||
| 5009 | struct btrfs_path *path, | ||
| 5010 | int *level, int root_level) | ||
| 5011 | { | ||
| 5012 | path->nodes[*level - 1] = read_node_slot(root, path->nodes[*level], | ||
| 5013 | path->slots[*level]); | ||
| 5014 | path->slots[*level - 1] = 0; | ||
| 5015 | (*level)--; | ||
| 5016 | } | ||
| 5017 | |||
| 5018 | static int tree_move_next_or_upnext(struct btrfs_root *root, | ||
| 5019 | struct btrfs_path *path, | ||
| 5020 | int *level, int root_level) | ||
| 5021 | { | ||
| 5022 | int ret = 0; | ||
| 5023 | int nritems; | ||
| 5024 | nritems = btrfs_header_nritems(path->nodes[*level]); | ||
| 5025 | |||
| 5026 | path->slots[*level]++; | ||
| 5027 | |||
| 5028 | while (path->slots[*level] == nritems) { | ||
| 5029 | if (*level == root_level) | ||
| 5030 | return -1; | ||
| 5031 | |||
| 5032 | /* move upnext */ | ||
| 5033 | path->slots[*level] = 0; | ||
| 5034 | free_extent_buffer(path->nodes[*level]); | ||
| 5035 | path->nodes[*level] = NULL; | ||
| 5036 | (*level)++; | ||
| 5037 | path->slots[*level]++; | ||
| 5038 | |||
| 5039 | nritems = btrfs_header_nritems(path->nodes[*level]); | ||
| 5040 | ret = 1; | ||
| 5041 | } | ||
| 5042 | return ret; | ||
| 5043 | } | ||
| 5044 | |||
| 5045 | /* | ||
| 5046 | * Returns 1 if it had to move up and next. 0 is returned if it moved only next | ||
| 5047 | * or down. | ||
| 5048 | */ | ||
| 5049 | static int tree_advance(struct btrfs_root *root, | ||
| 5050 | struct btrfs_path *path, | ||
| 5051 | int *level, int root_level, | ||
| 5052 | int allow_down, | ||
| 5053 | struct btrfs_key *key) | ||
| 5054 | { | ||
| 5055 | int ret; | ||
| 5056 | |||
| 5057 | if (*level == 0 || !allow_down) { | ||
| 5058 | ret = tree_move_next_or_upnext(root, path, level, root_level); | ||
| 5059 | } else { | ||
| 5060 | tree_move_down(root, path, level, root_level); | ||
| 5061 | ret = 0; | ||
| 5062 | } | ||
| 5063 | if (ret >= 0) { | ||
| 5064 | if (*level == 0) | ||
| 5065 | btrfs_item_key_to_cpu(path->nodes[*level], key, | ||
| 5066 | path->slots[*level]); | ||
| 5067 | else | ||
| 5068 | btrfs_node_key_to_cpu(path->nodes[*level], key, | ||
| 5069 | path->slots[*level]); | ||
| 5070 | } | ||
| 5071 | return ret; | ||
| 5072 | } | ||
| 5073 | |||
| 5074 | static int tree_compare_item(struct btrfs_root *left_root, | ||
| 5075 | struct btrfs_path *left_path, | ||
| 5076 | struct btrfs_path *right_path, | ||
| 5077 | char *tmp_buf) | ||
| 5078 | { | ||
| 5079 | int cmp; | ||
| 5080 | int len1, len2; | ||
| 5081 | unsigned long off1, off2; | ||
| 5082 | |||
| 5083 | len1 = btrfs_item_size_nr(left_path->nodes[0], left_path->slots[0]); | ||
| 5084 | len2 = btrfs_item_size_nr(right_path->nodes[0], right_path->slots[0]); | ||
| 5085 | if (len1 != len2) | ||
| 5086 | return 1; | ||
| 5087 | |||
| 5088 | off1 = btrfs_item_ptr_offset(left_path->nodes[0], left_path->slots[0]); | ||
| 5089 | off2 = btrfs_item_ptr_offset(right_path->nodes[0], | ||
| 5090 | right_path->slots[0]); | ||
| 5091 | |||
| 5092 | read_extent_buffer(left_path->nodes[0], tmp_buf, off1, len1); | ||
| 5093 | |||
| 5094 | cmp = memcmp_extent_buffer(right_path->nodes[0], tmp_buf, off2, len1); | ||
| 5095 | if (cmp) | ||
| 5096 | return 1; | ||
| 5097 | return 0; | ||
| 5098 | } | ||
| 5099 | |||
| 5100 | #define ADVANCE 1 | ||
| 5101 | #define ADVANCE_ONLY_NEXT -1 | ||
| 5102 | |||
| 5103 | /* | ||
| 5104 | * This function compares two trees and calls the provided callback for | ||
| 5105 | * every changed/new/deleted item it finds. | ||
| 5106 | * If shared tree blocks are encountered, whole subtrees are skipped, making | ||
| 5107 | * the compare pretty fast on snapshotted subvolumes. | ||
| 5108 | * | ||
| 5109 | * This currently works on commit roots only. As commit roots are read only, | ||
| 5110 | * we don't do any locking. The commit roots are protected with transactions. | ||
| 5111 | * Transactions are ended and rejoined when a commit is tried in between. | ||
| 5112 | * | ||
| 5113 | * This function checks for modifications done to the trees while comparing. | ||
| 5114 | * If it detects a change, it aborts immediately. | ||
| 5115 | */ | ||
| 5116 | int btrfs_compare_trees(struct btrfs_root *left_root, | ||
| 5117 | struct btrfs_root *right_root, | ||
| 5118 | btrfs_changed_cb_t changed_cb, void *ctx) | ||
| 5119 | { | ||
| 5120 | int ret; | ||
| 5121 | int cmp; | ||
| 5122 | struct btrfs_trans_handle *trans = NULL; | ||
| 5123 | struct btrfs_path *left_path = NULL; | ||
| 5124 | struct btrfs_path *right_path = NULL; | ||
| 5125 | struct btrfs_key left_key; | ||
| 5126 | struct btrfs_key right_key; | ||
| 5127 | char *tmp_buf = NULL; | ||
| 5128 | int left_root_level; | ||
| 5129 | int right_root_level; | ||
| 5130 | int left_level; | ||
| 5131 | int right_level; | ||
| 5132 | int left_end_reached; | ||
| 5133 | int right_end_reached; | ||
| 5134 | int advance_left; | ||
| 5135 | int advance_right; | ||
| 5136 | u64 left_blockptr; | ||
| 5137 | u64 right_blockptr; | ||
| 5138 | u64 left_start_ctransid; | ||
| 5139 | u64 right_start_ctransid; | ||
| 5140 | u64 ctransid; | ||
| 5141 | |||
| 5142 | left_path = btrfs_alloc_path(); | ||
| 5143 | if (!left_path) { | ||
| 5144 | ret = -ENOMEM; | ||
| 5145 | goto out; | ||
| 5146 | } | ||
| 5147 | right_path = btrfs_alloc_path(); | ||
| 5148 | if (!right_path) { | ||
| 5149 | ret = -ENOMEM; | ||
| 5150 | goto out; | ||
| 5151 | } | ||
| 5152 | |||
| 5153 | tmp_buf = kmalloc(left_root->leafsize, GFP_NOFS); | ||
| 5154 | if (!tmp_buf) { | ||
| 5155 | ret = -ENOMEM; | ||
| 5156 | goto out; | ||
| 5157 | } | ||
| 5158 | |||
| 5159 | left_path->search_commit_root = 1; | ||
| 5160 | left_path->skip_locking = 1; | ||
| 5161 | right_path->search_commit_root = 1; | ||
| 5162 | right_path->skip_locking = 1; | ||
| 5163 | |||
| 5164 | spin_lock(&left_root->root_times_lock); | ||
| 5165 | left_start_ctransid = btrfs_root_ctransid(&left_root->root_item); | ||
| 5166 | spin_unlock(&left_root->root_times_lock); | ||
| 5167 | |||
| 5168 | spin_lock(&right_root->root_times_lock); | ||
| 5169 | right_start_ctransid = btrfs_root_ctransid(&right_root->root_item); | ||
| 5170 | spin_unlock(&right_root->root_times_lock); | ||
| 5171 | |||
| 5172 | trans = btrfs_join_transaction(left_root); | ||
| 5173 | if (IS_ERR(trans)) { | ||
| 5174 | ret = PTR_ERR(trans); | ||
| 5175 | trans = NULL; | ||
| 5176 | goto out; | ||
| 5177 | } | ||
| 5178 | |||
| 5179 | /* | ||
| 5180 | * Strategy: Go to the first items of both trees. Then do | ||
| 5181 | * | ||
| 5182 | * If both trees are at level 0 | ||
| 5183 | * Compare keys of current items | ||
| 5184 | * If left < right treat left item as new, advance left tree | ||
| 5185 | * and repeat | ||
| 5186 | * If left > right treat right item as deleted, advance right tree | ||
| 5187 | * and repeat | ||
| 5188 | * If left == right do deep compare of items, treat as changed if | ||
| 5189 | * needed, advance both trees and repeat | ||
| 5190 | * If both trees are at the same level but not at level 0 | ||
| 5191 | * Compare keys of current nodes/leafs | ||
| 5192 | * If left < right advance left tree and repeat | ||
| 5193 | * If left > right advance right tree and repeat | ||
| 5194 | * If left == right compare blockptrs of the next nodes/leafs | ||
| 5195 | * If they match advance both trees but stay at the same level | ||
| 5196 | * and repeat | ||
| 5197 | * If they don't match advance both trees while allowing to go | ||
| 5198 | * deeper and repeat | ||
| 5199 | * If tree levels are different | ||
| 5200 | * Advance the tree that needs it and repeat | ||
| 5201 | * | ||
| 5202 | * Advancing a tree means: | ||
| 5203 | * If we are at level 0, try to go to the next slot. If that's not | ||
| 5204 | * possible, go one level up and repeat. Stop when we found a level | ||
| 5205 | * where we could go to the next slot. We may at this point be on a | ||
| 5206 | * node or a leaf. | ||
| 5207 | * | ||
| 5208 | * If we are not at level 0 and not on shared tree blocks, go one | ||
| 5209 | * level deeper. | ||
| 5210 | * | ||
| 5211 | * If we are not at level 0 and on shared tree blocks, go one slot to | ||
| 5212 | * the right if possible or go up and right. | ||
| 5213 | */ | ||
| 5214 | |||
| 5215 | left_level = btrfs_header_level(left_root->commit_root); | ||
| 5216 | left_root_level = left_level; | ||
| 5217 | left_path->nodes[left_level] = left_root->commit_root; | ||
| 5218 | extent_buffer_get(left_path->nodes[left_level]); | ||
| 5219 | |||
| 5220 | right_level = btrfs_header_level(right_root->commit_root); | ||
| 5221 | right_root_level = right_level; | ||
| 5222 | right_path->nodes[right_level] = right_root->commit_root; | ||
| 5223 | extent_buffer_get(right_path->nodes[right_level]); | ||
| 5224 | |||
| 5225 | if (left_level == 0) | ||
| 5226 | btrfs_item_key_to_cpu(left_path->nodes[left_level], | ||
| 5227 | &left_key, left_path->slots[left_level]); | ||
| 5228 | else | ||
| 5229 | btrfs_node_key_to_cpu(left_path->nodes[left_level], | ||
| 5230 | &left_key, left_path->slots[left_level]); | ||
| 5231 | if (right_level == 0) | ||
| 5232 | btrfs_item_key_to_cpu(right_path->nodes[right_level], | ||
| 5233 | &right_key, right_path->slots[right_level]); | ||
| 5234 | else | ||
| 5235 | btrfs_node_key_to_cpu(right_path->nodes[right_level], | ||
| 5236 | &right_key, right_path->slots[right_level]); | ||
| 5237 | |||
| 5238 | left_end_reached = right_end_reached = 0; | ||
| 5239 | advance_left = advance_right = 0; | ||
| 5240 | |||
| 5241 | while (1) { | ||
| 5242 | /* | ||
| 5243 | * We need to make sure the transaction does not get committed | ||
| 5244 | * while we do anything on commit roots. This means, we need to | ||
| 5245 | * join and leave transactions for every item that we process. | ||
| 5246 | */ | ||
| 5247 | if (trans && btrfs_should_end_transaction(trans, left_root)) { | ||
| 5248 | btrfs_release_path(left_path); | ||
| 5249 | btrfs_release_path(right_path); | ||
| 5250 | |||
| 5251 | ret = btrfs_end_transaction(trans, left_root); | ||
| 5252 | trans = NULL; | ||
| 5253 | if (ret < 0) | ||
| 5254 | goto out; | ||
| 5255 | } | ||
| 5256 | /* now rejoin the transaction */ | ||
| 5257 | if (!trans) { | ||
| 5258 | trans = btrfs_join_transaction(left_root); | ||
| 5259 | if (IS_ERR(trans)) { | ||
| 5260 | ret = PTR_ERR(trans); | ||
| 5261 | trans = NULL; | ||
| 5262 | goto out; | ||
| 5263 | } | ||
| 5264 | |||
| 5265 | spin_lock(&left_root->root_times_lock); | ||
| 5266 | ctransid = btrfs_root_ctransid(&left_root->root_item); | ||
| 5267 | spin_unlock(&left_root->root_times_lock); | ||
| 5268 | if (ctransid != left_start_ctransid) | ||
| 5269 | left_start_ctransid = 0; | ||
| 5270 | |||
| 5271 | spin_lock(&right_root->root_times_lock); | ||
| 5272 | ctransid = btrfs_root_ctransid(&right_root->root_item); | ||
| 5273 | spin_unlock(&right_root->root_times_lock); | ||
| 5274 | if (ctransid != right_start_ctransid) | ||
| 5275 | right_start_ctransid = 0; | ||
| 5276 | |||
| 5277 | if (!left_start_ctransid || !right_start_ctransid) { | ||
| 5278 | WARN(1, KERN_WARNING | ||
| 5279 | "btrfs: btrfs_compare_tree detected " | ||
| 5280 | "a change in one of the trees while " | ||
| 5281 | "iterating. This is probably a " | ||
| 5282 | "bug.\n"); | ||
| 5283 | ret = -EIO; | ||
| 5284 | goto out; | ||
| 5285 | } | ||
| 5286 | |||
| 5287 | /* | ||
| 5288 | * the commit root may have changed, so start again | ||
| 5289 | * where we stopped | ||
| 5290 | */ | ||
| 5291 | left_path->lowest_level = left_level; | ||
| 5292 | right_path->lowest_level = right_level; | ||
| 5293 | ret = btrfs_search_slot(NULL, left_root, | ||
| 5294 | &left_key, left_path, 0, 0); | ||
| 5295 | if (ret < 0) | ||
| 5296 | goto out; | ||
| 5297 | ret = btrfs_search_slot(NULL, right_root, | ||
| 5298 | &right_key, right_path, 0, 0); | ||
| 5299 | if (ret < 0) | ||
| 5300 | goto out; | ||
| 5301 | } | ||
| 5302 | |||
| 5303 | if (advance_left && !left_end_reached) { | ||
| 5304 | ret = tree_advance(left_root, left_path, &left_level, | ||
| 5305 | left_root_level, | ||
| 5306 | advance_left != ADVANCE_ONLY_NEXT, | ||
| 5307 | &left_key); | ||
| 5308 | if (ret < 0) | ||
| 5309 | left_end_reached = ADVANCE; | ||
| 5310 | advance_left = 0; | ||
| 5311 | } | ||
| 5312 | if (advance_right && !right_end_reached) { | ||
| 5313 | ret = tree_advance(right_root, right_path, &right_level, | ||
| 5314 | right_root_level, | ||
| 5315 | advance_right != ADVANCE_ONLY_NEXT, | ||
| 5316 | &right_key); | ||
| 5317 | if (ret < 0) | ||
| 5318 | right_end_reached = ADVANCE; | ||
| 5319 | advance_right = 0; | ||
| 5320 | } | ||
| 5321 | |||
| 5322 | if (left_end_reached && right_end_reached) { | ||
| 5323 | ret = 0; | ||
| 5324 | goto out; | ||
| 5325 | } else if (left_end_reached) { | ||
| 5326 | if (right_level == 0) { | ||
| 5327 | ret = changed_cb(left_root, right_root, | ||
| 5328 | left_path, right_path, | ||
| 5329 | &right_key, | ||
| 5330 | BTRFS_COMPARE_TREE_DELETED, | ||
| 5331 | ctx); | ||
| 5332 | if (ret < 0) | ||
| 5333 | goto out; | ||
| 5334 | } | ||
| 5335 | advance_right = ADVANCE; | ||
| 5336 | continue; | ||
| 5337 | } else if (right_end_reached) { | ||
| 5338 | if (left_level == 0) { | ||
| 5339 | ret = changed_cb(left_root, right_root, | ||
| 5340 | left_path, right_path, | ||
| 5341 | &left_key, | ||
| 5342 | BTRFS_COMPARE_TREE_NEW, | ||
| 5343 | ctx); | ||
| 5344 | if (ret < 0) | ||
| 5345 | goto out; | ||
| 5346 | } | ||
| 5347 | advance_left = ADVANCE; | ||
| 5348 | continue; | ||
| 5349 | } | ||
| 5350 | |||
| 5351 | if (left_level == 0 && right_level == 0) { | ||
| 5352 | cmp = btrfs_comp_cpu_keys(&left_key, &right_key); | ||
| 5353 | if (cmp < 0) { | ||
| 5354 | ret = changed_cb(left_root, right_root, | ||
| 5355 | left_path, right_path, | ||
| 5356 | &left_key, | ||
| 5357 | BTRFS_COMPARE_TREE_NEW, | ||
| 5358 | ctx); | ||
| 5359 | if (ret < 0) | ||
| 5360 | goto out; | ||
| 5361 | advance_left = ADVANCE; | ||
| 5362 | } else if (cmp > 0) { | ||
| 5363 | ret = changed_cb(left_root, right_root, | ||
| 5364 | left_path, right_path, | ||
| 5365 | &right_key, | ||
| 5366 | BTRFS_COMPARE_TREE_DELETED, | ||
| 5367 | ctx); | ||
| 5368 | if (ret < 0) | ||
| 5369 | goto out; | ||
| 5370 | advance_right = ADVANCE; | ||
| 5371 | } else { | ||
| 5372 | ret = tree_compare_item(left_root, left_path, | ||
| 5373 | right_path, tmp_buf); | ||
| 5374 | if (ret) { | ||
| 5375 | ret = changed_cb(left_root, right_root, | ||
| 5376 | left_path, right_path, | ||
| 5377 | &left_key, | ||
| 5378 | BTRFS_COMPARE_TREE_CHANGED, | ||
| 5379 | ctx); | ||
| 5380 | if (ret < 0) | ||
| 5381 | goto out; | ||
| 5382 | } | ||
| 5383 | advance_left = ADVANCE; | ||
| 5384 | advance_right = ADVANCE; | ||
| 5385 | } | ||
| 5386 | } else if (left_level == right_level) { | ||
| 5387 | cmp = btrfs_comp_cpu_keys(&left_key, &right_key); | ||
| 5388 | if (cmp < 0) { | ||
| 5389 | advance_left = ADVANCE; | ||
| 5390 | } else if (cmp > 0) { | ||
| 5391 | advance_right = ADVANCE; | ||
| 5392 | } else { | ||
| 5393 | left_blockptr = btrfs_node_blockptr( | ||
| 5394 | left_path->nodes[left_level], | ||
| 5395 | left_path->slots[left_level]); | ||
| 5396 | right_blockptr = btrfs_node_blockptr( | ||
| 5397 | right_path->nodes[right_level], | ||
| 5398 | right_path->slots[right_level]); | ||
| 5399 | if (left_blockptr == right_blockptr) { | ||
| 5400 | /* | ||
| 5401 | * As we're on a shared block, don't | ||
| 5402 | * allow to go deeper. | ||
| 5403 | */ | ||
| 5404 | advance_left = ADVANCE_ONLY_NEXT; | ||
| 5405 | advance_right = ADVANCE_ONLY_NEXT; | ||
| 5406 | } else { | ||
| 5407 | advance_left = ADVANCE; | ||
| 5408 | advance_right = ADVANCE; | ||
| 5409 | } | ||
| 5410 | } | ||
| 5411 | } else if (left_level < right_level) { | ||
| 5412 | advance_right = ADVANCE; | ||
| 5413 | } else { | ||
| 5414 | advance_left = ADVANCE; | ||
| 5415 | } | ||
| 5416 | } | ||
| 5417 | |||
| 5418 | out: | ||
| 5419 | btrfs_free_path(left_path); | ||
| 5420 | btrfs_free_path(right_path); | ||
| 5421 | kfree(tmp_buf); | ||
| 5422 | |||
| 5423 | if (trans) { | ||
| 5424 | if (!ret) | ||
| 5425 | ret = btrfs_end_transaction(trans, left_root); | ||
| 5426 | else | ||
| 5427 | btrfs_end_transaction(trans, left_root); | ||
| 5428 | } | ||
| 5429 | |||
| 5430 | return ret; | ||
| 5431 | } | ||
| 5432 | |||
| 5008 | /* | 5433 | /* |
| 5009 | * this is similar to btrfs_next_leaf, but does not try to preserve | 5434 | * this is similar to btrfs_next_leaf, but does not try to preserve |
| 5010 | * and fixup the path. It looks for and returns the next key in the | 5435 | * and fixup the path. It looks for and returns the next key in the |
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index d5f6d7458676..2fbbe738caed 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
| @@ -2722,6 +2722,21 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key, | |||
| 2722 | struct btrfs_key *max_key, | 2722 | struct btrfs_key *max_key, |
| 2723 | struct btrfs_path *path, int cache_only, | 2723 | struct btrfs_path *path, int cache_only, |
| 2724 | u64 min_trans); | 2724 | u64 min_trans); |
| 2725 | enum btrfs_compare_tree_result { | ||
| 2726 | BTRFS_COMPARE_TREE_NEW, | ||
| 2727 | BTRFS_COMPARE_TREE_DELETED, | ||
| 2728 | BTRFS_COMPARE_TREE_CHANGED, | ||
| 2729 | }; | ||
| 2730 | typedef int (*btrfs_changed_cb_t)(struct btrfs_root *left_root, | ||
| 2731 | struct btrfs_root *right_root, | ||
| 2732 | struct btrfs_path *left_path, | ||
| 2733 | struct btrfs_path *right_path, | ||
| 2734 | struct btrfs_key *key, | ||
| 2735 | enum btrfs_compare_tree_result result, | ||
| 2736 | void *ctx); | ||
| 2737 | int btrfs_compare_trees(struct btrfs_root *left_root, | ||
| 2738 | struct btrfs_root *right_root, | ||
| 2739 | btrfs_changed_cb_t cb, void *ctx); | ||
| 2725 | int btrfs_cow_block(struct btrfs_trans_handle *trans, | 2740 | int btrfs_cow_block(struct btrfs_trans_handle *trans, |
| 2726 | struct btrfs_root *root, struct extent_buffer *buf, | 2741 | struct btrfs_root *root, struct extent_buffer *buf, |
| 2727 | struct extent_buffer *parent, int parent_slot, | 2742 | struct extent_buffer *parent, int parent_slot, |
