diff options
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r-- | fs/btrfs/ctree.c | 137 |
1 files changed, 113 insertions, 24 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index df4a19d65206..afa5bc5c7c1a 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -12,6 +12,9 @@ static int split_leaf(struct ctree_root *root, struct ctree_path *path, | |||
12 | int data_size); | 12 | int data_size); |
13 | static int push_node_left(struct ctree_root *root, struct tree_buffer *dst, | 13 | static int push_node_left(struct ctree_root *root, struct tree_buffer *dst, |
14 | struct tree_buffer *src); | 14 | struct tree_buffer *src); |
15 | static int balance_node_right(struct ctree_root *root, | ||
16 | struct tree_buffer *dst_buf, | ||
17 | struct tree_buffer *src_buf); | ||
15 | static int del_ptr(struct ctree_root *root, struct ctree_path *path, int level, | 18 | static int del_ptr(struct ctree_root *root, struct ctree_path *path, int level, |
16 | int slot); | 19 | int slot); |
17 | 20 | ||
@@ -217,15 +220,16 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path, | |||
217 | int ret = 0; | 220 | int ret = 0; |
218 | int wret; | 221 | int wret; |
219 | int pslot; | 222 | int pslot; |
220 | int used = 0; | ||
221 | int count; | ||
222 | int orig_slot = path->slots[level]; | 223 | int orig_slot = path->slots[level]; |
224 | u64 orig_ptr; | ||
223 | 225 | ||
224 | if (level == 0) | 226 | if (level == 0) |
225 | return 0; | 227 | return 0; |
226 | 228 | ||
227 | mid_buf = path->nodes[level]; | 229 | mid_buf = path->nodes[level]; |
228 | mid = &mid_buf->node; | 230 | mid = &mid_buf->node; |
231 | orig_ptr = mid->blockptrs[orig_slot]; | ||
232 | |||
229 | if (level < MAX_LEVEL - 1) | 233 | if (level < MAX_LEVEL - 1) |
230 | parent_buf = path->nodes[level + 1]; | 234 | parent_buf = path->nodes[level + 1]; |
231 | pslot = path->slots[level + 1]; | 235 | pslot = path->slots[level + 1]; |
@@ -253,24 +257,26 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path, | |||
253 | if (mid->header.nritems > NODEPTRS_PER_BLOCK / 4) | 257 | if (mid->header.nritems > NODEPTRS_PER_BLOCK / 4) |
254 | return 0; | 258 | return 0; |
255 | 259 | ||
256 | // print_tree(root, root->node); | ||
257 | left_buf = read_node_slot(root, parent_buf, pslot - 1); | 260 | left_buf = read_node_slot(root, parent_buf, pslot - 1); |
258 | right_buf = read_node_slot(root, parent_buf, pslot + 1); | 261 | right_buf = read_node_slot(root, parent_buf, pslot + 1); |
259 | if (right_buf) { | 262 | |
260 | right = &right_buf->node; | 263 | /* first, try to make some room in the middle buffer */ |
261 | used = right->header.nritems; | ||
262 | count = 1; | ||
263 | } | ||
264 | if (left_buf) { | 264 | if (left_buf) { |
265 | left = &left_buf->node; | 265 | left = &left_buf->node; |
266 | used += left->header.nritems; | ||
267 | orig_slot += left->header.nritems; | 266 | orig_slot += left->header.nritems; |
268 | count++; | 267 | wret = push_node_left(root, left_buf, mid_buf); |
268 | if (wret < 0) | ||
269 | ret = wret; | ||
269 | } | 270 | } |
270 | if (left_buf) | 271 | |
271 | push_node_left(root, left_buf, mid_buf); | 272 | /* |
273 | * then try to empty the right most buffer into the middle | ||
274 | */ | ||
272 | if (right_buf) { | 275 | if (right_buf) { |
273 | push_node_left(root, mid_buf, right_buf); | 276 | right = &right_buf->node; |
277 | wret = push_node_left(root, mid_buf, right_buf); | ||
278 | if (wret < 0) | ||
279 | ret = wret; | ||
274 | if (right->header.nritems == 0) { | 280 | if (right->header.nritems == 0) { |
275 | u64 blocknr = right_buf->blocknr; | 281 | u64 blocknr = right_buf->blocknr; |
276 | tree_block_release(root, right_buf); | 282 | tree_block_release(root, right_buf); |
@@ -285,9 +291,29 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path, | |||
285 | } else { | 291 | } else { |
286 | memcpy(parent->keys + pslot + 1, right->keys, | 292 | memcpy(parent->keys + pslot + 1, right->keys, |
287 | sizeof(struct key)); | 293 | sizeof(struct key)); |
294 | wret = write_tree_block(root, parent_buf); | ||
295 | if (wret) | ||
296 | ret = wret; | ||
288 | } | 297 | } |
289 | } | 298 | } |
299 | if (mid->header.nritems == 1) { | ||
300 | /* | ||
301 | * we're not allowed to leave a node with one item in the | ||
302 | * tree during a delete. A deletion from lower in the tree | ||
303 | * could try to delete the only pointer in this node. | ||
304 | * So, pull some keys from the left. | ||
305 | * There has to be a left pointer at this point because | ||
306 | * otherwise we would have pulled some pointers from the | ||
307 | * right | ||
308 | */ | ||
309 | BUG_ON(!left_buf); | ||
310 | wret = balance_node_right(root, mid_buf, left_buf); | ||
311 | if (wret < 0) | ||
312 | ret = wret; | ||
313 | BUG_ON(wret == 1); | ||
314 | } | ||
290 | if (mid->header.nritems == 0) { | 315 | if (mid->header.nritems == 0) { |
316 | /* we've managed to empty the middle node, drop it */ | ||
291 | u64 blocknr = mid_buf->blocknr; | 317 | u64 blocknr = mid_buf->blocknr; |
292 | tree_block_release(root, mid_buf); | 318 | tree_block_release(root, mid_buf); |
293 | mid_buf = NULL; | 319 | mid_buf = NULL; |
@@ -298,11 +324,17 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path, | |||
298 | wret = free_extent(root, blocknr, 1); | 324 | wret = free_extent(root, blocknr, 1); |
299 | if (wret) | 325 | if (wret) |
300 | ret = wret; | 326 | ret = wret; |
301 | } else | 327 | } else { |
328 | /* update the parent key to reflect our changes */ | ||
302 | memcpy(parent->keys + pslot, mid->keys, sizeof(struct key)); | 329 | memcpy(parent->keys + pslot, mid->keys, sizeof(struct key)); |
330 | wret = write_tree_block(root, parent_buf); | ||
331 | if (wret) | ||
332 | ret = wret; | ||
333 | } | ||
303 | 334 | ||
335 | /* update the path */ | ||
304 | if (left_buf) { | 336 | if (left_buf) { |
305 | if (left->header.nritems >= orig_slot) { | 337 | if (left->header.nritems > orig_slot) { |
306 | left_buf->count++; // released below | 338 | left_buf->count++; // released below |
307 | path->nodes[level] = left_buf; | 339 | path->nodes[level] = left_buf; |
308 | path->slots[level + 1] -= 1; | 340 | path->slots[level + 1] -= 1; |
@@ -314,12 +346,15 @@ static int balance_level(struct ctree_root *root, struct ctree_path *path, | |||
314 | path->slots[level] = orig_slot; | 346 | path->slots[level] = orig_slot; |
315 | } | 347 | } |
316 | } | 348 | } |
349 | /* double check we haven't messed things up */ | ||
350 | check_block(path, level); | ||
351 | if (orig_ptr != path->nodes[level]->node.blockptrs[path->slots[level]]) | ||
352 | BUG(); | ||
317 | 353 | ||
318 | if (right_buf) | 354 | if (right_buf) |
319 | tree_block_release(root, right_buf); | 355 | tree_block_release(root, right_buf); |
320 | if (left_buf) | 356 | if (left_buf) |
321 | tree_block_release(root, left_buf); | 357 | tree_block_release(root, left_buf); |
322 | |||
323 | return ret; | 358 | return ret; |
324 | } | 359 | } |
325 | 360 | ||
@@ -378,6 +413,7 @@ again: | |||
378 | goto again; | 413 | goto again; |
379 | c = &b->node; | 414 | c = &b->node; |
380 | slot = p->slots[level]; | 415 | slot = p->slots[level]; |
416 | BUG_ON(c->header.nritems == 1); | ||
381 | } | 417 | } |
382 | b = read_tree_block(root, c->blockptrs[slot]); | 418 | b = read_tree_block(root, c->blockptrs[slot]); |
383 | } else { | 419 | } else { |
@@ -433,13 +469,7 @@ static int fixup_low_keys(struct ctree_root *root, | |||
433 | 469 | ||
434 | /* | 470 | /* |
435 | * try to push data from one node into the next node left in the | 471 | * try to push data from one node into the next node left in the |
436 | * tree. The src node is found at specified level in the path. | 472 | * tree. |
437 | * If some bytes were pushed, return 0, otherwise return 1. | ||
438 | * | ||
439 | * Lower nodes/leaves in the path are not touched, higher nodes may | ||
440 | * be modified to reflect the push. | ||
441 | * | ||
442 | * The path is altered to reflect the push. | ||
443 | * | 473 | * |
444 | * returns 0 if some ptrs were pushed left, < 0 if there was some horrible | 474 | * returns 0 if some ptrs were pushed left, < 0 if there was some horrible |
445 | * error, and > 0 if there was no room in the left hand block. | 475 | * error, and > 0 if there was no room in the left hand block. |
@@ -463,7 +493,8 @@ static int push_node_left(struct ctree_root *root, struct tree_buffer *dst_buf, | |||
463 | } | 493 | } |
464 | 494 | ||
465 | if (src_nritems < push_items) | 495 | if (src_nritems < push_items) |
466 | push_items =src_nritems; | 496 | push_items = src_nritems; |
497 | |||
467 | memcpy(dst->keys + dst_nritems, src->keys, | 498 | memcpy(dst->keys + dst_nritems, src->keys, |
468 | push_items * sizeof(struct key)); | 499 | push_items * sizeof(struct key)); |
469 | memcpy(dst->blockptrs + dst_nritems, src->blockptrs, | 500 | memcpy(dst->blockptrs + dst_nritems, src->blockptrs, |
@@ -488,6 +519,64 @@ static int push_node_left(struct ctree_root *root, struct tree_buffer *dst_buf, | |||
488 | } | 519 | } |
489 | 520 | ||
490 | /* | 521 | /* |
522 | * try to push data from one node into the next node right in the | ||
523 | * tree. | ||
524 | * | ||
525 | * returns 0 if some ptrs were pushed, < 0 if there was some horrible | ||
526 | * error, and > 0 if there was no room in the right hand block. | ||
527 | * | ||
528 | * this will only push up to 1/2 the contents of the left node over | ||
529 | */ | ||
530 | static int balance_node_right(struct ctree_root *root, | ||
531 | struct tree_buffer *dst_buf, | ||
532 | struct tree_buffer *src_buf) | ||
533 | { | ||
534 | struct node *src = &src_buf->node; | ||
535 | struct node *dst = &dst_buf->node; | ||
536 | int push_items = 0; | ||
537 | int max_push; | ||
538 | int src_nritems; | ||
539 | int dst_nritems; | ||
540 | int ret = 0; | ||
541 | int wret; | ||
542 | |||
543 | src_nritems = src->header.nritems; | ||
544 | dst_nritems = dst->header.nritems; | ||
545 | push_items = NODEPTRS_PER_BLOCK - dst_nritems; | ||
546 | if (push_items <= 0) { | ||
547 | return 1; | ||
548 | } | ||
549 | |||
550 | max_push = src_nritems / 2 + 1; | ||
551 | /* don't try to empty the node */ | ||
552 | if (max_push > src_nritems) | ||
553 | return 1; | ||
554 | if (max_push < push_items) | ||
555 | push_items = max_push; | ||
556 | |||
557 | memmove(dst->keys + push_items, dst->keys, | ||
558 | dst_nritems * sizeof(struct key)); | ||
559 | memmove(dst->blockptrs + push_items, dst->blockptrs, | ||
560 | dst_nritems * sizeof(u64)); | ||
561 | memcpy(dst->keys, src->keys + src_nritems - push_items, | ||
562 | push_items * sizeof(struct key)); | ||
563 | memcpy(dst->blockptrs, src->blockptrs + src_nritems - push_items, | ||
564 | push_items * sizeof(u64)); | ||
565 | |||
566 | src->header.nritems -= push_items; | ||
567 | dst->header.nritems += push_items; | ||
568 | |||
569 | wret = write_tree_block(root, src_buf); | ||
570 | if (wret < 0) | ||
571 | ret = wret; | ||
572 | |||
573 | wret = write_tree_block(root, dst_buf); | ||
574 | if (wret < 0) | ||
575 | ret = wret; | ||
576 | return ret; | ||
577 | } | ||
578 | |||
579 | /* | ||
491 | * helper function to insert a new root level in the tree. | 580 | * helper function to insert a new root level in the tree. |
492 | * A new node is allocated, and a single item is inserted to | 581 | * A new node is allocated, and a single item is inserted to |
493 | * point to the existing root | 582 | * point to the existing root |