Skip to content

Commit b50c6e2

Browse files
author
Josef Bacik
committed
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 <[email protected]>
1 parent 3d7b5a2 commit b50c6e2

File tree

3 files changed

+59
-32
lines changed

3 files changed

+59
-32
lines changed

fs/btrfs/extent-tree.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5210,9 +5210,11 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root,
52105210
u64 bytenr, u64 num_bytes)
52115211
{
52125212
struct btrfs_block_group_cache *cache;
5213+
int ret;
52135214

52145215
cache = btrfs_lookup_block_group(root->fs_info, bytenr);
5215-
BUG_ON(!cache); /* Logic error */
5216+
if (!cache)
5217+
return -EINVAL;
52165218

52175219
/*
52185220
* 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,
52255227
pin_down_extent(root, cache, bytenr, num_bytes, 0);
52265228

52275229
/* 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);
52295231
btrfs_put_block_group(cache);
5230-
return 0;
5232+
return ret;
52315233
}
52325234

52335235
/**
@@ -6611,40 +6613,42 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
66116613
if (!caching_ctl) {
66126614
BUG_ON(!block_group_cache_done(block_group));
66136615
ret = btrfs_remove_free_space(block_group, start, num_bytes);
6614-
BUG_ON(ret); /* -ENOMEM */
6616+
if (ret)
6617+
goto out;
66156618
} else {
66166619
mutex_lock(&caching_ctl->mutex);
66176620

66186621
if (start >= caching_ctl->progress) {
66196622
ret = add_excluded_extent(root, start, num_bytes);
6620-
BUG_ON(ret); /* -ENOMEM */
66216623
} else if (start + num_bytes <= caching_ctl->progress) {
66226624
ret = btrfs_remove_free_space(block_group,
66236625
start, num_bytes);
6624-
BUG_ON(ret); /* -ENOMEM */
66256626
} else {
66266627
num_bytes = caching_ctl->progress - start;
66276628
ret = btrfs_remove_free_space(block_group,
66286629
start, num_bytes);
6629-
BUG_ON(ret); /* -ENOMEM */
6630+
if (ret)
6631+
goto out_lock;
66306632

66316633
start = caching_ctl->progress;
66326634
num_bytes = ins->objectid + ins->offset -
66336635
caching_ctl->progress;
66346636
ret = add_excluded_extent(root, start, num_bytes);
6635-
BUG_ON(ret); /* -ENOMEM */
66366637
}
6637-
6638+
out_lock:
66386639
mutex_unlock(&caching_ctl->mutex);
66396640
put_caching_control(caching_ctl);
6641+
if (ret)
6642+
goto out;
66406643
}
66416644

66426645
ret = btrfs_update_reserved_bytes(block_group, ins->offset,
66436646
RESERVE_ALLOC_NO_ACCOUNT);
66446647
BUG_ON(ret); /* logic error */
6645-
btrfs_put_block_group(block_group);
66466648
ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
66476649
0, owner, offset, ins, 1);
6650+
out:
6651+
btrfs_put_block_group(block_group);
66486652
return ret;
66496653
}
66506654

fs/btrfs/free-space-cache.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,8 @@ static noinline int remove_from_bitmap(struct btrfs_free_space_ctl *ctl,
15671567
search_bytes = ctl->unit;
15681568
search_bytes = min(search_bytes, end - search_start + 1);
15691569
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;
15711572

15721573
/* We may have found more bits than what we need */
15731574
search_bytes = min(search_bytes, *bytes);
@@ -1973,7 +1974,6 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
19731974
re_search = true;
19741975
goto again;
19751976
}
1976-
BUG_ON(ret); /* logic error */
19771977
out_lock:
19781978
spin_unlock(&ctl->tree_lock);
19791979
out:

