summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsmh <smh@FreeBSD.org>2014-10-10 01:01:04 +0000
committersmh <smh@FreeBSD.org>2014-10-10 01:01:04 +0000
commit4347b3c38b4237555ff0fc5763cb47854bdf8d31 (patch)
tree4698971160d9cf1c54e2bdb1bd15e2002ec52084
parent303356caa9a2204687c43e218ab0b0b090dfd1fe (diff)
downloadFreeBSD-src-4347b3c38b4237555ff0fc5763cb47854bdf8d31.zip
FreeBSD-src-4347b3c38b4237555ff0fc5763cb47854bdf8d31.tar.gz
MFC r272474:
Fix various issues with zvols Sponsored by: Multiplay
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c23
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c23
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c18
3 files changed, 58 insertions, 6 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
index a97a10a..d013206 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
@@ -2257,6 +2257,9 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
dsl_dir_t *odd = NULL;
uint64_t oldnext_obj;
int64_t delta;
+#if defined(__FreeBSD__) && defined(_KERNEL)
+ char *oldname, *newname;
+#endif
VERIFY0(promote_hold(ddpa, dp, FTAG));
hds = ddpa->ddpa_clone;
@@ -2322,6 +2325,14 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
dd->dd_phys->dd_clones, origin_head->ds_object, tx));
}
+#if defined(__FreeBSD__) && defined(_KERNEL)
+ /* Take the spa_namespace_lock early so zvol renames don't deadlock. */
+ mutex_enter(&spa_namespace_lock);
+
+ oldname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ newname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+#endif
+
/* move snapshots to this dir */
for (snap = list_head(&ddpa->shared_snaps); snap;
snap = list_next(&ddpa->shared_snaps, snap)) {
@@ -2356,6 +2367,12 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
VERIFY0(dsl_dir_hold_obj(dp, dd->dd_object,
NULL, ds, &ds->ds_dir));
+#if defined(__FreeBSD__) && defined(_KERNEL)
+ dsl_dataset_name(ds, newname);
+ zfsvfs_update_fromname(oldname, newname);
+ zvol_rename_minors(oldname, newname);
+#endif
+
/* move any clone references */
if (ds->ds_phys->ds_next_clones_obj &&
spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) {
@@ -2393,6 +2410,12 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
ASSERT(!dsl_prop_hascb(ds));
}
+#if defined(__FreeBSD__) && defined(_KERNEL)
+ mutex_exit(&spa_namespace_lock);
+
+ kmem_free(newname, MAXPATHLEN);
+ kmem_free(oldname, MAXPATHLEN);
+#endif
/*
* Change space accounting.
* Note, pa->*usedsnap and dd_used_breakdown[SNAP] will either
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
index 680c3e6..8c2e9b9 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
@@ -3541,6 +3541,7 @@ zfs_destroy_unmount_origin(const char *fsname)
static int
zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
{
+ int error, poollen;
nvlist_t *snaps;
nvpair_t *pair;
boolean_t defer;
@@ -3549,9 +3550,25 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
return (SET_ERROR(EINVAL));
defer = nvlist_exists(innvl, "defer");
+ poollen = strlen(poolname);
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
pair = nvlist_next_nvpair(snaps, pair)) {
- (void) zfs_unmount_snap(nvpair_name(pair));
+ const char *name = nvpair_name(pair);
+
+ /*
+ * The snap must be in the specified pool to prevent the
+ * invalid removal of zvol minors below.
+ */
+ if (strncmp(name, poolname, poollen) != 0 ||
+ (name[poollen] != '/' && name[poollen] != '@'))
+ return (SET_ERROR(EXDEV));
+
+ error = zfs_unmount_snap(name);
+ if (error != 0)
+ return (error);
+#if defined(__FreeBSD__)
+ zvol_remove_minors(name);
+#endif
}
return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl));
@@ -3676,7 +3693,11 @@ zfs_ioc_destroy(zfs_cmd_t *zc)
else
err = dsl_destroy_head(zc->zc_name);
if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
+#ifdef __FreeBSD__
+ zvol_remove_minors(zc->zc_name);
+#else
(void) zvol_remove_minor(zc->zc_name);
+#endif
return (err);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
index f05c90d..ab7d50f 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
@@ -883,7 +883,8 @@ zvol_remove_minors(const char *name)
LIST_FOREACH_SAFE(zv, &all_zvols, zv_links, tzv) {
if (strcmp(zv->zv_name, name) == 0 ||
(strncmp(zv->zv_name, name, namelen) == 0 &&
- zv->zv_name[namelen] == '/')) {
+ strlen(zv->zv_name) > namelen && (zv->zv_name[namelen] == '/' ||
+ zv->zv_name[namelen] == '@'))) {
(void) zvol_remove_zv(zv);
}
}
@@ -2571,9 +2572,10 @@ zvol_create_minors(const char *name)
if (dmu_objset_type(os) == DMU_OST_ZVOL) {
dsl_dataset_long_hold(os->os_dsl_dataset, FTAG);
dsl_pool_rele(dmu_objset_pool(os), FTAG);
- if ((error = zvol_create_minor(name)) == 0)
+ error = zvol_create_minor(name);
+ if (error == 0 || error == EEXIST) {
error = zvol_create_snapshots(os, name);
- else {
+ } else {
printf("ZFS WARNING: Unable to create ZVOL %s (error=%d).\n",
name, error);
}
@@ -2674,12 +2676,17 @@ zvol_rename_minors(const char *oldname, const char *newname)
size_t oldnamelen, newnamelen;
zvol_state_t *zv;
char *namebuf;
+ boolean_t locked = B_FALSE;
oldnamelen = strlen(oldname);
newnamelen = strlen(newname);
DROP_GIANT();
- mutex_enter(&spa_namespace_lock);
+ /* See comment in zvol_open(). */
+ if (!MUTEX_HELD(&spa_namespace_lock)) {
+ mutex_enter(&spa_namespace_lock);
+ locked = B_TRUE;
+ }
LIST_FOREACH(zv, &all_zvols, zv_links) {
if (strcmp(zv->zv_name, oldname) == 0) {
@@ -2694,7 +2701,8 @@ zvol_rename_minors(const char *oldname, const char *newname)
}
}
- mutex_exit(&spa_namespace_lock);
+ if (locked)
+ mutex_exit(&spa_namespace_lock);
PICKUP_GIANT();
}
OpenPOWER on IntegriCloud