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 | |
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')
-rw-r--r-- | fs/btrfs/extent-tree.c | 24 | ||||
-rw-r--r-- | fs/btrfs/free-space-cache.c | 4 | ||||
-rw-r--r-- | fs/btrfs/tree-log.c | 63 |
3 files changed, 59 insertions, 32 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index ef4ce2c026d6..b0a3fab98713 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -5210,9 +5210,11 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root, | |||
5210 | u64 bytenr, u64 num_bytes) | 5210 | u64 bytenr, u64 num_bytes) |
5211 | { | 5211 | { |
5212 | struct btrfs_block_group_cache *cache; | 5212 | struct btrfs_block_group_cache *cache; |
5213 | int ret; | ||
5213 | 5214 | ||
5214 | cache = btrfs_lookup_block_group(root->fs_info, bytenr); | 5215 | cache = btrfs_lookup_block_group(root->fs_info, bytenr); |
5215 | BUG_ON(!cache); /* Logic error */ | 5216 | if (!cache) |
5217 | return -EINVAL; | ||
5216 | 5218 | ||
5217 | /* | 5219 | /* |
5218 | * pull in the free space cache (if any) so that our pin | 5220 | * pull in the free space cache (if any) so that our pin |
@@ -5225,9 +5227,9 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root, | |||
5225 | pin_down_extent(root, cache, bytenr, num_bytes, 0); | 5227 | pin_down_extent(root, cache, bytenr, num_bytes, 0); |
5226 | 5228 | ||
5227 | /* remove us from the free space cache (if we're there at all) */ | 5229 | /* remove us from the free space cache (if we're there at all) */ |
5228 | btrfs_remove_free_space(cache, bytenr, num_bytes); | 5230 | ret = btrfs_remove_free_space(cache, bytenr, num_bytes); |
5229 | btrfs_put_block_group(cache); | 5231 | btrfs_put_block_group(cache); |
5230 | return 0; | 5232 | return ret; |
5231 | } | 5233 | } |
5232 | 5234 | ||
5233 | /** | 5235 | /** |
@@ -6611,40 +6613,42 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans, | |||
6611 | if (!caching_ctl) { | 6613 | if (!caching_ctl) { |
6612 | BUG_ON(!block_group_cache_done(block_group)); | 6614 | BUG_ON(!block_group_cache_done(block_group)); |
6613 | ret = btrfs_remove_free_space(block_group, start, num_bytes); | 6615 | ret = btrfs_remove_free_space(block_group, start, num_bytes); |
6614 | BUG_ON(ret); /* -ENOMEM */ | 6616 | if (ret) |
6617 | goto out; | ||
6615 | } else { | 6618 | } else { |
6616 | mutex_lock(&caching_ctl->mutex); | 6619 | mutex_lock(&caching_ctl->mutex); |
6617 | 6620 | ||
6618 | if (start >= caching_ctl->progress) { | 6621 | if (start >= caching_ctl->progress) { |
6619 | ret = add_excluded_extent(root, start, num_bytes); | 6622 | ret = add_excluded_extent(root, start, num_bytes); |
6620 | BUG_ON(ret); /* -ENOMEM */ | ||
6621 | } else if (start + num_bytes <= caching_ctl->progress) { | 6623 | } else if (start + num_bytes <= caching_ctl->progress) { |
6622 | ret = btrfs_remove_free_space(block_group, | 6624 | ret = btrfs_remove_free_space(block_group, |
6623 | start, num_bytes); | 6625 | start, num_bytes); |
6624 | BUG_ON(ret); /* -ENOMEM */ | ||
6625 | } else { | 6626 | } else { |
6626 | num_bytes = caching_ctl->progress - start; | 6627 | num_bytes = caching_ctl->progress - start; |
6627 | ret = btrfs_remove_free_space(block_group, | 6628 | ret = btrfs_remove_free_space(block_group, |
6628 | start, num_bytes); | 6629 | start, num_bytes); |
6629 | BUG_ON(ret); /* -ENOMEM */ | 6630 | if (ret) |
6631 | goto out_lock; | ||
6630 | 6632 | ||
6631 | start = caching_ctl->progress; | 6633 | start = caching_ctl->progress; |
6632 | num_bytes = ins->objectid + ins->offset - | 6634 | num_bytes = ins->objectid + ins->offset - |
6633 | caching_ctl->progress; | 6635 | caching_ctl->progress; |
6634 | ret = add_excluded_extent(root, start, num_bytes); | 6636 | ret = add_excluded_extent(root, start, num_bytes); |
6635 | BUG_ON(ret); /* -ENOMEM */ | ||
6636 | } | 6637 | } |
6637 | 6638 | out_lock: | |
6638 | mutex_unlock(&caching_ctl->mutex); | 6639 | mutex_unlock(&caching_ctl->mutex); |
6639 | put_caching_control(caching_ctl); | 6640 | put_caching_control(caching_ctl); |
6641 | if (ret) | ||
6642 | goto out; | ||
6640 | } | 6643 | } |
6641 | 6644 | ||
6642 | ret = btrfs_update_reserved_bytes(block_group, ins->offset, | 6645 | ret = btrfs_update_reserved_bytes(block_group, ins->offset, |
6643 | RESERVE_ALLOC_NO_ACCOUNT); | 6646 | RESERVE_ALLOC_NO_ACCOUNT); |
6644 | BUG_ON(ret); /* logic error */ | 6647 | BUG_ON(ret); /* logic error */ |
6645 | btrfs_put_block_group(block_group); | ||
6646 | ret = alloc_reserved_file_extent(trans, root, 0, root_objectid, | 6648 | ret = alloc_reserved_file_extent(trans, root, 0, root_objectid, |
6647 | 0, owner, offset, ins, 1); | 6649 | 0, owner, offset, ins, 1); |
6650 | out: | ||
6651 | btrfs_put_block_group(block_group); | ||
6648 | return ret; | 6652 | return ret; |
6649 | } | 6653 | } |
6650 | 6654 | ||
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index fa1a8140bfb5..37b2b89a28f6 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c | |||
@@ -1567,7 +1567,8 @@ again: | |||
1567 | search_bytes = ctl->unit; | 1567 | search_bytes = ctl->unit; |
1568 | search_bytes = min(search_bytes, end - search_start + 1); | 1568 | search_bytes = min(search_bytes, end - search_start + 1); |
1569 | ret = search_bitmap(ctl, bitmap_info, &search_start, &search_bytes); | 1569 | ret = search_bitmap(ctl, bitmap_info, &search_start, &search_bytes); |
1570 | BUG_ON(ret < 0 || search_start != *offset); | 1570 | if (ret < 0 || search_start != *offset) |
1571 | return -EINVAL; | ||
1571 | 1572 | ||
1572 | /* We may have found more bits than what we need */ | 1573 | /* We may have found more bits than what we need */ |
1573 | search_bytes = min(search_bytes, *bytes); | 1574 | search_bytes = min(search_bytes, *bytes); |
@@ -1973,7 +1974,6 @@ again: | |||
1973 | re_search = true; | 1974 | re_search = true; |
1974 | goto again; | 1975 | goto again; |
1975 | } | 1976 | } |
1976 | BUG_ON(ret); /* logic error */ | ||
1977 | out_lock: | 1977 | out_lock: |
1978 | spin_unlock(&ctl->tree_lock); | 1978 | spin_unlock(&ctl->tree_lock); |
1979 | out: | 1979 | out: |
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 | } |