Skip to content

Commit 18f687d

Browse files
Wang Shilongmasoncl
authored andcommitted
Btrfs: fix protection between send and root deletion
We should gurantee that parent and clone roots can not be destroyed during send, for this we have two ideas. 1.by holding @subvol_sem, this might be a nightmare, because it will block all subvolumes deletion for a long time. 2.Miao pointed out we can reuse @send_in_progress, that mean we will skip snapshot deletion if root sending is in progress. Here we adopt the second approach since it won't block other subvolumes deletion for a long time. Besides in btrfs_clean_one_deleted_snapshot(), we only check first root , if this root is involved in send, we return directly rather than continue to check.There are several reasons about it: 1.this case happen seldomly. 2.after sending,cleaner thread can continue to drop that root. 3.make code simple Cc: David Sterba <dsterba@suse.cz> Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com> Reviewed-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
1 parent 896c14f commit 18f687d

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

fs/btrfs/send.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4753,6 +4753,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
47534753
u64 *clone_sources_tmp = NULL;
47544754
int clone_sources_to_rollback = 0;
47554755
int sort_clone_roots = 0;
4756+
int index;
47564757

47574758
if (!capable(CAP_SYS_ADMIN))
47584759
return -EPERM;
@@ -4893,8 +4894,12 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
48934894
key.objectid = clone_sources_tmp[i];
48944895
key.type = BTRFS_ROOT_ITEM_KEY;
48954896
key.offset = (u64)-1;
4897+
4898+
index = srcu_read_lock(&fs_info->subvol_srcu);
4899+
48964900
clone_root = btrfs_read_fs_root_no_name(fs_info, &key);
48974901
if (IS_ERR(clone_root)) {
4902+
srcu_read_unlock(&fs_info->subvol_srcu, index);
48984903
ret = PTR_ERR(clone_root);
48994904
goto out;
49004905
}
@@ -4903,10 +4908,13 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
49034908
clone_root->send_in_progress++;
49044909
if (!btrfs_root_readonly(clone_root)) {
49054910
spin_unlock(&clone_root->root_item_lock);
4911+
srcu_read_unlock(&fs_info->subvol_srcu, index);
49064912
ret = -EPERM;
49074913
goto out;
49084914
}
49094915
spin_unlock(&clone_root->root_item_lock);
4916+
srcu_read_unlock(&fs_info->subvol_srcu, index);
4917+
49104918
sctx->clone_roots[i].root = clone_root;
49114919
}
49124920
vfree(clone_sources_tmp);
@@ -4917,19 +4925,27 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
49174925
key.objectid = arg->parent_root;
49184926
key.type = BTRFS_ROOT_ITEM_KEY;
49194927
key.offset = (u64)-1;
4928+
4929+
index = srcu_read_lock(&fs_info->subvol_srcu);
4930+
49204931
sctx->parent_root = btrfs_read_fs_root_no_name(fs_info, &key);
49214932
if (IS_ERR(sctx->parent_root)) {
4933+
srcu_read_unlock(&fs_info->subvol_srcu, index);
49224934
ret = PTR_ERR(sctx->parent_root);
49234935
goto out;
49244936
}
4937+
49254938
spin_lock(&sctx->parent_root->root_item_lock);
49264939
sctx->parent_root->send_in_progress++;
49274940
if (!btrfs_root_readonly(sctx->parent_root)) {
49284941
spin_unlock(&sctx->parent_root->root_item_lock);
4942+
srcu_read_unlock(&fs_info->subvol_srcu, index);
49294943
ret = -EPERM;
49304944
goto out;
49314945
}
49324946
spin_unlock(&sctx->parent_root->root_item_lock);
4947+
4948+
srcu_read_unlock(&fs_info->subvol_srcu, index);
49334949
}
49344950

49354951
/*

fs/btrfs/transaction.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,19 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root)
19721972
}
19731973
root = list_first_entry(&fs_info->dead_roots,
19741974
struct btrfs_root, root_list);
1975+
/*
1976+
* Make sure root is not involved in send,
1977+
* if we fail with first root, we return
1978+
* directly rather than continue.
1979+
*/
1980+
spin_lock(&root->root_item_lock);
1981+
if (root->send_in_progress) {
1982+
spin_unlock(&fs_info->trans_lock);
1983+
spin_unlock(&root->root_item_lock);
1984+
return 0;
1985+
}
1986+
spin_unlock(&root->root_item_lock);
1987+
19751988
list_del_init(&root->root_list);
19761989
spin_unlock(&fs_info->trans_lock);
19771990

0 commit comments

Comments
 (0)