summaryrefslogtreecommitdiffstats
path: root/module/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'module/zfs')
-rw-r--r--module/zfs/zvol.c259
1 files changed, 115 insertions, 144 deletions
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c
index 97b2e4d78..ecba516fc 100644
--- a/module/zfs/zvol.c
+++ b/module/zfs/zvol.c
@@ -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);
@@ -1131,7 +1104,7 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, 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));
@@ -1143,7 +1116,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);
}
@@ -1159,8 +1132,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)
@@ -1238,8 +1211,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);
@@ -1361,9 +1334,7 @@ zvol_open(struct block_device *bdev, fmode_t flag)
int error = 0;
boolean_t drop_suspend = B_TRUE;
- ASSERT(!MUTEX_HELD(&zvol_state_lock));
-
- 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
@@ -1372,7 +1343,7 @@ 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));
}
@@ -1396,7 +1367,7 @@ zvol_open(struct block_device *bdev, fmode_t flag)
} else {
drop_suspend = B_FALSE;
}
- mutex_exit(&zvol_state_lock);
+ rw_exit(&zvol_state_lock);
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
ASSERT(zv->zv_open_count != 0 || RW_READ_HELD(&zv->zv_suspend_lock));
@@ -1414,11 +1385,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)
@@ -1439,9 +1417,7 @@ zvol_release(struct gendisk *disk, fmode_t mode)
zvol_state_t *zv;
boolean_t drop_suspend = B_TRUE;
- ASSERT(!MUTEX_HELD(&zvol_state_lock));
-
- mutex_enter(&zvol_state_lock);
+ rw_enter(&zvol_state_lock, RW_READER);
zv = disk->private_data;
mutex_enter(&zv->zv_state_lock);
@@ -1465,7 +1441,7 @@ zvol_release(struct gendisk *disk, fmode_t mode)
} else {
drop_suspend = B_FALSE;
}
- mutex_exit(&zvol_state_lock);
+ 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));
@@ -1491,7 +1467,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:
@@ -1531,23 +1507,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);
}
@@ -1564,7 +1579,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);
@@ -1597,68 +1612,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
@@ -1711,6 +1678,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
@@ -1755,7 +1726,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);
@@ -1882,9 +1852,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);
@@ -1901,7 +1871,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));
@@ -2141,7 +2111,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);
@@ -2166,15 +2136,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)
@@ -2185,11 +2155,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);
@@ -2208,7 +2176,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);
@@ -2228,7 +2196,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);
@@ -2239,7 +2210,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);
@@ -2260,7 +2231,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);
@@ -2288,7 +2259,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 {
@@ -2665,7 +2636,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,
@@ -2702,7 +2673,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));
@@ -2719,7 +2690,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);
}