Commit 18f687d5 authored by Wang Shilong's avatar Wang Shilong Committed by Chris Mason

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: default avatarWang Shilong <wangsl.fnst@cn.fujitsu.com>
Reviewed-by: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarJosef Bacik <jbacik@fb.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent 896c14f9
...@@ -4753,6 +4753,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) ...@@ -4753,6 +4753,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
u64 *clone_sources_tmp = NULL; u64 *clone_sources_tmp = NULL;
int clone_sources_to_rollback = 0; int clone_sources_to_rollback = 0;
int sort_clone_roots = 0; int sort_clone_roots = 0;
int index;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
...@@ -4893,8 +4894,12 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) ...@@ -4893,8 +4894,12 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
key.objectid = clone_sources_tmp[i]; key.objectid = clone_sources_tmp[i];
key.type = BTRFS_ROOT_ITEM_KEY; key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1; key.offset = (u64)-1;
index = srcu_read_lock(&fs_info->subvol_srcu);
clone_root = btrfs_read_fs_root_no_name(fs_info, &key); clone_root = btrfs_read_fs_root_no_name(fs_info, &key);
if (IS_ERR(clone_root)) { if (IS_ERR(clone_root)) {
srcu_read_unlock(&fs_info->subvol_srcu, index);
ret = PTR_ERR(clone_root); ret = PTR_ERR(clone_root);
goto out; goto out;
} }
...@@ -4903,10 +4908,13 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) ...@@ -4903,10 +4908,13 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
clone_root->send_in_progress++; clone_root->send_in_progress++;
if (!btrfs_root_readonly(clone_root)) { if (!btrfs_root_readonly(clone_root)) {
spin_unlock(&clone_root->root_item_lock); spin_unlock(&clone_root->root_item_lock);
srcu_read_unlock(&fs_info->subvol_srcu, index);
ret = -EPERM; ret = -EPERM;
goto out; goto out;
} }
spin_unlock(&clone_root->root_item_lock); spin_unlock(&clone_root->root_item_lock);
srcu_read_unlock(&fs_info->subvol_srcu, index);
sctx->clone_roots[i].root = clone_root; sctx->clone_roots[i].root = clone_root;
} }
vfree(clone_sources_tmp); vfree(clone_sources_tmp);
...@@ -4917,19 +4925,27 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) ...@@ -4917,19 +4925,27 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
key.objectid = arg->parent_root; key.objectid = arg->parent_root;
key.type = BTRFS_ROOT_ITEM_KEY; key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1; key.offset = (u64)-1;
index = srcu_read_lock(&fs_info->subvol_srcu);
sctx->parent_root = btrfs_read_fs_root_no_name(fs_info, &key); sctx->parent_root = btrfs_read_fs_root_no_name(fs_info, &key);
if (IS_ERR(sctx->parent_root)) { if (IS_ERR(sctx->parent_root)) {
srcu_read_unlock(&fs_info->subvol_srcu, index);
ret = PTR_ERR(sctx->parent_root); ret = PTR_ERR(sctx->parent_root);
goto out; goto out;
} }
spin_lock(&sctx->parent_root->root_item_lock); spin_lock(&sctx->parent_root->root_item_lock);
sctx->parent_root->send_in_progress++; sctx->parent_root->send_in_progress++;
if (!btrfs_root_readonly(sctx->parent_root)) { if (!btrfs_root_readonly(sctx->parent_root)) {
spin_unlock(&sctx->parent_root->root_item_lock); spin_unlock(&sctx->parent_root->root_item_lock);
srcu_read_unlock(&fs_info->subvol_srcu, index);
ret = -EPERM; ret = -EPERM;
goto out; goto out;
} }
spin_unlock(&sctx->parent_root->root_item_lock); spin_unlock(&sctx->parent_root->root_item_lock);
srcu_read_unlock(&fs_info->subvol_srcu, index);
} }
/* /*
......
...@@ -1972,6 +1972,19 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root) ...@@ -1972,6 +1972,19 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root)
} }
root = list_first_entry(&fs_info->dead_roots, root = list_first_entry(&fs_info->dead_roots,
struct btrfs_root, root_list); struct btrfs_root, root_list);
/*
* Make sure root is not involved in send,
* if we fail with first root, we return
* directly rather than continue.
*/
spin_lock(&root->root_item_lock);
if (root->send_in_progress) {
spin_unlock(&fs_info->trans_lock);
spin_unlock(&root->root_item_lock);
return 0;
}
spin_unlock(&root->root_item_lock);
list_del_init(&root->root_list); list_del_init(&root->root_list);
spin_unlock(&fs_info->trans_lock); spin_unlock(&fs_info->trans_lock);
......
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