Skip to content

Commit f44deb7

Browse files
josefbacikkdave
authored andcommitted
btrfs: hold a ref on the root->reloc_root
We previously were relying on root->reloc_root to be cleaned up by the drop snapshot, or the error handling. However if btrfs_drop_snapshot() failed it wouldn't drop the ref for the root. Also we sort of depend on the right thing to happen with moving reloc roots between lists and the fs root they belong to, which makes it hard to figure out who owns the reference. Fix this by explicitly holding a reference on the reloc root for roo->reloc_root. This means that we hold two references on reloc roots, one for whichever reloc_roots list it's attached to, and the root->reloc_root we're on. This makes it easier to reason out who owns a reference on the root, and when it needs to be dropped. Reviewed-by: Qu Wenruo <[email protected]> Signed-off-by: Josef Bacik <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent f28de8d commit f44deb7

File tree

1 file changed

+48
-10
lines changed

1 file changed

+48
-10
lines changed

fs/btrfs/relocation.c

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,7 @@ static void __del_reloc_root(struct btrfs_root *root)
13681368
struct rb_node *rb_node;
13691369
struct mapping_node *node = NULL;
13701370
struct reloc_control *rc = fs_info->reloc_ctl;
1371+
bool put_ref = false;
13711372

13721373
if (rc && root->node) {
13731374
spin_lock(&rc->reloc_root_tree.lock);
@@ -1383,9 +1384,22 @@ static void __del_reloc_root(struct btrfs_root *root)
13831384
BUG_ON((struct btrfs_root *)node->data != root);
13841385
}
13851386

1387+
/*
1388+
* We only put the reloc root here if it's on the list. There's a lot
1389+
* of places where the pattern is to splice the rc->reloc_roots, process
1390+
* the reloc roots, and then add the reloc root back onto
1391+
* rc->reloc_roots. If we call __del_reloc_root while it's off of the
1392+
* list we don't want the reference being dropped, because the guy
1393+
* messing with the list is in charge of the reference.
1394+
*/
13861395
spin_lock(&fs_info->trans_lock);
1387-
list_del_init(&root->root_list);
1396+
if (!list_empty(&root->root_list)) {
1397+
put_ref = true;
1398+
list_del_init(&root->root_list);
1399+
}
13881400
spin_unlock(&fs_info->trans_lock);
1401+
if (put_ref)
1402+
btrfs_put_root(root);
13891403
kfree(node);
13901404
}
13911405

@@ -1500,6 +1514,9 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
15001514
/*
15011515
* create reloc tree for a given fs tree. reloc tree is just a
15021516
* snapshot of the fs tree with special root objectid.
1517+
*
1518+
* The reloc_root comes out of here with two references, one for
1519+
* root->reloc_root, and another for being on the rc->reloc_roots list.
15031520
*/
15041521
int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
15051522
struct btrfs_root *root)
@@ -1539,7 +1556,7 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
15391556

15401557
ret = __add_reloc_root(reloc_root);
15411558
BUG_ON(ret < 0);
1542-
root->reloc_root = reloc_root;
1559+
root->reloc_root = btrfs_grab_root(reloc_root);
15431560
return 0;
15441561
}
15451562

@@ -1560,6 +1577,13 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
15601577
reloc_root = root->reloc_root;
15611578
root_item = &reloc_root->root_item;
15621579

1580+
/*
1581+
* We are probably ok here, but __del_reloc_root() will drop its ref of
1582+
* the root. We have the ref for root->reloc_root, but just in case
1583+
* hold it while we update the reloc root.
1584+
*/
1585+
btrfs_grab_root(reloc_root);
1586+
15631587
/* root->reloc_root will stay until current relocation finished */
15641588
if (fs_info->reloc_ctl->merge_reloc_tree &&
15651589
btrfs_root_refs(root_item) == 0) {
@@ -1581,7 +1605,7 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
15811605
ret = btrfs_update_root(trans, fs_info->tree_root,
15821606
&reloc_root->root_key, root_item);
15831607
BUG_ON(ret);
1584-
1608+
btrfs_put_root(reloc_root);
15851609
out:
15861610
return 0;
15871611
}
@@ -2281,18 +2305,28 @@ static int clean_dirty_subvols(struct reloc_control *rc)
22812305
*/
22822306
smp_wmb();
22832307
clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
2284-
22852308
if (reloc_root) {
2309+
/*
2310+
* btrfs_drop_snapshot drops our ref we hold for
2311+
* ->reloc_root. If it fails however we must
2312+
* drop the ref ourselves.
2313+
*/
22862314
ret2 = btrfs_drop_snapshot(reloc_root, 0, 1);
2287-
if (ret2 < 0 && !ret)
2288-
ret = ret2;
2315+
if (ret2 < 0) {
2316+
btrfs_put_root(reloc_root);
2317+
if (!ret)
2318+
ret = ret2;
2319+
}
22892320
}
22902321
btrfs_put_root(root);
22912322
} else {
22922323
/* Orphan reloc tree, just clean it up */
22932324
ret2 = btrfs_drop_snapshot(root, 0, 1);
2294-
if (ret2 < 0 && !ret)
2295-
ret = ret2;
2325+
if (ret2 < 0) {
2326+
btrfs_put_root(root);
2327+
if (!ret)
2328+
ret = ret2;
2329+
}
22962330
}
22972331
}
22982332
return ret;
@@ -4510,7 +4544,7 @@ int btrfs_recover_relocation(struct btrfs_root *root)
45104544

45114545
err = __add_reloc_root(reloc_root);
45124546
BUG_ON(err < 0); /* -ENOMEM or logic error */
4513-
fs_root->reloc_root = reloc_root;
4547+
fs_root->reloc_root = btrfs_grab_root(reloc_root);
45144548
btrfs_put_root(fs_root);
45154549
}
45164550

@@ -4698,6 +4732,10 @@ void btrfs_reloc_pre_snapshot(struct btrfs_pending_snapshot *pending,
46984732
/*
46994733
* called after snapshot is created. migrate block reservation
47004734
* and create reloc root for the newly created snapshot
4735+
*
4736+
* This is similar to btrfs_init_reloc_root(), we come out of here with two
4737+
* references held on the reloc_root, one for root->reloc_root and one for
4738+
* rc->reloc_roots.
47014739
*/
47024740
int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
47034741
struct btrfs_pending_snapshot *pending)
@@ -4730,7 +4768,7 @@ int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
47304768

47314769
ret = __add_reloc_root(reloc_root);
47324770
BUG_ON(ret < 0);
4733-
new_root->reloc_root = reloc_root;
4771+
new_root->reloc_root = btrfs_grab_root(reloc_root);
47344772

47354773
if (rc->create_reloc_tree)
47364774
ret = clone_backref_node(trans, rc, root, reloc_root);

0 commit comments

Comments
 (0)