Btrfs: fix oops caused by the space balance and dead roots

When doing space balance and subvolume destroy at the same time, we met
the following oops:

kernel BUG at fs/btrfs/relocation.c:2247!
RIP: 0010: [<ffffffffa04cec16>] prepare_to_merge+0x154/0x1f0 [btrfs]
Call Trace:
[<ffffffffa04b5ab7>] relocate_block_group+0x466/0x4e6 [btrfs]
[<ffffffffa04b5c7a>] btrfs_relocate_block_group+0x143/0x275 [btrfs]
[<ffffffffa0495c56>] btrfs_relocate_chunk.isra.27+0x5c/0x5a2 [btrfs]
[<ffffffffa0459871>] ? btrfs_item_key_to_cpu+0x15/0x31 [btrfs]
[<ffffffffa048b46a>] ? btrfs_get_token_64+0x7e/0xcd [btrfs]
[<ffffffffa04a3467>] ? btrfs_tree_read_unlock_blocking+0xb2/0xb7 [btrfs]
[<ffffffffa049907d>] btrfs_balance+0x9c7/0xb6f [btrfs]
[<ffffffffa049ef84>] btrfs_ioctl_balance+0x234/0x2ac [btrfs]
[<ffffffffa04a1e8e>] btrfs_ioctl+0xd87/0x1ef9 [btrfs]
[<ffffffff81122f53>] ? path_openat+0x234/0x4db
[<ffffffff813c3b78>] ? __do_page_fault+0x31d/0x391
[<ffffffff810f8ab6>] ? vma_link+0x74/0x94
[<ffffffff811250f5>] vfs_ioctl+0x1d/0x39
[<ffffffff811258c8>] do_vfs_ioctl+0x32d/0x3e2
[<ffffffff811259d4>] SyS_ioctl+0x57/0x83
[<ffffffff813c3bfa>] ? do_page_fault+0xe/0x10
[<ffffffff813c73c2>] system_call_fastpath+0x16/0x1b

It is because we returned the error number if the reference of the root was 0
when doing space relocation. It was not right here, because though the root
was dead(refs == 0), but the space it held still need be relocated, or we
could not remove the block group. So in this case, we should return the root
no matter it is dead or not.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>

authored by Miao Xie and committed by Chris Mason c00869f1 14927d95

Changed files
+17 -7
fs
+5 -4
fs/btrfs/disk-io.c
··· 1561 1561 return ret; 1562 1562 } 1563 1563 1564 - struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info, 1565 - struct btrfs_key *location) 1564 + struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info, 1565 + struct btrfs_key *location, 1566 + bool check_ref) 1566 1567 { 1567 1568 struct btrfs_root *root; 1568 1569 int ret; ··· 1587 1586 again: 1588 1587 root = btrfs_lookup_fs_root(fs_info, location->objectid); 1589 1588 if (root) { 1590 - if (btrfs_root_refs(&root->root_item) == 0) 1589 + if (check_ref && btrfs_root_refs(&root->root_item) == 0) 1591 1590 return ERR_PTR(-ENOENT); 1592 1591 return root; 1593 1592 } ··· 1596 1595 if (IS_ERR(root)) 1597 1596 return root; 1598 1597 1599 - if (btrfs_root_refs(&root->root_item) == 0) { 1598 + if (check_ref && btrfs_root_refs(&root->root_item) == 0) { 1600 1599 ret = -ENOENT; 1601 1600 goto fail; 1602 1601 }
+11 -2
fs/btrfs/disk-io.h
··· 68 68 int btrfs_init_fs_root(struct btrfs_root *root); 69 69 int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info, 70 70 struct btrfs_root *root); 71 - struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info, 72 - struct btrfs_key *location); 71 + 72 + struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info, 73 + struct btrfs_key *key, 74 + bool check_ref); 75 + static inline struct btrfs_root * 76 + btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info, 77 + struct btrfs_key *location) 78 + { 79 + return btrfs_get_fs_root(fs_info, location, true); 80 + } 81 + 73 82 int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info); 74 83 void btrfs_btree_balance_dirty(struct btrfs_root *root); 75 84 void btrfs_btree_balance_dirty_nodelay(struct btrfs_root *root);
+1 -1
fs/btrfs/relocation.c
··· 588 588 else 589 589 key.offset = (u64)-1; 590 590 591 - return btrfs_read_fs_root_no_name(fs_info, &key); 591 + return btrfs_get_fs_root(fs_info, &key, false); 592 592 } 593 593 594 594 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0