Author: avg Date: Thu Jun 11 10:41:31 2020 New Revision: 362047 URL: https://svnweb.freebsd.org/changeset/base/362047
Log: rework how ZVOLs are updated in response to DSL operations With this change all ZVOL updates are initiated from the SPA sync context instead of a mix of the sync and open contexts. The updates are queued to be applied by a dedicated thread in the original order. This should ensure that ZVOLs always accurately reflect the corresponding datasets. ZFS ioctl operations wait on the mentioned thread to complete its work. Thus, the illusion of the synchronous ZVOL update is preserved. At the same time, the SPA sync thread never blocks on ZVOL related operations avoiding problems like reported in bug 203864. This change is based on earlier work in the same direction: D7179 and D14669 by Anthoine Bourgeois. D7179 tried to perform ZVOL operations in the open context and that opened races between them. D14669 uses a design very similar to this change but with different implementation details. This change also heavily borrows from similar code in ZoL, but there are many differences too. See: - https://github.com/zfsonlinux/zfs/commit/a0bd735adb1b1eb81fef10b4db102ee051c4d4ff - https://github.com/zfsonlinux/zfs/issues/3681 - https://github.com/zfsonlinux/zfs/issues/2217 PR: 203864 MFC after: 5 weeks Sponsored by: CyberSecure Differential Revision: https://reviews.freebsd.org/D23478 Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c Thu Jun 11 10:41:31 2020 (r362047) @@ -1053,6 +1053,9 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx) doca->doca_cred, tx); } +#if defined(__FreeBSD__) && defined(_KERNEL) + zvol_create_minors(dp->dp_spa, doca->doca_name); +#endif spa_history_log_internal_ds(ds, "create", tx, ""); dsl_dataset_rele(ds, FTAG); dsl_dir_rele(pdd, FTAG); @@ -1148,6 +1151,9 @@ dmu_objset_clone_sync(void *arg, dmu_tx_t *tx) VERIFY0(dsl_dataset_hold_obj(pdd->dd_pool, obj, FTAG, &ds)); dsl_dataset_name(origin, namebuf); +#if defined(__FreeBSD__) && defined(_KERNEL) + zvol_create_minors(dp->dp_spa, doca->doca_clone); +#endif spa_history_log_internal_ds(ds, "clone", tx, "origin=%s (%llu)", namebuf, origin->ds_object); dsl_dataset_rele(ds, FTAG); Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c Thu Jun 11 10:41:31 2020 (r362047) @@ -57,6 +57,9 @@ #include <sys/dsl_bookmark.h> #include <sys/zfeature.h> #include <sys/bqueue.h> +#ifdef __FreeBSD__ +#include <sys/zvol.h> +#endif #ifdef __FreeBSD__ #undef dump_write @@ -3445,6 +3448,11 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx) drc->drc_newsnapobj = dsl_dataset_phys(drc->drc_ds)->ds_prev_snap_obj; } + +#if defined(__FreeBSD__) && defined(_KERNEL) + zvol_create_minors(dp->dp_spa, drc->drc_tofs); +#endif + /* * 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, Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c Thu Jun 11 10:41:31 2020 (r362047) @@ -1572,6 +1572,9 @@ 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); } +#if defined(__FreeBSD__) && defined(_KERNEL) + zvol_create_minors(dp->dp_spa, name); +#endif dsl_dataset_rele(ds, FTAG); } } @@ -1646,17 +1649,6 @@ dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, 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); } @@ -2535,7 +2527,7 @@ dsl_dataset_rename_snapshot_sync_impl(dsl_pool_t *dp, snprintf(newname, ZFS_MAX_DATASET_NAME_LEN, "%s@%s", ddrsa->ddrsa_fsname, ddrsa->ddrsa_newsnapname); zfsvfs_update_fromname(oldname, newname); - zvol_rename_minors(oldname, newname); + zvol_rename_minors(dp->dp_spa, oldname, newname); kmem_free(newname, ZFS_MAX_DATASET_NAME_LEN); kmem_free(oldname, ZFS_MAX_DATASET_NAME_LEN); #endif @@ -3087,9 +3079,6 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *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(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP); newname = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP); #endif @@ -3135,7 +3124,7 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx) #if defined(__FreeBSD__) && defined(_KERNEL) dsl_dataset_name(ds, newname); zfsvfs_update_fromname(oldname, newname); - zvol_rename_minors(oldname, newname); + zvol_rename_minors(dp->dp_spa, oldname, newname); #endif /* move any clone references */ @@ -3177,8 +3166,6 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx) } #if defined(__FreeBSD__) && defined(_KERNEL) - mutex_exit(&spa_namespace_lock); - kmem_free(newname, ZFS_MAX_DATASET_NAME_LEN); kmem_free(oldname, ZFS_MAX_DATASET_NAME_LEN); #endif Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c Thu Jun 11 10:41:31 2020 (r362047) @@ -43,7 +43,11 @@ #include <sys/dsl_deleg.h> #include <sys/dmu_impl.h> #include <sys/zcp.h> +#if defined(__FreeBSD__) && defined(_KERNEL) +#include <sys/zvol.h> +#endif + int dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer) { @@ -489,6 +493,14 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, bool if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, tx)); + +#if defined(__FreeBSD__) && defined(_KERNEL) + char dsname[ZFS_MAX_DATASET_NAME_LEN]; + + dsl_dataset_name(ds, dsname); + zvol_remove_minors(dp->dp_spa, dsname); +#endif + dsl_dir_rele(ds->ds_dir, ds); ds->ds_dir = NULL; dmu_object_free_zapified(mos, obj, tx); @@ -979,6 +991,9 @@ dsl_destroy_head_sync(void *arg, dmu_tx_t *tx) VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds)); dsl_destroy_head_sync_impl(ds, tx); +#if defined(__FreeBSD__) && defined(_KERNEL) + zvol_remove_minors(dp->dp_spa, ddha->ddha_name); +#endif dsl_dataset_rele(ds, FTAG); } Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c Thu Jun 11 10:41:31 2020 (r362047) @@ -2093,7 +2093,7 @@ dsl_dir_rename_sync(void *arg, dmu_tx_t *tx) #ifdef __FreeBSD__ #ifdef _KERNEL zfsvfs_update_fromname(ddra->ddra_oldname, ddra->ddra_newname); - zvol_rename_minors(ddra->ddra_oldname, ddra->ddra_newname); + zvol_rename_minors(dp->dp_spa, ddra->ddra_oldname, ddra->ddra_newname); #endif #endif Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c Thu Jun 11 10:41:31 2020 (r362047) @@ -32,6 +32,7 @@ * Copyright (c) 2017, Intel Corporation. * Copyright (c) 2017 Datto Inc. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. + * Copyright (c) 2016 Actifio, Inc. All rights reserved. */ /* @@ -1280,6 +1281,24 @@ spa_activate(spa_t *spa, int mode) */ trim_thread_create(spa); + /* + * This taskq is used to perform zvol-minor-related tasks + * asynchronously. This has several advantages, including easy + * resolution of various deadlocks (zfsonlinux bug #3681). + * + * The taskq must be single threaded to ensure tasks are always + * processed in the order in which they were dispatched. + * + * A taskq per pool allows one to keep the pools independent. + * This way if one pool is suspended, it will not impact another. + * + * The preferred location to dispatch a zvol minor task is a sync + * task. In this context, there is easy access to the spa_t and minimal + * error handling is required because the sync task must succeed. + */ + spa->spa_zvol_taskq = taskq_create("z_zvol", 1, minclsyspri, + 1, INT_MAX, 0); + for (size_t i = 0; i < TXG_SIZE; i++) { spa->spa_txg_zio[i] = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL); @@ -1323,6 +1342,11 @@ spa_deactivate(spa_t *spa) spa_evicting_os_wait(spa); + if (spa->spa_zvol_taskq) { + taskq_destroy(spa->spa_zvol_taskq); + spa->spa_zvol_taskq = NULL; + } + txg_list_destroy(&spa->spa_vdev_txg_list); list_destroy(&spa->spa_config_dirty_list); @@ -4614,7 +4638,7 @@ spa_open_common(const char *pool, spa_t **spapp, void #ifdef __FreeBSD__ #ifdef _KERNEL if (firstopen) - zvol_create_minors(spa->spa_name); + zvol_create_minors(spa, spa->spa_name); #endif #endif } @@ -5970,7 +5994,7 @@ spa_import(const char *pool, nvlist_t *config, nvlist_ #ifdef __FreeBSD__ #ifdef _KERNEL - zvol_create_minors(pool); + zvol_create_minors(spa, pool); #endif #endif return (0); @@ -6119,6 +6143,10 @@ spa_export_common(char *pool, int new_state, nvlist_t spa_open_ref(spa, FTAG); mutex_exit(&spa_namespace_lock); spa_async_suspend(spa); + if (spa->spa_zvol_taskq) { + zvol_remove_minors(spa, spa_name(spa)); + taskq_wait(spa->spa_zvol_taskq); + } mutex_enter(&spa_namespace_lock); spa_close(spa, FTAG); Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h Thu Jun 11 10:41:31 2020 (r362047) @@ -27,6 +27,7 @@ * Copyright 2013 Saso Kiselkov. All rights reserved. * Copyright (c) 2017 Datto Inc. * Copyright (c) 2017, Intel Corporation. + * Copyright (c) 2016 Actifio, Inc. All rights reserved. */ #ifndef _SYS_SPA_IMPL_H @@ -398,6 +399,8 @@ struct spa { uint64_t spa_lowmem_last_txg; /* txg window start */ hrtime_t spa_ccw_fail_time; /* Conf cache write fail time */ + + taskq_t *spa_zvol_taskq; /* Taskq for minor management */ uint64_t spa_multihost; /* multihost aware (mmp) */ mmp_thread_t spa_mmp; /* multihost mmp thread */ Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h Thu Jun 11 10:41:31 2020 (r362047) @@ -21,6 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016 Actifio, Inc. All rights reserved. */ #ifndef _SYS_ZVOL_H @@ -40,9 +41,6 @@ extern int zvol_check_volsize(uint64_t volsize, uint64 extern int zvol_check_volblocksize(uint64_t volblocksize); 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_set_volsize(const char *, uint64_t); #ifdef illumos @@ -72,8 +70,10 @@ extern void zvol_log_write_minor(void *minor_hdl, dmu_ #endif /* illumos */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -extern int zvol_create_minors(const char *name); -extern void zvol_rename_minors(const char *oldname, const char *newname); +extern void zvol_create_minors(spa_t *spa, const char *name); +extern void zvol_remove_minors(spa_t *spa, const char *name); +extern void zvol_rename_minors(spa_t *spa, const char *oldname, + const char *newname); #endif #endif /* _KERNEL */ Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c Thu Jun 11 10:41:31 2020 (r362047) @@ -1642,8 +1642,10 @@ zfs_ioc_pool_destroy(zfs_cmd_t *zc) int error; zfs_log_history(zc); error = spa_destroy(zc->zc_name); +#ifndef __FreeBSD__ if (error == 0) zvol_remove_minors(zc->zc_name); +#endif return (error); } @@ -1694,8 +1696,10 @@ zfs_ioc_pool_export(zfs_cmd_t *zc) zfs_log_history(zc); error = spa_export(zc->zc_name, NULL, force, hardforce); +#ifndef __FreeBSD__ if (error == 0) zvol_remove_minors(zc->zc_name); +#endif return (error); } @@ -3395,13 +3399,23 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nv if (error == 0) { error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, nvprops, outnvl); +#if defined(__FreeBSD__) && defined(_KERNEL) + /* + * Wait for ZVOL operations to settle down before destroying. + */ + if (error != 0) { + spa_t *spa; + + if (spa_open(fsname, &spa, FTAG) == 0) { + taskqueue_drain_all( + spa->spa_zvol_taskq->tq_queue); + spa_close(spa, FTAG); + } + } +#endif if (error != 0) (void) dsl_destroy_head(fsname); } -#ifdef __FreeBSD__ - if (error == 0 && type == DMU_OST_ZVOL) - zvol_create_minors(fsname); -#endif return (error); } @@ -3443,10 +3457,6 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvl if (error != 0) (void) dsl_destroy_head(fsname); } -#ifdef __FreeBSD__ - if (error == 0) - zvol_create_minors(fsname); -#endif return (error); } @@ -3738,9 +3748,6 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t * return (SET_ERROR(EXDEV)); zfs_unmount_snap(nvpair_name(pair)); -#if defined(__FreeBSD__) - zvol_remove_minors(name); -#endif } return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); @@ -3924,10 +3931,8 @@ zfs_ioc_destroy(zfs_cmd_t *zc) err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); else err = dsl_destroy_head(zc->zc_name); +#ifndef __FreeBSD__ if (ost == DMU_OST_ZVOL && err == 0) -#ifdef __FreeBSD__ - zvol_remove_minors(zc->zc_name); -#else (void) zvol_remove_minor(zc->zc_name); #endif return (err); @@ -4822,11 +4827,6 @@ zfs_ioc_recv(zfs_cmd_t *zc) } #endif -#ifdef __FreeBSD__ - if (error == 0) - zvol_create_minors(tofs); -#endif - /* * On error, restore the original props. */ @@ -6967,6 +6967,24 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t ar out: nvlist_free(innvl); + +#if defined(__FreeBSD__) && defined(_KERNEL) + /* + * Wait for ZVOL changes to get applied. + * NB: taskqueue_drain_all() does less than taskq_wait(), + * but enough for what we want. + * And there is no equivalent illumos API. + */ + if (error == 0) { + spa_t *spa; + + if (spa_open(saved_poolname, &spa, FTAG) == 0) { + taskqueue_drain_all( + spa->spa_zvol_taskq->tq_queue); + spa_close(spa, FTAG); + } + } +#endif #ifdef illumos rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c Thu Jun 11 10:00:56 2020 (r362046) +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c Thu Jun 11 10:41:31 2020 (r362047) @@ -30,6 +30,7 @@ * Copyright (c) 2012, 2017 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2014 Integros [integros.com] + * Copyright (c) 2016 Actifio, Inc. All rights reserved. */ /* Portions Copyright 2011 Martin Matuska <m...@freebsd.org> */ @@ -185,6 +186,20 @@ typedef struct zvol_state { #endif } zvol_state_t; +typedef enum { + ZVOL_ASYNC_CREATE_MINORS, + ZVOL_ASYNC_REMOVE_MINORS, + ZVOL_ASYNC_RENAME_MINORS, + ZVOL_ASYNC_MAX +} zvol_async_op_t; + +typedef struct { + zvol_async_op_t op; + char pool[ZFS_MAX_DATASET_NAME_LEN]; + char name1[ZFS_MAX_DATASET_NAME_LEN]; + char name2[ZFS_MAX_DATASET_NAME_LEN]; +} zvol_task_t; + #ifndef illumos static LIST_HEAD(, zvol_state) all_zvols; #endif @@ -607,7 +622,7 @@ zvol_name2minor(const char *name, minor_t *minor) /* * Create a minor node (plus a whole lot more) for the specified volume. */ -int +static int zvol_create_minor(const char *name) { zfs_soft_state_t *zs; @@ -691,7 +706,6 @@ zvol_create_minor(const char *name) if (error != 0 || mode == ZFS_VOLMODE_DEFAULT) mode = volmode; - DROP_GIANT(); zv->zv_volmode = mode; if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { g_topology_lock(); @@ -766,7 +780,6 @@ zvol_create_minor(const char *name) zvol_geom_run(zv); g_topology_unlock(); } - PICKUP_GIANT(); ZFS_LOG(1, "ZVOL %s created.", name); #endif @@ -820,22 +833,6 @@ zvol_remove_zv(zvol_state_t *zv) } int -zvol_remove_minor(const char *name) -{ - zvol_state_t *zv; - int rc; - - mutex_enter(&zfsdev_state_lock); - if ((zv = zvol_minor_lookup(name)) == NULL) { - mutex_exit(&zfsdev_state_lock); - return (SET_ERROR(ENXIO)); - } - rc = zvol_remove_zv(zv); - mutex_exit(&zfsdev_state_lock); - return (rc); -} - -int zvol_first_open(zvol_state_t *zv) { dmu_object_info_t doi; @@ -976,7 +973,7 @@ zvol_update_volsize(objset_t *os, uint64_t volsize) } void -zvol_remove_minors(const char *name) +zvol_remove_minors_impl(const char *name) { #ifdef illumos zvol_state_t *zv; @@ -1004,7 +1001,6 @@ zvol_remove_minors(const char *name) namelen = strlen(name); - DROP_GIANT(); mutex_enter(&zfsdev_state_lock); LIST_FOREACH_SAFE(zv, &all_zvols, zv_links, tzv) { @@ -1017,7 +1013,6 @@ zvol_remove_minors(const char *name) } mutex_exit(&zfsdev_state_lock); - PICKUP_GIANT(); #endif /* illumos */ } @@ -2920,7 +2915,7 @@ zvol_create_snapshots(objset_t *os, const char *name) } int -zvol_create_minors(const char *name) +zvol_create_minors_impl(const char *name) { uint64_t cookie; objset_t *os; @@ -2976,7 +2971,7 @@ zvol_create_minors(const char *name) while (dmu_dir_list_next(os, MAXPATHLEN - (p - osname), p, NULL, &cookie) == 0) { dmu_objset_rele(os, FTAG); - (void)zvol_create_minors(osname); + (void)zvol_create_minors_impl(osname); if ((error = dmu_objset_hold(name, FTAG, &os)) != 0) { printf("ZFS WARNING: Unable to put hold on %s (error=%d).\n", name, error); @@ -3045,7 +3040,7 @@ zvol_rename_minor(zvol_state_t *zv, const char *newnam } void -zvol_rename_minors(const char *oldname, const char *newname) +zvol_rename_minors_impl(const char *oldname, const char *newname) { char name[MAXPATHLEN]; struct g_provider *pp; @@ -3058,7 +3053,6 @@ zvol_rename_minors(const char *oldname, const char *ne oldnamelen = strlen(oldname); newnamelen = strlen(newname); - DROP_GIANT(); /* See comment in zvol_open(). */ if (!MUTEX_HELD(&zfsdev_state_lock)) { mutex_enter(&zfsdev_state_lock); @@ -3080,7 +3074,88 @@ zvol_rename_minors(const char *oldname, const char *ne if (locked) mutex_exit(&zfsdev_state_lock); - PICKUP_GIANT(); +} + +static zvol_task_t * +zvol_task_alloc(zvol_async_op_t op, const char *name1, const char *name2) +{ + zvol_task_t *task; + char *delim; + + task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP); + task->op = op; + delim = strchr(name1, '/'); + strlcpy(task->pool, name1, delim ? (delim - name1 + 1) : MAXNAMELEN); + + strlcpy(task->name1, name1, MAXNAMELEN); + if (name2 != NULL) + strlcpy(task->name2, name2, MAXNAMELEN); + + return (task); +} + +static void +zvol_task_free(zvol_task_t *task) +{ + kmem_free(task, sizeof (zvol_task_t)); +} + +/* + * The worker thread function performed asynchronously. + */ +static void +zvol_task_cb(void *param) +{ + zvol_task_t *task = (zvol_task_t *)param; + + 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; + case ZVOL_ASYNC_RENAME_MINORS: + zvol_rename_minors_impl(task->name1, task->name2); + break; + default: + VERIFY(0); + break; + } + + zvol_task_free(task); +} + +static void +zvol_minors_helper(spa_t *spa, zvol_async_op_t op, const char *name1, + const char *name2) +{ + zvol_task_t *task; + + if (dataset_name_hidden(name1)) + return; + if (name2 != NULL && dataset_name_hidden(name2)) + return; + task = zvol_task_alloc(op, name1, name2); + (void)taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP); +} + +void +zvol_create_minors(spa_t *spa, const char *name) +{ + zvol_minors_helper(spa, ZVOL_ASYNC_CREATE_MINORS, name, NULL); +} + +void +zvol_remove_minors(spa_t *spa, const char *name) +{ + zvol_minors_helper(spa, ZVOL_ASYNC_REMOVE_MINORS, name, NULL); +} + +void +zvol_rename_minors(spa_t *spa, const char *oldname, const char *newname) +{ + zvol_minors_helper(spa, ZVOL_ASYNC_RENAME_MINORS, oldname, newname); } static int _______________________________________________ svn-src-head@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"