diff options
author | smh <smh@FreeBSD.org> | 2015-01-21 09:45:48 +0000 |
---|---|---|
committer | smh <smh@FreeBSD.org> | 2015-01-21 09:45:48 +0000 |
commit | 13bbed0129b77cd1cf205b4c2eb378c68d56e923 (patch) | |
tree | 07b1c24ce4d495942a06c1a1af4bb1b5be98adc9 /sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c | |
parent | 48282fa7cc9bc6b7e908f1be9747e5c5eed1375f (diff) | |
download | FreeBSD-src-13bbed0129b77cd1cf205b4c2eb378c68d56e923.zip FreeBSD-src-13bbed0129b77cd1cf205b4c2eb378c68d56e923.tar.gz |
MFC r276063:
Standardise on illumos for #ifdef's in zvol.c
MFC r276066:
Refactor zvol locking to minimise diff with upstream
MFC r276069:
Fix panic when resizing ZFS zvol's
Sponsored by: Multiplay
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c | 573 |
1 files changed, 404 insertions, 169 deletions
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 b4a388c..25289ef 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c @@ -97,6 +97,7 @@ #include "zfs_namecheck.h" +#ifndef illumos struct g_class zfs_zvol_class = { .name = "ZFS::ZVOL", .version = G_VERSION, @@ -104,19 +105,30 @@ struct g_class zfs_zvol_class = { DECLARE_GEOM_CLASS(zfs_zvol_class, zfs_zvol); +#endif void *zfsdev_state; static char *zvol_tag = "zvol_tag"; #define ZVOL_DUMPSIZE "dumpsize" /* - * The spa_namespace_lock protects the zfsdev_state structure from being - * modified while it's being used, e.g. an open that comes in before a - * create finishes. It also protects temporary opens of the dataset so that, + * This lock protects the zfsdev_state structure from being modified + * while it's being used, e.g. an open that comes in before a create + * finishes. It also protects temporary opens of the dataset so that, * e.g., an open doesn't get a spurious EBUSY. */ +#ifdef illumos +kmutex_t zfsdev_state_lock; +#else +/* + * In FreeBSD we've replaced the upstream zfsdev_state_lock with the + * spa_namespace_lock in the ZVOL code. + */ +#define zfsdev_state_lock spa_namespace_lock +#endif static uint32_t zvol_minors; +#ifndef illumos SYSCTL_DECL(_vfs_zfs); SYSCTL_NODE(_vfs_zfs, OID_AUTO, vol, CTLFLAG_RW, 0, "ZFS VOLUME"); static int volmode = ZFS_VOLMODE_GEOM; @@ -124,6 +136,7 @@ TUNABLE_INT("vfs.zfs.vol.mode", &volmode); SYSCTL_INT(_vfs_zfs_vol, OID_AUTO, mode, CTLFLAG_RWTUN, &volmode, 0, "Expose as GEOM providers (1), device files (2) or neither"); +#endif typedef struct zvol_extent { list_node_t ze_node; dva_t ze_dva; /* dva associated with this extent */ @@ -134,28 +147,40 @@ typedef struct zvol_extent { * The in-core state of each volume. */ typedef struct zvol_state { +#ifndef illumos LIST_ENTRY(zvol_state) zv_links; +#endif char zv_name[MAXPATHLEN]; /* pool/dd name */ uint64_t zv_volsize; /* amount of space we advertise */ uint64_t zv_volblocksize; /* volume block size */ +#ifdef illumos + minor_t zv_minor; /* minor number */ +#else struct cdev *zv_dev; /* non-GEOM device */ struct g_provider *zv_provider; /* GEOM provider */ +#endif uint8_t zv_min_bs; /* minimum addressable block shift */ uint8_t zv_flags; /* readonly, dumpified, etc. */ objset_t *zv_objset; /* objset handle */ +#ifdef illumos + uint32_t zv_open_count[OTYPCNT]; /* open counts */ +#endif uint32_t zv_total_opens; /* total open count */ zilog_t *zv_zilog; /* ZIL handle */ list_t zv_extents; /* List of extents for dump */ znode_t zv_znode; /* for range locking */ dmu_buf_t *zv_dbuf; /* bonus handle */ +#ifndef illumos int zv_state; int zv_volmode; /* Provide GEOM or cdev */ struct bio_queue_head zv_queue; struct mtx zv_queue_mtx; /* zv_queue mutex */ +#endif } zvol_state_t; +#ifndef illumos static LIST_HEAD(, zvol_state) all_zvols; - +#endif /* * zvol specific flags */ @@ -173,6 +198,7 @@ int zvol_maxphys = DMU_MAX_ACCESS/2; * Toggle unmap functionality. */ boolean_t zvol_unmap_enabled = B_TRUE; +#ifndef illumos SYSCTL_INT(_vfs_zfs_vol, OID_AUTO, unmap_enabled, CTLFLAG_RWTUN, &zvol_unmap_enabled, 0, "Enable UNMAP functionality"); @@ -196,28 +222,30 @@ static struct cdevsw zvol_cdevsw = { .d_flags = D_DISK | D_TRACKCLOSE, }; -extern int zfs_set_prop_nvlist(const char *, zprop_source_t, - nvlist_t *, nvlist_t *); +static void zvol_geom_run(zvol_state_t *zv); +static void zvol_geom_destroy(zvol_state_t *zv); +static int zvol_geom_access(struct g_provider *pp, int acr, int acw, int ace); +static void zvol_geom_start(struct bio *bp); +static void zvol_geom_worker(void *arg); static void zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off, uint64_t len, boolean_t sync); +#endif /* !illumos */ + +extern int zfs_set_prop_nvlist(const char *, zprop_source_t, + nvlist_t *, nvlist_t *); static int zvol_remove_zv(zvol_state_t *); static int zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio); static int zvol_dumpify(zvol_state_t *zv); static int zvol_dump_fini(zvol_state_t *zv); static int zvol_dump_init(zvol_state_t *zv, boolean_t resize); -static void zvol_geom_run(zvol_state_t *zv); -static void zvol_geom_destroy(zvol_state_t *zv); -static int zvol_geom_access(struct g_provider *pp, int acr, int acw, int ace); -static void zvol_geom_start(struct bio *bp); -static void zvol_geom_worker(void *arg); - static void -zvol_size_changed(zvol_state_t *zv) +zvol_size_changed(zvol_state_t *zv, uint64_t volsize) { -#ifdef sun - dev_t dev = makedevice(maj, min); +#ifdef illumos + dev_t dev = makedevice(ddi_driver_major(zfs_dip), zv->zv_minor); + zv->zv_volsize = volsize; VERIFY(ddi_prop_update_int64(dev, zfs_dip, "Size", volsize) == DDI_SUCCESS); VERIFY(ddi_prop_update_int64(dev, zfs_dip, @@ -226,7 +254,8 @@ zvol_size_changed(zvol_state_t *zv) /* Notify specfs to invalidate the cached size */ spec_size_invalidate(dev, VBLK); spec_size_invalidate(dev, VCHR); -#else /* !sun */ +#else /* !illumos */ + zv->zv_volsize = volsize; if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { struct g_provider *pp; @@ -237,7 +266,7 @@ zvol_size_changed(zvol_state_t *zv) g_resize_provider(pp, zv->zv_volsize); g_topology_unlock(); } -#endif /* !sun */ +#endif /* illumos */ } int @@ -293,16 +322,26 @@ zvol_get_stats(objset_t *os, nvlist_t *nv) static zvol_state_t * zvol_minor_lookup(const char *name) { +#ifdef illumos + minor_t minor; +#endif zvol_state_t *zv; - ASSERT(MUTEX_HELD(&spa_namespace_lock)); + ASSERT(MUTEX_HELD(&zfsdev_state_lock)); +#ifdef illumos + for (minor = 1; minor <= ZFSDEV_MAX_MINOR; minor++) { + zv = zfsdev_get_soft_state(minor, ZSST_ZVOL); + if (zv == NULL) + continue; +#else LIST_FOREACH(zv, &all_zvols, zv_links) { +#endif if (strcmp(zv->zv_name, name) == 0) - break; + return (zv); } - return (zv); + return (NULL); } /* extent mapping arg */ @@ -518,20 +557,20 @@ zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = { zvol_replay_err, /* TX_WRITE2 */ }; -#ifdef sun +#ifdef illumos int zvol_name2minor(const char *name, minor_t *minor) { zvol_state_t *zv; - mutex_enter(&spa_namespace_lock); + mutex_enter(&zfsdev_state_lock); zv = zvol_minor_lookup(name); if (minor && zv) *minor = zv->zv_minor; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (zv ? 0 : -1); } -#endif /* sun */ +#endif /* illumos */ /* * Create a minor node (plus a whole lot more) for the specified volume. @@ -542,19 +581,26 @@ zvol_create_minor(const char *name) zfs_soft_state_t *zs; zvol_state_t *zv; objset_t *os; + dmu_object_info_t doi; +#ifdef illumos + minor_t minor = 0; + char chrbuf[30], blkbuf[30]; +#else struct cdev *dev; struct g_provider *pp; struct g_geom *gp; - dmu_object_info_t doi; uint64_t volsize, mode; +#endif int error; +#ifndef illumos ZFS_LOG(1, "Creating ZVOL %s...", name); +#endif - mutex_enter(&spa_namespace_lock); + mutex_enter(&zfsdev_state_lock); if (zvol_minor_lookup(name) != NULL) { - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(EEXIST)); } @@ -562,20 +608,20 @@ zvol_create_minor(const char *name) error = dmu_objset_own(name, DMU_OST_ZVOL, B_TRUE, FTAG, &os); if (error) { - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (error); } -#ifdef sun +#ifdef illumos if ((minor = zfsdev_minor_alloc()) == 0) { dmu_objset_disown(os, FTAG); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(ENXIO)); } if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) { dmu_objset_disown(os, FTAG); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(EAGAIN)); } (void) ddi_prop_update_string(minor, zfs_dip, ZVOL_PROP_NAME, @@ -587,7 +633,7 @@ zvol_create_minor(const char *name) minor, DDI_PSEUDO, 0) == DDI_FAILURE) { ddi_soft_state_free(zfsdev_state, minor); dmu_objset_disown(os, FTAG); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(EAGAIN)); } @@ -598,14 +644,14 @@ zvol_create_minor(const char *name) ddi_remove_minor_node(zfs_dip, chrbuf); ddi_soft_state_free(zfsdev_state, minor); dmu_objset_disown(os, FTAG); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(EAGAIN)); } zs = ddi_get_soft_state(zfsdev_state, minor); zs->zss_type = ZSST_ZVOL; zv = zs->zss_data = kmem_zalloc(sizeof (zvol_state_t), KM_SLEEP); -#else /* !sun */ +#else /* !illumos */ zv = kmem_zalloc(sizeof(*zv), KM_SLEEP); zv->zv_state = 0; @@ -613,7 +659,7 @@ zvol_create_minor(const char *name) if (error) { kmem_free(zv, sizeof(*zv)); dmu_objset_disown(os, zvol_tag); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (error); } error = dsl_prop_get_integer(name, @@ -644,7 +690,7 @@ zvol_create_minor(const char *name) 0640, "%s/%s", ZVOL_DRIVER, name) != 0) { kmem_free(zv, sizeof(*zv)); dmu_objset_disown(os, FTAG); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(ENXIO)); } zv->zv_dev = dev; @@ -652,10 +698,13 @@ zvol_create_minor(const char *name) dev->si_drv2 = zv; } LIST_INSERT_HEAD(&all_zvols, zv, zv_links); -#endif /* !sun */ +#endif /* illumos */ (void) strlcpy(zv->zv_name, name, MAXPATHLEN); zv->zv_min_bs = DEV_BSHIFT; +#ifdef illumos + zv->zv_minor = minor; +#endif zv->zv_objset = os; if (dmu_objset_is_snapshot(os) || !spa_writeable(dmu_objset_spa(os))) zv->zv_flags |= ZVOL_RDONLY; @@ -680,17 +729,16 @@ zvol_create_minor(const char *name) zvol_minors++; - mutex_exit(&spa_namespace_lock); - -#ifndef sun + mutex_exit(&zfsdev_state_lock); +#ifndef illumos if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { zvol_geom_run(zv); g_topology_unlock(); } PICKUP_GIANT(); -#endif ZFS_LOG(1, "ZVOL %s created.", name); +#endif return (0); } @@ -701,20 +749,24 @@ zvol_create_minor(const char *name) static int zvol_remove_zv(zvol_state_t *zv) { -#ifdef sun +#ifdef illumos + char nmbuf[20]; minor_t minor = zv->zv_minor; #endif - ASSERT(MUTEX_HELD(&spa_namespace_lock)); + ASSERT(MUTEX_HELD(&zfsdev_state_lock)); if (zv->zv_total_opens != 0) return (SET_ERROR(EBUSY)); - ZFS_LOG(1, "ZVOL %s destroyed.", zv->zv_name); - -#ifdef sun +#ifdef illumos (void) snprintf(nmbuf, sizeof (nmbuf), "%u,raw", minor); ddi_remove_minor_node(zfs_dip, nmbuf); + + (void) snprintf(nmbuf, sizeof (nmbuf), "%u", minor); + ddi_remove_minor_node(zfs_dip, nmbuf); #else + ZFS_LOG(1, "ZVOL %s destroyed.", zv->zv_name); + LIST_REMOVE(zv, zv_links); if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { g_topology_lock(); @@ -722,13 +774,15 @@ zvol_remove_zv(zvol_state_t *zv) g_topology_unlock(); } else if (zv->zv_volmode == ZFS_VOLMODE_DEV) destroy_dev(zv->zv_dev); -#endif /* sun */ +#endif avl_destroy(&zv->zv_znode.z_range_avl); mutex_destroy(&zv->zv_znode.z_range_lock); - kmem_free(zv, sizeof(*zv)); - + kmem_free(zv, sizeof (zvol_state_t)); +#ifdef illumos + ddi_soft_state_free(zfsdev_state, minor); +#endif zvol_minors--; return (0); } @@ -739,13 +793,13 @@ zvol_remove_minor(const char *name) zvol_state_t *zv; int rc; - mutex_enter(&spa_namespace_lock); + mutex_enter(&zfsdev_state_lock); if ((zv = zvol_minor_lookup(name)) == NULL) { - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(ENXIO)); } rc = zvol_remove_zv(zv); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (rc); } @@ -763,21 +817,22 @@ zvol_first_open(zvol_state_t *zv) if (error) return (error); + zv->zv_objset = os; error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize); if (error) { ASSERT(error == 0); dmu_objset_disown(os, zvol_tag); return (error); } - zv->zv_objset = os; + error = dmu_bonus_hold(os, ZVOL_OBJ, zvol_tag, &zv->zv_dbuf); if (error) { dmu_objset_disown(os, zvol_tag); return (error); } - zv->zv_volsize = volsize; + + zvol_size_changed(zv, volsize); zv->zv_zilog = zil_open(os, zvol_get_data); - zvol_size_changed(zv); VERIFY(dsl_prop_get_integer(zv->zv_name, "readonly", &readonly, NULL) == 0); @@ -810,7 +865,7 @@ zvol_last_close(zvol_state_t *zv) zv->zv_objset = NULL; } -#ifdef sun +#ifdef illumos int zvol_prealloc(zvol_state_t *zv) { @@ -849,7 +904,7 @@ zvol_prealloc(zvol_state_t *zv) return (0); } -#endif /* sun */ +#endif /* illumos */ static int zvol_update_volsize(objset_t *os, uint64_t volsize) @@ -857,7 +912,7 @@ zvol_update_volsize(objset_t *os, uint64_t volsize) dmu_tx_t *tx; int error; - ASSERT(MUTEX_HELD(&spa_namespace_lock)); + ASSERT(MUTEX_HELD(&zfsdev_state_lock)); tx = dmu_tx_create(os); dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL); @@ -881,13 +936,34 @@ zvol_update_volsize(objset_t *os, uint64_t volsize) void zvol_remove_minors(const char *name) { +#ifdef illumos + zvol_state_t *zv; + char *namebuf; + minor_t minor; + + namebuf = kmem_zalloc(strlen(name) + 2, KM_SLEEP); + (void) strncpy(namebuf, name, strlen(name)); + (void) strcat(namebuf, "/"); + mutex_enter(&zfsdev_state_lock); + for (minor = 1; minor <= ZFSDEV_MAX_MINOR; minor++) { + + zv = zfsdev_get_soft_state(minor, ZSST_ZVOL); + if (zv == NULL) + continue; + if (strncmp(namebuf, zv->zv_name, strlen(namebuf)) == 0) + (void) zvol_remove_zv(zv); + } + kmem_free(namebuf, strlen(name) + 2); + + mutex_exit(&zfsdev_state_lock); +#else /* !illumos */ zvol_state_t *zv, *tzv; size_t namelen; namelen = strlen(name); DROP_GIANT(); - mutex_enter(&spa_namespace_lock); + mutex_enter(&zfsdev_state_lock); LIST_FOREACH_SAFE(zv, &all_zvols, zv_links, tzv) { if (strcmp(zv->zv_name, name) == 0 || @@ -898,69 +974,48 @@ zvol_remove_minors(const char *name) } } - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); PICKUP_GIANT(); +#endif /* illumos */ } -int -zvol_set_volsize(const char *name, major_t maj, uint64_t volsize) +static int +zvol_update_live_volsize(zvol_state_t *zv, uint64_t volsize) { - zvol_state_t *zv = NULL; - objset_t *os; - int error; - dmu_object_info_t doi; uint64_t old_volsize = 0ULL; - uint64_t readonly; - - mutex_enter(&spa_namespace_lock); - zv = zvol_minor_lookup(name); - if ((error = dmu_objset_hold(name, FTAG, &os)) != 0) { - mutex_exit(&spa_namespace_lock); - return (error); - } + int error = 0; - if ((error = dmu_object_info(os, ZVOL_OBJ, &doi)) != 0 || - (error = zvol_check_volsize(volsize, - doi.doi_data_block_size)) != 0) - goto out; + ASSERT(MUTEX_HELD(&zfsdev_state_lock)); - VERIFY(dsl_prop_get_integer(name, "readonly", &readonly, - NULL) == 0); - if (readonly) { - error = EROFS; - goto out; - } - - error = zvol_update_volsize(os, volsize); /* * Reinitialize the dump area to the new size. If we * failed to resize the dump area then restore it back to - * its original size. + * its original size. We must set the new volsize prior + * to calling dumpvp_resize() to ensure that the devices' + * size(9P) is not visible by the dump subsystem. */ - if (zv && error == 0) { + old_volsize = zv->zv_volsize; + zvol_size_changed(zv, volsize); + #ifdef ZVOL_DUMP - if (zv->zv_flags & ZVOL_DUMPIFIED) { - old_volsize = zv->zv_volsize; - zv->zv_volsize = volsize; - if ((error = zvol_dumpify(zv)) != 0 || - (error = dumpvp_resize()) != 0) { - (void) zvol_update_volsize(os, old_volsize); - zv->zv_volsize = old_volsize; - error = zvol_dumpify(zv); - } - } -#endif /* ZVOL_DUMP */ - if (error == 0) { - zv->zv_volsize = volsize; - zvol_size_changed(zv); + if (zv->zv_flags & ZVOL_DUMPIFIED) { + if ((error = zvol_dumpify(zv)) != 0 || + (error = dumpvp_resize()) != 0) { + int dumpify_error; + + (void) zvol_update_volsize(zv->zv_objset, old_volsize); + zvol_size_changed(zv, old_volsize); + dumpify_error = zvol_dumpify(zv); + error = dumpify_error ? dumpify_error : error; } } +#endif /* ZVOL_DUMP */ -#ifdef sun +#ifdef illumos /* * Generate a LUN expansion event. */ - if (zv && error == 0) { + if (error == 0) { sysevent_id_t eid; nvlist_t *attr; char *physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); @@ -977,22 +1032,89 @@ zvol_set_volsize(const char *name, major_t maj, uint64_t volsize) nvlist_free(attr); kmem_free(physpath, MAXPATHLEN); } -#endif /* sun */ +#endif /* illumos */ + return (error); +} -out: - dmu_objset_rele(os, FTAG); +int +zvol_set_volsize(const char *name, uint64_t volsize) +{ + zvol_state_t *zv = NULL; + objset_t *os; + int error; + dmu_object_info_t doi; + uint64_t readonly; + boolean_t owned = B_FALSE; + + error = dsl_prop_get_integer(name, + zfs_prop_to_name(ZFS_PROP_READONLY), &readonly, NULL); + if (error != 0) + return (error); + if (readonly) + return (SET_ERROR(EROFS)); + + mutex_enter(&zfsdev_state_lock); + zv = zvol_minor_lookup(name); + + if (zv == NULL || zv->zv_objset == NULL) { + if ((error = dmu_objset_own(name, DMU_OST_ZVOL, B_FALSE, + FTAG, &os)) != 0) { + mutex_exit(&zfsdev_state_lock); + return (error); + } + owned = B_TRUE; + if (zv != NULL) + zv->zv_objset = os; + } else { + os = zv->zv_objset; + } + + if ((error = dmu_object_info(os, ZVOL_OBJ, &doi)) != 0 || + (error = zvol_check_volsize(volsize, doi.doi_data_block_size)) != 0) + goto out; - mutex_exit(&spa_namespace_lock); + error = zvol_update_volsize(os, volsize); + if (error == 0 && zv != NULL) + error = zvol_update_live_volsize(zv, volsize); +out: + if (owned) { + dmu_objset_disown(os, FTAG); + if (zv != NULL) + zv->zv_objset = NULL; + } + mutex_exit(&zfsdev_state_lock); return (error); } /*ARGSUSED*/ +#ifdef illumos +int +zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr) +#else static int zvol_open(struct g_provider *pp, int flag, int count) +#endif { zvol_state_t *zv; int err = 0; +#ifdef illumos + + mutex_enter(&zfsdev_state_lock); + + zv = zfsdev_get_soft_state(getminor(*devp), ZSST_ZVOL); + if (zv == NULL) { + mutex_exit(&zfsdev_state_lock); + return (SET_ERROR(ENXIO)); + } + + if (zv->zv_total_opens == 0) + err = zvol_first_open(zv); + if (err) { + mutex_exit(&zfsdev_state_lock); + return (err); + } +#else /* !illumos */ boolean_t locked = B_FALSE; /* @@ -1006,15 +1128,15 @@ zvol_open(struct g_provider *pp, int flag, int count) * recursively, but that function already has all the * necessary protection. */ - if (!MUTEX_HELD(&spa_namespace_lock)) { - mutex_enter(&spa_namespace_lock); + if (!MUTEX_HELD(&zfsdev_state_lock)) { + mutex_enter(&zfsdev_state_lock); locked = B_TRUE; } zv = pp->private; if (zv == NULL) { if (locked) - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(ENXIO)); } @@ -1022,13 +1144,14 @@ zvol_open(struct g_provider *pp, int flag, int count) err = zvol_first_open(zv); if (err) { if (locked) - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (err); } pp->mediasize = zv->zv_volsize; pp->stripeoffset = 0; pp->stripesize = zv->zv_volblocksize; } +#endif /* illumos */ if ((flag & FWRITE) && (zv->zv_flags & ZVOL_RDONLY)) { err = SET_ERROR(EROFS); goto out; @@ -1047,20 +1170,46 @@ zvol_open(struct g_provider *pp, int flag, int count) } #endif +#ifdef illumos + if (zv->zv_open_count[otyp] == 0 || otyp == OTYP_LYR) { + zv->zv_open_count[otyp]++; + zv->zv_total_opens++; + } + mutex_exit(&zfsdev_state_lock); +#else zv->zv_total_opens += count; if (locked) - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); +#endif return (err); out: if (zv->zv_total_opens == 0) zvol_last_close(zv); +#ifdef illumos + mutex_exit(&zfsdev_state_lock); +#else if (locked) - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); +#endif return (err); } /*ARGSUSED*/ +#ifdef illumos +int +zvol_close(dev_t dev, int flag, int otyp, cred_t *cr) +{ + minor_t minor = getminor(dev); + zvol_state_t *zv; + int error = 0; + + mutex_enter(&zfsdev_state_lock); + + zv = zfsdev_get_soft_state(minor, ZSST_ZVOL); + if (zv == NULL) { + mutex_exit(&zfsdev_state_lock); +#else /* !illumos */ static int zvol_close(struct g_provider *pp, int flag, int count) { @@ -1069,15 +1218,16 @@ zvol_close(struct g_provider *pp, int flag, int count) boolean_t locked = B_FALSE; /* See comment in zvol_open(). */ - if (!MUTEX_HELD(&spa_namespace_lock)) { - mutex_enter(&spa_namespace_lock); + if (!MUTEX_HELD(&zfsdev_state_lock)) { + mutex_enter(&zfsdev_state_lock); locked = B_TRUE; } zv = pp->private; if (zv == NULL) { if (locked) - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); +#endif /* illumos */ return (SET_ERROR(ENXIO)); } @@ -1090,18 +1240,30 @@ zvol_close(struct g_provider *pp, int flag, int count) * If the open count is zero, this is a spurious close. * That indicates a bug in the kernel / DDI framework. */ +#ifdef illumos + ASSERT(zv->zv_open_count[otyp] != 0); +#endif ASSERT(zv->zv_total_opens != 0); /* * You may get multiple opens, but only one close. */ +#ifdef illumos + zv->zv_open_count[otyp]--; + zv->zv_total_opens--; +#else zv->zv_total_opens -= count; +#endif if (zv->zv_total_opens == 0) zvol_last_close(zv); +#ifdef illumos + mutex_exit(&zfsdev_state_lock); +#else if (locked) - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); +#endif return (error); } @@ -1261,7 +1423,7 @@ zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, offset_t off, ssize_t resid, } } -#ifdef sun +#ifdef illumos static int zvol_dumpio_vdev(vdev_t *vd, void *addr, uint64_t offset, uint64_t origoffset, uint64_t size, boolean_t doread, boolean_t isdump) @@ -1354,11 +1516,16 @@ zvol_dumpio(zvol_state_t *zv, void *addr, uint64_t offset, uint64_t size, return (error); } -#endif /* sun */ +int +zvol_strategy(buf_t *bp) +{ + zfs_soft_state_t *zs = NULL; +#else /* !illumos */ void zvol_strategy(struct bio *bp) { +#endif /* illumos */ zvol_state_t *zv; uint64_t off, volsize; size_t resid; @@ -1366,22 +1533,53 @@ zvol_strategy(struct bio *bp) objset_t *os; rl_t *rl; int error = 0; +#ifdef illumos + boolean_t doread = bp->b_flags & B_READ; +#else boolean_t doread = 0; +#endif boolean_t is_dumpified; boolean_t sync; +#ifdef illumos + if (getminor(bp->b_edev) == 0) { + error = SET_ERROR(EINVAL); + } else { + zs = ddi_get_soft_state(zfsdev_state, getminor(bp->b_edev)); + if (zs == NULL) + error = SET_ERROR(ENXIO); + else if (zs->zss_type != ZSST_ZVOL) + error = SET_ERROR(EINVAL); + } + + if (error) { + bioerror(bp, error); + biodone(bp); + return (0); + } + + zv = zs->zss_data; + + if (!(bp->b_flags & B_READ) && (zv->zv_flags & ZVOL_RDONLY)) { + bioerror(bp, EROFS); + biodone(bp); + return (0); + } + + off = ldbtob(bp->b_blkno); +#else /* !illumos */ if (bp->bio_to) zv = bp->bio_to->private; else zv = bp->bio_dev->si_drv2; if (zv == NULL) { - error = ENXIO; + error = SET_ERROR(ENXIO); goto out; } if (bp->bio_cmd != BIO_READ && (zv->zv_flags & ZVOL_RDONLY)) { - error = EROFS; + error = SET_ERROR(EROFS); goto out; } @@ -1399,26 +1597,41 @@ zvol_strategy(struct bio *bp) } off = bp->bio_offset; +#endif /* illumos */ volsize = zv->zv_volsize; os = zv->zv_objset; ASSERT(os != NULL); +#ifdef illumos + bp_mapin(bp); + addr = bp->b_un.b_addr; + resid = bp->b_bcount; + + if (resid > 0 && (off < 0 || off >= volsize)) { + bioerror(bp, EIO); + biodone(bp); + return (0); + } + + is_dumpified = zv->zv_flags & ZVOL_DUMPIFIED; + sync = ((!(bp->b_flags & B_ASYNC) && + !(zv->zv_flags & ZVOL_WCE)) || + (zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS)) && + !doread && !is_dumpified; +#else /* !illumos */ addr = bp->bio_data; resid = bp->bio_length; if (resid > 0 && (off < 0 || off >= volsize)) { - error = EIO; + error = SET_ERROR(EIO); goto out; } -#ifdef illumos - is_dumpified = zv->zv_flags & ZVOL_DUMPIFIED; -#else is_dumpified = B_FALSE; -#endif - sync = !doread && !is_dumpified && + sync = !doread && !is_dumpified && zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS; +#endif /* illumos */ /* * There must be no buffer changes when doing a dmu_sync() because @@ -1427,6 +1640,7 @@ zvol_strategy(struct bio *bp) rl = zfs_range_lock(&zv->zv_znode, off, resid, doread ? RL_READER : RL_WRITER); +#ifndef illumos if (bp->bio_cmd == BIO_DELETE) { dmu_tx_t *tx = dmu_tx_create(zv->zv_objset); error = dmu_tx_assign(tx, TXG_WAIT); @@ -1441,7 +1655,7 @@ zvol_strategy(struct bio *bp) } goto unlock; } - +#endif while (resid != 0 && off < volsize) { size_t size = MIN(resid, zvol_maxphys); #ifdef illumos @@ -1477,9 +1691,21 @@ zvol_strategy(struct bio *bp) addr += size; resid -= size; } +#ifndef illumos unlock: +#endif zfs_range_unlock(rl); +#ifdef illumos + if ((bp->b_resid = resid) == bp->b_bcount) + bioerror(bp, off > volsize ? EINVAL : error); + + if (sync) + zil_commit(zv->zv_zilog, ZVOL_OBJ); + biodone(bp); + + return (0); +#else /* !illumos */ bp->bio_completed = bp->bio_length - resid; if (bp->bio_completed < bp->bio_length && off > volsize) error = EINVAL; @@ -1493,9 +1719,10 @@ out: g_io_deliver(bp, error); else biofinish(bp, NULL, error); +#endif /* illumos */ } -#ifdef sun +#ifdef illumos /* * Set the buffer count to the zvol maximum transfer. * Using our own routine instead of the default minphys() @@ -1551,17 +1778,17 @@ int zvol_read(dev_t dev, uio_t *uio, cred_t *cr) { minor_t minor = getminor(dev); -#else +#else /* !illumos */ int zvol_read(struct cdev *dev, struct uio *uio, int ioflag) { -#endif +#endif /* illumos */ zvol_state_t *zv; uint64_t volsize; rl_t *rl; int error = 0; -#ifdef sun +#ifdef illumos zv = zfsdev_get_soft_state(minor, ZSST_ZVOL); if (zv == NULL) return (SET_ERROR(ENXIO)); @@ -1570,6 +1797,7 @@ zvol_read(struct cdev *dev, struct uio *uio, int ioflag) #endif volsize = zv->zv_volsize; + /* uio_loffset == volsize isn't an error as its required for EOF processing. */ if (uio->uio_resid > 0 && (uio->uio_loffset < 0 || uio->uio_loffset > volsize)) return (SET_ERROR(EIO)); @@ -1603,24 +1831,24 @@ zvol_read(struct cdev *dev, struct uio *uio, int ioflag) return (error); } -#ifdef sun +#ifdef illumos /*ARGSUSED*/ int zvol_write(dev_t dev, uio_t *uio, cred_t *cr) { minor_t minor = getminor(dev); -#else +#else /* !illumos */ int zvol_write(struct cdev *dev, struct uio *uio, int ioflag) { -#endif +#endif /* illumos */ zvol_state_t *zv; uint64_t volsize; rl_t *rl; int error = 0; boolean_t sync; -#ifdef sun +#ifdef illumos zv = zfsdev_get_soft_state(minor, ZSST_ZVOL); if (zv == NULL) return (SET_ERROR(ENXIO)); @@ -1629,6 +1857,7 @@ zvol_write(struct cdev *dev, struct uio *uio, int ioflag) #endif volsize = zv->zv_volsize; + /* uio_loffset == volsize isn't an error as its required for EOF processing. */ if (uio->uio_resid > 0 && (uio->uio_loffset < 0 || uio->uio_loffset > volsize)) return (SET_ERROR(EIO)); @@ -1639,9 +1868,7 @@ zvol_write(struct cdev *dev, struct uio *uio, int ioflag) zvol_minphys, uio); return (error); } -#endif -#ifdef sun sync = !(zv->zv_flags & ZVOL_WCE) || #else sync = (ioflag & IO_SYNC) || @@ -1678,7 +1905,7 @@ zvol_write(struct cdev *dev, struct uio *uio, int ioflag) return (error); } -#ifdef sun +#ifdef illumos int zvol_getefi(void *arg, int flag, uint64_t vs, uint8_t bs) { @@ -1807,7 +2034,7 @@ zvol_log_write_minor(void *minor_hdl, dmu_tx_t *tx, offset_t off, ssize_t resid, /* * END entry points to allow external callers access to the volume. */ -#endif /* sun */ +#endif /* illumos */ /* * Log a DKIOCFREE/free-long-range to the ZIL with TX_TRUNCATE. @@ -1833,7 +2060,7 @@ zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off, uint64_t len, zil_itx_assign(zilog, itx, tx); } -#ifdef sun +#ifdef illumos /* * Dirtbag ioctls to support mkfs(1M) for UFS filesystems. See dkio(7I). * Also a dirtbag dkio ioctl for unmap/free-block functionality. @@ -1847,12 +2074,12 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) int error = 0; rl_t *rl; - mutex_enter(&spa_namespace_lock); + mutex_enter(&zfsdev_state_lock); zv = zfsdev_get_soft_state(getminor(dev), ZSST_ZVOL); if (zv == NULL) { - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(ENXIO)); } ASSERT(zv->zv_total_opens > 0); @@ -1870,7 +2097,7 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) dki.dki_unit = getminor(dev); dki.dki_maxtransfer = 1 << (SPA_OLD_MAXBLOCKSHIFT - zv->zv_min_bs); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); if (ddi_copyout(&dki, (void *)arg, sizeof (dki), flag)) error = SET_ERROR(EFAULT); return (error); @@ -1884,7 +2111,7 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) dkm.dki_lbsize = 1U << zv->zv_min_bs; dkm.dki_capacity = zv->zv_volsize >> zv->zv_min_bs; dkm.dki_media_type = DK_UNKNOWN; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); if (ddi_copyout(&dkm, (void *)arg, sizeof (dkm), flag)) error = SET_ERROR(EFAULT); return (error); @@ -1899,7 +2126,7 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) dkmext.dki_pbsize = zv->zv_volblocksize; dkmext.dki_capacity = zv->zv_volsize >> zv->zv_min_bs; dkmext.dki_media_type = DK_UNKNOWN; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); if (ddi_copyout(&dkmext, (void *)arg, sizeof (dkmext), flag)) error = SET_ERROR(EFAULT); return (error); @@ -1910,14 +2137,14 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) uint64_t vs = zv->zv_volsize; uint8_t bs = zv->zv_min_bs; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); error = zvol_getefi((void *)arg, flag, vs, bs); return (error); } case DKIOCFLUSHWRITECACHE: dkc = (struct dk_callback *)arg; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); zil_commit(zv->zv_zilog, ZVOL_OBJ); if ((flag & FKIOCTL) && dkc != NULL && dkc->dkc_callback) { (*dkc->dkc_callback)(dkc->dkc_cookie, error); @@ -1943,10 +2170,10 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) } if (wce) { zv->zv_flags |= ZVOL_WCE; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); } else { zv->zv_flags &= ~ZVOL_WCE; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); zil_commit(zv->zv_zilog, ZVOL_OBJ); } return (0); @@ -1998,7 +2225,7 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) if (df.df_start >= zv->zv_volsize) break; /* No need to do anything... */ - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); rl = zfs_range_lock(&zv->zv_znode, df.df_start, df.df_length, RL_WRITER); @@ -2045,10 +2272,10 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) break; } - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (error); } -#endif /* sun */ +#endif /* illumos */ int zvol_busy(void) @@ -2061,17 +2288,24 @@ zvol_init(void) { VERIFY(ddi_soft_state_init(&zfsdev_state, sizeof (zfs_soft_state_t), 1) == 0); +#ifdef illumos + mutex_init(&zfsdev_state_lock, NULL, MUTEX_DEFAULT, NULL); +#else ZFS_LOG(1, "ZVOL Initialized."); +#endif } void zvol_fini(void) { +#ifdef illumos + mutex_destroy(&zfsdev_state_lock); +#endif ddi_soft_state_fini(&zfsdev_state); ZFS_LOG(1, "ZVOL Deinitialized."); } -#ifdef sun +#ifdef illumos /*ARGSUSED*/ static int zfs_mvdev_dump_feature_check(void *arg, dmu_tx_t *tx) @@ -2104,7 +2338,7 @@ zvol_dump_init(zvol_state_t *zv, boolean_t resize) uint64_t version = spa_version(spa); enum zio_checksum checksum; - ASSERT(MUTEX_HELD(&spa_namespace_lock)); + ASSERT(MUTEX_HELD(&zfsdev_state_lock)); ASSERT(vd->vdev_ops == &vdev_root_ops); error = dmu_free_long_range(zv->zv_objset, ZVOL_OBJ, 0, @@ -2356,7 +2590,7 @@ zvol_dump_fini(zvol_state_t *zv) return (0); } -#endif /* sun */ +#else /* !illumos */ static void zvol_geom_run(zvol_state_t *zv) @@ -2671,7 +2905,7 @@ zvol_rename_minor(zvol_state_t *zv, const char *newname) struct g_provider *pp; struct cdev *dev; - ASSERT(MUTEX_HELD(&spa_namespace_lock)); + ASSERT(MUTEX_HELD(&zfsdev_state_lock)); if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { g_topology_lock(); @@ -2724,8 +2958,8 @@ zvol_rename_minors(const char *oldname, const char *newname) DROP_GIANT(); /* See comment in zvol_open(). */ - if (!MUTEX_HELD(&spa_namespace_lock)) { - mutex_enter(&spa_namespace_lock); + if (!MUTEX_HELD(&zfsdev_state_lock)) { + mutex_enter(&zfsdev_state_lock); locked = B_TRUE; } @@ -2743,7 +2977,7 @@ zvol_rename_minors(const char *oldname, const char *newname) } if (locked) - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); PICKUP_GIANT(); } @@ -2753,17 +2987,17 @@ zvol_d_open(struct cdev *dev, int flags, int fmt, struct thread *td) zvol_state_t *zv; int err = 0; - mutex_enter(&spa_namespace_lock); + mutex_enter(&zfsdev_state_lock); zv = dev->si_drv2; if (zv == NULL) { - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return(ENXIO); /* zvol_create_minor() not done yet */ } if (zv->zv_total_opens == 0) err = zvol_first_open(zv); if (err) { - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (err); } if ((flags & FWRITE) && (zv->zv_flags & ZVOL_RDONLY)) { @@ -2785,12 +3019,12 @@ zvol_d_open(struct cdev *dev, int flags, int fmt, struct thread *td) #endif zv->zv_total_opens++; - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (err); out: if (zv->zv_total_opens == 0) zvol_last_close(zv); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (err); } @@ -2800,10 +3034,10 @@ zvol_d_close(struct cdev *dev, int flags, int fmt, struct thread *td) zvol_state_t *zv; int err = 0; - mutex_enter(&spa_namespace_lock); + mutex_enter(&zfsdev_state_lock); zv = dev->si_drv2; if (zv == NULL) { - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return(ENXIO); } @@ -2826,7 +3060,7 @@ zvol_d_close(struct cdev *dev, int flags, int fmt, struct thread *td) if (zv->zv_total_opens == 0) zvol_last_close(zv); - mutex_exit(&spa_namespace_lock); + mutex_exit(&zfsdev_state_lock); return (0); } @@ -2934,3 +3168,4 @@ zvol_d_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct threa return (error); } +#endif /* illumos */ |