Index: cddl/contrib/opensolaris/cmd/ztest/ztest.c =================================================================== --- cddl/contrib/opensolaris/cmd/ztest/ztest.c +++ cddl/contrib/opensolaris/cmd/ztest/ztest.c @@ -3566,7 +3566,7 @@ error = dmu_objset_own(snap2name, DMU_OST_ANY, B_TRUE, FTAG, &os); if (error) fatal(0, "dmu_objset_own(%s) = %d", snap2name, error); - error = dsl_dataset_promote(clone2name, NULL); + error = dsl_dataset_promote(clone2name, NULL, NULL); if (error == ENOSPC) { dmu_objset_disown(os, FTAG); ztest_record_enospc(FTAG); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c @@ -1639,17 +1639,6 @@ fnvlist_free(suspended); } -#ifdef __FreeBSD__ -#ifdef _KERNEL - if (error == 0) { - for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; - pair = nvlist_next_nvpair(snaps, pair)) { - char *snapname = nvpair_name(pair); - zvol_create_minors(snapname); - } - } -#endif -#endif return (error); } @@ -2425,6 +2414,7 @@ const char *ddrsa_oldsnapname; const char *ddrsa_newsnapname; boolean_t ddrsa_recursive; + nvlist_t *ddrsa_renamed; dmu_tx_t *ddrsa_tx; } dsl_dataset_rename_snapshot_arg_t; @@ -2485,11 +2475,7 @@ dsl_dataset_rename_snapshot_sync_impl(dsl_pool_t *dp, dsl_dataset_t *hds, void *arg) { -#ifdef __FreeBSD__ -#ifdef _KERNEL - char *oldname, *newname; -#endif -#endif + char namebuf[ZFS_MAX_DATASET_NAME_LEN]; dsl_dataset_rename_snapshot_arg_t *ddrsa = arg; dsl_dataset_t *ds; uint64_t val; @@ -2517,23 +2503,11 @@ VERIFY0(zap_add(dp->dp_meta_objset, dsl_dataset_phys(hds)->ds_snapnames_zapobj, ds->ds_snapname, 8, 1, &ds->ds_object, tx)); - -#ifdef __FreeBSD__ -#ifdef _KERNEL - oldname = kmem_alloc(MAXPATHLEN, KM_SLEEP); - newname = kmem_alloc(MAXPATHLEN, KM_SLEEP); - snprintf(oldname, MAXPATHLEN, "%s@%s", ddrsa->ddrsa_fsname, - ddrsa->ddrsa_oldsnapname); - snprintf(newname, MAXPATHLEN, "%s@%s", ddrsa->ddrsa_fsname, - ddrsa->ddrsa_newsnapname); - zfsvfs_update_fromname(oldname, newname); - zvol_rename_minors(oldname, newname); - kmem_free(newname, MAXPATHLEN); - kmem_free(oldname, MAXPATHLEN); -#endif -#endif dsl_dataset_rele(ds, FTAG); - + if (ddrsa->ddrsa_renamed != NULL) { + dsl_dataset_name(hds, namebuf); + fnvlist_add_boolean(ddrsa->ddrsa_renamed, namebuf); + } return (0); } @@ -2558,7 +2532,8 @@ int dsl_dataset_rename_snapshot(const char *fsname, - const char *oldsnapname, const char *newsnapname, boolean_t recursive) + const char *oldsnapname, const char *newsnapname, boolean_t recursive, + nvlist_t *renamed) { dsl_dataset_rename_snapshot_arg_t ddrsa; @@ -2566,6 +2541,7 @@ ddrsa.ddrsa_oldsnapname = oldsnapname; ddrsa.ddrsa_newsnapname = newsnapname; ddrsa.ddrsa_recursive = recursive; + ddrsa.ddrsa_renamed = renamed; return (dsl_sync_task(fsname, dsl_dataset_rename_snapshot_check, dsl_dataset_rename_snapshot_sync, &ddrsa, @@ -3008,9 +2984,6 @@ 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; @@ -3078,14 +3051,6 @@ dsl_dir_phys(dd)->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)) { @@ -3111,6 +3076,9 @@ dsl_fs_ss_count_adjust(hds->ds_dir, 1, DD_FIELD_SNAPSHOT_COUNT, tx); + if (ddpa->ddpa_moved != NULL) + fnvlist_add_boolean(ddpa->ddpa_moved, ds->ds_snapname); + /* change containing dsl_dir */ dmu_buf_will_dirty(ds->ds_dbuf, tx); ASSERT3U(dsl_dataset_phys(ds)->ds_dir_obj, ==, odd->dd_object); @@ -3120,12 +3088,6 @@ 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 (dsl_dataset_phys(ds)->ds_next_clones_obj && spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) { @@ -3164,12 +3126,6 @@ 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 @@ -3338,7 +3294,7 @@ * in with the name. (It must be at least ZFS_MAX_DATASET_NAME_LEN bytes long.) */ int -dsl_dataset_promote(const char *name, char *conflsnap) +dsl_dataset_promote(const char *name, char *conflsnap, nvlist_t *moved) { dsl_dataset_promote_arg_t ddpa = { 0 }; uint64_t numsnaps; @@ -3361,6 +3317,7 @@ return (error); ddpa.ddpa_clonename = name; + ddpa.ddpa_moved = moved; ddpa.err_ds = fnvlist_alloc(); ddpa.cr = CRED(); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c @@ -2032,13 +2032,6 @@ VERIFY0(zap_add(mos, dsl_dir_phys(newparent)->dd_child_dir_zapobj, dd->dd_myname, 8, 1, &dd->dd_object, tx)); -#ifdef __FreeBSD__ -#ifdef _KERNEL - zfsvfs_update_fromname(ddra->ddra_oldname, ddra->ddra_newname); - zvol_rename_minors(ddra->ddra_oldname, ddra->ddra_newname); -#endif -#endif - dsl_prop_notify_all(dd); dsl_dir_rele(newparent, FTAG); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h @@ -291,8 +291,6 @@ int dmu_objset_find(char *name, int func(const char *, void *), void *arg, int flags); void dmu_objset_byteswap(void *buf, size_t size); -int dsl_dataset_rename_snapshot(const char *fsname, - const char *oldsnapname, const char *newsnapname, boolean_t recursive); int dmu_objset_remap_indirects(const char *fsname); typedef struct dmu_buf { Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h @@ -252,6 +252,7 @@ typedef struct dsl_dataset_promote_arg { const char *ddpa_clonename; + nvlist_t *ddpa_moved; dsl_dataset_t *ddpa_clone; list_t shared_snaps, origin_snaps, clone_snaps; dsl_dataset_t *origin_origin; /* origin of the origin */ @@ -311,11 +312,12 @@ int dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors); void dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx); int dsl_dataset_promote_check(void *arg, dmu_tx_t *tx); -int dsl_dataset_promote(const char *name, char *conflsnap); +int dsl_dataset_promote(const char *name, char *conflsnap, nvlist_t *moved); int dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head, boolean_t force); int dsl_dataset_rename_snapshot(const char *fsname, - const char *oldsnapname, const char *newsnapname, boolean_t recursive); + const char *oldsnapname, const char *newsnapname, boolean_t recursive, + nvlist_t *renamed); int dsl_dataset_snapshot_tmp(const char *fsname, const char *snapname, minor_t cleanup_minor, const char *htag); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -250,6 +250,23 @@ ZFS_DELEG_PERM_GROUPQUOTA, }; +typedef enum { + RENAME_OP, + PROMOTE_OP +} zfs_ioc_queue_op_t; + +typedef struct zfs_ioc_cmd_entry { + STAILQ_ENTRY(zfs_ioc_cmd_entry) link; + zfs_ioc_queue_op_t op; + zfs_cmd_t *cmd; +} zfs_ioc_cmd_entry_t; + +static kmutex_t zfs_cmdq_lock; +static kcondvar_t zfs_cmdq_cv; + +static STAILQ_HEAD(, zfs_ioc_cmd_entry) zfs_ioc_cmd_queue = + STAILQ_HEAD_INITIALIZER(zfs_ioc_cmd_queue); + static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); static int zfs_check_settable(const char *name, nvpair_t *property, cred_t *cr); @@ -3462,6 +3479,15 @@ } error = dsl_dataset_snapshot(snaps, props, outnvl); +#if defined(__FreeBSD__) && defined(_KERNEL) + if (error == 0) { + for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; + pair = nvlist_next_nvpair(snaps, pair)) { + const char *snapname = nvpair_name(pair); + (void) zvol_create_minor(snapname); + } + } +#endif return (error); } @@ -3900,6 +3926,67 @@ return (0); } +static void +zfs_common_zvol_rename(zfs_cmd_t *zc, nvlist_t *renamed, + const char *oldsnapname, const char *newsnapname) +{ + nvpair_t *pair; + zfs_ioc_cmd_entry_t *entry; + + mutex_enter(&zfs_cmdq_lock); + do { + entry = STAILQ_FIRST(&zfs_ioc_cmd_queue); + if (entry->cmd == zc) { + mutex_exit(&zfs_cmdq_lock); + + switch (entry->op) { + case RENAME_OP: + if (renamed == NULL) { + zfsvfs_update_fromname(zc->zc_name, zc->zc_value); + zvol_rename_minors(zc->zc_name, zc->zc_value); + } else { + for (pair = nvlist_next_nvpair(renamed, NULL); + pair != NULL; + pair = nvlist_next_nvpair(renamed, pair)) { + char oldname[ZFS_MAX_DATASET_NAME_LEN]; + char newname[ZFS_MAX_DATASET_NAME_LEN]; + const char *fsname; + + fsname = nvpair_name(pair); + snprintf(oldname, sizeof (oldname), "%s@%s", fsname, + oldsnapname); + snprintf(newname, sizeof (newname), "%s@%s", fsname, + newsnapname); + zfsvfs_update_fromname(oldname, newname); + zvol_rename_minors(oldname, newname); + } + } + break; + case PROMOTE_OP: + for (pair = nvlist_next_nvpair(renamed, NULL); + pair != NULL; + pair = nvlist_next_nvpair(renamed, pair)) { + char oldname[ZFS_MAX_DATASET_NAME_LEN]; + char newname[ZFS_MAX_DATASET_NAME_LEN]; + const char *snapname; + + snapname = nvpair_name(pair); + snprintf(oldname, sizeof (oldname), "%s@%s", zc->zc_value, + snapname); + snprintf(newname, sizeof (newname), "%s@%s", zc->zc_name, + snapname); + zfsvfs_update_fromname(oldname, newname); + zvol_rename_minors(oldname, newname); + } + break; + } + return; + } + cv_wait(&zfs_cmdq_cv, &zfs_cmdq_lock); + } while (1); + mutex_exit(&zfs_cmdq_lock); +} + /* * inputs: * zc_name old name of dataset @@ -3914,6 +4001,8 @@ boolean_t recursive = zc->zc_cookie & 1; char *at; boolean_t allow_mounted = B_TRUE; + int error; + zfs_ioc_cmd_entry_t cmd_entry = {NULL, RENAME_OP, zc}; #ifdef __FreeBSD__ allow_mounted = (zc->zc_cookie & 2) != 0; @@ -3927,11 +4016,16 @@ strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%')) return (SET_ERROR(EINVAL)); + mutex_enter(&zfs_cmdq_lock); + STAILQ_INSERT_TAIL(&zfs_ioc_cmd_queue, &cmd_entry, link); + mutex_exit(&zfs_cmdq_lock); at = strchr(zc->zc_name, '@'); if (at != NULL) { - /* snaps must be in same fs */ - int error; + nvlist_t *renamed; + const char *oldsnapname; + const char *newsnapname; + /* snaps must be in same fs */ if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1)) return (SET_ERROR(EXDEV)); *at = '\0'; @@ -3944,18 +4038,37 @@ return (error); } } + renamed = fnvlist_alloc(); + oldsnapname = at + 1; + newsnapname = strchr(zc->zc_value, '@') + 1; error = dsl_dataset_rename_snapshot(zc->zc_name, - at + 1, strchr(zc->zc_value, '@') + 1, recursive); + oldsnapname, newsnapname, recursive, renamed); +#if defined(__FreeBSD__) && defined(_KERNEL) + if (error == 0) { + zfs_common_zvol_rename(zc, renamed, oldsnapname, newsnapname); + } +#endif *at = '@'; - - return (error); + fnvlist_free(renamed); } else { #ifdef illumos if (zc->zc_objset_type == DMU_OST_ZVOL) (void) zvol_remove_minor(zc->zc_name); #endif - return (dsl_dir_rename(zc->zc_name, zc->zc_value)); + error = dsl_dir_rename(zc->zc_name, zc->zc_value); +#if defined(__FreeBSD__) && defined(_KERNEL) + if (error == 0) { + zfs_common_zvol_rename(zc, NULL, NULL, NULL); + } +#endif } + + mutex_enter(&zfs_cmdq_lock); + STAILQ_REMOVE_HEAD(&zfs_ioc_cmd_queue, link); + cv_broadcast(&zfs_cmdq_cv); + mutex_exit(&zfs_cmdq_lock); + + return (error); } static int @@ -4973,8 +5086,10 @@ dsl_pool_t *dp; dsl_dataset_t *ds, *ods; char origin[ZFS_MAX_DATASET_NAME_LEN]; + nvlist_t *moved; char *cp; int error; + zfs_ioc_cmd_entry_t cmd_entry = {NULL, PROMOTE_OP, zc}; zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 || @@ -5019,7 +5134,24 @@ *cp = '\0'; (void) dmu_objset_find(origin, zfs_unmount_snap_cb, NULL, DS_FIND_SNAPSHOTS); - return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); + + mutex_enter(&zfs_cmdq_lock); + STAILQ_INSERT_TAIL(&zfs_ioc_cmd_queue, &cmd_entry, link); + mutex_exit(&zfs_cmdq_lock); + + moved = fnvlist_alloc(); + error = dsl_dataset_promote(zc->zc_name, zc->zc_string, moved); + if (error == 0) { + zfs_common_zvol_rename(zc, moved, NULL, NULL); + } + fnvlist_free(moved); + + mutex_enter(&zfs_cmdq_lock); + STAILQ_REMOVE_HEAD(&zfs_ioc_cmd_queue, link); + cv_broadcast(&zfs_cmdq_cv); + mutex_exit(&zfs_cmdq_lock); + + return (error); } /* @@ -6933,6 +7065,9 @@ mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&zfs_cmdq_lock, "zfs_cmdq_lock", NULL, 0); + cv_init(&zfs_cmdq_cv, NULL, CV_DEFAULT, NULL); + spa_init(FREAD | FWRITE); zfs_init(); zvol_init(); @@ -6968,6 +7103,9 @@ tsd_destroy(&rrw_tsd_key); tsd_destroy(&zfs_allow_log_key); + cv_destroy(&zfs_cmdq_cv); + mutex_destroy(&zfs_cmdq_lock); + mutex_destroy(&zfs_share_lock); return (0);