]> git.proxmox.com Git - mirror_zfs-debian.git/blobdiff - module/zfs/zvol.c
New upstream version 0.7.11
[mirror_zfs-debian.git] / module / zfs / zvol.c
index 3e7059b340bbf4f975a4b559899969206950915a..03f95630283f098715c86e4ebbd748498e427731 100644 (file)
@@ -99,7 +99,7 @@ unsigned long zvol_max_discard_blocks = 16384;
 unsigned int zvol_volmode = ZFS_VOLMODE_GEOM;
 
 static taskq_t *zvol_taskq;
-static kmutex_t zvol_state_lock;
+static krwlock_t zvol_state_lock;
 static list_t zvol_state_list;
 
 #define        ZVOL_HT_SIZE    1024
@@ -176,17 +176,17 @@ zvol_find_by_dev(dev_t dev)
 {
        zvol_state_t *zv;
 
-       mutex_enter(&zvol_state_lock);
+       rw_enter(&zvol_state_lock, RW_READER);
        for (zv = list_head(&zvol_state_list); zv != NULL;
            zv = list_next(&zvol_state_list, zv)) {
                mutex_enter(&zv->zv_state_lock);
                if (zv->zv_dev == dev) {
-                       mutex_exit(&zvol_state_lock);
+                       rw_exit(&zvol_state_lock);
                        return (zv);
                }
                mutex_exit(&zv->zv_state_lock);
        }
-       mutex_exit(&zvol_state_lock);
+       rw_exit(&zvol_state_lock);
 
        return (NULL);
 }
@@ -204,7 +204,7 @@ zvol_find_by_name_hash(const char *name, uint64_t hash, int mode)
        zvol_state_t *zv;
        struct hlist_node *p = NULL;
 
-       mutex_enter(&zvol_state_lock);
+       rw_enter(&zvol_state_lock, RW_READER);
        hlist_for_each(p, ZVOL_HT_HEAD(hash)) {
                zv = hlist_entry(p, zvol_state_t, zv_hlink);
                mutex_enter(&zv->zv_state_lock);
@@ -227,12 +227,12 @@ zvol_find_by_name_hash(const char *name, uint64_t hash, int mode)
                                    strncmp(zv->zv_name, name, MAXNAMELEN)
                                    == 0);
                        }
-                       mutex_exit(&zvol_state_lock);
+                       rw_exit(&zvol_state_lock);
                        return (zv);
                }
                mutex_exit(&zv->zv_state_lock);
        }
-       mutex_exit(&zvol_state_lock);
+       rw_exit(&zvol_state_lock);
 
        return (NULL);
 }
@@ -339,24 +339,6 @@ zvol_get_stats(objset_t *os, nvlist_t *nv)
        return (SET_ERROR(error));
 }
 
-static void
-zvol_size_changed(zvol_state_t *zv, uint64_t volsize)
-{
-       struct block_device *bdev;
-
-       ASSERT(MUTEX_HELD(&zv->zv_state_lock));
-
-       bdev = bdget_disk(zv->zv_disk, 0);
-       if (bdev == NULL)
-               return;
-
-       set_capacity(zv->zv_disk, volsize >> 9);
-       zv->zv_volsize = volsize;
-       check_disk_size_change(zv->zv_disk, bdev);
-
-       bdput(bdev);
-}
-
 /*
  * Sanity check volume size.
  */
@@ -409,31 +391,17 @@ zvol_update_volsize(uint64_t volsize, objset_t *os)
        return (error);
 }
 
