Skip to content

Commit

Permalink
Btrfs: deal with free space cache errors while replaying log
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Josef Bacik committed May 6, 2013
1 parent 3d7b5a2 commit b50c6e2
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 32 deletions.
24 changes: 14 additions & 10 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -5210,9 +5210,11 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root,
u64 bytenr, u64 num_bytes)
{
struct btrfs_block_group_cache *cache;
int ret;

cache = btrfs_lookup_block_group(root->fs_info, bytenr);
BUG_ON(!cache); /* Logic error */
if (!cache)
return -EINVAL;

/*
* pull in the free space cache (if any) so that our pin
Expand All @@ -5225,9 +5227,9 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root,
pin_down_extent(root, cache, bytenr, num_bytes, 0);

/* remove us from the free space cache (if we're there at all) */
btrfs_remove_free_space(cache, bytenr, num_bytes);
ret = btrfs_remove_free_space(cache, bytenr, num_bytes);
btrfs_put_block_group(cache);
return 0;
return ret;
}

/**
Expand Down Expand Up @@ -6611,40 +6613,42 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
if (!caching_ctl) {
BUG_ON(!block_group_cache_done(block_group));
ret = btrfs_remove_free_space(block_group, start, num_bytes);
BUG_ON(ret); /* -ENOMEM */
if (ret)
goto out;
} else {
mutex_lock(&caching_ctl->mutex);

if (start >= caching_ctl->progress) {
ret = add_excluded_extent(root, start, num_bytes);
BUG_ON(ret); /* -ENOMEM */
} else if (start + num_bytes <= caching_ctl->progress) {
ret = btrfs_remove_free_space(block_group,
start, num_bytes);
BUG_ON(ret); /* -ENOMEM */
} else {
num_bytes = caching_ctl->progress - start;
ret = btrfs_remove_free_space(block_group,
start, num_bytes);
BUG_ON(ret); /* -ENOMEM */
if (ret)
goto out_lock;

start = caching_ctl->progress;
num_bytes = ins->objectid + ins->offset -
caching_ctl->progress;
ret = add_excluded_extent(root, start, num_bytes);
BUG_ON(ret); /* -ENOMEM */
}

out_lock:
mutex_unlock(&caching_ctl->mutex);
put_caching_control(caching_ctl);
if (ret)
goto out;
}

