aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/file.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/file.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/file.c')
-rw-r--r--fs/btrfs/file.c14
1 files changed, 6 insertions, 8 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index e81e428a8840..a56abed78104 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -552,6 +552,7 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
552 int testend = 1; 552 int testend = 1;
553 unsigned long flags; 553 unsigned long flags;
554 int compressed = 0; 554 int compressed = 0;
555 bool modified;
555 556
556 WARN_ON(end < start); 557 WARN_ON(end < start);
557 if (end == (u64)-1) { 558 if (end == (u64)-1) {
@@ -561,6 +562,7 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
561 while (1) { 562 while (1) {
562 int no_splits = 0; 563 int no_splits = 0;
563 564
565 modified = false;
564 if (!split) 566 if (!split)
565 split = alloc_extent_map(); 567 split = alloc_extent_map();
566 if (!split2) 568 if (!split2)
@@ -592,6 +594,7 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
592 compressed = test_bit(EXTENT_FLAG_COMPRESSED, &em->flags); 594 compressed = test_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
593 clear_bit(EXTENT_FLAG_PINNED, &em->flags); 595 clear_bit(EXTENT_FLAG_PINNED, &em->flags);
594 clear_bit(EXTENT_FLAG_LOGGING, &flags); 596 clear_bit(EXTENT_FLAG_LOGGING, &flags);
597 modified = !list_empty(&em->list);
595 remove_extent_mapping(em_tree, em); 598 remove_extent_mapping(em_tree, em);
596 if (no_splits) 599 if (no_splits)
597 goto next; 600 goto next;
@@ -614,9 +617,8 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
614 split->bdev = em->bdev; 617 split->bdev = em->bdev;
615 split->flags = flags; 618 split->flags = flags;
616 split->compress_type = em->compress_type; 619 split->compress_type = em->compress_type;
617 ret = add_extent_mapping(em_tree, split); 620 ret = add_extent_mapping(em_tree, split, modified);
618 BUG_ON(ret); /* Logic error */ 621 BUG_ON(ret); /* Logic error */
619 list_move(&split->list, &em_tree->modified_extents);
620 free_extent_map(split); 622 free_extent_map(split);
621 split = split2; 623 split = split2;
622 split2 = NULL; 624 split2 = NULL;
@@ -645,9 +647,8 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
645 split->orig_start = em->orig_start; 647 split->orig_start = em->orig_start;
646 } 648 }
647 649
648 ret = add_extent_mapping(em_tree, split); 650 ret = add_extent_mapping(em_tree, split, modified);
649 BUG_ON(ret); /* Logic error */ 651 BUG_ON(ret); /* Logic error */
650 list_move(&split->list, &em_tree->modified_extents);
651 free_extent_map(split); 652 free_extent_map(split);
652 split = NULL; 653 split = NULL;
653 } 654 }
@@ -1930,10 +1931,7 @@ out:
1930 do { 1931 do {
1931 btrfs_drop_extent_cache(inode, offset, end - 1, 0); 1932 btrfs_drop_extent_cache(inode, offset, end - 1, 0);
1932 write_lock(&em_tree->lock); 1933 write_lock(&em_tree->lock);
1933 ret = add_extent_mapping(em_tree, hole_em); 1934 ret = add_extent_mapping(em_tree, hole_em, 1);
1934 if (!ret)
1935 list_move(&hole_em->list,
1936 &em_tree->modified_extents);
1937 write_unlock(&em_tree->lock); 1935 write_unlock(&em_tree->lock);
1938 } while (ret == -EEXIST); 1936 } while (ret == -EEXIST);
1939 free_extent_map(hole_em); 1937 free_extent_map(hole_em);