Author: smh
Date: Mon Dec 22 18:39:38 2014
New Revision: 276069
URL: https://svnweb.freebsd.org/changeset/base/276069

Log:
  Fix panic when resizing ZFS zvol's
  
  Resizing a ZFS ZVOL with debug enabled would result in a panic due to
  recursion on dp_config_rwlock.
  
  The upstream change "3464 zfs synctask code needs restructuring" changed
  zvol_set_volsize to avoid the recursion on dp_config_rwlock, but this was
  missed when originally merged in by r248571 due to significant differences
  in our codebases in this area.
  
  These changes also relied on bring in changes from upstream:
  3557 dumpvp_size is not updated correctly when a dump zvol's size is
  changed, which where also not present.
  
  In order to help prevent future issues in this area a direct comparison
  and diff minimisation from current upstream version (b515258) of zvol.c.
  
  Differential Revision:        https://reviews.freebsd.org/D1302
  MFC after:    1 month
  X-MFC-With:   r276063 & r276066
  Sponsored by: Multiplay

Modified:
  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/sys/zvol.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h      Mon Dec 
22 17:54:26 2014        (r276068)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h      Mon Dec 
22 18:39:38 2014        (r276069)
@@ -43,7 +43,7 @@ extern void zvol_create_cb(objset_t *os,
 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 *, major_t, uint64_t);
+extern int zvol_set_volsize(const char *, uint64_t);
 
 #ifdef sun
 extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr);

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     Mon Dec 
22 17:54:26 2014        (r276068)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Mon Dec 
22 18:39:38 2014        (r276069)
@@ -2482,8 +2482,7 @@ zfs_prop_set_special(const char *dsname,
                err = dsl_dataset_set_refreservation(dsname, source, intval);
                break;
        case ZFS_PROP_VOLSIZE:
-               err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
-                   intval);
+               err = zvol_set_volsize(dsname, intval);
                break;
        case ZFS_PROP_VERSION:
        {

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c  Mon Dec 22 
17:54:26 2014        (r276068)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c  Mon Dec 22 
18:39:38 2014        (r276069)
@@ -97,6 +97,7 @@
 
 #include "zfs_namecheck.h"
 
+#ifndef illumos
 struct g_class zfs_zvol_class = {
        .name = "ZFS::ZVOL",
        .version = G_VERSION,
@@ -104,6 +105,7 @@ struct g_class zfs_zvol_class = {
 
 DECLARE_GEOM_CLASS(zfs_zvol_class, zfs_zvol);
 
+#endif
 void *zfsdev_state;
 static char *zvol_tag = "zvol_tag";
 
@@ -126,12 +128,14 @@ kmutex_t zfsdev_state_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;
 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 */
@@ -142,28 +146,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
  */
@@ -181,6 +197,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");
@@ -204,28 +221,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 illumos
-       dev_t dev = makedevice(maj, min);
+       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,
@@ -235,6 +254,7 @@ zvol_size_changed(zvol_state_t *zv)
        spec_size_invalidate(dev, VBLK);
        spec_size_invalidate(dev, VCHR);
 #else  /* !illumos */
+       zv->zv_volsize = volsize;
        if (zv->zv_volmode == ZFS_VOLMODE_GEOM) {
                struct g_provider *pp;
 
@@ -301,16 +321,26 @@ zvol_get_stats(objset_t *os, nvlist_t *n
 static zvol_state_t *
 zvol_minor_lookup(const char *name)
 {
+#ifdef illumos
+       minor_t minor;
+#endif
        zvol_state_t *zv;
 
        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 */
@@ -550,14 +580,21 @@ 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(&zfsdev_state_lock);
 
@@ -664,6 +701,9 @@ zvol_create_minor(const char *name)
 
        (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;
@@ -689,16 +729,15 @@ zvol_create_minor(const char *name)
        zvol_minors++;
 
        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);
 }
@@ -710,6 +749,7 @@ static int
 zvol_remove_zv(zvol_state_t *zv)
 {
 #ifdef illumos
+       char nmbuf[20];
        minor_t minor = zv->zv_minor;
 #endif
 
@@ -717,12 +757,15 @@ zvol_remove_zv(zvol_state_t *zv)
        if (zv->zv_total_opens != 0)
                return (SET_ERROR(EBUSY));
 
-       ZFS_LOG(1, "ZVOL %s destroyed.", zv->zv_name);
-
 #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();
@@ -735,8 +778,10 @@ zvol_remove_zv(zvol_state_t *zv)
        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);
 }
@@ -771,21 +816,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);
@@ -889,6 +935,27 @@ zvol_update_volsize(objset_t *os, uint64
 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;
 
@@ -908,67 +975,46 @@ zvol_remove_minors(const char *name)
 
        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(&zfsdev_state_lock);
-       zv = zvol_minor_lookup(name);
-       if ((error = dmu_objset_hold(name, FTAG, &os)) != 0) {
-               mutex_exit(&zfsdev_state_lock);
-               return (error);
-       }
-
-       if ((error = dmu_object_info(os, ZVOL_OBJ, &doi)) != 0 ||
-           (error = zvol_check_volsize(volsize,
-           doi.doi_data_block_size)) != 0)
-               goto out;
+       int error = 0;
 
-       VERIFY(dsl_prop_get_integer(name, "readonly", &readonly,
-           NULL) == 0);
-       if (readonly) {
-               error = EROFS;
-               goto out;
-       }
+       ASSERT(MUTEX_HELD(&zfsdev_state_lock));
 
-       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 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);
@@ -986,21 +1032,88 @@ zvol_set_volsize(const char *name, major
                kmem_free(physpath, MAXPATHLEN);
        }
 #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;
 
-       mutex_exit(&zfsdev_state_lock);
+       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;
+
+       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;
 
        /*
@@ -1037,6 +1150,7 @@ zvol_open(struct g_provider *pp, int fla
                pp->stripeoffset = 0;
                pp->stripesize = zv->zv_volblocksize;
        }
+#endif /* illumos */
        if ((flag & FWRITE) && (zv->zv_flags & ZVOL_RDONLY)) {
                err = SET_ERROR(EROFS);
                goto out;
@@ -1055,20 +1169,46 @@ zvol_open(struct g_provider *pp, int fla
        }
 #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(&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(&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)
 {
@@ -1086,6 +1226,7 @@ zvol_close(struct g_provider *pp, int fl
        if (zv == NULL) {
                if (locked)
                        mutex_exit(&zfsdev_state_lock);
+#endif /* illumos */
                return (SET_ERROR(ENXIO));
        }
 
@@ -1098,18 +1239,30 @@ zvol_close(struct g_provider *pp, int fl
         * 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(&zfsdev_state_lock);
+#endif
        return (error);
 }
 
@@ -1362,11 +1515,16 @@ zvol_dumpio(zvol_state_t *zv, void *addr
 
        return (error);
 }
-#endif /* illumos */
 
+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;
@@ -1374,22 +1532,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;
        }
 
@@ -1407,26 +1596,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
@@ -1435,6 +1639,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);
@@ -1449,7 +1654,7 @@ zvol_strategy(struct bio *bp)
                }
                goto unlock;
        }
-
+#endif
        while (resid != 0 && off < volsize) {
                size_t size = MIN(resid, zvol_maxphys);
 #ifdef illumos
@@ -1485,9 +1690,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;
@@ -1501,6 +1718,7 @@ out:
                g_io_deliver(bp, error);
        else
                biofinish(bp, NULL, error);
+#endif /* illumos */
 }
 
 #ifdef illumos
@@ -1578,6 +1796,7 @@ zvol_read(struct cdev *dev, struct uio *
 #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));
@@ -1637,6 +1856,7 @@ zvol_write(struct cdev *dev, struct uio 
 #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));
@@ -2369,7 +2589,7 @@ zvol_dump_fini(zvol_state_t *zv)
 
        return (0);
 }
-#endif /* illumos */
+#else  /* !illumos */
 
 static void
 zvol_geom_run(zvol_state_t *zv)
@@ -2947,3 +3167,4 @@ zvol_d_ioctl(struct cdev *dev, u_long cm
 
        return (error);
 }
+#endif /* illumos */
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to