zvol: reject suspend attempts when zvol is shutting down

Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #17690
This commit is contained in:
Rob Norris 2025-09-04 04:13:09 +10:00 committed by Brian Behlendorf
parent 24baccb75e
commit 56e8ab4a3e
3 changed files with 22 additions and 7 deletions

View File

@ -53,7 +53,7 @@ extern int zvol_set_volsize(const char *, uint64_t);
extern int zvol_set_volthreading(const char *, boolean_t);
extern int zvol_set_common(const char *, zfs_prop_t, zprop_source_t, uint64_t);
extern int zvol_set_ro(const char *, boolean_t);
extern zvol_state_handle_t *zvol_suspend(const char *);
extern int zvol_suspend(const char *, zvol_state_handle_t **);
extern int zvol_resume(zvol_state_handle_t *);
extern void *zvol_tag(zvol_state_handle_t *);

View File

@ -4726,7 +4726,7 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
error = error ? error : resume_err;
}
zfs_vfs_rele(zfsvfs);
} else if ((zv = zvol_suspend(fsname)) != NULL) {
} else if (zvol_suspend(fsname, &zv) == 0) {
error = dsl_dataset_rollback(fsname, target, zvol_tag(zv),
outnvl);
zvol_resume(zv);
@ -5448,7 +5448,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, const char *origin,
}
error = error ? error : end_err;
zfs_vfs_rele(zfsvfs);
} else if ((zv = zvol_suspend(tofs)) != NULL) {
} else if (zvol_suspend(tofs, &zv) == 0) {
error = dmu_recv_end(&drc, zvol_tag(zv));
zvol_resume(zv);
} else {

View File

@ -1145,20 +1145,34 @@ zvol_tag(zvol_state_t *zv)
/*
* Suspend the zvol for recv and rollback.
*/
zvol_state_t *
zvol_suspend(const char *name)
int
zvol_suspend(const char *name, zvol_state_t **zvp)
{
zvol_state_t *zv;
zv = zvol_find_by_name(name, RW_WRITER);
if (zv == NULL)
return (NULL);
return (SET_ERROR(ENOENT));
/* block all I/O, release in zvol_resume. */
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
ASSERT(RW_WRITE_HELD(&zv->zv_suspend_lock));
/*
* If it's being removed, unlock and return error. It doesn't make any
* sense to try to suspend a zvol being removed, but being here also
* means that zvol_remove_minors_impl() is about to call zvol_remove()
* and then destroy the zvol_state_t, so returning a pointer to it for
* the caller to mess with would be a disaster anyway.
*/
if (zv->zv_flags & ZVOL_REMOVING) {
mutex_exit(&zv->zv_state_lock);
rw_exit(&zv->zv_suspend_lock);
/* NB: Returning EIO here to match zfsvfs_teardown() */
return (SET_ERROR(EIO));
}
atomic_inc(&zv->zv_suspend_ref);
if (zv->zv_open_count > 0)
@ -1171,7 +1185,8 @@ zvol_suspend(const char *name)
mutex_exit(&zv->zv_state_lock);
/* zv_suspend_lock is released in zvol_resume() */
return (zv);
*zvp = zv;
return (0);
}
int