diff options
author | Josef Bacik <jbacik@fusionio.com> | 2013-04-25 15:55:30 -0400 |
---|---|---|
committer | Josef Bacik <jbacik@fusionio.com> | 2013-05-06 15:55:20 -0400 |
commit | b50c6e250ef91313518dbca96663578237ba8d3c (patch) | |
tree | 419d87afe4386be50be15fe5cc0cb0b367eeb1ed /fs/btrfs/tree-log.c | |
parent | 3d7b5a2882133a04716903b1f4878a64c6610842 (diff) |
Btrfs: deal with free space cache errors while replaying log
So everybody who got hit by my fsync bug will still continue to hit this
BUG_ON() in the free space cache, which is pretty heavy handed. So I took a
file system that had this bug and fixed up all the BUG_ON()'s and leaks that
popped up when I tried to mount a broken file system like this. With this patch
we just fail to mount instead of panicing. Thanks,
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 63 |
1 files changed, 43 insertions, 20 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index f50137a98fb1..aebfb2d7b7d2 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -277,17 +277,19 @@ static int process_one_buffer(struct btrfs_root *log, | |||
277 | struct extent_buffer *eb, | 277 | struct extent_buffer *eb, |
278 | struct walk_control *wc, u64 gen) | 278 | struct walk_control *wc, u64 gen) |
279 | { | 279 | { |
280 | int ret = 0; | ||
281 | |||
280 | if (wc->pin) | 282 | if (wc->pin) |
281 | btrfs_pin_extent_for_log_replay(log->fs_info->extent_root, | 283 | ret = btrfs_pin_extent_for_log_replay(log->fs_info->extent_root, |
282 | eb->start, eb->len); | 284 | eb->start, eb->len); |
283 | 285 | ||
284 | if (btrfs_buffer_uptodate(eb, gen, 0)) { | 286 | if (!ret && btrfs_buffer_uptodate(eb, gen, 0)) { |
285 | if (wc->write) | 287 | if (wc->write) |
286 | btrfs_write_tree_block(eb); | 288 | btrfs_write_tree_block(eb); |
287 | if (wc->wait) | 289 | if (wc->wait) |
288 | btrfs_wait_tree_block_writeback(eb); | 290 | btrfs_wait_tree_block_writeback(eb); |
289 | } | 291 | } |
290 | return 0; | 292 | return ret; |
291 | } | 293 | } |
292 | 294 | ||
293 | /* | 295 | /* |
@@ -623,7 +625,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, | |||
623 | ins.objectid, ins.offset, | 625 | ins.objectid, ins.offset, |
624 | 0, root->root_key.objectid, | 626 | 0, root->root_key.objectid, |
625 | key->objectid, offset, 0); | 627 | key->objectid, offset, 0); |
626 | BUG_ON(ret); | 628 | if (ret) |
629 | goto out; | ||
627 | } else { | 630 | } else { |
628 | /* | 631 | /* |
629 | * insert the extent pointer in the extent | 632 | * insert the extent pointer in the extent |
@@ -632,7 +635,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, | |||
632 | ret = btrfs_alloc_logged_file_extent(trans, | 635 | ret = btrfs_alloc_logged_file_extent(trans, |
633 | root, root->root_key.objectid, | 636 | root, root->root_key.objectid, |
634 | key->objectid, offset, &ins); | 637 | key->objectid, offset, &ins); |
635 | BUG_ON(ret); | 638 | if (ret) |
639 | goto out; | ||
636 | } | 640 | } |
637 | btrfs_release_path(path); | 641 | btrfs_release_path(path); |
638 | 642 | ||
@@ -1952,11 +1956,13 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb, | |||
1952 | if (S_ISDIR(mode)) { | 1956 | if (S_ISDIR(mode)) { |
1953 | ret = replay_dir_deletes(wc->trans, | 1957 | ret = replay_dir_deletes(wc->trans, |
1954 | root, log, path, key.objectid, 0); | 1958 | root, log, path, key.objectid, 0); |
1955 | BUG_ON(ret); | 1959 | if (ret) |
1960 | break; | ||
1956 | } | 1961 | } |
1957 | ret = overwrite_item(wc->trans, root, path, | 1962 | ret = overwrite_item(wc->trans, root, path, |
1958 | eb, i, &key); | 1963 | eb, i, &key); |
1959 | BUG_ON(ret); | 1964 | if (ret) |
1965 | break; | ||
1960 | 1966 | ||
1961 | /* for regular files, make sure corresponding | 1967 | /* for regular files, make sure corresponding |
1962 | * orhpan item exist. extents past the new EOF | 1968 | * orhpan item exist. extents past the new EOF |
@@ -1965,12 +1971,14 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb, | |||
1965 | if (S_ISREG(mode)) { | 1971 | if (S_ISREG(mode)) { |
1966 | ret = insert_orphan_item(wc->trans, root, | 1972 | ret = insert_orphan_item(wc->trans, root, |
1967 | key.objectid); | 1973 | key.objectid); |
1968 | BUG_ON(ret); | 1974 | if (ret) |
1975 | break; | ||
1969 | } | 1976 | } |
1970 | 1977 | ||
1971 | ret = link_to_fixup_dir(wc->trans, root, | 1978 | ret = link_to_fixup_dir(wc->trans, root, |
1972 | path, key.objectid); | 1979 | path, key.objectid); |
1973 | BUG_ON(ret); | 1980 | if (ret) |
1981 | break; | ||
1974 | } | 1982 | } |
1975 | if (wc->stage < LOG_WALK_REPLAY_ALL) | 1983 | if (wc->stage < LOG_WALK_REPLAY_ALL) |
1976 | continue; | 1984 | continue; |
@@ -1979,28 +1987,35 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb, | |||
1979 | if (key.type == BTRFS_XATTR_ITEM_KEY) { | 1987 | if (key.type == BTRFS_XATTR_ITEM_KEY) { |
1980 | ret = overwrite_item(wc->trans, root, path, | 1988 | ret = overwrite_item(wc->trans, root, path, |
1981 | eb, i, &key); | 1989 | eb, i, &key); |
1982 | BUG_ON(ret); | 1990 | if (ret) |
1991 | break; | ||
1983 | } else if (key.type == BTRFS_INODE_REF_KEY) { | 1992 | } else if (key.type == BTRFS_INODE_REF_KEY) { |
1984 | ret = add_inode_ref(wc->trans, root, log, path, | 1993 | ret = add_inode_ref(wc->trans, root, log, path, |
1985 | eb, i, &key); | 1994 | eb, i, &key); |
1986 | BUG_ON(ret && ret != -ENOENT); | 1995 | if (ret && ret != -ENOENT) |
1996 | break; | ||
1997 | ret = 0; | ||
1987 | } else if (key.type == BTRFS_INODE_EXTREF_KEY) { | 1998 | } else if (key.type == BTRFS_INODE_EXTREF_KEY) { |
1988 | ret = add_inode_ref(wc->trans, root, log, path, | 1999 | ret = add_inode_ref(wc->trans, root, log, path, |
1989 | eb, i, &key); | 2000 | eb, i, &key); |
1990 | BUG_ON(ret && ret != -ENOENT); | 2001 | if (ret && ret != -ENOENT) |
2002 | break; | ||
2003 | ret = 0; | ||
1991 | } else if (key.type == BTRFS_EXTENT_DATA_KEY) { | 2004 | } else if (key.type == BTRFS_EXTENT_DATA_KEY) { |
1992 | ret = replay_one_extent(wc->trans, root, path, | 2005 | ret = replay_one_extent(wc->trans, root, path, |
1993 | eb, i, &key); | 2006 | eb, i, &key); |
1994 | BUG_ON(ret); | 2007 | if (ret) |
2008 | break; | ||
1995 | } else if (key.type == BTRFS_DIR_ITEM_KEY || | 2009 | } else if (key.type == BTRFS_DIR_ITEM_KEY || |
1996 | key.type == BTRFS_DIR_INDEX_KEY) { | 2010 | key.type == BTRFS_DIR_INDEX_KEY) { |
1997 | ret = replay_one_dir_item(wc->trans, root, path, | 2011 | ret = replay_one_dir_item(wc->trans, root, path, |
1998 | eb, i, &key); | 2012 | eb, i, &key); |
1999 | BUG_ON(ret); | 2013 | if (ret) |
2014 | break; | ||
2000 | } | 2015 | } |
2001 | } | 2016 | } |
2002 | btrfs_free_path(path); | 2017 | btrfs_free_path(path); |
2003 | return 0; | 2018 | return ret; |
2004 | } | 2019 | } |
2005 | 2020 | ||
2006 | static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, | 2021 | static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, |
@@ -2045,8 +2060,10 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, | |||
2045 | 2060 | ||
2046 | if (*level == 1) { | 2061 | if (*level == 1) { |
2047 | ret = wc->process_func(root, next, wc, ptr_gen); | 2062 | ret = wc->process_func(root, next, wc, ptr_gen); |
2048 | if (ret) | 2063 | if (ret) { |
2064 | free_extent_buffer(next); | ||
2049 | return ret; | 2065 | return ret; |
2066 | } | ||
2050 | 2067 | ||
2051 | path->slots[*level]++; | 2068 | path->slots[*level]++; |
2052 | if (wc->free) { | 2069 | if (wc->free) { |
@@ -3970,6 +3987,9 @@ again: | |||
3970 | wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key); | 3987 | wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key); |
3971 | if (IS_ERR(wc.replay_dest)) { | 3988 | if (IS_ERR(wc.replay_dest)) { |
3972 | ret = PTR_ERR(wc.replay_dest); | 3989 | ret = PTR_ERR(wc.replay_dest); |
3990 | free_extent_buffer(log->node); | ||
3991 | free_extent_buffer(log->commit_root); | ||
3992 | kfree(log); | ||
3973 | btrfs_error(fs_info, ret, "Couldn't read target root " | 3993 | btrfs_error(fs_info, ret, "Couldn't read target root " |
3974 | "for tree log recovery."); | 3994 | "for tree log recovery."); |
3975 | goto error; | 3995 | goto error; |
@@ -3978,12 +3998,10 @@ again: | |||
3978 | wc.replay_dest->log_root = log; | 3998 | wc.replay_dest->log_root = log; |
3979 | btrfs_record_root_in_trans(trans, wc.replay_dest); | 3999 | btrfs_record_root_in_trans(trans, wc.replay_dest); |
3980 | ret = walk_log_tree(trans, log, &wc); | 4000 | ret = walk_log_tree(trans, log, &wc); |
3981 | BUG_ON(ret); | ||
3982 | 4001 | ||
3983 | if (wc.stage == LOG_WALK_REPLAY_ALL) { | 4002 | if (!ret && wc.stage == LOG_WALK_REPLAY_ALL) { |
3984 | ret = fixup_inode_link_counts(trans, wc.replay_dest, | 4003 | ret = fixup_inode_link_counts(trans, wc.replay_dest, |
3985 | path); | 4004 | path); |
3986 | BUG_ON(ret); | ||
3987 | } | 4005 | } |
3988 | 4006 | ||
3989 | key.offset = found_key.offset - 1; | 4007 | key.offset = found_key.offset - 1; |
@@ -3992,6 +4010,9 @@ again: | |||
3992 | free_extent_buffer(log->commit_root); | 4010 | free_extent_buffer(log->commit_root); |
3993 | kfree(log); | 4011 | kfree(log); |
3994 | 4012 | ||
4013 | if (ret) | ||
4014 | goto error; | ||
4015 | |||
3995 | if (found_key.offset == 0) | 4016 | if (found_key.offset == 0) |
3996 | break; | 4017 | break; |
3997 | } | 4018 | } |
@@ -4024,6 +4045,8 @@ again: | |||
4024 | 4045 | ||
4025 | return 0; | 4046 | return 0; |
4026 | error: | 4047 | error: |
4048 | if (wc.trans) | ||
4049 | btrfs_end_transaction(wc.trans, fs_info->tree_root); | ||
4027 | btrfs_free_path(path); | 4050 | btrfs_free_path(path); |
4028 | return ret; | 4051 | return ret; |
4029 | } | 4052 | } |