-static int
-zvol_update_live_volsize(zvol_state_t *zv, uint64_t volsize)
-{
-       zvol_size_changed(zv, volsize);
-
-       /*
-        * We should post a event here describing the expansion.  However,
-        * the zfs_ereport_post() interface doesn't nicely support posting
-        * events for zvols, it assumes events relate to vdevs or zios.
-        */
-
-       return (0);
-}
-
 /*
- * Set ZFS_PROP_VOLSIZE set entry point.
+ * Set ZFS_PROP_VOLSIZE set entry point.  Note that modifying the volume
+ * size will result in a udev "change" event being generated.
  */
 int
 zvol_set_volsize(const char *name, uint64_t volsize)
 {
-       zvol_state_t *zv = NULL;
        objset_t *os = NULL;
-       int error;
-       dmu_object_info_t *doi;
+       struct gendisk *disk = NULL;
        uint64_t readonly;
+       int error;
        boolean_t owned = B_FALSE;
 
        error = dsl_prop_get_integer(name,
@@ -443,7 +411,7 @@ zvol_set_volsize(const char *name, uint64_t volsize)
        if (readonly)
                return (SET_ERROR(EROFS));
 
-       zv = zvol_find_by_name(name, RW_READER);
+       zvol_state_t *zv = zvol_find_by_name(name, RW_READER);
 
        ASSERT(zv == NULL || (MUTEX_HELD(&zv->zv_state_lock) &&
            RW_READ_HELD(&zv->zv_suspend_lock)));
@@ -464,16 +432,18 @@ zvol_set_volsize(const char *name, uint64_t volsize)
                os = zv->zv_objset;
        }
 
-       doi = kmem_alloc(sizeof (dmu_object_info_t), KM_SLEEP);
+       dmu_object_info_t *doi = kmem_alloc(sizeof (*doi), KM_SLEEP);
 
        if ((error = dmu_object_info(os, ZVOL_OBJ, doi)) ||
            (error = zvol_check_volsize(volsize, doi->doi_data_block_size)))
                goto out;
 
        error = zvol_update_volsize(volsize, os);
-
-       if (error == 0 && zv != NULL)
-               error = zvol_update_live_volsize(zv, volsize);
+       if (error == 0 && zv != NULL) {
+               zv->zv_volsize = volsize;
+               zv->zv_changed = 1;
+               disk = zv->zv_disk;
+       }
 out:
        kmem_free(doi, sizeof (dmu_object_info_t));
 
@@ -488,6 +458,9 @@ out:
        if (zv != NULL)
                mutex_exit(&zv->zv_state_lock);
 
+       if (disk != NULL)
+               revalidate_disk(disk);
+
        return (SET_ERROR(error));
 }
 
@@ -543,8 +516,8 @@ zvol_set_volblocksize(const char *name, uint64_t volblocksize)
        if (zv == NULL)
                return (SET_ERROR(ENXIO));
 
-       ASSERT(MUTEX_HELD(&zv->zv_state_lock) &&
-           RW_READ_HELD(&zv->zv_suspend_lock));
+       ASSERT(MUTEX_HELD(&zv->zv_state_lock));
+       ASSERT(RW_READ_HELD(&zv->zv_suspend_lock));
 
        if (zv->zv_flags & ZVOL_RDONLY) {
                mutex_exit(&zv->zv_state_lock);
@@ -1120,7 +1093,7 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
 static void
 zvol_insert(zvol_state_t *zv)
 {
-       ASSERT(MUTEX_HELD(&zvol_state_lock));
+       ASSERT(RW_WRITE_HELD(&zvol_state_lock));
        ASSERT3U(MINOR(zv->zv_dev) & ZVOL_MINOR_MASK, ==, 0);
        list_insert_head(&zvol_state_list, zv);
        hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash));
@@ -1132,7 +1105,7 @@ zvol_insert(zvol_state_t *zv)
 static void
 zvol_remove(zvol_state_t *zv)
 {
-       ASSERT(MUTEX_HELD(&zvol_state_lock));
+       ASSERT(RW_WRITE_HELD(&zvol_state_lock));
        list_remove(&zvol_state_list, zv);
        hlist_del(&zv->zv_hlink);
 }
@@ -1148,8 +1121,8 @@ zvol_setup_zv(zvol_state_t *zv)
        uint64_t ro;
        objset_t *os = zv->zv_objset;
 
-       ASSERT(MUTEX_HELD(&zv->zv_state_lock) &&
-           RW_LOCK_HELD(&zv->zv_suspend_lock));
+       ASSERT(MUTEX_HELD(&zv->zv_state_lock));
+       ASSERT(RW_LOCK_HELD(&zv->zv_suspend_lock));
 
        error = dsl_prop_get_integer(zv->zv_name, "readonly", &ro, NULL);
        if (error)
@@ -1227,8 +1200,8 @@ zvol_suspend(const char *name)
                return (NULL);
 
        /* block all I/O, release in zvol_resume. */
-       ASSERT(MUTEX_HELD(&zv->zv_state_lock) &&
-           RW_WRITE_HELD(&zv->zv_suspend_lock));
+       ASSERT(MUTEX_HELD(&zv->zv_state_lock));
+       ASSERT(RW_WRITE_HELD(&zv->zv_suspend_lock));
 
        atomic_inc(&zv->zv_suspend_ref);
 
