ZVOL: Unify zvol minors operations and improve error handling

Now zvol minors creation logic is passed thru spa_zvol_taskq, like it
is doing for remove/rename zvol minors functions. Appropriate
zvol minors creation functions are refactored:
- The zvol_create_minor()/zvol_minors_create_recursive() were removed.
- The single zvol_create_minors() is added instead.

Also, it become possible to collect zvol minors subtasks status, to
detect, if some zvol minor subtask is failed in the subtasks chain.
The appropriate message is reported to zfs_dbgmsg buffer in this case.

Sponsored-by: vStack, Inc.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com>
Signed-off-by: Fedor Uporov <fuporov.vstack@gmail.com>
Closes #17575
This commit is contained in:
Fedor Uporov 2025-08-06 17:10:52 +03:00 committed by GitHub
parent 0f8a1105ee
commit 0b6fd024a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 265 additions and 180 deletions

View File

@ -36,8 +36,7 @@
#define SPEC_MAXOFFSET_T ((1LL << ((NBBY * sizeof (daddr32_t)) + \
DEV_BSHIFT - 1)) - 1)
extern void zvol_create_minor(const char *);
extern void zvol_create_minors_recursive(const char *);
extern void zvol_create_minors(const char *);
extern void zvol_remove_minors(spa_t *, const char *, boolean_t);
extern void zvol_rename_minors(spa_t *, const char *, const char *, boolean_t);

View File

@ -108,7 +108,6 @@ zvol_state_t *zvol_find_by_name_hash(const char *name,
uint64_t hash, int mode);
int zvol_first_open(zvol_state_t *zv, boolean_t readonly);
uint64_t zvol_name_hash(const char *name);
void zvol_remove_minors_impl(const char *name);
void zvol_last_close(zvol_state_t *zv);
void zvol_insert(zvol_state_t *zv);
void zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off,
@ -132,7 +131,7 @@ void zv_request_task_free(zv_request_task_t *task);
* platform dependent functions exported to platform independent code
*/
void zvol_os_free(zvol_state_t *zv);
void zvol_os_rename_minor(zvol_state_t *zv, const char *newname);
int zvol_os_rename_minor(zvol_state_t *zv, const char *newname);
int zvol_os_create_minor(const char *name);
int zvol_os_update_volsize(zvol_state_t *zv, uint64_t volsize);
boolean_t zvol_os_is_zvol(const char *path);

View File

@ -1031,13 +1031,7 @@ kmem_cache_reap_active(void)
}
void
zvol_create_minor(const char *name)
{
(void) name;
}
void
zvol_create_minors_recursive(const char *name)
zvol_create_minors(const char *name)
{
(void) name;
}

View File