ret = btrfs_update_reserved_bytes(block_group, ins->offset,
RESERVE_ALLOC_NO_ACCOUNT);
BUG_ON(ret); /* logic error */
btrfs_put_block_group(block_group);
ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
0, owner, offset, ins, 1);
out:
btrfs_put_block_group(block_group);
return ret;
}

Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/free-space-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -1567,7 +1567,8 @@ static noinline int remove_from_bitmap(struct btrfs_free_space_ctl *ctl,
search_bytes = ctl->unit;
search_bytes = min(search_bytes, end - search_start + 1);
ret = search_bitmap(ctl, bitmap_info, &search_start, &search_bytes);
BUG_ON(ret < 0 || search_start != *offset);
if (ret < 0 || search_start != *offset)
return -EINVAL;

/* We may have found more bits than what we need */
search_bytes = min(search_bytes, *bytes);
Expand Down Expand Up @@ -1973,7 +1974,6 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
re_search = true;
goto again;
}
BUG_ON(ret); /* logic error */
out_lock:
spin_unlock(&ctl->tree_lock);
out:
Expand Down
63 changes: 43 additions & 20 deletions fs/btrfs/tree-log.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,17 +277,19 @@ static int process_one_buffer(struct btrfs_root *log,
struct extent_buffer *eb,
struct walk_control *wc, u64 gen)
{
int ret = 0;

if (wc->pin)
btrfs_pin_extent_for_log_replay(log->fs_info->extent_root,
eb->start, eb->len);
ret = btrfs_pin_extent_for_log_replay(log->fs_info->extent_root,
eb->start, eb->len);

if (btrfs_buffer_uptodate(eb, gen, 0)) {
if (!ret && btrfs_buffer_uptodate(eb, gen, 0)) {
if (wc->write)
btrfs_write_tree_block(eb);
if (wc->wait)
btrfs_wait_tree_block_writeback(eb);
}
return 0;
return ret;
}

/*
Expand Down Expand Up @@ -623,7 +625,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
ins.objectid, ins.offset,
0, root->root_key.objectid,
key->objectid, offset, 0);
BUG_ON(ret);
if (ret)
goto out;
} else {
/*
* insert the extent pointer in the extent
Expand All @@ -632,7 +635,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
ret = btrfs_alloc_logged_file_extent(trans,
root, root->root_key.objectid,
key->objectid, offset, &ins);
BUG_ON(ret);
if (ret)
goto out;
}
btrfs_release_path(path);

Expand Down Expand Up @@ -1952,11 +1956,13 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
if (S_ISDIR(mode)) {
ret = replay_dir_deletes(wc->trans,
root, log, path, key.objectid, 0);
BUG_ON(ret);
if (ret)
break;
}
ret = overwrite_item(wc->trans, root, path,
eb, i, &key);
BUG_ON(ret);
if (ret)
break;

/* for regular files, make sure corresponding
* orhpan item exist. extents past the new EOF
Expand All @@ -1965,12 +1971,14 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
if (S_ISREG(mode)) {
ret = insert_orphan_item(wc->trans, root,
key.objectid);
BUG_ON(ret);
if (ret)
break;
}

ret = link_to_fixup_dir(wc->trans, root,
path, key.objectid);
BUG_ON(ret);
if (ret)
break;
}
if (wc->stage < LOG_WALK_REPLAY_ALL)
continue;
Expand All @@ -1979,28 +1987,35 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
if (key.type == BTRFS_XATTR_ITEM_KEY) {
ret = overwrite_item(wc->trans, root, path,
eb, i, &key);
BUG_ON(ret);
if (ret)
break;
} else if (key.type == BTRFS_INODE_REF_KEY) {
ret = add_inode_ref(wc->trans, root, log, path,
eb, i, &key);
BUG_ON(ret && ret != -ENOENT);
if (ret && ret != -ENOENT)
break;
ret = 0;
} else if (key.type == BTRFS_INODE_EXTREF_KEY) {
ret = add_inode_ref(wc->trans, root, log, path,
eb, i, &key);
BUG_ON(ret && ret != -ENOENT);
if (ret && ret != -ENOENT)
break;
ret = 0;
} else if (key.type == BTRFS_EXTENT_DATA_KEY) {
ret = replay_one_extent(wc->trans, root, path,
eb, i, &key);
BUG_ON(ret);
if (ret)
break;
} else if (key.type == BTRFS_DIR_ITEM_KEY ||
key.type == BTRFS_DIR_INDEX_KEY) {
ret = replay_one_dir_item(wc->trans, root, path,
eb, i, &key);
BUG_ON(ret);
if (ret)
break;
}
}
btrfs_free_path(path);
return 0;
return ret;
}

static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
Expand Down Expand Up @@ -2045,8 +2060,10 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,

if (*level == 1) {
ret = wc->process_func(root, next, wc, ptr_gen);
if (ret)
if (ret) {
free_extent_buffer(next);
return ret;
}

path->slots[*level]++;
if (wc->free) {
Expand Down Expand Up @@ -3970,6 +3987,9 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);
if (IS_ERR(wc.replay_dest)) {
ret = PTR_ERR(wc.replay_dest);
free_extent_buffer(log->node);
free_extent_buffer(log->commit_root);
kfree(log);
btrfs_error(fs_info, ret, "Couldn't read target root "
"for tree log recovery.");
goto error;
Expand All @@ -3978,12 +3998,10 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
wc.replay_dest->log_root = log;
btrfs_record_root_in_trans(trans, wc.replay_dest);
ret = walk_log_tree(trans, log, &wc);
BUG_ON(ret);

if (wc.stage == LOG_WALK_REPLAY_ALL) {
if (!ret && wc.stage == LOG_WALK_REPLAY_ALL) {
ret = fixup_inode_link_counts(trans, wc.replay_dest,
path);
BUG_ON(ret);
}

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

if (ret)
goto error;

if (found_key.offset == 0)
break;
}
Expand Down Expand Up @@ -4024,6 +4045,8 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)

return 0;
error:
if (wc.trans)
btrfs_end_transaction(wc.trans, fs_info->tree_root);
btrfs_free_path(path);
return ret;
}
Expand Down

0 comments on commit b50c6e2

Please sign in to comment.