@@ -1347,11 +1320,9 @@ zvol_open(struct block_device *bdev, fmode_t flag)
 {
        zvol_state_t *zv;
        int error = 0;
-       boolean_t drop_suspend = B_FALSE;
-
-       ASSERT(!mutex_owned(&zvol_state_lock));
+       boolean_t drop_suspend = B_TRUE;
 
-       mutex_enter(&zvol_state_lock);
+       rw_enter(&zvol_state_lock, RW_READER);
        /*
         * Obtain a copy of private_data under the zvol_state_lock to make
         * sure that either the result of zvol free code path setting
@@ -1360,26 +1331,34 @@ zvol_open(struct block_device *bdev, fmode_t flag)
         */
        zv = bdev->bd_disk->private_data;
        if (zv == NULL) {
-               mutex_exit(&zvol_state_lock);
+               rw_exit(&zvol_state_lock);
                return (SET_ERROR(-ENXIO));
        }
 
-       /* take zv_suspend_lock before zv_state_lock */
-       rw_enter(&zv->zv_suspend_lock, RW_READER);
-
        mutex_enter(&zv->zv_state_lock);
-
        /*
         * make sure zvol is not suspended during first open
-        * (hold zv_suspend_lock), otherwise, drop the lock
+        * (hold zv_suspend_lock) and respect proper lock acquisition
+        * ordering - zv_suspend_lock before zv_state_lock
         */
        if (zv->zv_open_count == 0) {
-               drop_suspend = B_TRUE;
+               if (!rw_tryenter(&zv->zv_suspend_lock, RW_READER)) {
+                       mutex_exit(&zv->zv_state_lock);
+                       rw_enter(&zv->zv_suspend_lock, RW_READER);
+                       mutex_enter(&zv->zv_state_lock);
+                       /* check to see if zv_suspend_lock is needed */
+                       if (zv->zv_open_count != 0) {
+                               rw_exit(&zv->zv_suspend_lock);
+                               drop_suspend = B_FALSE;
+                       }
+               }
        } else {
-               rw_exit(&zv->zv_suspend_lock);
+               drop_suspend = B_FALSE;
        }
+       rw_exit(&zvol_state_lock);
 
-       mutex_exit(&zvol_state_lock);
+       ASSERT(MUTEX_HELD(&zv->zv_state_lock));
+       ASSERT(zv->zv_open_count != 0 || RW_READ_HELD(&zv->zv_suspend_lock));
 
        if (zv->zv_open_count == 0) {
                error = zvol_first_open(zv);
@@ -1394,11 +1373,18 @@ zvol_open(struct block_device *bdev, fmode_t flag)
 
        zv->zv_open_count++;
 
+       mutex_exit(&zv->zv_state_lock);
+       if (drop_suspend)
+               rw_exit(&zv->zv_suspend_lock);
+
        check_disk_change(bdev);
 
+       return (0);
+
 out_open_count:
        if (zv->zv_open_count == 0)
                zvol_last_close(zv);
+
 out_mutex:
        mutex_exit(&zv->zv_state_lock);
        if (drop_suspend)
@@ -1417,28 +1403,36 @@ static int
 zvol_release(struct gendisk *disk, fmode_t mode)
 {
        zvol_state_t *zv;
-       boolean_t drop_suspend = B_FALSE;
+       boolean_t drop_suspend = B_TRUE;
 
-       ASSERT(!mutex_owned(&zvol_state_lock));
-
-       mutex_enter(&zvol_state_lock);
+       rw_enter(&zvol_state_lock, RW_READER);
        zv = disk->private_data;
-       ASSERT(zv && zv->zv_open_count > 0);
-
-       /* take zv_suspend_lock before zv_state_lock */
-       rw_enter(&zv->zv_suspend_lock, RW_READER);
 
        mutex_enter(&zv->zv_state_lock);
-       mutex_exit(&zvol_state_lock);
-
+       ASSERT(zv->zv_open_count > 0);
        /*
         * make sure zvol is not suspended during last close
-        * (hold zv_suspend_lock), otherwise, drop the lock
+        * (hold zv_suspend_lock) and respect proper lock acquisition
+        * ordering - zv_suspend_lock before zv_state_lock
         */
-       if (zv->zv_open_count == 1)
-               drop_suspend = B_TRUE;
-       else
-               rw_exit(&zv->zv_suspend_lock);
+       if (zv->zv_open_count == 1) {
+               if (!rw_tryenter(&zv->zv_suspend_lock, RW_READER)) {
+                       mutex_exit(&zv->zv_state_lock);
+                       rw_enter(&zv->zv_suspend_lock, RW_READER);
+                       mutex_enter(&zv->zv_state_lock);
+                       /* check to see if zv_suspend_lock is needed */
+                       if (zv->zv_open_count != 1) {
+                               rw_exit(&zv->zv_suspend_lock);
+                               drop_suspend = B_FALSE;
+                       }
+               }
+       } else {
+               drop_suspend = B_FALSE;
+       }
+       rw_exit(&zvol_state_lock);
+
+       ASSERT(MUTEX_HELD(&zv->zv_state_lock));
+       ASSERT(zv->zv_open_count != 1 || RW_READ_HELD(&zv->zv_suspend_lock));
 
        zv->zv_open_count--;
        if (zv->zv_open_count == 0)
@@ -1461,7 +1455,7 @@ zvol_ioctl(struct block_device *bdev, fmode_t mode,
        zvol_state_t *zv = bdev->bd_disk->private_data;
        int error = 0;
 
-       ASSERT(zv && zv->zv_open_count > 0);
+       ASSERT3U(zv->zv_open_count, >, 0);
 
        switch (cmd) {
        case BLKFLSBUF:
@@ -1501,23 +1495,62 @@ zvol_compat_ioctl(struct block_device *bdev, fmode_t mode,
 #define        zvol_compat_ioctl       NULL
 #endif
 
+/*
+ * Linux 2.6.38 preferred interface.
+ */
+#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS
+static unsigned int
+zvol_check_events(struct gendisk *disk, unsigned int clearing)
+{
+       unsigned int mask = 0;
+
+       rw_enter(&zvol_state_lock, RW_READER);
+
+       zvol_state_t *zv = disk->private_data;
+       if (zv != NULL) {
+               mutex_enter(&zv->zv_state_lock);
+               mask = zv->zv_changed ? DISK_EVENT_MEDIA_CHANGE : 0;
+               zv->zv_changed = 0;
+               mutex_exit(&zv->zv_state_lock);
+       }
+
+       rw_exit(&zvol_state_lock);
+
+       return (mask);
+}
+#else
 static int zvol_media_changed(struct gendisk *disk)
 {
+       int changed = 0;
+
+       rw_enter(&zvol_state_lock, RW_READER);
+
        zvol_state_t *zv = disk->private_data;
+       if (zv != NULL) {
+               mutex_enter(&zv->zv_state_lock);
+               changed = zv->zv_changed;
+               zv->zv_changed = 0;
+               mutex_exit(&zv->zv_state_lock);
+       }
 
-       ASSERT(zv && zv->zv_open_count > 0);
+       rw_exit(&zvol_state_lock);
 
-       return (zv->zv_changed);
+       return (changed);
 }
+#endif
 
 static int zvol_revalidate_disk(struct gendisk *disk)
 {
-       zvol_state_t *zv = disk->private_data;
+       rw_enter(&zvol_state_lock, RW_READER);
 
-       ASSERT(zv && zv->zv_open_count > 0);
+       zvol_state_t *zv = disk->private_data;
+       if (zv != NULL) {
+               mutex_enter(&zv->zv_state_lock);
+               set_capacity(zv->zv_disk, zv->zv_volsize >> SECTOR_BITS);
+               mutex_exit(&zv->zv_state_lock);
+       }
 
-       zv->zv_changed = 0;
-       set_capacity(zv->zv_disk, zv->zv_volsize >> 9);
+       rw_exit(&zvol_state_lock);
 
        return (0);
 }
@@ -1534,7 +1567,7 @@ zvol_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        zvol_state_t *zv = bdev->bd_disk->private_data;
        sector_t sectors;
 
-       ASSERT(zv && zv->zv_open_count > 0);
+       ASSERT3U(zv->zv_open_count, >, 0);
 
        sectors = get_capacity(zv->zv_disk);
 
@@ -1567,68 +1600,20 @@ zvol_probe(dev_t dev, int *part, void *arg)
        return (kobj);
 }
 
-#ifdef HAVE_BDEV_BLOCK_DEVICE_OPERATIONS
 static struct block_device_operations zvol_ops = {
        .open                   = zvol_open,
        .release                = zvol_release,
        .ioctl                  = zvol_ioctl,
        .compat_ioctl           = zvol_compat_ioctl,
-       .media_changed          = zvol_media_changed,
-       .revalidate_disk        = zvol_revalidate_disk,
-       .getgeo                 = zvol_getgeo,
-       .owner                  = THIS_MODULE,
-};
-
-#else /* HAVE_BDEV_BLOCK_DEVICE_OPERATIONS */
-
-static int
-zvol_open_by_inode(struct inode *inode, struct file *file)
-{
-       return (zvol_open(inode->i_bdev, file->f_mode));
-}
-
-static int
-zvol_release_by_inode(struct inode *inode, struct file *file)
-{
-       return (zvol_release(inode->i_bdev->bd_disk, file->f_mode));
-}
-
-static int
-zvol_ioctl_by_inode(struct inode *inode, struct file *file,
-    unsigned int cmd, unsigned long arg)
-{
-       if (file == NULL || inode == NULL)
-               return (SET_ERROR(-EINVAL));
-
-       return (zvol_ioctl(inode->i_bdev, file->f_mode, cmd, arg));
-}
-
-#ifdef CONFIG_COMPAT
-static long
-zvol_compat_ioctl_by_inode(struct file *file,
-    unsigned int cmd, unsigned long arg)
-{
-       if (file == NULL)
-               return (SET_ERROR(-EINVAL));
-
-       return (zvol_compat_ioctl(file->f_dentry->d_inode->i_bdev,
-           file->f_mode, cmd, arg));
-}
+#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS
+       .check_events           = zvol_check_events,
 #else
-#define        zvol_compat_ioctl_by_inode      NULL
-#endif
-
-static struct block_device_operations zvol_ops = {
-       .open                   = zvol_open_by_inode,
-       .release                = zvol_release_by_inode,
-       .ioctl                  = zvol_ioctl_by_inode,
-       .compat_ioctl           = zvol_compat_ioctl_by_inode,
        .media_changed          = zvol_media_changed,
+#endif
        .revalidate_disk        = zvol_revalidate_disk,
        .getgeo                 = zvol_getgeo,
        .owner                  = THIS_MODULE,
 };
-#endif /* HAVE_BDEV_BLOCK_DEVICE_OPERATIONS */
 
 /*
  * Allocate memory for a new zvol_state_t and setup the required
@@ -1681,6 +1666,10 @@ zvol_alloc(dev_t dev, const char *name)
        rw_init(&zv->zv_suspend_lock, NULL, RW_DEFAULT, NULL);
 
        zv->zv_disk->major = zvol_major;
+#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS
+       zv->zv_disk->events = DISK_EVENT_MEDIA_CHANGE;
+#endif
+
        if (volmode == ZFS_VOLMODE_DEV) {
                /*
                 * ZFS_VOLMODE_DEV disable partitioning on ZVOL devices: set
@@ -1725,7 +1714,6 @@ zvol_free(void *arg)
 {
        zvol_state_t *zv = arg;
 
-       ASSERT(!MUTEX_HELD(&zvol_state_lock));
        ASSERT(!RW_LOCK_HELD(&zv->zv_suspend_lock));
        ASSERT(!MUTEX_HELD(&zv->zv_state_lock));
        ASSERT(zv->zv_open_count == 0);
@@ -1852,9 +1840,9 @@ out_doi:
        kmem_free(doi, sizeof (dmu_object_info_t));
 
        if (error == 0) {
-               mutex_enter(&zvol_state_lock);
+               rw_enter(&zvol_state_lock, RW_WRITER);
                zvol_insert(zv);
-               mutex_exit(&zvol_state_lock);
+               rw_exit(&zvol_state_lock);
                add_disk(zv->zv_disk);
        } else {
                ida_simple_remove(&zvol_ida, idx);
@@ -1871,7 +1859,7 @@ zvol_rename_minor(zvol_state_t *zv, const char *newname)
 {
        int readonly = get_disk_ro(zv->zv_disk);
 
-       ASSERT(MUTEX_HELD(&zvol_state_lock));
+       ASSERT(RW_LOCK_HELD(&zvol_state_lock));
        ASSERT(MUTEX_HELD(&zv->zv_state_lock));
 
        strlcpy(zv->zv_name, newname, sizeof (zv->zv_name));
@@ -2111,7 +2099,7 @@ zvol_remove_minors_impl(const char *name)
        list_create(&free_list, sizeof (zvol_state_t),
            offsetof(zvol_state_t, zv_next));
 
-       mutex_enter(&zvol_state_lock);
+       rw_enter(&zvol_state_lock, RW_WRITER);
 
        for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) {
                zv_next = list_next(&zvol_state_list, zv);
@@ -2136,15 +2124,15 @@ zvol_remove_minors_impl(const char *name)
                        zvol_remove(zv);
 
                        /*
-                        * clear this while holding zvol_state_lock so
-                        * zvol_open won't open it
+                        * Cleared while holding zvol_state_lock as a writer
+                        * which will prevent zvol_open() from opening it.
                         */
                        zv->zv_disk->private_data = NULL;
 
                        /* Drop zv_state_lock before zvol_free() */
                        mutex_exit(&zv->zv_state_lock);
 
-                       /* try parallel zv_free, if failed do it in place */
+                       /* Try parallel zv_free, if failed do it in place */
                        t = taskq_dispatch(system_taskq, zvol_free, zv,
                            TQ_SLEEP);
                        if (t == TASKQID_INVALID)
@@ -2155,11 +2143,9 @@ zvol_remove_minors_impl(const char *name)
                        mutex_exit(&zv->zv_state_lock);
                }
        }
-       mutex_exit(&zvol_state_lock);
+       rw_exit(&zvol_state_lock);
 
-       /*
-        * Drop zvol_state_lock before calling zvol_free()
-        */
+       /* Drop zvol_state_lock before calling zvol_free() */
        while ((zv = list_head(&free_list)) != NULL) {
                list_remove(&free_list, zv);
                zvol_free(zv);
@@ -2178,7 +2164,7 @@ zvol_remove_minor_impl(const char *name)
        if (zvol_inhibit_dev)
                return;
 
-       mutex_enter(&zvol_state_lock);
+       rw_enter(&zvol_state_lock, RW_WRITER);
 
        for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) {
                zv_next = list_next(&zvol_state_list, zv);
@@ -2198,7 +2184,10 @@ zvol_remove_minor_impl(const char *name)
                        }
                        zvol_remove(zv);
 
-                       /* clear this so zvol_open won't open it */
+                       /*
+                        * Cleared while holding zvol_state_lock as a writer
+                        * which will prevent zvol_open() from opening it.
+                        */
                        zv->zv_disk->private_data = NULL;
 
                        mutex_exit(&zv->zv_state_lock);
@@ -2209,7 +2198,7 @@ zvol_remove_minor_impl(const char *name)
        }
 
        /* Drop zvol_state_lock before calling zvol_free() */
-       mutex_exit(&zvol_state_lock);
+       rw_exit(&zvol_state_lock);
 
        if (zv != NULL)
                zvol_free(zv);
@@ -2230,7 +2219,7 @@ zvol_rename_minors_impl(const char *oldname, const char *newname)
        oldnamelen = strlen(oldname);
        newnamelen = strlen(newname);
 
-       mutex_enter(&zvol_state_lock);
+       rw_enter(&zvol_state_lock, RW_READER);
 
        for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) {
                zv_next = list_next(&zvol_state_list, zv);
@@ -2258,7 +2247,7 @@ zvol_rename_minors_impl(const char *oldname, const char *newname)
                mutex_exit(&zv->zv_state_lock);
        }
 
-       mutex_exit(&zvol_state_lock);
+       rw_exit(&zvol_state_lock);
 }
 
 typedef struct zvol_snapdev_cb_arg {
@@ -2635,7 +2624,7 @@ zvol_init(void)
 
        list_create(&zvol_state_list, sizeof (zvol_state_t),
            offsetof(zvol_state_t, zv_next));
-       mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL);
+       rw_init(&zvol_state_lock, NULL, RW_DEFAULT, NULL);
        ida_init(&zvol_ida);
 
        zvol_taskq = taskq_create(ZVOL_DRIVER, threads, maxclsyspri,
@@ -2672,7 +2661,7 @@ out_taskq:
        taskq_destroy(zvol_taskq);
 out:
        ida_destroy(&zvol_ida);
-       mutex_destroy(&zvol_state_lock);
+       rw_destroy(&zvol_state_lock);
        list_destroy(&zvol_state_list);
 
        return (SET_ERROR(error));
@@ -2689,7 +2678,7 @@ zvol_fini(void)
 
        taskq_destroy(zvol_taskq);
        list_destroy(&zvol_state_list);
-       mutex_destroy(&zvol_state_lock);
+       rw_destroy(&zvol_state_lock);
 
        ida_destroy(&zvol_ida);
 }