aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'module/zfs')
-rw-r--r--module/zfs/dmu_objset.c19
-rw-r--r--module/zfs/dmu_recv.c25
-rw-r--r--module/zfs/dsl_crypt.c2
-rw-r--r--module/zfs/dsl_dataset.c8
-rw-r--r--module/zfs/spa.c4
-rw-r--r--module/zfs/zcp.c12
-rw-r--r--module/zfs/zcp_synctask.c10
-rw-r--r--module/zfs/zfs_ioctl.c5
-rw-r--r--module/zfs/zvol.c78
9 files changed, 107 insertions, 56 deletions
diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c
index 2a9464e2a..4db8e581c 100644
--- a/module/zfs/dmu_objset.c
+++ b/module/zfs/dmu_objset.c
@@ -1263,7 +1263,6 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx)
}
spa_history_log_internal_ds(ds, "create", tx, " ");
- zvol_create_minors(spa, doca->doca_name, B_TRUE);
dsl_dataset_rele_flags(ds, DS_HOLD_FLAG_DECRYPT, FTAG);
dsl_dir_rele(pdd, FTAG);
@@ -1293,9 +1292,13 @@ dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags,
*/
doca.doca_dcp = (dcp != NULL) ? dcp : &tmp_dcp;
- return (dsl_sync_task(name,
+ int rv = dsl_sync_task(name,
dmu_objset_create_check, dmu_objset_create_sync, &doca,
- 6, ZFS_SPACE_CHECK_NORMAL));
+ 6, ZFS_SPACE_CHECK_NORMAL);
+
+ if (rv == 0)
+ zvol_create_minor(name);
+ return (rv);
}
typedef struct dmu_objset_clone_arg {
@@ -1376,7 +1379,6 @@ dmu_objset_clone_sync(void *arg, dmu_tx_t *tx)
dsl_dataset_name(origin, namebuf);
spa_history_log_internal_ds(ds, "clone", tx,
"origin=%s (%llu)", namebuf, (u_longlong_t)origin->ds_object);
- zvol_create_minors(dp->dp_spa, doca->doca_clone, B_TRUE);
dsl_dataset_rele(ds, FTAG);
dsl_dataset_rele(origin, FTAG);
dsl_dir_rele(pdd, FTAG);
@@ -1391,9 +1393,14 @@ dmu_objset_clone(const char *clone, const char *origin)
doca.doca_origin = origin;
doca.doca_cred = CRED();
- return (dsl_sync_task(clone,
+ int rv = dsl_sync_task(clone,
dmu_objset_clone_check, dmu_objset_clone_sync, &doca,
- 6, ZFS_SPACE_CHECK_NORMAL));
+ 6, ZFS_SPACE_CHECK_NORMAL);
+
+ if (rv == 0)
+ zvol_create_minor(clone);
+
+ return (rv);
}
int
diff --git a/module/zfs/dmu_recv.c b/module/zfs/dmu_recv.c
index 46a42197b..97a3c7cee 100644
--- a/module/zfs/dmu_recv.c
+++ b/module/zfs/dmu_recv.c
@@ -2859,6 +2859,12 @@ out:
if (drc->drc_next_rrd != NULL)
kmem_free(drc->drc_next_rrd, sizeof (*drc->drc_next_rrd));
+ /*
+ * The objset will be invalidated by dmu_recv_end() when we do
+ * dsl_dataset_clone_swap_sync_impl().
+ */
+ drc->drc_os = NULL;
+
kmem_free(rwa, sizeof (*rwa));
nvlist_free(drc->drc_begin_nvl);
if ((drc->drc_featureflags & DMU_BACKUP_FEATURE_DEDUP) &&
@@ -3085,8 +3091,6 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx)
&drc->drc_ivset_guid, tx));
}
- zvol_create_minors(dp->dp_spa, drc->drc_tofs, B_TRUE);
-
/*
* Release the hold from dmu_recv_begin. This must be done before
* we return to open context, so that when we free the dataset's dnode
@@ -3195,9 +3199,20 @@ dmu_recv_end(dmu_recv_cookie_t *drc, void *owner)
if (error != 0) {
dmu_recv_cleanup_ds(drc);
nvlist_free(drc->drc_keynvl);
- } else if (drc->drc_guid_to_ds_map != NULL) {
- (void) add_ds_to_guidmap(drc->drc_tofs, drc->drc_guid_to_ds_map,
- drc->drc_newsnapobj, drc->drc_raw);
+ } else {
+ if (drc->drc_newfs) {
+ zvol_create_minor(drc->drc_tofs);
+ }
+ char *snapname = kmem_asprintf("%s@%s",
+ drc->drc_tofs, drc->drc_tosnap);
+ zvol_create_minor(snapname);
+ kmem_strfree(snapname);
+
+ if (drc->drc_guid_to_ds_map != NULL) {
+ (void) add_ds_to_guidmap(drc->drc_tofs,
+ drc->drc_guid_to_ds_map,
+ drc->drc_newsnapobj, drc->drc_raw);
+ }
}
return (error);
}
diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c
index 762933dd0..8e7f15ad2 100644
--- a/module/zfs/dsl_crypt.c
+++ b/module/zfs/dsl_crypt.c
@@ -854,7 +854,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(dp->dp_spa, dsname, B_TRUE);
+ zvol_create_minors_recursive(dsname);
return (0);
diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c
index ea2b60076..126d1b688 100644
--- a/module/zfs/dsl_dataset.c
+++ b/module/zfs/dsl_dataset.c
@@ -1869,7 +1869,6 @@ dsl_dataset_snapshot_sync(void *arg, dmu_tx_t *tx)
dsl_props_set_sync_impl(ds->ds_prev,
ZPROP_SRC_LOCAL, ddsa->ddsa_props, tx);
}
- zvol_create_minors(dp->dp_spa, nvpair_name(pair), B_TRUE);
dsl_dataset_rele(ds, FTAG);
}
}
@@ -1944,6 +1943,13 @@ dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors)
fnvlist_free(suspended);
}
+ if (error == 0) {
+ for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(snaps, pair)) {
+ zvol_create_minor(nvpair_name(pair));
+ }
+ }
+
return (error);
}
diff --git a/module/zfs/spa.c b/module/zfs/spa.c
index 39b59d5ce..c12b20270 100644
--- a/module/zfs/spa.c
+++ b/module/zfs/spa.c
@@ -5114,7 +5114,7 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t *nvpolicy,
}
if (firstopen)
- zvol_create_minors(spa, spa_name(spa), B_TRUE);
+ zvol_create_minors_recursive(spa_name(spa));
*spapp = spa;
@@ -6083,7 +6083,7 @@ spa_import(char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags)
mutex_exit(&spa_namespace_lock);
- zvol_create_minors(spa, pool, B_TRUE);
+ zvol_create_minors_recursive(pool);
return (0);
}
diff --git a/module/zfs/zcp.c b/module/zfs/zcp.c
index 870ba0366..e8cf96d49 100644
--- a/module/zfs/zcp.c
+++ b/module/zfs/zcp.c
@@ -100,6 +100,7 @@
#include <sys/zcp_iter.h>
#include <sys/zcp_prop.h>
#include <sys/zcp_global.h>
+#include <sys/zvol.h>
#ifndef KM_NORMALPRI
#define KM_NORMALPRI 0
@@ -1155,6 +1156,7 @@ zcp_eval(const char *poolname, const char *program, boolean_t sync,
runinfo.zri_space_used = 0;
runinfo.zri_curinstrs = 0;
runinfo.zri_maxinstrs = instrlimit;
+ runinfo.zri_new_zvols = fnvlist_alloc();
if (sync) {
err = dsl_sync_task_sig(poolname, NULL, zcp_eval_sync,
@@ -1166,6 +1168,16 @@ zcp_eval(const char *poolname, const char *program, boolean_t sync,
}
lua_close(state);
+ /*
+ * Create device minor nodes for any new zvols.
+ */
+ 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));
+ }
+ fnvlist_free(runinfo.zri_new_zvols);
+
return (runinfo.zri_result);
}
diff --git a/module/zfs/zcp_synctask.c b/module/zfs/zcp_synctask.c
index 3b6015f24..22fec6f3f 100644
--- a/module/zfs/zcp_synctask.c
+++ b/module/zfs/zcp_synctask.c
@@ -276,6 +276,16 @@ zcp_synctask_snapshot(lua_State *state, boolean_t sync, nvlist_t *err_details)
err = zcp_sync_task(state, dsl_dataset_snapshot_check,
dsl_dataset_snapshot_sync, &ddsa, sync, dsname);
+ if (err == 0) {
+ /*
+ * We may need to create a new device minor node for this
+ * dataset (if it is a zvol and the "snapdev" property is set).
+ * Save it in the nvlist so that it can be processed in open
+ * context.
+ */
+ fnvlist_add_boolean(ri->zri_new_zvols, dsname);
+ }
+
zcp_deregister_cleanup(state, zch);
fnvlist_free(ddsa.ddsa_snaps);
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index fb8034f70..b2517d84f 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -3312,8 +3312,9 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
/*
* Volumes will return EBUSY and cannot be destroyed
- * until all asynchronous minor handling has completed.
- * Wait for the spa_zvol_taskq to drain then retry.
+ * until all asynchronous minor handling (e.g. from
+ * setting the volmode property) has completed. Wait for
+ * the spa_zvol_taskq to drain then retry.
*/
error2 = dsl_destroy_head(fsname);
while ((error2 == EBUSY) && (type == DMU_OST_ZVOL)) {
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c
index 5006b6af8..bbda652ee 100644
--- a/module/zfs/zvol.c
+++ b/module/zfs/zvol.c
@@ -97,7 +97,6 @@ krwlock_t zvol_state_lock;
const zvol_platform_ops_t *ops;
typedef enum {
- ZVOL_ASYNC_CREATE_MINORS,
ZVOL_ASYNC_REMOVE_MINORS,
ZVOL_ASYNC_RENAME_MINORS,
ZVOL_ASYNC_SET_SNAPDEV,
@@ -1098,17 +1097,14 @@ 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.
*/
-static int
-zvol_create_minors_impl(const char *name)
+void
+zvol_create_minors_recursive(const char *name)
{
- int error = 0;
- fstrans_cookie_t cookie;
- char *atp, *parent;
list_t minors_list;
minors_job_t *job;
if (zvol_inhibit_dev)
- return (0);
+ return;
/*
* This is the list for prefetch jobs. Whenever we found a match
@@ -1122,26 +1118,22 @@ zvol_create_minors_impl(const char *name)
list_create(&minors_list, sizeof (minors_job_t),
offsetof(minors_job_t, link));
- parent = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- (void) strlcpy(parent, name, MAXPATHLEN);
- if ((atp = strrchr(parent, '@')) != NULL) {
+ if (strchr(name, '@') != NULL) {
uint64_t snapdev;
- *atp = '\0';
- error = dsl_prop_get_integer(parent, "snapdev",
+ int error = dsl_prop_get_integer(name, "snapdev",
&snapdev, NULL);
if (error == 0 && snapdev == ZFS_SNAPDEV_VISIBLE)
- error = ops->zv_create_minor(name);
+ (void) ops->zv_create_minor(name);
} else {
- cookie = spl_fstrans_mark();
- error = dmu_objset_find(parent, zvol_create_minors_cb,
+ fstrans_cookie_t cookie = spl_fstrans_mark();
+ (void) dmu_objset_find(name, zvol_create_minors_cb,
&minors_list, DS_FIND_CHILDREN);
spl_fstrans_unmark(cookie);
}
- kmem_free(parent, MAXPATHLEN);
taskq_wait_outstanding(system_taskq, 0);
/*
@@ -1151,14 +1143,40 @@ zvol_create_minors_impl(const char *name)
while ((job = list_head(&minors_list)) != NULL) {
list_remove(&minors_list, job);
if (!job->error)
- ops->zv_create_minor(job->name);
+ (void) ops->zv_create_minor(job->name);
kmem_strfree(job->name);
kmem_free(job, sizeof (minors_job_t));
}
list_destroy(&minors_list);
+}
- return (SET_ERROR(error));
+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) ops->zv_create_minor(name);
+ } else {
+ (void) ops->zv_create_minor(name);
+ }
}
/*
@@ -1366,7 +1384,7 @@ zvol_set_volmode_impl(char *name, uint64_t volmode)
/*
* It's unfortunate we need to remove minors before we create new ones:
* this is necessary because our backing gendisk (zvol_state->zv_disk)
- * coule be different when we set, for instance, volmode from "geom"
+ * could be different when we set, for instance, volmode from "geom"
* to "dev" (or vice versa).
* A possible optimization is to modify our consumers so we don't get
* called when "volmode" does not change.
@@ -1426,14 +1444,11 @@ zvol_task_free(zvol_task_t *task)
* The worker thread function performed asynchronously.
*/
static void
-zvol_task_cb(void *param)
+zvol_task_cb(void *arg)
{
- zvol_task_t *task = (zvol_task_t *)param;
+ zvol_task_t *task = arg;
switch (task->op) {
- case ZVOL_ASYNC_CREATE_MINORS:
- (void) zvol_create_minors_impl(task->name1);
- break;
case ZVOL_ASYNC_REMOVE_MINORS:
zvol_remove_minors_impl(task->name1);
break;
@@ -1635,21 +1650,6 @@ zvol_set_volmode(const char *ddname, zprop_source_t source, uint64_t volmode)
}
void
-zvol_create_minors(spa_t *spa, const char *name, boolean_t async)
-{
- zvol_task_t *task;
- taskqid_t id;
-
- task = zvol_task_alloc(ZVOL_ASYNC_CREATE_MINORS, name, NULL, ~0ULL);
- if (task == NULL)
- return;
-
- 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);
-}
-
-void
zvol_remove_minors(spa_t *spa, const char *name, boolean_t async)
{
zvol_task_t *task;