Various fixes for zvol devices not being correctly maintained when operations which effect them occur. --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c.orig 2014-03-26 17:52:33.000000000 +0000 +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c 2014-09-29 18:52:24.903520480 +0000 @@ -1655,11 +1655,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; @@ -1686,8 +1684,7 @@ dsl_dataset_rename_snapshot_sync_impl(ds 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, @@ -1699,7 +1696,6 @@ dsl_dataset_rename_snapshot_sync_impl(ds kmem_free(newname, MAXPATHLEN); kmem_free(oldname, MAXPATHLEN); #endif -#endif dsl_dataset_rele(ds, FTAG); return (0); @@ -2076,6 +2072,9 @@ dsl_dataset_promote_sync(void *arg, dmu_ 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; @@ -2141,6 +2140,14 @@ dsl_dataset_promote_sync(void *arg, dmu_ 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)) { @@ -2173,6 +2180,12 @@ dsl_dataset_promote_sync(void *arg, dmu_ 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) { @@ -2210,6 +2223,12 @@ dsl_dataset_promote_sync(void *arg, dmu_ 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 --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h.orig 2014-03-26 17:52:33.000000000 +0000 +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h 2014-09-29 18:52:24.903520480 +0000 @@ -41,8 +41,7 @@ extern int zvol_check_volblocksize(uint6 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 --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c.orig 2014-03-26 17:52:33.000000000 +0000 +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c 2014-09-29 18:52:24.908521871 +0000 @@ -3517,7 +3517,7 @@ zfs_ioc_destroy_snaps(const char *poolna error = zfs_unmount_snap(name); if (error != 0) return (error); - (void) zvol_remove_minor(name); + (void) zvol_remove_minors(name); } return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); @@ -3547,7 +3547,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); } @@ -3643,7 +3643,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)); } --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c.orig 2014-09-29 18:52:05.348521778 +0000 +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c 2014-09-29 18:52:24.909520935 +0000 @@ -630,24 +630,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)); - } - g_topology_lock(); - rc = zvol_remove_zv(zv); - g_topology_unlock(); - mutex_exit(&spa_namespace_lock); - return (rc); -} - -int zvol_first_open(zvol_state_t *zv) { objset_t *os; @@ -775,15 +757,17 @@ zvol_update_volsize(objset_t *os, uint64 return (error); } -void +int zvol_remove_minors(const char *name) { struct g_geom *gp, *gptmp; struct g_provider *pp; zvol_state_t *zv; size_t namelen; + int rc; namelen = strlen(name); + rc = ENXIO; DROP_GIANT(); mutex_enter(&spa_namespace_lock); @@ -798,14 +782,18 @@ zvol_remove_minors(const char *name) continue; 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; } } g_topology_unlock(); mutex_exit(&spa_namespace_lock); PICKUP_GIANT(); + + return (rc); } int @@ -2393,9 +2381,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); } @@ -2479,12 +2468,16 @@ zvol_rename_minors(const char *oldname, 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; + } g_topology_lock(); LIST_FOREACH(gp, &zfs_zvol_class.geom, geom) { @@ -2507,6 +2500,7 @@ zvol_rename_minors(const char *oldname, } g_topology_unlock(); - mutex_exit(&spa_namespace_lock); + if (locked) + mutex_exit(&spa_namespace_lock); PICKUP_GIANT(); }