Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c (revision 272220) +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c (working copy) @@ -1806,11 +1806,9 @@ static int dsl_dataset_rename_snapshot_sync_impl(dsl_pool_t *dp, dsl_dataset_t *hds, void *arg) { -#ifdef __FreeBSD__ -#ifdef _KERNEL +#if defined(__FreeBSD__) && defined(_KERNEL) char *oldname, *newname; #endif -#endif dsl_dataset_rename_snapshot_arg_t *ddrsa = arg; dsl_dataset_t *ds; uint64_t val; @@ -1838,8 +1836,7 @@ dsl_dataset_rename_snapshot_sync_impl(dsl_pool_t * VERIFY0(zap_add(dp->dp_meta_objset, hds->ds_phys->ds_snapnames_zapobj, ds->ds_snapname, 8, 1, &ds->ds_object, tx)); -#ifdef __FreeBSD__ -#ifdef _KERNEL +#if defined(__FreeBSD__) && defined (_KERNEL) oldname = kmem_alloc(MAXPATHLEN, KM_SLEEP); newname = kmem_alloc(MAXPATHLEN, KM_SLEEP); snprintf(oldname, MAXPATHLEN, "%s@%s", ddrsa->ddrsa_fsname, @@ -1851,7 +1848,6 @@ dsl_dataset_rename_snapshot_sync_impl(dsl_pool_t * kmem_free(newname, MAXPATHLEN); kmem_free(oldname, MAXPATHLEN); #endif -#endif dsl_dataset_rele(ds, FTAG); return (0); @@ -2257,6 +2253,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 +2321,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 so zvol renames don't livelock */ + 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 +2363,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 +2406,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 Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h (revision 272220) +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h (working copy) @@ -41,8 +41,7 @@ extern int zvol_check_volblocksize(uint64_t volblo extern int zvol_get_stats(objset_t *os, nvlist_t *nv); extern void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); extern int zvol_create_minor(const char *); -extern int zvol_remove_minor(const char *); -extern void zvol_remove_minors(const char *); +extern int zvol_remove_minors(const char *); extern int zvol_set_volsize(const char *, major_t, uint64_t); #ifdef sun Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c (revision 272220) +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c (working copy) @@ -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,23 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist 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 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); + (void) zvol_remove_minors(name); } return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); @@ -3623,7 +3638,7 @@ static int zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) { - int error, poollen; + int poollen; poollen = strlen(poolname); for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); @@ -3645,11 +3660,10 @@ zfs_ioc_destroy_bookmarks(const char *poolname, nv if (strncmp(name, poolname, poollen) != 0 || (name[poollen] != '/' && name[poollen] != '#')) return (SET_ERROR(EXDEV)); - (void) zvol_remove_minor(name); + (void) zvol_remove_minors(name); } - error = dsl_bookmark_destroy(innvl, outnvl); - return (error); + return (dsl_bookmark_destroy(innvl, outnvl)); } /* @@ -3676,7 +3690,7 @@ 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) - (void) zvol_remove_minor(zc->zc_name); + (void) zvol_remove_minors(zc->zc_name); return (err); } @@ -3772,7 +3786,7 @@ zfs_ioc_rename(zfs_cmd_t *zc) } else { #ifdef illumos if (zc->zc_objset_type == DMU_OST_ZVOL) - (void) zvol_remove_minor(zc->zc_name); + (void) zvol_remove_minors(zc->zc_name); #endif return (dsl_dir_rename(zc->zc_name, zc->zc_value)); } Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c (revision 272220) +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c (working copy) @@ -725,22 +725,6 @@ zvol_remove_zv(zvol_state_t *zv) } int -zvol_remove_minor(const char *name) -{ - zvol_state_t *zv; - int rc; - - mutex_enter(&spa_namespace_lock); - if ((zv = zvol_minor_lookup(name)) == NULL) { - mutex_exit(&spa_namespace_lock); - return (SET_ERROR(ENXIO)); - } - rc = zvol_remove_zv(zv); - mutex_exit(&spa_namespace_lock); - return (rc); -} - -int zvol_first_open(zvol_state_t *zv) { objset_t *os; @@ -869,13 +853,15 @@ zvol_update_volsize(objset_t *os, uint64_t volsize return (error); } -void +int zvol_remove_minors(const char *name) { zvol_state_t *zv, *tzv; size_t namelen; + int rc; namelen = strlen(name); + rc = ENXIO; DROP_GIANT(); mutex_enter(&spa_namespace_lock); @@ -883,13 +869,17 @@ 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] == '/')) { - (void) zvol_remove_zv(zv); + strlen(zv->zv_name) > namelen && (zv->zv_name[namelen] == '/' || + zv->zv_name[namelen] == '@'))) { + if ((rc = zvol_remove_zv(zv)) != 0) + break; } } mutex_exit(&spa_namespace_lock); PICKUP_GIANT(); + + return (rc); } int @@ -2571,9 +2561,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 +2665,16 @@ zvol_rename_minors(const char *oldname, const char 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); + 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 +2689,8 @@ zvol_rename_minors(const char *oldname, const char } } - mutex_exit(&spa_namespace_lock); + if (locked) + mutex_exit(&spa_namespace_lock); PICKUP_GIANT(); }