aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs/zvol.c
diff options
context:
space:
mode:
authorMatthew Macy <[email protected]>2019-09-25 09:20:30 -0700
committerBrian Behlendorf <[email protected]>2019-09-25 09:20:30 -0700
commit5df7e9d85ced424292e8312a7848b6092a29c930 (patch)
tree391e1488d4a8081ce22c7401c6392a72dc661c13 /module/zfs/zvol.c
parentd359e99c38f66732d42278c32d52cfcf1839aa4f (diff)
OpenZFS restructuring - zvol
Refactor the zvol in to platform dependent and independent bits. Reviewed-by: Allan Jude <[email protected]> Reviewed-by: Jorgen Lundman <[email protected]> Reviewed-by: Igor Kozhukhov <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Matt Macy <[email protected]> Closes #9295
Diffstat (limited to 'module/zfs/zvol.c')
-rw-r--r--module/zfs/zvol.c1157
1 files changed, 55 insertions, 1102 deletions
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c
index 840b8d008..9b26084bb 100644
--- a/module/zfs/zvol.c
+++ b/module/zfs/zvol.c
@@ -68,10 +68,6 @@
* allocated and placed on zvol_state_list, and then other minor operations
* for this zvol are going to proceed in the order of issue.
*
- * It is also worth keeping in mind that once add_disk() is called, the zvol is
- * announced to the world, and zvol_open()/zvol_release() can be called at any
- * time. Incidentally, add_disk() itself calls zvol_open()->zvol_first_open()
- * and zvol_release()->zvol_last_close() directly as well.
*/
#include <sys/dataset_kstats.h>
@@ -89,52 +85,16 @@
#include <sys/spa_impl.h>
#include <sys/zvol.h>
-#include <linux/blkdev_compat.h>
-#include <linux/task_io_accounting_ops.h>
+#include <sys/zvol_impl.h>
+
unsigned int zvol_inhibit_dev = 0;
-unsigned int zvol_major = ZVOL_MAJOR;
-unsigned int zvol_threads = 32;
-unsigned int zvol_request_sync = 0;
-unsigned int zvol_prefetch_bytes = (128 * 1024);
-unsigned long zvol_max_discard_blocks = 16384;
unsigned int zvol_volmode = ZFS_VOLMODE_GEOM;
-static taskq_t *zvol_taskq;
-static krwlock_t zvol_state_lock;
-static list_t zvol_state_list;
-
-#define ZVOL_HT_SIZE 1024
-static struct hlist_head *zvol_htable;
-#define ZVOL_HT_HEAD(hash) (&zvol_htable[(hash) & (ZVOL_HT_SIZE-1)])
-
-static struct ida zvol_ida;
-
-/*
- * The in-core state of each volume.
- */
-struct zvol_state {
- char zv_name[MAXNAMELEN]; /* name */
- uint64_t zv_volsize; /* advertised space */
- uint64_t zv_volblocksize; /* volume block size */
- objset_t *zv_objset; /* objset handle */
- uint32_t zv_flags; /* ZVOL_* flags */
- uint32_t zv_open_count; /* open counts */
- uint32_t zv_changed; /* disk changed */
- zilog_t *zv_zilog; /* ZIL handle */
- rangelock_t zv_rangelock; /* for range locking */
- dnode_t *zv_dn; /* dnode hold */
- dev_t zv_dev; /* device id */
- struct gendisk *zv_disk; /* generic disk */
- struct request_queue *zv_queue; /* request queue */
- dataset_kstats_t zv_kstat; /* zvol kstats */
- list_node_t zv_next; /* next zvol_state_t linkage */
- uint64_t zv_hash; /* name hash */
- struct hlist_node zv_hlink; /* hash link */
- kmutex_t zv_state_lock; /* protects zvol_state_t */
- atomic_t zv_suspend_ref; /* refcount for suspend */
- krwlock_t zv_suspend_lock; /* suspend lock */
-};
+struct hlist_head *zvol_htable;
+list_t zvol_state_list;
+krwlock_t zvol_state_lock;
+const zvol_platform_ops_t *ops;
typedef enum {
ZVOL_ASYNC_CREATE_MINORS,
@@ -154,19 +114,12 @@ typedef struct {
uint64_t value;
} zvol_task_t;
-#define ZVOL_RDONLY 0x1
-/*
- * Whether the zvol has been written to (as opposed to ZVOL_RDONLY, which
- * specifies whether or not the zvol _can_ be written to)
- */
-#define ZVOL_WRITTEN_TO 0x2
-
-static uint64_t
+uint64_t
zvol_name_hash(const char *name)
{
int i;
uint64_t crc = -1ULL;
- uint8_t *p = (uint8_t *)name;
+ const uint8_t *p = (const uint8_t *)name;
ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY);
for (i = 0; i < MAXNAMELEN - 1 && *p; i++, p++) {
crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ (*p)) & 0xFF];
@@ -175,38 +128,13 @@ zvol_name_hash(const char *name)
}
/*
- * Find a zvol_state_t given the full major+minor dev_t. If found,
- * return with zv_state_lock taken, otherwise, return (NULL) without
- * taking zv_state_lock.
- */
-static zvol_state_t *
-zvol_find_by_dev(dev_t dev)
-{
- zvol_state_t *zv;
-
- 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) {
- rw_exit(&zvol_state_lock);
- return (zv);
- }
- mutex_exit(&zv->zv_state_lock);
- }
- rw_exit(&zvol_state_lock);
-
- return (NULL);
-}
-
-/*
* Find a zvol_state_t given the name and hash generated by zvol_name_hash.
* If found, return with zv_suspend_lock and zv_state_lock taken, otherwise,
* return (NULL) without the taking locks. The zv_suspend_lock is always taken
* before zv_state_lock. The mode argument indicates the mode (including none)
* for zv_suspend_lock to be taken.
*/
-static zvol_state_t *
+zvol_state_t *
zvol_find_by_name_hash(const char *name, uint64_t hash, int mode)
{
zvol_state_t *zv;
@@ -258,29 +186,6 @@ zvol_find_by_name(const char *name, int mode)
return (zvol_find_by_name_hash(name, zvol_name_hash(name), mode));
}
-
-/*
- * Given a path, return TRUE if path is a ZVOL.
- */
-boolean_t
-zvol_is_zvol(const char *device)
-{
- struct block_device *bdev;
- unsigned int major;
-
- bdev = vdev_lookup_bdev(device);
- if (IS_ERR(bdev))
- return (B_FALSE);
-
- major = MAJOR(bdev->bd_dev);
- bdput(bdev);
-
- if (major == zvol_major)
- return (B_TRUE);
-
- return (B_FALSE);
-}
-
/*
* ZFS_IOC_CREATE callback handles dmu zvol and zap object creation.
*/
@@ -407,7 +312,6 @@ int
zvol_set_volsize(const char *name, uint64_t volsize)
{
objset_t *os = NULL;
- struct gendisk *disk = NULL;
uint64_t readonly;
int error;
boolean_t owned = B_FALSE;
@@ -450,7 +354,6 @@ zvol_set_volsize(const char *name, uint64_t 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));
@@ -466,8 +369,8 @@ out:
if (zv != NULL)
mutex_exit(&zv->zv_state_lock);
- if (disk != NULL)
- revalidate_disk(disk);
+ if (error == 0 && zv != NULL)
+ ops->zv_update_volsize(zv, volsize);
return (SET_ERROR(error));
}
@@ -657,7 +560,7 @@ zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = {
*/
ssize_t zvol_immediate_write_sz = 32768;
-static void
+void
zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, uint64_t offset,
uint64_t size, int sync)
{
@@ -717,92 +620,10 @@ zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, uint64_t offset,
}
}
-typedef struct zv_request {
- zvol_state_t *zv;
- struct bio *bio;
- locked_range_t *lr;
-} zv_request_t;
-
-static void
-uio_from_bio(uio_t *uio, struct bio *bio)
-{
- uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)];
- uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio);
- uio->uio_loffset = BIO_BI_SECTOR(bio) << 9;
- uio->uio_segflg = UIO_BVEC;
- uio->uio_limit = MAXOFFSET_T;
- uio->uio_resid = BIO_BI_SIZE(bio);
- uio->uio_skip = BIO_BI_SKIP(bio);
-}
-
-static void
-zvol_write(void *arg)
-{
- int error = 0;
-
- zv_request_t *zvr = arg;
- struct bio *bio = zvr->bio;
- uio_t uio = { { 0 }, 0 };
- uio_from_bio(&uio, bio);
-
- zvol_state_t *zv = zvr->zv;
- ASSERT(zv && zv->zv_open_count > 0);
- ASSERT(zv->zv_zilog != NULL);
-
- ssize_t start_resid = uio.uio_resid;
- unsigned long start_jif = jiffies;
- blk_generic_start_io_acct(zv->zv_queue, WRITE, bio_sectors(bio),
- &zv->zv_disk->part0);
-
- boolean_t sync =
- bio_is_fua(bio) || zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS;
-
- uint64_t volsize = zv->zv_volsize;
- while (uio.uio_resid > 0 && uio.uio_loffset < volsize) {
- uint64_t bytes = MIN(uio.uio_resid, DMU_MAX_ACCESS >> 1);
- uint64_t off = uio.uio_loffset;
- dmu_tx_t *tx = dmu_tx_create(zv->zv_objset);
-
- if (bytes > volsize - off) /* don't write past the end */
- bytes = volsize - off;
-
- dmu_tx_hold_write(tx, ZVOL_OBJ, off, bytes);
-
- /* This will only fail for ENOSPC */
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- dmu_tx_abort(tx);
- break;
- }
- error = dmu_write_uio_dnode(zv->zv_dn, &uio, bytes, tx);
- if (error == 0) {
- zvol_log_write(zv, tx, off, bytes, sync);
- }
- dmu_tx_commit(tx);
-
- if (error)
- break;
- }
- rangelock_exit(zvr->lr);
-
- int64_t nwritten = start_resid - uio.uio_resid;
- dataset_kstats_update_write_kstats(&zv->zv_kstat, nwritten);
- task_io_account_write(nwritten);
-
- if (sync)
- zil_commit(zv->zv_zilog, ZVOL_OBJ);
-
- rw_exit(&zv->zv_suspend_lock);
- blk_generic_end_io_acct(zv->zv_queue, WRITE, &zv->zv_disk->part0,
- start_jif);
- BIO_END_IO(bio, -error);
- kmem_free(zvr, sizeof (zv_request_t));
-}
-
/*
* Log a DKIOCFREE/free-long-range to the ZIL with TX_TRUNCATE.
*/
-static void
+void
zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off, uint64_t len,
boolean_t sync)
{
@@ -823,119 +644,6 @@ zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off, uint64_t len,
zil_itx_assign(zilog, itx, tx);
}
-static void
-zvol_discard(void *arg)
-{
- zv_request_t *zvr = arg;
- struct bio *bio = zvr->bio;
- zvol_state_t *zv = zvr->zv;
- uint64_t start = BIO_BI_SECTOR(bio) << 9;
- uint64_t size = BIO_BI_SIZE(bio);
- uint64_t end = start + size;
- boolean_t sync;
- int error = 0;
- dmu_tx_t *tx;
- unsigned long start_jif;
-
- ASSERT(zv && zv->zv_open_count > 0);
- ASSERT(zv->zv_zilog != NULL);
-
- start_jif = jiffies;
- blk_generic_start_io_acct(zv->zv_queue, WRITE, bio_sectors(bio),
- &zv->zv_disk->part0);
-
- sync = bio_is_fua(bio) || zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS;
-
- if (end > zv->zv_volsize) {
- error = SET_ERROR(EIO);
- goto unlock;
- }
-
- /*
- * Align the request to volume block boundaries when a secure erase is
- * not required. This will prevent dnode_free_range() from zeroing out
- * the unaligned parts which is slow (read-modify-write) and useless
- * since we are not freeing any space by doing so.
- */
- if (!bio_is_secure_erase(bio)) {
- start = P2ROUNDUP(start, zv->zv_volblocksize);
- end = P2ALIGN(end, zv->zv_volblocksize);
- size = end - start;
- }
-
- if (start >= end)
- goto unlock;
-
- tx = dmu_tx_create(zv->zv_objset);
- dmu_tx_mark_netfree(tx);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error != 0) {
- dmu_tx_abort(tx);
- } else {
- zvol_log_truncate(zv, tx, start, size, B_TRUE);
- dmu_tx_commit(tx);
- error = dmu_free_long_range(zv->zv_objset,
- ZVOL_OBJ, start, size);
- }
-unlock:
- rangelock_exit(zvr->lr);
-
- if (error == 0 && sync)
- zil_commit(zv->zv_zilog, ZVOL_OBJ);
-
- rw_exit(&zv->zv_suspend_lock);
- blk_generic_end_io_acct(zv->zv_queue, WRITE, &zv->zv_disk->part0,
- start_jif);
- BIO_END_IO(bio, -error);
- kmem_free(zvr, sizeof (zv_request_t));
-}
-
-static void
-zvol_read(void *arg)
-{
- int error = 0;
-
- zv_request_t *zvr = arg;
- struct bio *bio = zvr->bio;
- uio_t uio = { { 0 }, 0 };
- uio_from_bio(&uio, bio);
-
- zvol_state_t *zv = zvr->zv;
- ASSERT(zv && zv->zv_open_count > 0);
-
- ssize_t start_resid = uio.uio_resid;
- unsigned long start_jif = jiffies;
- blk_generic_start_io_acct(zv->zv_queue, READ, bio_sectors(bio),
- &zv->zv_disk->part0);
-
- uint64_t volsize = zv->zv_volsize;
- while (uio.uio_resid > 0 && uio.uio_loffset < volsize) {
- uint64_t bytes = MIN(uio.uio_resid, DMU_MAX_ACCESS >> 1);
-
- /* don't read past the end */
- if (bytes > volsize - uio.uio_loffset)
- bytes = volsize - uio.uio_loffset;
-
- error = dmu_read_uio_dnode(zv->zv_dn, &uio, bytes);
- if (error) {
- /* convert checksum errors into IO errors */
- if (error == ECKSUM)
- error = SET_ERROR(EIO);
- break;
- }
- }
- rangelock_exit(zvr->lr);
-
- int64_t nread = start_resid - uio.uio_resid;
- dataset_kstats_update_read_kstats(&zv->zv_kstat, nread);
- task_io_account_read(nread);
-
- rw_exit(&zv->zv_suspend_lock);
- blk_generic_end_io_acct(zv->zv_queue, READ, &zv->zv_disk->part0,
- start_jif);
- BIO_END_IO(bio, -error);
- kmem_free(zvr, sizeof (zv_request_t));
-}
/* ARGSUSED */
static void
@@ -952,7 +660,7 @@ zvol_get_done(zgd_t *zgd, int error)
/*
* Get data to generate a TX_WRITE intent log record.
*/
-static int
+int
zvol_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
{
zvol_state_t *zv = arg;
@@ -1017,141 +725,14 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
return (SET_ERROR(error));
}
-static MAKE_REQUEST_FN_RET
-zvol_request(struct request_queue *q, struct bio *bio)
-{
- zvol_state_t *zv = q->queuedata;
- fstrans_cookie_t cookie = spl_fstrans_mark();
- uint64_t offset = BIO_BI_SECTOR(bio) << 9;
- uint64_t size = BIO_BI_SIZE(bio);
- int rw = bio_data_dir(bio);
- zv_request_t *zvr;
-
- if (bio_has_data(bio) && offset + size > zv->zv_volsize) {
- printk(KERN_INFO
- "%s: bad access: offset=%llu, size=%lu\n",
- zv->zv_disk->disk_name,
- (long long unsigned)offset,
- (long unsigned)size);
-
- BIO_END_IO(bio, -SET_ERROR(EIO));
- goto out;
- }
-
- if (rw == WRITE) {
- boolean_t need_sync = B_FALSE;
-
- if (unlikely(zv->zv_flags & ZVOL_RDONLY)) {
- BIO_END_IO(bio, -SET_ERROR(EROFS));
- goto out;
- }
-
- /*
- * To be released in the I/O function. See the comment on
- * rangelock_enter() below.
- */
- rw_enter(&zv->zv_suspend_lock, RW_READER);
-
- /*
- * Open a ZIL if this is the first time we have written to this
- * zvol. We protect zv->zv_zilog with zv_suspend_lock rather
- * than zv_state_lock so that we don't need to acquire an
- * additional lock in this path.
- */
- if (zv->zv_zilog == NULL) {
- rw_exit(&zv->zv_suspend_lock);
- rw_enter(&zv->zv_suspend_lock, RW_WRITER);
- if (zv->zv_zilog == NULL) {
- zv->zv_zilog = zil_open(zv->zv_objset,
- zvol_get_data);
- zv->zv_flags |= ZVOL_WRITTEN_TO;
- }
- rw_downgrade(&zv->zv_suspend_lock);
- }
-
- /* bio marked as FLUSH need to flush before write */
- if (bio_is_flush(bio))
- zil_commit(zv->zv_zilog, ZVOL_OBJ);
-
- /* Some requests are just for flush and nothing else. */
- if (size == 0) {
- rw_exit(&zv->zv_suspend_lock);
- BIO_END_IO(bio, 0);
- goto out;
- }
-
- zvr = kmem_alloc(sizeof (zv_request_t), KM_SLEEP);
- zvr->zv = zv;
- zvr->bio = bio;
-
- /*
- * To be released in the I/O function. Since the I/O functions
- * are asynchronous, we take it here synchronously to make
- * sure overlapped I/Os are properly ordered.
- */
- zvr->lr = rangelock_enter(&zv->zv_rangelock, offset, size,
- RL_WRITER);
- /*
- * Sync writes and discards execute zil_commit() which may need
- * to take a RL_READER lock on the whole block being modified
- * via its zillog->zl_get_data(): to avoid circular dependency
- * issues with taskq threads execute these requests
- * synchronously here in zvol_request().
- */
- need_sync = bio_is_fua(bio) ||
- zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS;
- if (bio_is_discard(bio) || bio_is_secure_erase(bio)) {
- if (zvol_request_sync || need_sync ||
- taskq_dispatch(zvol_taskq, zvol_discard, zvr,
- TQ_SLEEP) == TASKQID_INVALID)
- zvol_discard(zvr);
- } else {
- if (zvol_request_sync || need_sync ||
- taskq_dispatch(zvol_taskq, zvol_write, zvr,
- TQ_SLEEP) == TASKQID_INVALID)
- zvol_write(zvr);
- }
- } else {
- /*
- * The SCST driver, and possibly others, may issue READ I/Os
- * with a length of zero bytes. These empty I/Os contain no
- * data and require no additional handling.
- */
- if (size == 0) {
- BIO_END_IO(bio, 0);
- goto out;
- }
-
- zvr = kmem_alloc(sizeof (zv_request_t), KM_SLEEP);
- zvr->zv = zv;
- zvr->bio = bio;
-
- rw_enter(&zv->zv_suspend_lock, RW_READER);
-
- zvr->lr = rangelock_enter(&zv->zv_rangelock, offset, size,
- RL_READER);
- if (zvol_request_sync || taskq_dispatch(zvol_taskq,
- zvol_read, zvr, TQ_SLEEP) == TASKQID_INVALID)
- zvol_read(zvr);
- }
-
-out:
- spl_fstrans_unmark(cookie);
-#ifdef HAVE_MAKE_REQUEST_FN_RET_INT
- return (0);
-#elif defined(HAVE_MAKE_REQUEST_FN_RET_QC)
- return (BLK_QC_T_NONE);
-#endif
-}
-
/*
* The zvol_state_t's are inserted into zvol_state_list and zvol_htable.
*/
-static void
+
+void
zvol_insert(zvol_state_t *zv)
{
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));
}
@@ -1196,15 +777,15 @@ zvol_setup_zv(zvol_state_t *zv)
if (error)
return (SET_ERROR(error));
- set_capacity(zv->zv_disk, volsize >> 9);
+ ops->zv_set_capacity(zv, volsize >> 9);
zv->zv_volsize = volsize;
if (ro || dmu_objset_is_snapshot(os) ||
!spa_writeable(dmu_objset_spa(os))) {
- set_disk_ro(zv->zv_disk, 1);
+ ops->zv_set_disk_ro(zv, 1);
zv->zv_flags |= ZVOL_RDONLY;
} else {
- set_disk_ro(zv->zv_disk, 0);
+ ops->zv_set_disk_ro(zv, 0);
zv->zv_flags &= ~ZVOL_RDONLY;
}
return (0);
@@ -1313,7 +894,7 @@ zvol_resume(zvol_state_t *zv)
return (SET_ERROR(error));
}
-static int
+int
zvol_first_open(zvol_state_t *zv, boolean_t readonly)
{
objset_t *os;
@@ -1344,7 +925,7 @@ zvol_first_open(zvol_state_t *zv, boolean_t readonly)
if (!mutex_owned(&spa_namespace_lock)) {
locked = mutex_tryenter(&spa_namespace_lock);
if (!locked)
- return (-SET_ERROR(ERESTARTSYS));
+ return (SET_ERROR(EINTR));
}
ro = (readonly || (strchr(zv->zv_name, '@') != NULL));
@@ -1364,10 +945,10 @@ zvol_first_open(zvol_state_t *zv, boolean_t readonly)
out_mutex:
if (locked)
mutex_exit(&spa_namespace_lock);
- return (SET_ERROR(-error));
+ return (SET_ERROR(error));
}
-static void
+void
zvol_last_close(zvol_state_t *zv)
{
ASSERT(RW_READ_HELD(&zv->zv_suspend_lock));
@@ -1379,578 +960,6 @@ zvol_last_close(zvol_state_t *zv)
zv->zv_objset = NULL;
}
-static int
-zvol_open(struct block_device *bdev, fmode_t flag)
-{
- zvol_state_t *zv;
- int error = 0;
- boolean_t drop_suspend = B_TRUE;
-
- 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
- * bdev->bd_disk->private_data to NULL is observed, or zvol_free()
- * is not called on this zv because of the positive zv_open_count.
- */
- zv = bdev->bd_disk->private_data;
- if (zv == NULL) {
- rw_exit(&zvol_state_lock);
- return (SET_ERROR(-ENXIO));
- }
-
- mutex_enter(&zv->zv_state_lock);
- /*
- * make sure zvol is not suspended during first open
- * (hold zv_suspend_lock) and respect proper lock acquisition
- * ordering - zv_suspend_lock before zv_state_lock
- */
- if (zv->zv_open_count == 0) {
- 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 {
- drop_suspend = B_FALSE;
- }
- 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));
-
- if (zv->zv_open_count == 0) {
- error = zvol_first_open(zv, !(flag & FMODE_WRITE));
- if (error)
- goto out_mutex;
- }
-
- if ((flag & FMODE_WRITE) && (zv->zv_flags & ZVOL_RDONLY)) {
- error = -EROFS;
- goto out_open_count;
- }
-
- 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)
- rw_exit(&zv->zv_suspend_lock);
- if (error == -ERESTARTSYS)
- schedule();
-
- return (SET_ERROR(error));
-}
-
-#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_RELEASE_VOID
-static void
-#else
-static int
-#endif
-zvol_release(struct gendisk *disk, fmode_t mode)
-{
- zvol_state_t *zv;
- boolean_t drop_suspend = B_TRUE;
-
- rw_enter(&zvol_state_lock, RW_READER);
- zv = disk->private_data;
-
- mutex_enter(&zv->zv_state_lock);
- ASSERT(zv->zv_open_count > 0);
- /*
- * make sure zvol is not suspended during last close
- * (hold zv_suspend_lock) and respect proper lock acquisition
- * ordering - zv_suspend_lock before zv_state_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)
- zvol_last_close(zv);
-
- mutex_exit(&zv->zv_state_lock);
-
- if (drop_suspend)
- rw_exit(&zv->zv_suspend_lock);
-
-#ifndef HAVE_BLOCK_DEVICE_OPERATIONS_RELEASE_VOID
- return (0);
-#endif
-}
-
-static int
-zvol_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg)
-{
- zvol_state_t *zv = bdev->bd_disk->private_data;
- int error = 0;
-
- ASSERT3U(zv->zv_open_count, >, 0);
-
- switch (cmd) {
- case BLKFLSBUF:
- fsync_bdev(bdev);
- invalidate_bdev(bdev);
- rw_enter(&zv->zv_suspend_lock, RW_READER);
-
- if (!(zv->zv_flags & ZVOL_RDONLY))
- txg_wait_synced(dmu_objset_pool(zv->zv_objset), 0);
-
- rw_exit(&zv->zv_suspend_lock);
- break;
-
- case BLKZNAME:
- mutex_enter(&zv->zv_state_lock);
- error = copy_to_user((void *)arg, zv->zv_name, MAXNAMELEN);
- mutex_exit(&zv->zv_state_lock);
- break;
-
- default:
- error = -ENOTTY;
- break;
- }
-
- return (SET_ERROR(error));
-}
-
-#ifdef CONFIG_COMPAT
-static int
-zvol_compat_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned cmd, unsigned long arg)
-{
- return (zvol_ioctl(bdev, mode, cmd, arg));
-}
-#else
-#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);
- }
-
- rw_exit(&zvol_state_lock);
-
- return (changed);
-}
-#endif
-
-static int zvol_revalidate_disk(struct gendisk *disk)
-{
- rw_enter(&zvol_state_lock, RW_READER);
-
- 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);
- }
-
- rw_exit(&zvol_state_lock);
-
- return (0);
-}
-
-/*
- * Provide a simple virtual geometry for legacy compatibility. For devices
- * smaller than 1 MiB a small head and sector count is used to allow very
- * tiny devices. For devices over 1 Mib a standard head and sector count
- * is used to keep the cylinders count reasonable.
- */
-static int
-zvol_getgeo(struct block_device *bdev, struct hd_geometry *geo)
-{
- zvol_state_t *zv = bdev->bd_disk->private_data;
- sector_t sectors;
-
- ASSERT3U(zv->zv_open_count, >, 0);
-
- sectors = get_capacity(zv->zv_disk);
-
- if (sectors > 2048) {
- geo->heads = 16;
- geo->sectors = 63;
- } else {
- geo->heads = 2;
- geo->sectors = 4;
- }
-
- geo->start = 0;
- geo->cylinders = sectors / (geo->heads * geo->sectors);
-
- return (0);
-}
-
-static struct kobject *
-zvol_probe(dev_t dev, int *part, void *arg)
-{
- zvol_state_t *zv;
- struct kobject *kobj;
-
- zv = zvol_find_by_dev(dev);
- kobj = zv ? get_disk_and_module(zv->zv_disk) : NULL;
- ASSERT(zv == NULL || MUTEX_HELD(&zv->zv_state_lock));
- if (zv)
- mutex_exit(&zv->zv_state_lock);
-
- return (kobj);
-}
-
-static struct block_device_operations zvol_ops = {
- .open = zvol_open,
- .release = zvol_release,
- .ioctl = zvol_ioctl,
- .compat_ioctl = zvol_compat_ioctl,
-#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS
- .check_events = zvol_check_events,
-#else
- .media_changed = zvol_media_changed,
-#endif
- .revalidate_disk = zvol_revalidate_disk,
- .getgeo = zvol_getgeo,
- .owner = THIS_MODULE,
-};
-
-/*
- * Allocate memory for a new zvol_state_t and setup the required
- * request queue and generic disk structures for the block device.
- */
-static zvol_state_t *
-zvol_alloc(dev_t dev, const char *name)
-{
- zvol_state_t *zv;
- uint64_t volmode;
-
- if (dsl_prop_get_integer(name, "volmode", &volmode, NULL) != 0)
- return (NULL);
-
- if (volmode == ZFS_VOLMODE_DEFAULT)
- volmode = zvol_volmode;
-
- if (volmode == ZFS_VOLMODE_NONE)
- return (NULL);
-
- zv = kmem_zalloc(sizeof (zvol_state_t), KM_SLEEP);
-
- list_link_init(&zv->zv_next);
-
- mutex_init(&zv->zv_state_lock, NULL, MUTEX_DEFAULT, NULL);
-
- zv->zv_queue = blk_alloc_queue(GFP_ATOMIC);
- if (zv->zv_queue == NULL)
- goto out_kmem;
-
- blk_queue_make_request(zv->zv_queue, zvol_request);
- blk_queue_set_write_cache(zv->zv_queue, B_TRUE, B_TRUE);
-
- /* Limit read-ahead to a single page to prevent over-prefetching. */
- blk_queue_set_read_ahead(zv->zv_queue, 1);
-
- /* Disable write merging in favor of the ZIO pipeline. */
- blk_queue_flag_set(QUEUE_FLAG_NOMERGES, zv->zv_queue);
-
- zv->zv_disk = alloc_disk(ZVOL_MINORS);
- if (zv->zv_disk == NULL)
- goto out_queue;
-
- zv->zv_queue->queuedata = zv;
- zv->zv_dev = dev;
- zv->zv_open_count = 0;
- strlcpy(zv->zv_name, name, MAXNAMELEN);
-
- rangelock_init(&zv->zv_rangelock, NULL, NULL);
- 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
- * gendisk->minors = 1 as noted in include/linux/genhd.h.
- * Also disable extended partition numbers (GENHD_FL_EXT_DEVT)
- * and suppresses partition scanning (GENHD_FL_NO_PART_SCAN)
- * setting gendisk->flags accordingly.
- */
- zv->zv_disk->minors = 1;
-#if defined(GENHD_FL_EXT_DEVT)
- zv->zv_disk->flags &= ~GENHD_FL_EXT_DEVT;
-#endif
-#if defined(GENHD_FL_NO_PART_SCAN)
- zv->zv_disk->flags |= GENHD_FL_NO_PART_SCAN;
-#endif
- }
- zv->zv_disk->first_minor = (dev & MINORMASK);
- zv->zv_disk->fops = &zvol_ops;
- zv->zv_disk->private_data = zv;
- zv->zv_disk->queue = zv->zv_queue;
- snprintf(zv->zv_disk->disk_name, DISK_NAME_LEN, "%s%d",
- ZVOL_DEV_NAME, (dev & MINORMASK));
-
- return (zv);
-
-out_queue:
- blk_cleanup_queue(zv->zv_queue);
-out_kmem:
- kmem_free(zv, sizeof (zvol_state_t));
-
- return (NULL);
-}
-
-/*
- * Cleanup then free a zvol_state_t which was created by zvol_alloc().
- * At this time, the structure is not opened by anyone, is taken off
- * the zvol_state_list, and has its private data set to NULL.
- * The zvol_state_lock is dropped.
- */
-static void
-zvol_free(void *arg)
-{
- zvol_state_t *zv = arg;
-
- ASSERT(!RW_LOCK_HELD(&zv->zv_suspend_lock));
- ASSERT(!MUTEX_HELD(&zv->zv_state_lock));
- ASSERT(zv->zv_open_count == 0);
- ASSERT(zv->zv_disk->private_data == NULL);
-
- rw_destroy(&zv->zv_suspend_lock);
- rangelock_fini(&zv->zv_rangelock);
-
- del_gendisk(zv->zv_disk);
- blk_cleanup_queue(zv->zv_queue);
- put_disk(zv->zv_disk);
-
- ida_simple_remove(&zvol_ida, MINOR(zv->zv_dev) >> ZVOL_MINOR_BITS);
-
- mutex_destroy(&zv->zv_state_lock);
- dataset_kstats_destroy(&zv->zv_kstat);
-
- kmem_free(zv, sizeof (zvol_state_t));
-}
-
-/*
- * Create a block device minor node and setup the linkage between it
- * and the specified volume. Once this function returns the block
- * device is live and ready for use.
- */
-static int
-zvol_create_minor_impl(const char *name)
-{
- zvol_state_t *zv;
- objset_t *os;
- dmu_object_info_t *doi;
- uint64_t volsize;
- uint64_t len;
- unsigned minor = 0;
- int error = 0;
- int idx;
- uint64_t hash = zvol_name_hash(name);
-
- if (zvol_inhibit_dev)
- return (0);
-
- idx = ida_simple_get(&zvol_ida, 0, 0, kmem_flags_convert(KM_SLEEP));
- if (idx < 0)
- return (SET_ERROR(-idx));
- minor = idx << ZVOL_MINOR_BITS;
-
- zv = zvol_find_by_name_hash(name, hash, RW_NONE);
- if (zv) {
- ASSERT(MUTEX_HELD(&zv->zv_state_lock));
- mutex_exit(&zv->zv_state_lock);
- ida_simple_remove(&zvol_ida, idx);
- return (SET_ERROR(EEXIST));
- }
-
- doi = kmem_alloc(sizeof (dmu_object_info_t), KM_SLEEP);
-
- error = dmu_objset_own(name, DMU_OST_ZVOL, B_TRUE, B_TRUE, FTAG, &os);
- if (error)
- goto out_doi;
-
- error = dmu_object_info(os, ZVOL_OBJ, doi);
- if (error)
- goto out_dmu_objset_disown;
-
- error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize);
- if (error)
- goto out_dmu_objset_disown;
-
- zv = zvol_alloc(MKDEV(zvol_major, minor), name);
- if (zv == NULL) {
- error = SET_ERROR(EAGAIN);
- goto out_dmu_objset_disown;
- }
- zv->zv_hash = hash;
-
- if (dmu_objset_is_snapshot(os))
- zv->zv_flags |= ZVOL_RDONLY;
-
- zv->zv_volblocksize = doi->doi_data_block_size;
- zv->zv_volsize = volsize;
- zv->zv_objset = os;
-
- set_capacity(zv->zv_disk, zv->zv_volsize >> 9);
-
- blk_queue_max_hw_sectors(zv->zv_queue, (DMU_MAX_ACCESS / 4) >> 9);
- blk_queue_max_segments(zv->zv_queue, UINT16_MAX);
- blk_queue_max_segment_size(zv->zv_queue, UINT_MAX);
- blk_queue_physical_block_size(zv->zv_queue, zv->zv_volblocksize);
- blk_queue_io_opt(zv->zv_queue, zv->zv_volblocksize);
- blk_queue_max_discard_sectors(zv->zv_queue,
- (zvol_max_discard_blocks * zv->zv_volblocksize) >> 9);
- blk_queue_discard_granularity(zv->zv_queue, zv->zv_volblocksize);
- blk_queue_flag_set(QUEUE_FLAG_DISCARD, zv->zv_queue);
-#ifdef QUEUE_FLAG_NONROT
- blk_queue_flag_set(QUEUE_FLAG_NONROT, zv->zv_queue);
-#endif
-#ifdef QUEUE_FLAG_ADD_RANDOM
- blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, zv->zv_queue);
-#endif
- /* This flag was introduced in kernel version 4.12. */
-#ifdef QUEUE_FLAG_SCSI_PASSTHROUGH
- blk_queue_flag_set(QUEUE_FLAG_SCSI_PASSTHROUGH, zv->zv_queue);
-#endif
-
- if (spa_writeable(dmu_objset_spa(os))) {
- if (zil_replay_disable)
- zil_destroy(dmu_objset_zil(os), B_FALSE);
- else
- zil_replay(os, zv, zvol_replay_vector);
- }
- ASSERT3P(zv->zv_kstat.dk_kstats, ==, NULL);
- dataset_kstats_create(&zv->zv_kstat, zv->zv_objset);
-
- /*
- * When udev detects the addition of the device it will immediately
- * invoke blkid(8) to determine the type of content on the device.
- * Prefetching the blocks commonly scanned by blkid(8) will speed
- * up this process.
- */
- len = MIN(MAX(zvol_prefetch_bytes, 0), SPA_MAXBLOCKSIZE);
- if (len > 0) {
- dmu_prefetch(os, ZVOL_OBJ, 0, 0, len, ZIO_PRIORITY_SYNC_READ);
- dmu_prefetch(os, ZVOL_OBJ, 0, volsize - len, len,
- ZIO_PRIORITY_SYNC_READ);
- }
-
- zv->zv_objset = NULL;
-out_dmu_objset_disown:
- dmu_objset_disown(os, B_TRUE, FTAG);
-out_doi:
- kmem_free(doi, sizeof (dmu_object_info_t));
-
- if (error == 0) {
- rw_enter(&zvol_state_lock, RW_WRITER);
- zvol_insert(zv);
- rw_exit(&zvol_state_lock);
- add_disk(zv->zv_disk);
- } else {
- ida_simple_remove(&zvol_ida, idx);
- }
-
- return (SET_ERROR(error));
-}
-
-/*
- * Rename a block device minor mode for the specified volume.
- */
-static void
-zvol_rename_minor(zvol_state_t *zv, const char *newname)
-{
- int readonly = get_disk_ro(zv->zv_disk);
-
- ASSERT(RW_LOCK_HELD(&zvol_state_lock));
- ASSERT(MUTEX_HELD(&zv->zv_state_lock));
-
- strlcpy(zv->zv_name, newname, sizeof (zv->zv_name));
-
- /* move to new hashtable entry */
- zv->zv_hash = zvol_name_hash(zv->zv_name);
- hlist_del(&zv->zv_hlink);
- hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash));
-
- /*
- * The block device's read-only state is briefly changed causing
- * a KOBJ_CHANGE uevent to be issued. This ensures udev detects
- * the name change and fixes the symlinks. This does not change
- * ZVOL_RDONLY in zv->zv_flags so the actual read-only state never
- * changes. This would normally be done using kobject_uevent() but
- * that is a GPL-only symbol which is why we need this workaround.
- */
- set_disk_ro(zv->zv_disk, !readonly);
- set_disk_ro(zv->zv_disk, readonly);
-}
-
typedef struct minors_job {
list_t *list;
list_node_t link;
@@ -2060,7 +1069,7 @@ zvol_create_minors_cb(const char *dsname, void *arg)
* traverse snapshots only, do not traverse children,
* and skip the 'dsname'
*/
- error = dmu_objset_find((char *)dsname,
+ error = dmu_objset_find(dsname,
zvol_create_snap_minor_cb, (void *)job,
DS_FIND_SNAPSHOTS);
}
@@ -2124,7 +1133,7 @@ zvol_create_minors_impl(const char *name)
&snapdev, NULL);
if (error == 0 && snapdev == ZFS_SNAPDEV_VISIBLE)
- error = zvol_create_minor_impl(name);
+ error = ops->zv_create_minor(name);
} else {
cookie = spl_fstrans_mark();
error = dmu_objset_find(parent, zvol_create_minors_cb,
@@ -2142,7 +1151,7 @@ zvol_create_minors_impl(const char *name)
while ((job = list_head(&minors_list)) != NULL) {
list_remove(&minors_list, job);
if (!job->error)
- zvol_create_minor_impl(job->name);
+ ops->zv_create_minor(job->name);
strfree(job->name);
kmem_free(job, sizeof (minors_job_t));
}
@@ -2155,7 +1164,8 @@ zvol_create_minors_impl(const char *name)
/*
* Remove minors for specified dataset including children and snapshots.
*/
-static void
+
+void
zvol_remove_minors_impl(const char *name)
{
zvol_state_t *zv, *zv_next;
@@ -2197,14 +1207,14 @@ zvol_remove_minors_impl(const char *name)
* Cleared while holding zvol_state_lock as a writer
* which will prevent zvol_open() from opening it.
*/
- zv->zv_disk->private_data = NULL;
+ ops->zv_clear_private(zv);
/* Drop zv_state_lock before zvol_free() */
mutex_exit(&zv->zv_state_lock);
/* Try parallel zv_free, if failed do it in place */
- t = taskq_dispatch(system_taskq, zvol_free, zv,
- TQ_SLEEP);
+ t = taskq_dispatch(system_taskq,
+ (task_func_t *)ops->zv_free, zv, TQ_SLEEP);
if (t == TASKQID_INVALID)
list_insert_head(&free_list, zv);
else
@@ -2218,7 +1228,7 @@ zvol_remove_minors_impl(const char *name)
/* Drop zvol_state_lock before calling zvol_free() */
while ((zv = list_head(&free_list)) != NULL) {
list_remove(&free_list, zv);
- zvol_free(zv);
+ ops->zv_free(zv);
}
if (tid != TASKQID_INVALID)
@@ -2254,12 +1264,7 @@ zvol_remove_minor_impl(const char *name)
}
zvol_remove(zv);
- /*
- * Cleared while holding zvol_state_lock as a writer
- * which will prevent zvol_open() from opening it.
- */
- zv->zv_disk->private_data = NULL;
-
+ ops->zv_clear_private(zv);
mutex_exit(&zv->zv_state_lock);
break;
} else {
@@ -2271,7 +1276,7 @@ zvol_remove_minor_impl(const char *name)
rw_exit(&zvol_state_lock);
if (zv != NULL)
- zvol_free(zv);
+ ops->zv_free(zv);
}
/*
@@ -2297,14 +1302,14 @@ zvol_rename_minors_impl(const char *oldname, const char *newname)
mutex_enter(&zv->zv_state_lock);
if (strcmp(zv->zv_name, oldname) == 0) {
- zvol_rename_minor(zv, newname);
+ ops->zv_rename_minor(zv, newname);
} else if (strncmp(zv->zv_name, oldname, oldnamelen) == 0 &&
(zv->zv_name[oldnamelen] == '/' ||
zv->zv_name[oldnamelen] == '@')) {
char *name = kmem_asprintf("%s%c%s", newname,
zv->zv_name[oldnamelen],
zv->zv_name + oldnamelen + 1);
- zvol_rename_minor(zv, name);
+ ops->zv_rename_minor(zv, name);
strfree(name);
}
@@ -2328,7 +1333,7 @@ zvol_set_snapdev_cb(const char *dsname, void *param)
switch (arg->snapdev) {
case ZFS_SNAPDEV_VISIBLE:
- (void) zvol_create_minor_impl(dsname);
+ (void) ops->zv_create_minor(dsname);
break;
case ZFS_SNAPDEV_HIDDEN:
(void) zvol_remove_minor_impl(dsname);
@@ -2378,14 +1383,14 @@ zvol_set_volmode_impl(char *name, uint64_t volmode)
case ZFS_VOLMODE_GEOM:
case ZFS_VOLMODE_DEV:
(void) zvol_remove_minor_impl(name);
- (void) zvol_create_minor_impl(name);
+ (void) ops->zv_create_minor(name);
break;
case ZFS_VOLMODE_DEFAULT:
(void) zvol_remove_minor_impl(name);
if (zvol_volmode == ZFS_VOLMODE_NONE)
break;
else /* if zvol_volmode is invalid defaults to "geom" */
- (void) zvol_create_minor_impl(name);
+ (void) ops->zv_create_minor(name);
break;
}
@@ -2680,92 +1685,40 @@ zvol_rename_minors(spa_t *spa, const char *name1, const char *name2,
taskq_wait_id(spa->spa_zvol_taskq, id);
}
+boolean_t
+zvol_is_zvol(const char *name)
+{
+
+ return (ops->zv_is_zvol(name));
+}
+
+void
+zvol_register_ops(const zvol_platform_ops_t *zvol_ops)
+{
+ ops = zvol_ops;
+}
+
int
-zvol_init(void)
+zvol_init_impl(void)
{
- int threads = MIN(MAX(zvol_threads, 1), 1024);
- int i, error;
+ int i;
list_create(&zvol_state_list, sizeof (zvol_state_t),
offsetof(zvol_state_t, zv_next));
rw_init(&zvol_state_lock, NULL, RW_DEFAULT, NULL);
- ida_init(&zvol_ida);
-
- zvol_taskq = taskq_create(ZVOL_DRIVER, threads, maxclsyspri,
- threads * 2, INT_MAX, TASKQ_PREPOPULATE | TASKQ_DYNAMIC);
- if (zvol_taskq == NULL) {
- printk(KERN_INFO "ZFS: taskq_create() failed\n");
- error = -ENOMEM;
- goto out;
- }
zvol_htable = kmem_alloc(ZVOL_HT_SIZE * sizeof (struct hlist_head),
KM_SLEEP);
- if (!zvol_htable) {
- error = -ENOMEM;
- goto out_taskq;
- }
for (i = 0; i < ZVOL_HT_SIZE; i++)
INIT_HLIST_HEAD(&zvol_htable[i]);
- error = register_blkdev(zvol_major, ZVOL_DRIVER);
- if (error) {
- printk(KERN_INFO "ZFS: register_blkdev() failed %d\n", error);
- goto out_free;
- }
-
- blk_register_region(MKDEV(zvol_major, 0), 1UL << MINORBITS,
- THIS_MODULE, zvol_probe, NULL, NULL);
-
return (0);
-
-out_free:
- kmem_free(zvol_htable, ZVOL_HT_SIZE * sizeof (struct hlist_head));
-out_taskq:
- taskq_destroy(zvol_taskq);
-out:
- ida_destroy(&zvol_ida);
- rw_destroy(&zvol_state_lock);
- list_destroy(&zvol_state_list);
-
- return (SET_ERROR(error));
}
void
-zvol_fini(void)
+zvol_fini_impl(void)
{
- zvol_remove_minors_impl(NULL);
-
- blk_unregister_region(MKDEV(zvol_major, 0), 1UL << MINORBITS);
- unregister_blkdev(zvol_major, ZVOL_DRIVER);
kmem_free(zvol_htable, ZVOL_HT_SIZE * sizeof (struct hlist_head));
-
- taskq_destroy(zvol_taskq);
list_destroy(&zvol_state_list);
rw_destroy(&zvol_state_lock);
-
- ida_destroy(&zvol_ida);
}
-
-/* BEGIN CSTYLED */
-module_param(zvol_inhibit_dev, uint, 0644);
-MODULE_PARM_DESC(zvol_inhibit_dev, "Do not create zvol device nodes");
-
-module_param(zvol_major, uint, 0444);
-MODULE_PARM_DESC(zvol_major, "Major number for zvol device");
-
-module_param(zvol_threads, uint, 0444);
-MODULE_PARM_DESC(zvol_threads, "Max number of threads to handle I/O requests");
-
-module_param(zvol_request_sync, uint, 0644);
-MODULE_PARM_DESC(zvol_request_sync, "Synchronously handle bio requests");
-
-module_param(zvol_max_discard_blocks, ulong, 0444);
-MODULE_PARM_DESC(zvol_max_discard_blocks, "Max number of blocks to discard");
-
-module_param(zvol_prefetch_bytes, uint, 0644);
-MODULE_PARM_DESC(zvol_prefetch_bytes, "Prefetch N bytes at zvol start+end");
-
-module_param(zvol_volmode, uint, 0644);
-MODULE_PARM_DESC(zvol_volmode, "Default volmode property value");
-/* END CSTYLED */