Commit c974c464 authored by Wang Shilong's avatar Wang Shilong Committed by Chris Mason

Btrfs: fix an oops when doing balance relocation

I hit an oops when inserting reloc root into @reloc_root_tree(it can be
easily triggered when forcing cow for relocation root)

[  866.494539]  [<ffffffffa0499579>] btrfs_init_reloc_root+0x79/0xb0 [btrfs]
[  866.495321]  [<ffffffffa044c240>] record_root_in_trans+0xb0/0x110 [btrfs]
[  866.496109]  [<ffffffffa044d758>] btrfs_record_root_in_trans+0x48/0x80 [btrfs]
[  866.496908]  [<ffffffffa0494da8>] select_reloc_root+0xa8/0x210 [btrfs]
[  866.497703]  [<ffffffffa0495c8a>] do_relocation+0x16a/0x540 [btrfs]

This is because reloc root inserted into @reloc_root_tree is not within one
transaction,reloc root may be cowed and root block bytenr will be reused then
oops happens.We should update reloc root in @reloc_root_tree when cow reloc
root node, fix it.
Signed-off-by: default avatarWang Shilong <wangsl.fnst@cn.fujitsu.com>
Reviewed-by: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent 639eefc8
...@@ -1264,10 +1264,10 @@ static int __must_check __add_reloc_root(struct btrfs_root *root) ...@@ -1264,10 +1264,10 @@ static int __must_check __add_reloc_root(struct btrfs_root *root)
} }
/* /*
* helper to update/delete the 'address of tree root -> reloc tree' * helper to delete the 'address of tree root -> reloc tree'
* mapping * mapping
*/ */
static int __update_reloc_root(struct btrfs_root *root, int del) static void __del_reloc_root(struct btrfs_root *root)
{ {
struct rb_node *rb_node; struct rb_node *rb_node;
struct mapping_node *node = NULL; struct mapping_node *node = NULL;
...@@ -1275,7 +1275,7 @@ static int __update_reloc_root(struct btrfs_root *root, int del) ...@@ -1275,7 +1275,7 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
spin_lock(&rc->reloc_root_tree.lock); spin_lock(&rc->reloc_root_tree.lock);
rb_node = tree_search(&rc->reloc_root_tree.rb_root, rb_node = tree_search(&rc->reloc_root_tree.rb_root,
root->commit_root->start); root->node->start);
if (rb_node) { if (rb_node) {
node = rb_entry(rb_node, struct mapping_node, rb_node); node = rb_entry(rb_node, struct mapping_node, rb_node);
rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root); rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root);
...@@ -1283,23 +1283,45 @@ static int __update_reloc_root(struct btrfs_root *root, int del) ...@@ -1283,23 +1283,45 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
spin_unlock(&rc->reloc_root_tree.lock); spin_unlock(&rc->reloc_root_tree.lock);
if (!node) if (!node)
return 0; return;
BUG_ON((struct btrfs_root *)node->data != root); BUG_ON((struct btrfs_root *)node->data != root);
if (!del) { spin_lock(&root->fs_info->trans_lock);
spin_lock(&rc->reloc_root_tree.lock); list_del_init(&root->root_list);
node->bytenr = root->node->start; spin_unlock(&root->fs_info->trans_lock);
rb_node = tree_insert(&rc->reloc_root_tree.rb_root, kfree(node);
node->bytenr, &node->rb_node); }
spin_unlock(&rc->reloc_root_tree.lock);
if (rb_node) /*
backref_tree_panic(rb_node, -EEXIST, node->bytenr); * helper to update the 'address of tree root -> reloc tree'
} else { * mapping
spin_lock(&root->fs_info->trans_lock); */
list_del_init(&root->root_list); static int __update_reloc_root(struct btrfs_root *root, u64 new_bytenr)
spin_unlock(&root->fs_info->trans_lock); {
kfree(node); struct rb_node *rb_node;
struct mapping_node *node = NULL;
struct reloc_control *rc = root->fs_info->reloc_ctl;
spin_lock(&rc->reloc_root_tree.lock);
rb_node = tree_search(&rc->reloc_root_tree.rb_root,
root->node->start);
if (rb_node) {
node = rb_entry(rb_node, struct mapping_node, rb_node);
rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root);
} }
spin_unlock(&rc->reloc_root_tree.lock);
if (!node)
return 0;
BUG_ON((struct btrfs_root *)node->data != root);
spin_lock(&rc->reloc_root_tree.lock);
node->bytenr = new_bytenr;
rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
node->bytenr, &node->rb_node);
spin_unlock(&rc->reloc_root_tree.lock);
if (rb_node)
backref_tree_panic(rb_node, -EEXIST, node->bytenr);
return 0; return 0;
} }
...@@ -1420,7 +1442,6 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans, ...@@ -1420,7 +1442,6 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
{ {
struct btrfs_root *reloc_root; struct btrfs_root *reloc_root;
struct btrfs_root_item *root_item; struct btrfs_root_item *root_item;
int del = 0;
int ret; int ret;
if (!root->reloc_root) if (!root->reloc_root)
...@@ -1432,11 +1453,9 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans, ...@@ -1432,11 +1453,9 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
if (root->fs_info->reloc_ctl->merge_reloc_tree && if (root->fs_info->reloc_ctl->merge_reloc_tree &&
btrfs_root_refs(root_item) == 0) { btrfs_root_refs(root_item) == 0) {
root->reloc_root = NULL; root->reloc_root = NULL;
del = 1; __del_reloc_root(reloc_root);
} }
__update_reloc_root(reloc_root, del);
if (reloc_root->commit_root != reloc_root->node) { if (reloc_root->commit_root != reloc_root->node) {
btrfs_set_root_node(root_item, reloc_root->node); btrfs_set_root_node(root_item, reloc_root->node);
free_extent_buffer(reloc_root->commit_root); free_extent_buffer(reloc_root->commit_root);
...@@ -2287,7 +2306,7 @@ void free_reloc_roots(struct list_head *list) ...@@ -2287,7 +2306,7 @@ void free_reloc_roots(struct list_head *list)
while (!list_empty(list)) { while (!list_empty(list)) {
reloc_root = list_entry(list->next, struct btrfs_root, reloc_root = list_entry(list->next, struct btrfs_root,
root_list); root_list);
__update_reloc_root(reloc_root, 1); __del_reloc_root(reloc_root);
free_extent_buffer(reloc_root->node); free_extent_buffer(reloc_root->node);
free_extent_buffer(reloc_root->commit_root); free_extent_buffer(reloc_root->commit_root);
kfree(reloc_root); kfree(reloc_root);
...@@ -2332,7 +2351,7 @@ int merge_reloc_roots(struct reloc_control *rc) ...@@ -2332,7 +2351,7 @@ int merge_reloc_roots(struct reloc_control *rc)
ret = merge_reloc_root(rc, root); ret = merge_reloc_root(rc, root);
if (ret) { if (ret) {
__update_reloc_root(reloc_root, 1); __del_reloc_root(reloc_root);
free_extent_buffer(reloc_root->node); free_extent_buffer(reloc_root->node);
free_extent_buffer(reloc_root->commit_root); free_extent_buffer(reloc_root->commit_root);
kfree(reloc_root); kfree(reloc_root);
...@@ -4522,6 +4541,11 @@ int btrfs_reloc_cow_block(struct btrfs_trans_handle *trans, ...@@ -4522,6 +4541,11 @@ int btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
BUG_ON(rc->stage == UPDATE_DATA_PTRS && BUG_ON(rc->stage == UPDATE_DATA_PTRS &&
root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID); root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID);
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
if (buf == root->node)
__update_reloc_root(root, cow->start);
}
level = btrfs_header_level(buf); level = btrfs_header_level(buf);
if (btrfs_header_generation(buf) <= if (btrfs_header_generation(buf) <=
btrfs_root_last_snapshot(&root->root_item)) btrfs_root_last_snapshot(&root->root_item))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment