aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2015-05-05 10:21:27 -0400
committerChris Mason <clm@fb.com>2015-05-11 10:59:10 -0400
commite43699d4b4c5d9ecbcb5998cdcbada060981171f (patch)
tree710a6440afd5503acc5234802b7f4369ec8d4600 /fs
parent1d3c61c2eb3fe4f96d3192212f1bdcee49ea55aa (diff)
Btrfs: fix crash after inode cache writeback failure
If the writeback of an inode cache failed we were unnecessarilly attempting to release again the delalloc metadata that we previously reserved. However attempting to do this a second time triggers an assertion at drop_outstanding_extent() because we have no more outstanding extents for our inode cache's inode. If we were able to start writeback of the cache the reserved metadata space is released at btrfs_finished_ordered_io(), even if an error happens during writeback. So make sure we don't repeat the metadata space release if writeback started for our inode cache. This issue was trivial to reproduce by running the fstest btrfs/088 with "-o inode_cache", which triggered the assertion leading to a BUG() call and requiring a reboot in order to run the remaining fstests. Trace produced by btrfs/088: [255289.385904] BTRFS: assertion failed: BTRFS_I(inode)->outstanding_extents >= num_extents, file: fs/btrfs/extent-tree.c, line: 5276 [255289.388094] ------------[ cut here ]------------ [255289.389184] kernel BUG at fs/btrfs/ctree.h:4057! [255289.390125] invalid opcode: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC (...) [255289.392068] Call Trace: [255289.392068] [<ffffffffa035e774>] drop_outstanding_extent+0x3d/0x6d [btrfs] [255289.392068] [<ffffffffa0364988>] btrfs_delalloc_release_metadata+0x54/0xe3 [btrfs] [255289.392068] [<ffffffffa03b4174>] btrfs_write_out_ino_cache+0x95/0xad [btrfs] [255289.392068] [<ffffffffa036f5c4>] btrfs_save_ino_cache+0x275/0x2dc [btrfs] [255289.392068] [<ffffffffa03e2d83>] commit_fs_roots.isra.12+0xaa/0x137 [btrfs] [255289.392068] [<ffffffff8107d33d>] ? trace_hardirqs_on+0xd/0xf [255289.392068] [<ffffffffa037841f>] ? btrfs_commit_transaction+0x4b1/0x9c9 [btrfs] [255289.392068] [<ffffffff814351a4>] ? _raw_spin_unlock+0x32/0x46 [255289.392068] [<ffffffffa037842e>] btrfs_commit_transaction+0x4c0/0x9c9 [btrfs] (...) Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/free-space-cache.c14
1 files changed, 12 insertions, 2 deletions
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 5e020d76fd07..9dbe5b548fa6 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -3466,6 +3466,7 @@ int btrfs_write_out_ino_cache(struct btrfs_root *root,
3466 struct btrfs_free_space_ctl *ctl = root->free_ino_ctl; 3466 struct btrfs_free_space_ctl *ctl = root->free_ino_ctl;
3467 int ret; 3467 int ret;
3468 struct btrfs_io_ctl io_ctl; 3468 struct btrfs_io_ctl io_ctl;
3469 bool release_metadata = true;
3469 3470
3470 if (!btrfs_test_opt(root, INODE_MAP_CACHE)) 3471 if (!btrfs_test_opt(root, INODE_MAP_CACHE))
3471 return 0; 3472 return 0;
@@ -3473,11 +3474,20 @@ int btrfs_write_out_ino_cache(struct btrfs_root *root,
3473 memset(&io_ctl, 0, sizeof(io_ctl)); 3474 memset(&io_ctl, 0, sizeof(io_ctl));
3474 ret = __btrfs_write_out_cache(root, inode, ctl, NULL, &io_ctl, 3475 ret = __btrfs_write_out_cache(root, inode, ctl, NULL, &io_ctl,
3475 trans, path, 0); 3476 trans, path, 0);
3476 if (!ret) 3477 if (!ret) {
3478 /*
3479 * At this point writepages() didn't error out, so our metadata
3480 * reservation is released when the writeback finishes, at
3481 * inode.c:btrfs_finish_ordered_io(), regardless of it finishing
3482 * with or without an error.
3483 */
3484 release_metadata = false;
3477 ret = btrfs_wait_cache_io(root, trans, NULL, &io_ctl, path, 0); 3485 ret = btrfs_wait_cache_io(root, trans, NULL, &io_ctl, path, 0);
3486 }
3478 3487
3479 if (ret) { 3488 if (ret) {
3480 btrfs_delalloc_release_metadata(inode, inode->i_size); 3489 if (release_metadata)
3490 btrfs_delalloc_release_metadata(inode, inode->i_size);
3481#ifdef DEBUG 3491#ifdef DEBUG
3482 btrfs_err(root->fs_info, 3492 btrfs_err(root->fs_info,
3483 "failed to write free ino cache for root %llu", 3493 "failed to write free ino cache for root %llu",