Suspend/resume zvol for recv and rollback

When doing recv and rollback, dsl_dataset_clone_swap_sync_impl will be
called to swap out the ds_objset and do dmu_objset_evict on the old one.
However, currently zv->zv_objset will not be swapped out accordingly, so
if anyone currently holds a fd on the zvol, we risk hitting a use-after-free.

We fix this by introducing the suspend and resume mechanism of zsb to
zv.  Before recv or rollback, we use zvol_suspend to block all access to
zv_objset and shut it down. After the recv or rollback, we use zvol_resume
to swap in zv_objset with the new ds_objset and unblock the access.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Chunwei Chen <david.chen@osnexus.com>
Closes #4866 
Closes #5609
This commit is contained in:
Chunwei Chen
2017-01-19 13:56:36 -08:00
committed by Brian Behlendorf
parent 76fe529b39
commit 040dab9939
4 changed files with 168 additions and 42 deletions
+8
View File
@@ -3641,6 +3641,7 @@ static int
zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl)
{
zfs_sb_t *zsb;
zvol_state_t *zv;
int error;
if (get_zfs_sb(fsname, &zsb) == 0) {
@@ -3653,6 +3654,9 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl)
error = error ? error : resume_err;
}
deactivate_super(zsb->z_sb);
} else if ((zv = zvol_suspend(fsname)) != NULL) {
error = dsl_dataset_rollback(fsname, zvol_tag(zv), outnvl);
zvol_resume(zv);
} else {
error = dsl_dataset_rollback(fsname, NULL, outnvl);
}
@@ -4240,6 +4244,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
if (error == 0) {
zfs_sb_t *zsb = NULL;
zvol_state_t *zv = NULL;
if (get_zfs_sb(tofs, &zsb) == 0) {
/* online recv */
@@ -4255,6 +4260,9 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
error = zfs_resume_fs(zsb, tofs);
error = error ? error : end_err;
deactivate_super(zsb->z_sb);
} else if ((zv = zvol_suspend(tofs)) != NULL) {
error = dmu_recv_end(&drc, zvol_tag(zv));
zvol_resume(zv);
} else {
error = dmu_recv_end(&drc, NULL);
}