mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-01-14 17:22:05 +03:00
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:
parent
0f8a1105ee
commit
0b6fd024a7
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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++;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user