aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ctree.c
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-04-05 16:51:15 -0400
committerJosef Bacik <jbacik@fusionio.com>2013-05-06 15:54:34 -0400
commit09a2a8f96e3009273bed1833b3f210e2c68728a5 (patch)
treef4742a6e962991e9f7b0252805186466f5a005c6 /fs/btrfs/ctree.c
parentcc95bef635a649d595cf8d1cd4fcff5b6bf13023 (diff)
Btrfs: fix bad extent logging
A user sent me a btrfs-image of a file system that was panicing on mount during the log recovery. I had originally thought these problems were from a bug in the free space cache code, but that was just a symptom of the problem. The problem is if your application does something like this [prealloc][prealloc][prealloc] the internal extent maps will merge those all together into one extent map, even though on disk they are 3 separate extents. So if you go to write into one of these ranges the extent map will be right since we use the physical extent when doing the write, but when we log the extents they will use the wrong sizes for the remainder prealloc space. If this doesn't happen to trip up the free space cache (which it won't in a lot of cases) then you will get bogus entries in your extent tree which will screw stuff up later. The data and such will still work, but everything else is broken. This patch fixes this by not allowing extents that are on the modified list to be merged. This has the side effect that we are no longer adding everything to the modified list all the time, which means we now have to call btrfs_drop_extents every time we log an extent into the tree. So this allows me to drop all this speciality code I was using to get around calling btrfs_drop_extents. With this patch the testcase I've created no longer creates a bogus file system after replaying the log. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r--fs/btrfs/ctree.c140
1 files changed, 2 insertions, 138 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index fe032ab6bd8a..9ca0f6aefa22 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -2211,9 +2211,6 @@ static noinline void unlock_up(struct btrfs_path *path, int level,
2211 int no_skips = 0; 2211 int no_skips = 0;
2212 struct extent_buffer *t; 2212 struct extent_buffer *t;
2213 2213
2214 if (path->really_keep_locks)
2215 return;
2216
2217 for (i = level; i < BTRFS_MAX_LEVEL; i++) { 2214 for (i = level; i < BTRFS_MAX_LEVEL; i++) {
2218 if (!path->nodes[i]) 2215 if (!path->nodes[i])
2219 break; 2216 break;
@@ -2261,7 +2258,7 @@ noinline void btrfs_unlock_up_safe(struct btrfs_path *path, int level)
2261{ 2258{
2262 int i; 2259 int i;
2263 2260
2264 if (path->keep_locks || path->really_keep_locks) 2261 if (path->keep_locks)
2265 return; 2262 return;
2266 2263
2267 for (i = level; i < BTRFS_MAX_LEVEL; i++) { 2264 for (i = level; i < BTRFS_MAX_LEVEL; i++) {
@@ -2494,7 +2491,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
2494 if (!cow) 2491 if (!cow)
2495 write_lock_level = -1; 2492 write_lock_level = -1;
2496 2493
2497 if (cow && (p->really_keep_locks || p->keep_locks || p->lowest_level)) 2494 if (cow && (p->keep_locks || p->lowest_level))
2498 write_lock_level = BTRFS_MAX_LEVEL; 2495 write_lock_level = BTRFS_MAX_LEVEL;
2499 2496
2500 min_write_lock_level = write_lock_level; 2497 min_write_lock_level = write_lock_level;
@@ -5465,139 +5462,6 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path)
5465 return btrfs_next_old_leaf(root, path, 0); 5462 return btrfs_next_old_leaf(root, path, 0);
5466} 5463}
5467 5464
5468/* Release the path up to but not including the given level */
5469static void btrfs_release_level(struct btrfs_path *path, int level)
5470{
5471 int i;
5472
5473 for (i = 0; i < level; i++) {
5474 path->slots[i] = 0;
5475 if (!path->nodes[i])
5476 continue;
5477 if (path->locks[i]) {
5478 btrfs_tree_unlock_rw(path->nodes[i], path->locks[i]);
5479 path->locks[i] = 0;
5480 }
5481 free_extent_buffer(path->nodes[i]);
5482 path->nodes[i] = NULL;
5483 }
5484}
5485
5486/*
5487 * This function assumes 2 things
5488 *
5489 * 1) You are using path->keep_locks
5490 * 2) You are not inserting items.
5491 *
5492 * If either of these are not true do not use this function. If you need a next
5493 * leaf with either of these not being true then this function can be easily
5494 * adapted to do that, but at the moment these are the limitations.
5495 */
5496int btrfs_next_leaf_write(struct btrfs_trans_handle *trans,
5497 struct btrfs_root *root, struct btrfs_path *path,
5498 int del)
5499{
5500 struct extent_buffer *b;
5501 struct btrfs_key key;
5502 u32 nritems;
5503 int level = 1;
5504 int slot;
5505 int ret = 1;
5506 int write_lock_level = BTRFS_MAX_LEVEL;
5507 int ins_len = del ? -1 : 0;
5508
5509 WARN_ON(!(path->keep_locks || path->really_keep_locks));
5510
5511 nritems = btrfs_header_nritems(path->nodes[0]);
5512 btrfs_item_key_to_cpu(path->nodes[0], &key, nritems - 1);
5513
5514 while (path->nodes[level]) {
5515 nritems = btrfs_header_nritems(path->nodes[level]);
5516 if (!(path->locks[level] & BTRFS_WRITE_LOCK)) {
5517search:
5518 btrfs_release_path(path);
5519 ret = btrfs_search_slot(trans, root, &key, path,
5520 ins_len, 1);
5521 if (ret < 0)
5522 goto out;
5523 level = 1;
5524 continue;
5525 }
5526
5527 if (path->slots[level] >= nritems - 1) {
5528 level++;
5529 continue;
5530 }
5531
5532 btrfs_release_level(path, level);
5533 break;
5534 }
5535
5536 if (!path->nodes[level]) {
5537 ret = 1;
5538 goto out;
5539 }
5540
5541 path->slots[level]++;
5542 b = path->nodes[level];
5543
5544 while (b) {
5545 level = btrfs_header_level(b);
5546
5547 if (!should_cow_block(trans, root, b))
5548 goto cow_done;
5549
5550 btrfs_set_path_blocking(path);
5551 ret = btrfs_cow_block(trans, root, b,
5552 path->nodes[level + 1],
5553 path->slots[level + 1], &b);
5554 if (ret)
5555 goto out;
5556cow_done:
5557 path->nodes[level] = b;
5558 btrfs_clear_path_blocking(path, NULL, 0);
5559 if (level != 0) {
5560 ret = setup_nodes_for_search(trans, root, path, b,
5561 level, ins_len,
5562 &write_lock_level);
5563 if (ret == -EAGAIN)
5564 goto search;
5565 if (ret)
5566 goto out;
5567
5568 b = path->nodes[level];
5569 slot = path->slots[level];
5570
5571 ret = read_block_for_search(trans, root, path,
5572 &b, level, slot, &key, 0);
5573 if (ret == -EAGAIN)
5574 goto search;
5575 if (ret)
5576 goto out;
5577 level = btrfs_header_level(b);
5578 if (!btrfs_try_tree_write_lock(b)) {
5579 btrfs_set_path_blocking(path);
5580 btrfs_tree_lock(b);
5581 btrfs_clear_path_blocking(path, b,
5582 BTRFS_WRITE_LOCK);
5583 }
5584 path->locks[level] = BTRFS_WRITE_LOCK;
5585 path->nodes[level] = b;
5586 path->slots[level] = 0;
5587 } else {
5588 path->slots[level] = 0;
5589 ret = 0;
5590 break;
5591 }
5592 }
5593
5594out:
5595 if (ret)
5596 btrfs_release_path(path);
5597
5598 return ret;
5599}
5600
5601int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, 5465int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path,
5602 u64 time_seq) 5466 u64 time_seq)
5603{ 5467{