@ -1248,9 +1248,11 @@ zvol_os_is_zvol(const char *device)
return (device && strncmp(device, ZVOL_DIR, strlen(ZVOL_DIR)) == 0);
}
void
int
zvol_os_rename_minor(zvol_state_t *zv, const char *newname)
{
int error = 0;
ASSERT(RW_LOCK_HELD(&zvol_state_lock));
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
@ -1304,42 +1306,47 @@ zvol_os_rename_minor(zvol_state_t *zv, const char *newname)
args.mda_gid = GID_OPERATOR;
args.mda_mode = 0640;
args.mda_si_drv2 = zv;
if (make_dev_s(&args, &dev, "%s/%s", ZVOL_DRIVER, newname)
== 0) {
error = make_dev_s(&args, &dev, "%s/%s", ZVOL_DRIVER, newname);
if (error == 0) {
dev->si_iosize_max = maxphys;
zsd->zsd_cdev = dev;
}
}
strlcpy(zv->zv_name, newname, sizeof (zv->zv_name));
dataset_kstats_rename(&zv->zv_kstat, newname);
return (error);
}
/*
* Allocate memory for a new zvol_state_t and setup the required
* request queue and generic disk structures for the block device.
*/
static zvol_state_t *
zvol_alloc(const char *name, uint64_t volblocksize)
static int
zvol_alloc(const char *name, uint64_t volsize, uint64_t volblocksize,
zvol_state_t **zvp)
{
zvol_state_t *zv;
uint64_t volmode;
int error;
if (dsl_prop_get_integer(name,
zfs_prop_to_name(ZFS_PROP_VOLMODE), &volmode, NULL) != 0)
return (NULL);
error = dsl_prop_get_integer(name, zfs_prop_to_name(ZFS_PROP_VOLMODE),
&volmode, NULL);
if (error)
return (error);
if (volmode == ZFS_VOLMODE_DEFAULT)
volmode = zvol_volmode;
if (volmode == ZFS_VOLMODE_NONE)
return (NULL);
return (0);
zv = kmem_zalloc(sizeof (*zv), KM_SLEEP);
zv->zv_hash = zvol_name_hash(name);
mutex_init(&zv->zv_state_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&zv->zv_removing_cv, NULL, CV_DEFAULT, NULL);
zv->zv_zso = kmem_zalloc(sizeof (struct zvol_state_os), KM_SLEEP);
zv->zv_volmode = volmode;
zv->zv_volsize = volsize;
zv->zv_volblocksize = volblocksize;
if (zv->zv_volmode == ZFS_VOLMODE_GEOM) {
struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom;
@ -1370,10 +1377,11 @@ zvol_alloc(const char *name, uint64_t volblocksize)
args.mda_gid = GID_OPERATOR;
args.mda_mode = 0640;
args.mda_si_drv2 = zv;
if (make_dev_s(&args, &dev, "%s/%s", ZVOL_DRIVER, name) != 0) {
error = make_dev_s(&args, &dev, "%s/%s", ZVOL_DRIVER, name);
if (error) {
kmem_free(zv->zv_zso, sizeof (struct zvol_state_os));
kmem_free(zv, sizeof (zvol_state_t));
return (NULL);
return (error);
}
dev->si_iosize_max = maxphys;
@ -1384,7 +1392,8 @@ zvol_alloc(const char *name, uint64_t volblocksize)
rw_init(&zv->zv_suspend_lock, NULL, RW_DEFAULT, NULL);
zfs_rangelock_init(&zv->zv_rangelock, NULL, NULL);
return (zv);
*zvp = zv;
return (error);
}
/*
@ -1437,7 +1446,7 @@ zvol_os_free(zvol_state_t *zv)
int
zvol_os_create_minor(const char *name)
{
zvol_state_t *zv;
zvol_state_t *zv = NULL;
objset_t *os;
dmu_object_info_t *doi;
uint64_t volsize;
@ -1473,16 +1482,15 @@ zvol_os_create_minor(const char *name)
if (error)
goto out_dmu_objset_disown;
zv = zvol_alloc(name, doi->doi_data_block_size);
if (zv == NULL) {
error = SET_ERROR(EAGAIN);
error = zvol_alloc(name, volsize, doi->doi_data_block_size, &zv);
if (error || zv == NULL)
goto out_dmu_objset_disown;
}
zv->zv_hash = hash;
if (dmu_objset_is_snapshot(os) || !spa_writeable(dmu_objset_spa(os)))
zv->zv_flags |= ZVOL_RDONLY;
zv->zv_volsize = volsize;
zv->zv_objset = os;
ASSERT3P(zv->zv_kstat.dk_kstats, ==, NULL);
@ -1512,14 +1520,14 @@ zvol_os_create_minor(const char *name)
out_dmu_objset_disown:
dmu_objset_disown(os, B_TRUE, FTAG);
if (error == 0 && zv->zv_volmode == ZFS_VOLMODE_GEOM) {
if (error == 0 && zv && zv->zv_volmode == ZFS_VOLMODE_GEOM) {
g_error_provider(zv->zv_zso->zso_geom.zsg_provider, 0);
/* geom was locked inside zvol_alloc() function */
g_topology_unlock();
}
out_doi:
kmem_free(doi, sizeof (dmu_object_info_t));
if (error == 0 && zv->zv_volmode != ZFS_VOLMODE_NONE) {
if (error == 0 && zv) {
rw_enter(&zvol_state_lock, RW_WRITER);
zvol_insert(zv);
zvol_minors++;

View File

@ -1302,27 +1302,30 @@ zvol_alloc_blk_mq(zvol_state_t *zv, zvol_queue_limits_t *limits)
* Allocate memory for a new zvol_state_t and setup the required
* request queue and generic disk structures for the block device.
*/
static zvol_state_t *
zvol_alloc(dev_t dev, const char *name, uint64_t volblocksize)
static int
zvol_alloc(dev_t dev, const char *name, uint64_t volsize, uint64_t volblocksize,
zvol_state_t **zvp)
{
zvol_state_t *zv;
struct zvol_state_os *zso;
uint64_t volmode;
int ret;
if (dsl_prop_get_integer(name, "volmode", &volmode, NULL) != 0)
return (NULL);
ret = dsl_prop_get_integer(name, "volmode", &volmode, NULL);
if (ret)
return (ret);
if (volmode == ZFS_VOLMODE_DEFAULT)
volmode = zvol_volmode;
if (volmode == ZFS_VOLMODE_NONE)
return (NULL);
return (0);
zv = kmem_zalloc(sizeof (zvol_state_t), KM_SLEEP);
zso = kmem_zalloc(sizeof (struct zvol_state_os), KM_SLEEP);
zv->zv_zso = zso;
zv->zv_volmode = volmode;
zv->zv_volsize = volsize;
zv->zv_volblocksize = volblocksize;
list_link_init(&zv->zv_next);
@ -1396,12 +1399,13 @@ zvol_alloc(dev_t dev, const char *name, uint64_t volblocksize)
snprintf(zso->zvo_disk->disk_name, DISK_NAME_LEN, "%s%d",
ZVOL_DEV_NAME, (dev & MINORMASK));
return (zv);
*zvp = zv;
return (ret);
out_kmem:
kmem_free(zso, sizeof (struct zvol_state_os));
kmem_free(zv, sizeof (zvol_state_t));
return (NULL);
return (ret);
}
/*
@ -1562,7 +1566,7 @@ zvol_os_add_disk(struct gendisk *disk)
int
zvol_os_create_minor(const char *name)
{
zvol_state_t *zv;
zvol_state_t *zv = NULL;
objset_t *os;
dmu_object_info_t *doi;
uint64_t volsize;
@ -1611,18 +1615,16 @@ zvol_os_create_minor(const char *name)
if (error)
goto out_dmu_objset_disown;
zv = zvol_alloc(MKDEV(zvol_major, minor), name,
doi->doi_data_block_size);
if (zv == NULL) {
error = SET_ERROR(EAGAIN);
error = zvol_alloc(MKDEV(zvol_major, minor), name,
volsize, doi->doi_data_block_size, &zv);
if (error || zv == NULL)
goto out_dmu_objset_disown;
}
zv->zv_hash = hash;
if (dmu_objset_is_snapshot(os))
zv->zv_flags |= ZVOL_RDONLY;
zv->zv_volsize = volsize;
zv->zv_objset = os;
/* Default */
@ -1689,7 +1691,7 @@ out_doi:
* zvol_open()->zvol_first_open() and zvol_release()->zvol_last_close()
* directly as well.
*/
if (error == 0) {
if (error == 0 && zv) {
rw_enter(&zvol_state_lock, RW_WRITER);
zvol_insert(zv);
rw_exit(&zvol_state_lock);
@ -1701,7 +1703,7 @@ out_doi:
return (error);
}
void
int
zvol_os_rename_minor(zvol_state_t *zv, const char *newname)
{
int readonly = get_disk_ro(zv->zv_zso->zvo_disk);
@ -1728,6 +1730,8 @@ zvol_os_rename_minor(zvol_state_t *zv, const char *newname)
set_disk_ro(zv->zv_zso->zvo_disk, readonly);
dataset_kstats_rename(&zv->zv_kstat, newname);
return (0);
}
void

View File

@ -1370,7 +1370,7 @@ dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags,
6, ZFS_SPACE_CHECK_NORMAL);
if (rv == 0)
zvol_create_minor(name);
zvol_create_minors(name);
crfree(cr);

View File

@ -3831,11 +3831,11 @@ dmu_recv_end(dmu_recv_cookie_t *drc, void *owner)
nvlist_free(drc->drc_keynvl);
} else if (!drc->drc_heal) {
if (drc->drc_newfs) {
zvol_create_minor(drc->drc_tofs);
zvol_create_minors(drc->drc_tofs);
}
char *snapname = kmem_asprintf("%s@%s",
drc->drc_tofs, drc->drc_tosnap);
zvol_create_minor(snapname);
zvol_create_minors(snapname);
kmem_strfree(snapname);
}

View File

@ -866,7 +866,7 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp,
dsl_pool_rele(dp, FTAG);
/* create any zvols under this ds */
zvol_create_minors_recursive(dsname);
zvol_create_minors(dsname);
return (0);

View File

@ -2005,7 +2005,7 @@ dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors)
if (error == 0) {
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
pair = nvlist_next_nvpair(snaps, pair)) {
zvol_create_minor(nvpair_name(pair));
zvol_create_minors(nvpair_name(pair));
}
}
@ -3413,7 +3413,7 @@ dsl_dataset_clone(const char *clone, const char *origin)
6, ZFS_SPACE_CHECK_NORMAL);
if (rv == 0)
zvol_create_minor(clone);
zvol_create_minors(clone);
crfree(cr);

