aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/inode.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/inode.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/inode.c')
-rw-r--r--fs/btrfs/inode.c34
1 files changed, 8 insertions, 26 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 456667b04fe6..c41637a1ed38 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -732,10 +732,7 @@ retry:
732 732
733 while (1) { 733 while (1) {
734 write_lock(&em_tree->lock); 734 write_lock(&em_tree->lock);
735 ret = add_extent_mapping(em_tree, em); 735 ret = add_extent_mapping(em_tree, em, 1);
736 if (!ret)
737 list_move(&em->list,
738 &em_tree->modified_extents);
739 write_unlock(&em_tree->lock); 736 write_unlock(&em_tree->lock);
740 if (ret != -EEXIST) { 737 if (ret != -EEXIST) {
741 free_extent_map(em); 738 free_extent_map(em);
@@ -941,10 +938,7 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
941 938
942 while (1) { 939 while (1) {
943 write_lock(&em_tree->lock); 940 write_lock(&em_tree->lock);
944 ret = add_extent_mapping(em_tree, em); 941 ret = add_extent_mapping(em_tree, em, 1);
945 if (!ret)
946 list_move(&em->list,
947 &em_tree->modified_extents);
948 write_unlock(&em_tree->lock); 942 write_unlock(&em_tree->lock);
949 if (ret != -EEXIST) { 943 if (ret != -EEXIST) {
950 free_extent_map(em); 944 free_extent_map(em);
@@ -1387,10 +1381,7 @@ out_check:
1387 em->generation = -1; 1381 em->generation = -1;
1388 while (1) { 1382 while (1) {
1389 write_lock(&em_tree->lock); 1383 write_lock(&em_tree->lock);
1390 ret = add_extent_mapping(em_tree, em); 1384 ret = add_extent_mapping(em_tree, em, 1);
1391 if (!ret)
1392 list_move(&em->list,
1393 &em_tree->modified_extents);
1394 write_unlock(&em_tree->lock); 1385 write_unlock(&em_tree->lock);
1395 if (ret != -EEXIST) { 1386 if (ret != -EEXIST) {
1396 free_extent_map(em); 1387 free_extent_map(em);
@@ -4467,10 +4458,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
4467 4458
4468 while (1) { 4459 while (1) {
4469 write_lock(&em_tree->lock); 4460 write_lock(&em_tree->lock);
4470 err = add_extent_mapping(em_tree, hole_em); 4461 err = add_extent_mapping(em_tree, hole_em, 1);
4471 if (!err)
4472 list_move(&hole_em->list,
4473 &em_tree->modified_extents);
4474 write_unlock(&em_tree->lock); 4462 write_unlock(&em_tree->lock);
4475 if (err != -EEXIST) 4463 if (err != -EEXIST)
4476 break; 4464 break;
@@ -5989,7 +5977,7 @@ static int merge_extent_mapping(struct extent_map_tree *em_tree,
5989 em->block_start += start_diff; 5977 em->block_start += start_diff;
5990 em->block_len -= start_diff; 5978 em->block_len -= start_diff;
5991 } 5979 }
5992 return add_extent_mapping(em_tree, em); 5980 return add_extent_mapping(em_tree, em, 0);
5993} 5981}
5994 5982
5995static noinline int uncompress_inline(struct btrfs_path *path, 5983static noinline int uncompress_inline(struct btrfs_path *path,
@@ -6283,7 +6271,7 @@ insert:
6283 6271
6284 err = 0; 6272 err = 0;
6285 write_lock(&em_tree->lock); 6273 write_lock(&em_tree->lock);
6286 ret = add_extent_mapping(em_tree, em); 6274 ret = add_extent_mapping(em_tree, em, 0);
6287 /* it is possible that someone inserted the extent into the tree 6275 /* it is possible that someone inserted the extent into the tree
6288 * while we had the lock dropped. It is also possible that 6276 * while we had the lock dropped. It is also possible that
6289 * an overlapping map exists in the tree 6277 * an overlapping map exists in the tree
@@ -6706,10 +6694,7 @@ static struct extent_map *create_pinned_em(struct inode *inode, u64 start,
6706 btrfs_drop_extent_cache(inode, em->start, 6694 btrfs_drop_extent_cache(inode, em->start,
6707 em->start + em->len - 1, 0); 6695 em->start + em->len - 1, 0);
6708 write_lock(&em_tree->lock); 6696 write_lock(&em_tree->lock);
6709 ret = add_extent_mapping(em_tree, em); 6697 ret = add_extent_mapping(em_tree, em, 1);
6710 if (!ret)
6711 list_move(&em->list,
6712 &em_tree->modified_extents);
6713 write_unlock(&em_tree->lock); 6698 write_unlock(&em_tree->lock);
6714 } while (ret == -EEXIST); 6699 } while (ret == -EEXIST);
6715 6700
@@ -8593,10 +8578,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
8593 8578
8594 while (1) { 8579 while (1) {
8595 write_lock(&em_tree->lock); 8580 write_lock(&em_tree->lock);
8596 ret = add_extent_mapping(em_tree, em); 8581 ret = add_extent_mapping(em_tree, em, 1);
8597 if (!ret)
8598 list_move(&em->list,
8599 &em_tree->modified_extents);
8600 write_unlock(&em_tree->lock); 8582 write_unlock(&em_tree->lock);
8601 if (ret != -EEXIST) 8583 if (ret != -EEXIST)
8602 break; 8584 break;