aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.c425
-rw-r--r--fs/btrfs/ctree.h15
2 files changed, 440 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index c82a9e4a953..4c10fd19d48 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
5008static 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
5018static 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 */
5049static 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
5074static 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 */
5116int 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
5418out:
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 d5f6d745867..2fbbe738cae 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);
2725enum btrfs_compare_tree_result {
2726 BTRFS_COMPARE_TREE_NEW,
2727 BTRFS_COMPARE_TREE_DELETED,
2728 BTRFS_COMPARE_TREE_CHANGED,
2729};
2730typedef 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);
2737int btrfs_compare_trees(struct btrfs_root *left_root,
2738 struct btrfs_root *right_root,
2739 btrfs_changed_cb_t cb, void *ctx);
2725int btrfs_cow_block(struct btrfs_trans_handle *trans, 2740int 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,