View File

@ -5905,7 +5905,7 @@ spa_open_common(const char *pool, spa_t **spapp, const void *tag,
}
if (firstopen)
zvol_create_minors_recursive(spa_name(spa));
zvol_create_minors(spa_name(spa));
*spapp = spa;
@ -6883,7 +6883,7 @@ spa_import(char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags)
mutex_exit(&spa_namespace_lock);
zvol_create_minors_recursive(pool);
zvol_create_minors(pool);
spa_import_os(spa);

View File

@ -1175,7 +1175,7 @@ zcp_eval(const char *poolname, const char *program, boolean_t sync,
for (nvpair_t *pair = nvlist_next_nvpair(runinfo.zri_new_zvols, NULL);
pair != NULL;
pair = nvlist_next_nvpair(runinfo.zri_new_zvols, pair)) {
zvol_create_minor(nvpair_name(pair));
zvol_create_minors(nvpair_name(pair));
}
fnvlist_free(runinfo.zri_new_zvols);

View File

@ -102,6 +102,7 @@ extern int zfs_bclone_wait_dirty;
zv_taskq_t zvol_taskqs;
typedef enum {
ZVOL_ASYNC_CREATE_MINORS,
ZVOL_ASYNC_REMOVE_MINORS,
ZVOL_ASYNC_RENAME_MINORS,
ZVOL_ASYNC_SET_SNAPDEV,
@ -110,10 +111,14 @@ typedef enum {
} zvol_async_op_t;
typedef struct {
zvol_async_op_t op;
char name1[MAXNAMELEN];
char name2[MAXNAMELEN];
uint64_t value;
zvol_async_op_t zt_op;
char zt_name1[MAXNAMELEN];
char zt_name2[MAXNAMELEN];
uint64_t zt_value;
uint32_t zt_total;
uint32_t zt_done;
int32_t zt_status;
int zt_error;
} zvol_task_t;
zv_request_task_t *
@ -1421,6 +1426,57 @@ zvol_create_minors_cb(const char *dsname, void *arg)
return (0);
}
static void
zvol_task_update_status(zvol_task_t *task, uint64_t total, uint64_t done,
int error)
{
task->zt_total += total;
task->zt_done += done;
if (task->zt_total != task->zt_done) {
task->zt_status = -1;
if (error)
task->zt_error = error;
}
}
static const char *
zvol_task_op_msg(zvol_async_op_t op)
{
switch (op) {
case ZVOL_ASYNC_CREATE_MINORS:
return ("create");
case ZVOL_ASYNC_REMOVE_MINORS:
return ("remove");
case ZVOL_ASYNC_RENAME_MINORS:
return ("rename");
case ZVOL_ASYNC_SET_SNAPDEV:
case ZVOL_ASYNC_SET_VOLMODE:
return ("set property");
default:
return ("unknown");
}
__builtin_unreachable();
return (NULL);
}
static void
zvol_task_report_status(zvol_task_t *task)
{
if (task->zt_status == 0)
return;
if (task->zt_error) {
dprintf("The %s minors zvol task was not ok, last error %d\n",
zvol_task_op_msg(task->zt_op), task->zt_error);
} else {
dprintf("The %s minors zvol task was not ok\n",
zvol_task_op_msg(task->zt_op));
}
}
/*
* Create minors for the specified dataset, including children and snapshots.
* Pay attention to the 'snapdev' property and iterate over the snapshots
@ -1438,14 +1494,27 @@ zvol_create_minors_cb(const char *dsname, void *arg)
* 'visible' (which also verifies that the parent is a zvol), and if so,
* a minor node for that snapshot is created.
*/
void
zvol_create_minors_recursive(const char *name)
static void
zvol_create_minors_impl(zvol_task_t *task)
{
const char *name = task->zt_name1;
list_t minors_list;
minors_job_t *job;
uint64_t snapdev;
int total = 0, done = 0, last_error, error;
if (zvol_inhibit_dev)
/*
* Note: the dsl_pool_config_lock must not be held.
* Minor node creation needs to obtain the zvol_state_lock.
* zvol_open() obtains the zvol_state_lock and then the dsl pool
* config lock. Therefore, we can't have the config lock now if
* we are going to wait for the zvol_state_lock, because it
* would be a lock order inversion which could lead to deadlock.
*/
if (zvol_inhibit_dev) {
return;
}
/*
* This is the list for prefetch jobs. Whenever we found a match
@ -1461,13 +1530,16 @@ zvol_create_minors_recursive(const char *name)
if (strchr(name, '@') != NULL) {
uint64_t snapdev;
int error = dsl_prop_get_integer(name, "snapdev",
&snapdev, NULL);
if (error == 0 && snapdev == ZFS_SNAPDEV_VISIBLE)
(void) zvol_os_create_minor(name);
error = dsl_prop_get_integer(name, "snapdev", &snapdev, NULL);
if (error == 0 && snapdev == ZFS_SNAPDEV_VISIBLE) {
error = zvol_os_create_minor(name);
if (error == 0) {
done++;
} else {
last_error = error;
}
total++;
}
} else {
fstrans_cookie_t cookie = spl_fstrans_mark();
(void) dmu_objset_find(name, zvol_create_minors_cb,
@ -1482,41 +1554,30 @@ zvol_create_minors_recursive(const char *name)
* sequentially.
*/
while ((job = list_remove_head(&minors_list)) != NULL) {
if (!job->error)
(void) zvol_os_create_minor(job->name);
if (!job->error) {
error = zvol_os_create_minor(job->name);
if (error == 0) {
done++;
} else {
last_error = error;
}
} else if (job->error == EINVAL) {
/*
* The objset, with the name requested by current job
* exist, but have the type different from zvol.
* Just ignore this sort of errors.
*/
done++;
} else {
last_error = job->error;
}
total++;
kmem_strfree(job->name);
kmem_free(job, sizeof (minors_job_t));
}
list_destroy(&minors_list);
}
void
zvol_create_minor(const char *name)
{
/*
* Note: the dsl_pool_config_lock must not be held.
* Minor node creation needs to obtain the zvol_state_lock.
* zvol_open() obtains the zvol_state_lock and then the dsl pool
* config lock. Therefore, we can't have the config lock now if
* we are going to wait for the zvol_state_lock, because it
* would be a lock order inversion which could lead to deadlock.
*/
if (zvol_inhibit_dev)
return;
if (strchr(name, '@') != NULL) {
uint64_t snapdev;
int error = dsl_prop_get_integer(name,
"snapdev", &snapdev, NULL);
if (error == 0 && snapdev == ZFS_SNAPDEV_VISIBLE)
(void) zvol_os_create_minor(name);
} else {
(void) zvol_os_create_minor(name);
}
zvol_task_update_status(task, total, done, last_error);
}
/*
@ -1564,10 +1625,11 @@ zvol_free_task(void *arg)
zvol_os_free(arg);
}
void
zvol_remove_minors_impl(const char *name)
static void
zvol_remove_minors_impl(zvol_task_t *task)
{
zvol_state_t *zv, *zv_next;
const char *name = task ? task->zt_name1 : NULL;
int namelen = ((name) ? strlen(name) : 0);
taskqid_t t;
list_t delay_list, free_list;
@ -1649,13 +1711,13 @@ zvol_remove_minors_impl(const char *name)
}
/* Remove minor for this specific volume only */
static void
static int
zvol_remove_minor_impl(const char *name)
{
zvol_state_t *zv = NULL, *zv_next;
if (zvol_inhibit_dev)
return;
return (0);
rw_enter(&zvol_state_lock, RW_WRITER);
@ -1671,7 +1733,7 @@ zvol_remove_minor_impl(const char *name)
if (zv == NULL) {
rw_exit(&zvol_state_lock);
return;
return (ENOENT);
}
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
@ -1685,7 +1747,7 @@ zvol_remove_minor_impl(const char *name)
mutex_exit(&zv->zv_state_lock);
rw_exit(&zvol_state_lock);
zvol_remove_minor_task(zv);
return;
return (0);
}
zvol_remove(zv);
@ -1695,16 +1757,20 @@ zvol_remove_minor_impl(const char *name)
rw_exit(&zvol_state_lock);
zvol_os_free(zv);
return (0);
}
/*
* Rename minors for specified dataset including children and snapshots.
*/
static void
zvol_rename_minors_impl(const char *oldname, const char *newname)
zvol_rename_minors_impl(zvol_task_t *task)
{
zvol_state_t *zv, *zv_next;
int oldnamelen;
const char *oldname = task->zt_name1;
const char *newname = task->zt_name2;
int total = 0, done = 0, last_error, error, oldnamelen;
if (zvol_inhibit_dev)
return;
@ -1719,24 +1785,31 @@ zvol_rename_minors_impl(const char *oldname, const char *newname)
mutex_enter(&zv->zv_state_lock);
if (strcmp(zv->zv_name, oldname) == 0) {
zvol_os_rename_minor(zv, newname);
error = zvol_os_rename_minor(zv, newname);
} else if (strncmp(zv->zv_name, oldname, oldnamelen) == 0 &&
(zv->zv_name[oldnamelen] == '/' ||
zv->zv_name[oldnamelen] == '@')) {
char *name = kmem_asprintf("%s%c%s", newname,
zv->zv_name[oldnamelen],
zv->zv_name + oldnamelen + 1);
zvol_os_rename_minor(zv, name);
error = zvol_os_rename_minor(zv, name);
kmem_strfree(name);
}
if (error) {
last_error = error;
} else {
done++;
}
total++;
mutex_exit(&zv->zv_state_lock);
}
rw_exit(&zvol_state_lock);
zvol_task_update_status(task, total, done, last_error);
}
typedef struct zvol_snapdev_cb_arg {
zvol_task_t *task;
uint64_t snapdev;
} zvol_snapdev_cb_arg_t;
@ -1744,26 +1817,31 @@ static int
zvol_set_snapdev_cb(const char *dsname, void *param)
{
zvol_snapdev_cb_arg_t *arg = param;
int error = 0;
if (strchr(dsname, '@') == NULL)
return (0);
switch (arg->snapdev) {
case ZFS_SNAPDEV_VISIBLE:
(void) zvol_os_create_minor(dsname);
error = zvol_os_create_minor(dsname);
break;
case ZFS_SNAPDEV_HIDDEN:
(void) zvol_remove_minor_impl(dsname);
error = zvol_remove_minor_impl(dsname);
break;
}
zvol_task_update_status(arg->task, 1, error == 0, error);
return (0);
}
static void
zvol_set_snapdev_impl(char *name, uint64_t snapdev)
zvol_set_snapdev_impl(zvol_task_t *task)
{
zvol_snapdev_cb_arg_t arg = {snapdev};
const char *name = task->zt_name1;
uint64_t snapdev = task->zt_value;
zvol_snapdev_cb_arg_t arg = {task, snapdev};
fstrans_cookie_t cookie = spl_fstrans_mark();
/*
* The zvol_set_snapdev_sync() sets snapdev appropriately
@ -1774,11 +1852,14 @@ zvol_set_snapdev_impl(char *name, uint64_t snapdev)
}
static void
zvol_set_volmode_impl(char *name, uint64_t volmode)
zvol_set_volmode_impl(zvol_task_t *task)
{
const char *name = task->zt_name1;
uint64_t volmode = task->zt_value;
fstrans_cookie_t cookie;
uint64_t old_volmode;
zvol_state_t *zv;
int error;
if (strchr(name, '@') != NULL)
return;
@ -1791,7 +1872,7 @@ zvol_set_volmode_impl(char *name, uint64_t volmode)
*/
zv = zvol_find_by_name(name, RW_NONE);
if (zv == NULL && volmode == ZFS_VOLMODE_NONE)
return;
return;
if (zv != NULL) {
old_volmode = zv->zv_volmode;
mutex_exit(&zv->zv_state_lock);
@ -1802,51 +1883,34 @@ zvol_set_volmode_impl(char *name, uint64_t volmode)
cookie = spl_fstrans_mark();
switch (volmode) {
case ZFS_VOLMODE_NONE:
(void) zvol_remove_minor_impl(name);
error = zvol_remove_minor_impl(name);
break;
case ZFS_VOLMODE_GEOM:
case ZFS_VOLMODE_DEV:
(void) zvol_remove_minor_impl(name);
(void) zvol_os_create_minor(name);
error = zvol_remove_minor_impl(name);
/*
* The remove minor function call above, might be not
* needed, if volmode was switched from 'none' value.
* Ignore error in this case.
*/
if (error == ENOENT)
error = 0;
else if (error)
break;
error = zvol_os_create_minor(name);
break;
case ZFS_VOLMODE_DEFAULT:
(void) zvol_remove_minor_impl(name);
error = zvol_remove_minor_impl(name);
if (zvol_volmode == ZFS_VOLMODE_NONE)
break;
else /* if zvol_volmode is invalid defaults to "geom" */
(void) zvol_os_create_minor(name);
error = zvol_os_create_minor(name);
break;
}
zvol_task_update_status(task, 1, error == 0, error);
spl_fstrans_unmark(cookie);
}
static zvol_task_t *
zvol_task_alloc(zvol_async_op_t op, const char *name1, const char *name2,
uint64_t value)
{
zvol_task_t *task;
/* Never allow tasks on hidden names. */
if (name1[0] == '$')
return (NULL);
task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP);
task->op = op;
task->value = value;
strlcpy(task->name1, name1, sizeof (task->name1));
if (name2 != NULL)
strlcpy(task->name2, name2, sizeof (task->name2));
return (task);
}
static void
zvol_task_free(zvol_task_t *task)
{
kmem_free(task, sizeof (zvol_task_t));
}
/*
* The worker thread function performed asynchronously.
*/
@ -1855,25 +1919,29 @@ zvol_task_cb(void *arg)
{
zvol_task_t *task = arg;
switch (task->op) {
switch (task->zt_op) {
case ZVOL_ASYNC_CREATE_MINORS:
zvol_create_minors_impl(task);
break;
case ZVOL_ASYNC_REMOVE_MINORS:
zvol_remove_minors_impl(task->name1);
zvol_remove_minors_impl(task);
break;
case ZVOL_ASYNC_RENAME_MINORS:
zvol_rename_minors_impl(task->name1, task->name2);
zvol_rename_minors_impl(task);
break;
case ZVOL_ASYNC_SET_SNAPDEV:
zvol_set_snapdev_impl(task->name1, task->value);
zvol_set_snapdev_impl(task);
break;
case ZVOL_ASYNC_SET_VOLMODE:
zvol_set_volmode_impl(task->name1, task->value);
zvol_set_volmode_impl(task);
break;
default:
VERIFY(0);
break;
}
zvol_task_free(task);
zvol_task_report_status(task);
kmem_free(task, sizeof (zvol_task_t));
}
typedef struct zvol_set_prop_int_arg {
@ -1918,23 +1986,17 @@ zvol_set_common_sync_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)
if (dsl_prop_get_int_ds(ds, prop_name, &prop) != 0)
return (0);
switch (zsda->zsda_prop) {
case ZFS_PROP_VOLMODE:
task = zvol_task_alloc(ZVOL_ASYNC_SET_VOLMODE, dsname,
NULL, prop);
break;
case ZFS_PROP_SNAPDEV:
task = zvol_task_alloc(ZVOL_ASYNC_SET_SNAPDEV, dsname,
NULL, prop);
break;
default:
task = NULL;
break;
}
if (task == NULL)
task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP);
if (zsda->zsda_prop == ZFS_PROP_VOLMODE) {
task->zt_op = ZVOL_ASYNC_SET_VOLMODE;
} else if (zsda->zsda_prop == ZFS_PROP_SNAPDEV) {
task->zt_op = ZVOL_ASYNC_SET_SNAPDEV;
} else {
kmem_free(task, sizeof (zvol_task_t));
return (0);
}
task->zt_value = prop;
strlcpy(task->zt_name1, dsname, sizeof (task->zt_name1));
(void) taskq_dispatch(dp->dp_spa->spa_zvol_taskq, zvol_task_cb,
task, TQ_SLEEP);
return (0);
@ -1987,16 +2049,35 @@ zvol_set_common(const char *ddname, zfs_prop_t prop, zprop_source_t source,
zvol_set_common_sync, &zsda, 0, ZFS_SPACE_CHECK_NONE));
}
void
zvol_create_minors(const char *name)
{
spa_t *spa;
zvol_task_t *task;
taskqid_t id;
if (spa_open(name, &spa, FTAG) != 0)
return;
task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP);
task->zt_op = ZVOL_ASYNC_CREATE_MINORS;
strlcpy(task->zt_name1, name, sizeof (task->zt_name1));
id = taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP);
if (id != TASKQID_INVALID)
taskq_wait_id(spa->spa_zvol_taskq, id);
spa_close(spa, FTAG);
}
void
zvol_remove_minors(spa_t *spa, const char *name, boolean_t async)
{
zvol_task_t *task;
taskqid_t id;
task = zvol_task_alloc(ZVOL_ASYNC_REMOVE_MINORS, name, NULL, ~0ULL);
if (task == NULL)
return;
task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP);
task->zt_op = ZVOL_ASYNC_REMOVE_MINORS;
strlcpy(task->zt_name1, name, sizeof (task->zt_name1));
id = taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP);
if ((async == B_FALSE) && (id != TASKQID_INVALID))
taskq_wait_id(spa->spa_zvol_taskq, id);
@ -2009,10 +2090,10 @@ zvol_rename_minors(spa_t *spa, const char *name1, const char *name2,
zvol_task_t *task;
taskqid_t id;
task = zvol_task_alloc(ZVOL_ASYNC_RENAME_MINORS, name1, name2, ~0ULL);
if (task == NULL)
return;
task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP);
task->zt_op = ZVOL_ASYNC_RENAME_MINORS;
strlcpy(task->zt_name1, name1, sizeof (task->zt_name1));
strlcpy(task->zt_name2, name2, sizeof (task->zt_name2));
id = taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP);
if ((async == B_FALSE) && (id != TASKQID_INVALID))
taskq_wait_id(spa->spa_zvol_taskq, id);