fs/btrfs/tree-log.c

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,19 @@ static int process_one_buffer(struct btrfs_root *log,
277277
struct extent_buffer *eb,
278278
struct walk_control *wc, u64 gen)
279279
{
280+
int ret = 0;
281+
280282
if (wc->pin)
281-
btrfs_pin_extent_for_log_replay(log->fs_info->extent_root,
282-
eb->start, eb->len);
283+
ret = btrfs_pin_extent_for_log_replay(log->fs_info->extent_root,
284+
eb->start, eb->len);
283285

284-
if (btrfs_buffer_uptodate(eb, gen, 0)) {
286+
if (!ret && btrfs_buffer_uptodate(eb, gen, 0)) {
285287
if (wc->write)
286288
btrfs_write_tree_block(eb);
287289
if (wc->wait)
288290
btrfs_wait_tree_block_writeback(eb);
289291
}
290-
return 0;
292+
return ret;
291293
}
292294

293295
/*
@@ -623,7 +625,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
623625
ins.objectid, ins.offset,
624626
0, root->root_key.objectid,
625627
key->objectid, offset, 0);
626-
BUG_ON(ret);
628+
if (ret)
629+
goto out;
627630
} else {
628631
/*
629632
* insert the extent pointer in the extent
@@ -632,7 +635,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
632635
ret = btrfs_alloc_logged_file_extent(trans,
633636
root, root->root_key.objectid,
634637
key->objectid, offset, &ins);
635-
BUG_ON(ret);
638+
if (ret)
639+
goto out;
636640
}
637641
btrfs_release_path(path);
638642

@@ -1952,11 +1956,13 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
19521956
if (S_ISDIR(mode)) {
19531957
ret = replay_dir_deletes(wc->trans,
19541958
root, log, path, key.objectid, 0);
1955-
BUG_ON(ret);
1959+
if (ret)
1960+
break;
19561961
}
19571962
ret = overwrite_item(wc->trans, root, path,
19581963
eb, i, &key);
1959-
BUG_ON(ret);
1964+
if (ret)
1965+
break;
19601966

19611967
/* for regular files, make sure corresponding
19621968
* 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,
19651971
if (S_ISREG(mode)) {
19661972
ret = insert_orphan_item(wc->trans, root,
19671973
key.objectid);
1968-
BUG_ON(ret);
1974+
if (ret)
1975+
break;
19691976
}
19701977

19711978
ret = link_to_fixup_dir(wc->trans, root,
19721979
path, key.objectid);
1973-
BUG_ON(ret);
1980+
if (ret)
1981+
break;
19741982
}
19751983
if (wc->stage < LOG_WALK_REPLAY_ALL)
19761984
continue;
@@ -1979,28 +1987,35 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
19791987
if (key.type == BTRFS_XATTR_ITEM_KEY) {
19801988
ret = overwrite_item(wc->trans, root, path,
19811989
eb, i, &key);
1982-
BUG_ON(ret);
1990+
if (ret)
1991+
break;
19831992
} else if (key.type == BTRFS_INODE_REF_KEY) {
19841993
ret = add_inode_ref(wc->trans, root, log, path,
19851994
eb, i, &key);
1986-
BUG_ON(ret && ret != -ENOENT);
1995+
if (ret && ret != -ENOENT)
1996+
break;
1997+
ret = 0;
19871998
} else if (key.type == BTRFS_INODE_EXTREF_KEY) {
19881999
ret = add_inode_ref(wc->trans, root, log, path,
19892000
eb, i, &key);
1990-
BUG_ON(ret && ret != -ENOENT);
2001+
if (ret && ret != -ENOENT)
2002+
break;
2003+
ret = 0;
19912004
} else if (key.type == BTRFS_EXTENT_DATA_KEY) {
19922005
ret = replay_one_extent(wc->trans, root, path,
19932006
eb, i, &key);
1994-
BUG_ON(ret);
2007+
if (ret)
2008+
break;
19952009
} else if (key.type == BTRFS_DIR_ITEM_KEY ||
19962010
key.type == BTRFS_DIR_INDEX_KEY) {
19972011
ret = replay_one_dir_item(wc->trans, root, path,
19982012
eb, i, &key);
1999-
BUG_ON(ret);
2013+
if (ret)
2014+
break;
20002015
}
20012016
}
20022017
btrfs_free_path(path);
2003-
return 0;
2018+
return ret;
20042019
}
20052020

20062021
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,
20452060

20462061
if (*level == 1) {
20472062
ret = wc->process_func(root, next, wc, ptr_gen);
2048-
if (ret)
2063+
if (ret) {
2064+
free_extent_buffer(next);
20492065
return ret;
2066+
}
20502067

20512068
path->slots[*level]++;
20522069
if (wc->free) {
@@ -3970,6 +3987,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
39703987
wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);
39713988
if (IS_ERR(wc.replay_dest)) {
39723989
ret = PTR_ERR(wc.replay_dest);
3990+
free_extent_buffer(log->node);
3991+
free_extent_buffer(log->commit_root);
3992+
kfree(log);
39733993
btrfs_error(fs_info, ret, "Couldn't read target root "
39743994
"for tree log recovery.");
39753995
goto error;
@@ -3978,12 +3998,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
39783998
wc.replay_dest->log_root = log;
39793999
btrfs_record_root_in_trans(trans, wc.replay_dest);
39804000
ret = walk_log_tree(trans, log, &wc);
3981-
BUG_ON(ret);
39824001

3983-
if (wc.stage == LOG_WALK_REPLAY_ALL) {
4002+
if (!ret && wc.stage == LOG_WALK_REPLAY_ALL) {
39844003
ret = fixup_inode_link_counts(trans, wc.replay_dest,
39854004
path);
3986-
BUG_ON(ret);
39874005
}
39884006

39894007
key.offset = found_key.offset - 1;
@@ -3992,6 +4010,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
39924010
free_extent_buffer(log->commit_root);
39934011
kfree(log);
39944012

4013+
if (ret)
4014+
goto error;
4015+
39954016
if (found_key.offset == 0)
39964017
break;
39974018
}
@@ -4024,6 +4045,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
40244045

40254046
return 0;
40264047
error:
4048+
if (wc.trans)
4049+
btrfs_end_transaction(wc.trans, fs_info->tree_root);
40274050
btrfs_free_path(path);
40284051
return ret;
40294052
}

0 commit comments

Comments
 (0)