summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/lofi.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/lofi.c')
-rw-r--r--usr/src/uts/common/io/lofi.c1460
1 files changed, 1146 insertions, 314 deletions
diff --git a/usr/src/uts/common/io/lofi.c b/usr/src/uts/common/io/lofi.c
index e11f910a91..0312a54b62 100644
--- a/usr/src/uts/common/io/lofi.c
+++ b/usr/src/uts/common/io/lofi.c
@@ -23,6 +23,7 @@
*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2016 Andrey Sokolov
+ * Copyright 2016 Toomas Soome <tsoome@me.com>
*/
/*
@@ -34,14 +35,25 @@
* mounting images of filesystems.
*
* lofi is controlled through /dev/lofictl - this is the only device exported
- * during attach, and is minor number 0. lofiadm communicates with lofi through
- * ioctls on this device. When a file is attached to lofi, block and character
- * devices are exported in /dev/lofi and /dev/rlofi. Currently, these devices
- * are identified by their minor number, and the minor number is also used
- * as the name in /dev/lofi. If we ever decide to support virtual disks,
- * we'll have to divide the minor number space to identify fdisk partitions
- * and slices, and the name will then be the minor number shifted down a
- * few bits. Minor devices are tracked with state structures handled with
+ * during attach, and is instance number 0. lofiadm communicates with lofi
+ * through ioctls on this device. When a file is attached to lofi, block and
+ * character devices are exported in /dev/lofi and /dev/rlofi. These devices
+ * are identified by lofi instance number, and the instance number is also used
+ * as the name in /dev/lofi.
+ *
+ * Virtual disks, or, labeled lofi, implements virtual disk support to
+ * support partition table and related tools. Such mappings will cause
+ * block and character devices to be exported in /dev/dsk and /dev/rdsk
+ * directories.
+ *
+ * To support virtual disks, the instance number space is divided to two
+ * parts, upper part for instance number and lower part for minor number
+ * space to identify partitions and slices. The virtual disk support is
+ * implemented by stacking cmlb module. For virtual disks, the partition
+ * related ioctl calls are routed to cmlb module. Compression and encryption
+ * is not supported for virtual disks.
+ *
+ * Mapped devices are tracked with state structures handled with
* ddi_soft_state(9F) for simplicity.
*
* A file attached to lofi is opened when attached and not closed until
@@ -131,6 +143,10 @@
#include <sys/crypto/common.h>
#include <sys/crypto/api.h>
#include <sys/rctl.h>
+#include <sys/vtoc.h>
+#include <sys/scsi/scsi.h> /* for DTYPE_DIRECT */
+#include <sys/scsi/impl/uscsi.h>
+#include <sys/sysevent/dev.h>
#include <LzmaDec.h>
#define NBLOCKS_PROP_NAME "Nblocks"
@@ -151,10 +167,16 @@
return (EINVAL); \
}
-static dev_info_t *lofi_dip = NULL;
-static void *lofi_statep = NULL;
+#define DEVFS_CHANNEL "devfsadm_event_channel"
+#define LOFI_TIMEOUT 30
+static evchan_t *lofi_chan;
+static kmutex_t lofi_chan_lock;
+static kcondvar_t lofi_chan_cv;
+static nvlist_t *lofi_devlink_cache;
+
+static void *lofi_statep;
static kmutex_t lofi_lock; /* state lock */
-static id_space_t *lofi_minor_id;
+static id_space_t *lofi_id; /* lofi ID values */
static list_t lofi_list;
static zone_key_t lofi_zone_key;
@@ -204,6 +226,17 @@ lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
{lzma_decompress, NULL, 0, "lzma"}
};
+static void lofi_strategy_task(void *);
+static int lofi_tg_rdwr(dev_info_t *, uchar_t, void *, diskaddr_t,
+ size_t, void *);
+static int lofi_tg_getinfo(dev_info_t *, int, void *, void *);
+
+struct cmlb_tg_ops lofi_tg_ops = {
+ TG_DK_OPS_VERSION_1,
+ lofi_tg_rdwr,
+ lofi_tg_getinfo
+};
+
/*ARGSUSED*/
static void
*SzAlloc(void *p, size_t size)
@@ -240,47 +273,24 @@ lofi_free_comp_cache(struct lofi_state *lsp)
static int
is_opened(struct lofi_state *lsp)
{
- ASSERT(MUTEX_HELD(&lofi_lock));
- return (lsp->ls_chr_open || lsp->ls_blk_open || lsp->ls_lyr_open_count);
-}
+ int i;
+ boolean_t last = B_TRUE;
-static int
-mark_opened(struct lofi_state *lsp, int otyp)
-{
ASSERT(MUTEX_HELD(&lofi_lock));
- switch (otyp) {
- case OTYP_CHR:
- lsp->ls_chr_open = 1;
- break;
- case OTYP_BLK:
- lsp->ls_blk_open = 1;
- break;
- case OTYP_LYR:
- lsp->ls_lyr_open_count++;
- break;
- default:
- return (-1);
+ for (i = 0; i < LOFI_PART_MAX; i++) {
+ if (lsp->ls_open_lyr[i]) {
+ last = B_FALSE;
+ break;
+ }
}
- return (0);
-}
-static void
-mark_closed(struct lofi_state *lsp, int otyp)
-{
- ASSERT(MUTEX_HELD(&lofi_lock));
- switch (otyp) {
- case OTYP_CHR:
- lsp->ls_chr_open = 0;
- break;
- case OTYP_BLK:
- lsp->ls_blk_open = 0;
- break;
- case OTYP_LYR:
- lsp->ls_lyr_open_count--;
- break;
- default:
- break;
+ for (i = 0; last && (i < OTYP_LYR); i++) {
+ if (lsp->ls_open_reg[i]) {
+ last = B_FALSE;
+ }
}
+
+ return (!last);
}
static void
@@ -320,10 +330,171 @@ lofi_free_crypto(struct lofi_state *lsp)
}
}
+/* ARGSUSED */
+static int
+lofi_tg_rdwr(dev_info_t *dip, uchar_t cmd, void *bufaddr, diskaddr_t start,
+ size_t length, void *tg_cookie)
+{
+ struct lofi_state *lsp;
+ buf_t *bp;
+ int instance;
+ int rv = 0;
+
+ instance = ddi_get_instance(dip);
+ if (instance == 0) /* control node does not have disk */
+ return (ENXIO);
+
+ lsp = ddi_get_soft_state(lofi_statep, instance);
+
+ if (lsp == NULL)
+ return (ENXIO);
+
+ if (cmd != TG_READ && cmd != TG_WRITE)
+ return (EINVAL);
+
+ /*
+ * Make sure the mapping is set up by checking lsp->ls_vp_ready.
+ */
+ mutex_enter(&lsp->ls_vp_lock);
+ while (lsp->ls_vp_ready == B_FALSE)
+ cv_wait(&lsp->ls_vp_cv, &lsp->ls_vp_lock);
+ mutex_exit(&lsp->ls_vp_lock);
+
+ if (P2PHASE(length, (1U << lsp->ls_lbshift)) != 0) {
+ /* We can only transfer whole blocks at a time! */
+ return (EINVAL);
+ }
+
+ bp = getrbuf(KM_SLEEP);
+
+ if (cmd == TG_READ) {
+ bp->b_flags = B_READ;
+ } else {
+ if (lsp->ls_readonly == B_TRUE) {
+ freerbuf(bp);
+ return (EROFS);
+ }
+ bp->b_flags = B_WRITE;
+ }
+
+ bp->b_un.b_addr = bufaddr;
+ bp->b_bcount = length;
+ bp->b_lblkno = start;
+ bp->b_private = NULL;
+ bp->b_edev = lsp->ls_dev;
+
+ if (lsp->ls_kstat) {
+ mutex_enter(lsp->ls_kstat->ks_lock);
+ kstat_waitq_enter(KSTAT_IO_PTR(lsp->ls_kstat));
+ mutex_exit(lsp->ls_kstat->ks_lock);
+ }
+ (void) taskq_dispatch(lsp->ls_taskq, lofi_strategy_task, bp, KM_SLEEP);
+ (void) biowait(bp);
+
+ rv = geterror(bp);
+ freerbuf(bp);
+ return (rv);
+}
+
+/*
+ * Get device geometry info for cmlb.
+ *
+ * We have mapped disk image as virtual block device and have to report
+ * physical/virtual geometry to cmlb.
+ *
+ * So we have two principal cases:
+ * 1. Uninitialised image without any existing labels,
+ * for this case we fabricate the data based on mapped image.
+ * 2. Image with existing label information.
+ * Since we have no information how the image was created (it may be
+ * dump from some physical device), we need to rely on label information
+ * from image, or we get "corrupted label" errors.
+ * NOTE: label can be MBR, MBR+SMI, GPT
+ */
+static int
+lofi_tg_getinfo(dev_info_t *dip, int cmd, void *arg, void *tg_cookie)
+{
+ struct lofi_state *lsp;
+ int instance;
+ int ashift;
+
+ _NOTE(ARGUNUSED(tg_cookie));
+ instance = ddi_get_instance(dip);
+ if (instance == 0) /* control device has no storage */
+ return (ENXIO);
+
+ lsp = ddi_get_soft_state(lofi_statep, instance);
+
+ if (lsp == NULL)
+ return (ENXIO);
+
+ /*
+ * Make sure the mapping is set up by checking lsp->ls_vp_ready.
+ *
+ * When mapping is created, new lofi instance is created and
+ * lofi_attach() will call cmlb_attach() as part of the procedure
+ * to set the mapping up. This chain of events will happen in
+ * the same thread.
+ * Since cmlb_attach() will call lofi_tg_getinfo to get
+ * capacity, we return error on that call if cookie is set,
+ * otherwise lofi_attach will be stuck as the mapping is not yet
+ * finalized and lofi is not yet ready.
+ * Note, such error is not fatal for cmlb, as the label setup
+ * will be finalized when cmlb_validate() is called.
+ */
+ mutex_enter(&lsp->ls_vp_lock);
+ if (tg_cookie != NULL && lsp->ls_vp_ready == B_FALSE) {
+ mutex_exit(&lsp->ls_vp_lock);
+ return (ENXIO);
+ }
+ while (lsp->ls_vp_ready == B_FALSE)
+ cv_wait(&lsp->ls_vp_cv, &lsp->ls_vp_lock);
+ mutex_exit(&lsp->ls_vp_lock);
+
+ ashift = lsp->ls_lbshift;
+
+ switch (cmd) {
+ case TG_GETPHYGEOM: {
+ cmlb_geom_t *geomp = arg;
+
+ geomp->g_capacity =
+ (lsp->ls_vp_size - lsp->ls_crypto_offset) >> ashift;
+ geomp->g_nsect = lsp->ls_dkg.dkg_nsect;
+ geomp->g_nhead = lsp->ls_dkg.dkg_nhead;
+ geomp->g_acyl = lsp->ls_dkg.dkg_acyl;
+ geomp->g_ncyl = lsp->ls_dkg.dkg_ncyl;
+ geomp->g_secsize = (1U << ashift);
+ geomp->g_intrlv = lsp->ls_dkg.dkg_intrlv;
+ geomp->g_rpm = lsp->ls_dkg.dkg_rpm;
+ return (0);
+ }
+
+ case TG_GETCAPACITY:
+ *(diskaddr_t *)arg =
+ (lsp->ls_vp_size - lsp->ls_crypto_offset) >> ashift;
+ return (0);
+
+ case TG_GETBLOCKSIZE:
+ *(uint32_t *)arg = (1U << ashift);
+ return (0);
+
+ case TG_GETATTR: {
+ tg_attribute_t *tgattr = arg;
+
+ tgattr->media_is_writable = !lsp->ls_readonly;
+ tgattr->media_is_solid_state = B_FALSE;
+ return (0);
+ }
+
+ default:
+ return (EINVAL);
+ }
+}
+
static void
lofi_destroy(struct lofi_state *lsp, cred_t *credp)
{
- minor_t minor = getminor(lsp->ls_dev);
+ int id = LOFI_MINOR2ID(getminor(lsp->ls_dev));
int i;
ASSERT(MUTEX_HELD(&lofi_lock));
@@ -345,13 +516,17 @@ lofi_destroy(struct lofi_state *lsp, cred_t *credp)
sizeof (struct compbuf) * lofi_taskq_nthreads);
}
- (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag,
- 1, 0, credp, NULL);
- VN_RELE(lsp->ls_vp);
+ if (lsp->ls_vp != NULL) {
+ (void) VOP_PUTPAGE(lsp->ls_vp, 0, 0, B_INVAL, credp, NULL);
+ (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag,
+ 1, 0, credp, NULL);
+ VN_RELE(lsp->ls_vp);
+ }
if (lsp->ls_stacked_vp != lsp->ls_vp)
VN_RELE(lsp->ls_stacked_vp);
- taskq_destroy(lsp->ls_taskq);
+ if (lsp->ls_taskq != NULL)
+ taskq_destroy(lsp->ls_taskq);
if (lsp->ls_kstat != NULL)
kstat_delete(lsp->ls_kstat);
@@ -374,28 +549,27 @@ lofi_destroy(struct lofi_state *lsp, cred_t *credp)
mutex_destroy(&lsp->ls_comp_bufs_lock);
mutex_destroy(&lsp->ls_kstat_lock);
mutex_destroy(&lsp->ls_vp_lock);
+ cv_destroy(&lsp->ls_vp_cv);
+ lsp->ls_vp_ready = B_FALSE;
- ASSERT(ddi_get_soft_state(lofi_statep, minor) == lsp);
- ddi_soft_state_free(lofi_statep, minor);
- id_free(lofi_minor_id, minor);
+ ASSERT(ddi_get_soft_state(lofi_statep, id) == lsp);
+ (void) ndi_devi_offline(lsp->ls_dip, NDI_DEVI_REMOVE);
+ id_free(lofi_id, id);
}
static void
-lofi_free_dev(dev_t dev)
+lofi_free_dev(struct lofi_state *lsp)
{
- minor_t minor = getminor(dev);
- char namebuf[50];
-
ASSERT(MUTEX_HELD(&lofi_lock));
- (void) ddi_prop_remove(dev, lofi_dip, ZONE_PROP_NAME);
- (void) ddi_prop_remove(dev, lofi_dip, SIZE_PROP_NAME);
- (void) ddi_prop_remove(dev, lofi_dip, NBLOCKS_PROP_NAME);
-
- (void) snprintf(namebuf, sizeof (namebuf), "%d", minor);
- ddi_remove_minor_node(lofi_dip, namebuf);
- (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", minor);
- ddi_remove_minor_node(lofi_dip, namebuf);
+ if (lsp->ls_cmlbhandle != NULL) {
+ cmlb_invalidate(lsp->ls_cmlbhandle, 0);
+ cmlb_detach(lsp->ls_cmlbhandle, 0);
+ cmlb_free_handle(&lsp->ls_cmlbhandle);
+ lsp->ls_cmlbhandle = NULL;
+ }
+ (void) ddi_prop_remove_all(lsp->ls_dip);
+ ddi_remove_minor_node(lsp->ls_dip, NULL);
}
/*ARGSUSED*/
@@ -424,7 +598,7 @@ lofi_zone_shutdown(zoneid_t zoneid, void *arg)
if (is_opened(lsp)) {
lsp->ls_cleanup = 1;
} else {
- lofi_free_dev(lsp->ls_dev);
+ lofi_free_dev(lsp);
lofi_destroy(lsp, kcred);
}
}
@@ -436,9 +610,20 @@ lofi_zone_shutdown(zoneid_t zoneid, void *arg)
static int
lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp)
{
- minor_t minor;
+ int id;
+ minor_t part;
+ uint64_t mask;
+ diskaddr_t nblks;
+ diskaddr_t lba;
+ boolean_t ndelay;
+
struct lofi_state *lsp;
+ if (otyp >= OTYPCNT)
+ return (EINVAL);
+
+ ndelay = (flag & (FNDELAY | FNONBLOCK)) ? B_TRUE : B_FALSE;
+
/*
* lofiadm -a /dev/lofi/1 gets us here.
*/
@@ -447,16 +632,18 @@ lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp)
mutex_enter(&lofi_lock);
- minor = getminor(*devp);
+ id = LOFI_MINOR2ID(getminor(*devp));
+ part = LOFI_PART(getminor(*devp));
+ mask = (1U << part);
/* master control device */
- if (minor == 0) {
+ if (id == 0) {
mutex_exit(&lofi_lock);
return (0);
}
/* otherwise, the mapping should already exist */
- lsp = ddi_get_soft_state(lofi_statep, minor);
+ lsp = ddi_get_soft_state(lofi_statep, id);
if (lsp == NULL) {
mutex_exit(&lofi_lock);
return (EINVAL);
@@ -472,9 +659,53 @@ lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp)
return (EROFS);
}
- if (mark_opened(lsp, otyp) == -1) {
+ if ((lsp->ls_open_excl) & (mask)) {
mutex_exit(&lofi_lock);
- return (EINVAL);
+ return (EBUSY);
+ }
+
+ if (flag & FEXCL) {
+ if (lsp->ls_open_lyr[part]) {
+ mutex_exit(&lofi_lock);
+ return (EBUSY);
+ }
+ for (int i = 0; i < OTYP_LYR; i++) {
+ if (lsp->ls_open_reg[i] & mask) {
+ mutex_exit(&lofi_lock);
+ return (EBUSY);
+ }
+ }
+ }
+
+ if (lsp->ls_cmlbhandle != NULL) {
+ if (cmlb_validate(lsp->ls_cmlbhandle, 0, 0) != 0) {
+ /*
+ * non-blocking opens are allowed to succeed to
+ * support format and fdisk to create partitioning.
+ */
+ if (!ndelay) {
+ mutex_exit(&lofi_lock);
+ return (ENXIO);
+ }
+ } else if (cmlb_partinfo(lsp->ls_cmlbhandle, part, &nblks, &lba,
+ NULL, NULL, 0) == 0) {
+ if ((!nblks) && ((!ndelay) || (otyp != OTYP_CHR))) {
+ mutex_exit(&lofi_lock);
+ return (ENXIO);
+ }
+ } else if (!ndelay) {
+ mutex_exit(&lofi_lock);
+ return (ENXIO);
+ }
+ }
+
+ if (otyp == OTYP_LYR) {
+ lsp->ls_open_lyr[part]++;
+ } else {
+ lsp->ls_open_reg[otyp] |= mask;
+ }
+ if (flag & FEXCL) {
+ lsp->ls_open_excl |= mask;
}
mutex_exit(&lofi_lock);
@@ -485,23 +716,35 @@ lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp)
static int
lofi_close(dev_t dev, int flag, int otyp, struct cred *credp)
{
- minor_t minor;
+ minor_t part;
+ int id;
+ uint64_t mask;
struct lofi_state *lsp;
+ id = LOFI_MINOR2ID(getminor(dev));
+ part = LOFI_PART(getminor(dev));
+ mask = (1U << part);
+
mutex_enter(&lofi_lock);
- minor = getminor(dev);
- lsp = ddi_get_soft_state(lofi_statep, minor);
+ lsp = ddi_get_soft_state(lofi_statep, id);
if (lsp == NULL) {
mutex_exit(&lofi_lock);
return (EINVAL);
}
- if (minor == 0) {
+ if (id == 0) {
mutex_exit(&lofi_lock);
return (0);
}
- mark_closed(lsp, otyp);
+ if (lsp->ls_open_excl & mask)
+ lsp->ls_open_excl &= ~mask;
+
+ if (otyp == OTYP_LYR) {
+ lsp->ls_open_lyr[part]--;
+ } else {
+ lsp->ls_open_reg[otyp] &= ~mask;
+ }
/*
* If we forcibly closed the underlying device (li_force), or
@@ -509,7 +752,7 @@ lofi_close(dev_t dev, int flag, int otyp, struct cred *credp)
* out of the door.
*/
if (!is_opened(lsp) && (lsp->ls_cleanup || lsp->ls_vp == NULL)) {
- lofi_free_dev(lsp->ls_dev);
+ lofi_free_dev(lsp);
lofi_destroy(lsp, credp);
}
@@ -970,7 +1213,7 @@ gzip_decompress(void *src, size_t srclen, void *dst,
/*ARGSUSED*/
static int
lzma_decompress(void *src, size_t srclen, void *dst,
- size_t *dstlen, int level)
+ size_t *dstlen, int level)
{
size_t insizepure;
void *actual_src;
@@ -1005,7 +1248,9 @@ lofi_strategy_task(void *arg)
size_t xfersize;
boolean_t bufinited = B_FALSE;
- lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev));
+ lsp = ddi_get_soft_state(lofi_statep,
+ LOFI_MINOR2ID(getminor(bp->b_edev)));
+
if (lsp == NULL) {
error = ENXIO;
goto errout;
@@ -1015,9 +1260,15 @@ lofi_strategy_task(void *arg)
kstat_waitq_to_runq(KSTAT_IO_PTR(lsp->ls_kstat));
mutex_exit(lsp->ls_kstat->ks_lock);
}
+
+ mutex_enter(&lsp->ls_vp_lock);
+ lsp->ls_vp_iocount++;
+ mutex_exit(&lsp->ls_vp_lock);
+
bp_mapin(bp);
bufaddr = bp->b_un.b_addr;
- offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */
+ offset = (bp->b_lblkno + (diskaddr_t)(uintptr_t)bp->b_private)
+ << lsp->ls_lbshift; /* offset within file */
if (lsp->ls_crypto_enabled) {
/* encrypted data really begins after crypto header */
offset += lsp->ls_crypto_offset;
@@ -1345,6 +1596,10 @@ lofi_strategy(struct buf *bp)
{
struct lofi_state *lsp;
offset_t offset;
+ minor_t part;
+ diskaddr_t p_lba;
+ diskaddr_t p_nblks;
+ int shift;
/*
* We cannot just do I/O here, because the current thread
@@ -1357,12 +1612,38 @@ lofi_strategy(struct buf *bp)
* do the I/O asynchronously, or we could use task queues. task
* queues were incredibly easy so they win.
*/
- lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev));
+
+ lsp = ddi_get_soft_state(lofi_statep,
+ LOFI_MINOR2ID(getminor(bp->b_edev)));
+ part = LOFI_PART(getminor(bp->b_edev));
+
if (lsp == NULL) {
bioerror(bp, ENXIO);
biodone(bp);
return (0);
}
+ shift = lsp->ls_lbshift;
+
+ p_lba = 0;
+ p_nblks = lsp->ls_vp_size >> shift;
+
+ if (lsp->ls_cmlbhandle != NULL) {
+ if (cmlb_partinfo(lsp->ls_cmlbhandle, part, &p_nblks, &p_lba,
+ NULL, NULL, 0)) {
+ bioerror(bp, ENXIO);
+ biodone(bp);
+ return (0);
+ }
+ }
+
+ /* start block past partition end? */
+ if (bp->b_lblkno > p_nblks) {
+ bioerror(bp, ENXIO);
+ biodone(bp);
+ return (0);
+ }
+
+ offset = (bp->b_lblkno+p_lba) << shift; /* offset within file */
mutex_enter(&lsp->ls_vp_lock);
if (lsp->ls_vp == NULL || lsp->ls_vp_closereq) {
@@ -1372,12 +1653,14 @@ lofi_strategy(struct buf *bp)
return (0);
}
- offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */
if (lsp->ls_crypto_enabled) {
/* encrypted data really begins after crypto header */
offset += lsp->ls_crypto_offset;
}
- if (offset == lsp->ls_vp_size) {
+
+ /* make sure we will not pass the file or partition size */
+ if (offset == lsp->ls_vp_size ||
+ offset == (((p_lba + p_nblks) << shift) + lsp->ls_crypto_offset)) {
/* EOF */
if ((bp->b_flags & B_READ) != 0) {
bp->b_resid = bp->b_bcount;
@@ -1390,13 +1673,15 @@ lofi_strategy(struct buf *bp)
mutex_exit(&lsp->ls_vp_lock);
return (0);
}
- if (offset > lsp->ls_vp_size) {
+ if ((offset > lsp->ls_vp_size) ||
+ (offset > (((p_lba + p_nblks) << shift) + lsp->ls_crypto_offset)) ||
+ ((offset + bp->b_bcount) > ((p_lba + p_nblks) << shift))) {
bioerror(bp, ENXIO);
biodone(bp);
mutex_exit(&lsp->ls_vp_lock);
return (0);
}
- lsp->ls_vp_iocount++;
+
mutex_exit(&lsp->ls_vp_lock);
if (lsp->ls_kstat) {
@@ -1404,6 +1689,7 @@ lofi_strategy(struct buf *bp)
kstat_waitq_enter(KSTAT_IO_PTR(lsp->ls_kstat));
mutex_exit(lsp->ls_kstat->ks_lock);
}
+ bp->b_private = (void *)(uintptr_t)p_lba; /* partition start */
(void) taskq_dispatch(lsp->ls_taskq, lofi_strategy_task, bp, KM_SLEEP);
return (0);
}
@@ -1452,54 +1738,269 @@ lofi_awrite(dev_t dev, struct aio_req *aio, struct cred *credp)
static int
lofi_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
+ struct lofi_state *lsp;
+ dev_t dev = (dev_t)arg;
+ int instance;
+
+ instance = LOFI_MINOR2ID(getminor(dev));
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
- *result = lofi_dip;
+ lsp = ddi_get_soft_state(lofi_statep, instance);
+ if (lsp == NULL)
+ return (DDI_FAILURE);
+ *result = lsp->ls_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
- *result = 0;
+ *result = (void *) (intptr_t)instance;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
-lofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+lofi_create_minor_nodes(struct lofi_state *lsp, boolean_t labeled)
{
- int error;
+ int error = 0;
+ int instance = ddi_get_instance(lsp->ls_dip);
+
+ if (labeled == B_TRUE) {
+ cmlb_alloc_handle(&lsp->ls_cmlbhandle);
+ error = cmlb_attach(lsp->ls_dip, &lofi_tg_ops, DTYPE_DIRECT,
+ B_FALSE, B_FALSE, DDI_NT_BLOCK_CHAN,
+ CMLB_CREATE_P0_MINOR_NODE, lsp->ls_cmlbhandle, (void *)1);
+
+ if (error != DDI_SUCCESS) {
+ cmlb_free_handle(&lsp->ls_cmlbhandle);
+ lsp->ls_cmlbhandle = NULL;
+ error = ENXIO;
+ }
+ } else {
+ /* create minor nodes */
+ error = ddi_create_minor_node(lsp->ls_dip, LOFI_BLOCK_NODE,
+ S_IFBLK, LOFI_ID2MINOR(instance), DDI_PSEUDO, 0);
+ if (error == DDI_SUCCESS) {
+ error = ddi_create_minor_node(lsp->ls_dip,
+ LOFI_CHAR_NODE, S_IFCHR, LOFI_ID2MINOR(instance),
+ DDI_PSEUDO, 0);
+ if (error != DDI_SUCCESS) {
+ ddi_remove_minor_node(lsp->ls_dip,
+ LOFI_BLOCK_NODE);
+ error = ENXIO;
+ }
+ } else
+ error = ENXIO;
+ }
+ return (error);
+}
- if (cmd != DDI_ATTACH)
- return (DDI_FAILURE);
+static int
+lofi_zone_bind(struct lofi_state *lsp)
+{
+ int error = 0;
- lofi_minor_id = id_space_create("lofi_minor_id", 1, L_MAXMIN32 + 1);
+ mutex_enter(&curproc->p_lock);
+ if ((error = rctl_incr_lofi(curproc, curproc->p_zone, 1)) != 0) {
+ mutex_exit(&curproc->p_lock);
+ return (error);
+ }
+ mutex_exit(&curproc->p_lock);
- if (!lofi_minor_id)
- return (DDI_FAILURE);
+ if (ddi_prop_update_string(lsp->ls_dev, lsp->ls_dip, ZONE_PROP_NAME,
+ (char *)curproc->p_zone->zone_name) != DDI_PROP_SUCCESS) {
+ rctl_decr_lofi(curproc->p_zone, 1);
+ error = EINVAL;
+ } else {
+ zone_init_ref(&lsp->ls_zone);
+ zone_hold_ref(curzone, &lsp->ls_zone, ZONE_REF_LOFI);
+ }
+ return (error);
+}
- error = ddi_soft_state_zalloc(lofi_statep, 0);
+static void
+lofi_zone_unbind(struct lofi_state *lsp)
+{
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, lsp->ls_dip, ZONE_PROP_NAME);
+ rctl_decr_lofi(curproc->p_zone, 1);
+ zone_rele_ref(&lsp->ls_zone, ZONE_REF_LOFI);
+}
+
+static int
+lofi_online_dev(dev_info_t *dip)
+{
+ boolean_t labeled;
+ int error;
+ int instance = ddi_get_instance(dip);
+ struct lofi_state *lsp;
+
+ labeled = B_FALSE;
+ if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "labeled"))
+ labeled = B_TRUE;
+
+ /* lsp alloc+init, soft state is freed in lofi_detach */
+ error = ddi_soft_state_zalloc(lofi_statep, instance);
if (error == DDI_FAILURE) {
- id_space_destroy(lofi_minor_id);
- return (DDI_FAILURE);
+ return (ENOMEM);
}
- error = ddi_create_minor_node(dip, LOFI_CTL_NODE, S_IFCHR, 0,
- DDI_PSEUDO, NULL);
- if (error == DDI_FAILURE) {
- ddi_soft_state_free(lofi_statep, 0);
- id_space_destroy(lofi_minor_id);
- return (DDI_FAILURE);
+
+ lsp = ddi_get_soft_state(lofi_statep, instance);
+ lsp->ls_dip = dip;
+
+ if ((error = lofi_zone_bind(lsp)) != 0)
+ goto err;
+
+ cv_init(&lsp->ls_vp_cv, NULL, CV_DRIVER, NULL);
+ mutex_init(&lsp->ls_comp_cache_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&lsp->ls_comp_bufs_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&lsp->ls_kstat_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&lsp->ls_vp_lock, NULL, MUTEX_DRIVER, NULL);
+
+ if ((error = lofi_create_minor_nodes(lsp, labeled)) != 0) {
+ lofi_zone_unbind(lsp);
+ goto lerr;
}
+
/* driver handles kernel-issued IOCTLs */
if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
DDI_KERNEL_IOCTL, NULL, 0) != DDI_PROP_SUCCESS) {
- ddi_remove_minor_node(dip, NULL);
- ddi_soft_state_free(lofi_statep, 0);
- id_space_destroy(lofi_minor_id);
- return (DDI_FAILURE);
+ error = DDI_FAILURE;
+ goto merr;
+ }
+
+ lsp->ls_kstat = kstat_create_zone(LOFI_DRIVER_NAME, instance,
+ NULL, "disk", KSTAT_TYPE_IO, 1, 0, getzoneid());
+ if (lsp->ls_kstat == NULL) {
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, lsp->ls_dip,
+ DDI_KERNEL_IOCTL);
+ error = ENOMEM;
+ goto merr;
+ }
+
+ lsp->ls_kstat->ks_lock = &lsp->ls_kstat_lock;
+ kstat_zone_add(lsp->ls_kstat, GLOBAL_ZONEID);
+ kstat_install(lsp->ls_kstat);
+ return (DDI_SUCCESS);
+merr:
+ if (lsp->ls_cmlbhandle != NULL) {
+ cmlb_detach(lsp->ls_cmlbhandle, 0);
+ cmlb_free_handle(&lsp->ls_cmlbhandle);
+ }
+ ddi_remove_minor_node(dip, NULL);
+ lofi_zone_unbind(lsp);
+lerr:
+ mutex_destroy(&lsp->ls_comp_cache_lock);
+ mutex_destroy(&lsp->ls_comp_bufs_lock);
+ mutex_destroy(&lsp->ls_kstat_lock);
+ mutex_destroy(&lsp->ls_vp_lock);
+ cv_destroy(&lsp->ls_vp_cv);
+err:
+ ddi_soft_state_free(lofi_statep, instance);
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+lofi_dev_callback(sysevent_t *ev, void *cookie)
+{
+ nvlist_t *nvlist;
+ char *class, *driver;
+ char name[10];
+ int32_t instance;
+
+ class = sysevent_get_class_name(ev);
+ if (strcmp(class, EC_DEV_ADD) && strcmp(class, EC_DEV_REMOVE))
+ return (0);
+
+ (void) sysevent_get_attr_list(ev, &nvlist);
+ driver = fnvlist_lookup_string(nvlist, DEV_DRIVER_NAME);
+ instance = fnvlist_lookup_int32(nvlist, DEV_INSTANCE);
+
+ if (strcmp(driver, LOFI_DRIVER_NAME) != 0) {
+ fnvlist_free(nvlist);
+ return (0);
}
- zone_key_create(&lofi_zone_key, NULL, lofi_zone_shutdown, NULL);
+ /*
+ * insert or remove device info, then announce the change
+ * via cv_broadcast.
+ * This allows the MAP/UNMAP to monitor device change.
+ */
+ (void) snprintf(name, sizeof (name), "%d", instance);
+ if (strcmp(class, EC_DEV_ADD) == 0) {
+ mutex_enter(&lofi_chan_lock);
+ fnvlist_add_nvlist(lofi_devlink_cache, name, nvlist);
+ cv_broadcast(&lofi_chan_cv);
+ mutex_exit(&lofi_chan_lock);
+ } else if (strcmp(class, EC_DEV_REMOVE) == 0) {
+ mutex_enter(&lofi_chan_lock);
+ /* Can not use fnvlist_remove() as we can get ENOENT. */
+ (void) nvlist_remove_all(lofi_devlink_cache, name);
+ cv_broadcast(&lofi_chan_cv);
+ mutex_exit(&lofi_chan_lock);
+ }
+
+ fnvlist_free(nvlist);
+ return (0);
+}
+
+static int
+lofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int rv;
+ int instance = ddi_get_instance(dip);
+ struct lofi_state *lsp;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ /*
+ * Instance 0 is control instance, attaching control instance
+ * will set the lofi up and ready.
+ */
+ if (instance == 0) {
+ rv = ddi_soft_state_zalloc(lofi_statep, 0);
+ if (rv == DDI_FAILURE) {
+ return (DDI_FAILURE);
+ }
+ lsp = ddi_get_soft_state(lofi_statep, instance);
+ rv = ddi_create_minor_node(dip, LOFI_CTL_NODE, S_IFCHR, 0,
+ DDI_PSEUDO, 0);
+ if (rv == DDI_FAILURE) {
+ ddi_soft_state_free(lofi_statep, 0);
+ return (DDI_FAILURE);
+ }
+ /* driver handles kernel-issued IOCTLs */
+ if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+ DDI_KERNEL_IOCTL, NULL, 0) != DDI_PROP_SUCCESS) {
+ ddi_remove_minor_node(dip, NULL);
+ ddi_soft_state_free(lofi_statep, 0);
+ return (DDI_FAILURE);
+ }
+
+ rv = sysevent_evc_bind(DEVFS_CHANNEL, &lofi_chan,
+ EVCH_CREAT | EVCH_HOLD_PEND);
+ if (rv == 0) {
+ rv = sysevent_evc_subscribe(lofi_chan, "lofi",
+ EC_ALL, lofi_dev_callback, NULL, 0);
+ rv |= sysevent_evc_subscribe(lofi_chan, "disk",
+ EC_ALL, lofi_dev_callback, NULL, 0);
+ } else
+ lofi_chan = NULL;
+ if (rv != 0) {
+ if (lofi_chan != NULL)
+ (void) sysevent_evc_unbind(lofi_chan);
+ ddi_prop_remove_all(dip);
+ ddi_remove_minor_node(dip, NULL);
+ ddi_soft_state_free(lofi_statep, 0);
+ return (DDI_FAILURE);
+ }
+ zone_key_create(&lofi_zone_key, NULL, lofi_zone_shutdown, NULL);
+
+ lsp->ls_dip = dip;
+ } else {
+ if (lofi_online_dev(dip) == DDI_FAILURE)
+ return (DDI_FAILURE);
+ }
- lofi_dip = dip;
ddi_report_dev(dip);
return (DDI_SUCCESS);
}
@@ -1507,9 +2008,25 @@ lofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
static int
lofi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
+ struct lofi_state *lsp;
+ int instance = ddi_get_instance(dip);
+
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
+ /*
+ * If the instance is not 0, release state.
+ * The instance 0 is control device, we can not detach it
+ * before other instances are detached.
+ */
+ if (instance != 0) {
+ lsp = ddi_get_soft_state(lofi_statep, instance);
+ if (lsp != NULL && lsp->ls_vp_ready == B_FALSE) {
+ ddi_soft_state_free(lofi_statep, instance);
+ return (DDI_SUCCESS);
+ } else
+ return (DDI_FAILURE);
+ }
mutex_enter(&lofi_lock);
if (!list_is_empty(&lofi_list)) {
@@ -1517,26 +2034,24 @@ lofi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
return (DDI_FAILURE);
}
- lofi_dip = NULL;
ddi_remove_minor_node(dip, NULL);
ddi_prop_remove_all(dip);
mutex_exit(&lofi_lock);
+ (void) sysevent_evc_unbind(lofi_chan);
if (zone_key_delete(lofi_zone_key) != 0)
cmn_err(CE_WARN, "failed to delete zone key");
ddi_soft_state_free(lofi_statep, 0);
- id_space_destroy(lofi_minor_id);
-
return (DDI_SUCCESS);
}
/*
- * With addition of encryption, be careful that encryption key is wiped before
- * kernel memory structures are freed, and also that key is not accidentally
- * passed out into userland structures.
+ * With the addition of encryption, we must be careful that encryption key is
+ * wiped before kernel's data structures are freed so it cannot accidentally
+ * slip out to userland through uninitialized data elsewhere.
*/
static void
free_lofi_ioctl(struct lofi_ioctl *klip)
@@ -1547,7 +2062,7 @@ free_lofi_ioctl(struct lofi_ioctl *klip)
}
/*
- * These two just simplify the rest of the ioctls that need to copyin/out
+ * These two functions simplify the rest of the ioctls that need to copyin/out
* the lofi_ioctl structure.
*/
int
@@ -1564,11 +2079,12 @@ copy_in_lofi_ioctl(const struct lofi_ioctl *ulip, struct lofi_ioctl **klipp,
/* ensure NULL termination */
klip->li_filename[MAXPATHLEN-1] = '\0';
+ klip->li_devpath[MAXPATHLEN-1] = '\0';
klip->li_algorithm[MAXALGLEN-1] = '\0';
klip->li_cipher[CRYPTO_MAX_MECH_NAME-1] = '\0';
klip->li_iv_cipher[CRYPTO_MAX_MECH_NAME-1] = '\0';
- if (klip->li_minor > L_MAXMIN32) {
+ if (klip->li_id > L_MAXMIN32) {
error = EINVAL;
goto err;
}
@@ -1582,7 +2098,7 @@ err:
int
copy_out_lofi_ioctl(const struct lofi_ioctl *klip, struct lofi_ioctl *ulip,
- int flag)
+ int flag)
{
int error;
@@ -1698,12 +2214,9 @@ file_to_lofi(char *filename, boolean_t readonly, struct lofi_state **lspp)
}
/*
- * Fakes up a disk geometry, and one big partition, based on the size
- * of the file. This is needed because we allow newfs'ing the device,
- * and newfs will do several disk ioctls to figure out the geometry and
- * partition information. It uses that information to determine the parameters
- * to pass to mkfs. Geometry is pretty much irrelevant these days, but we
- * have to support it.
+ * Fakes up a disk geometry based on the size of the file. This is needed
+ * to support newfs on traditional lofi device, but also will provide
+ * geometry hint for cmlb.
*/
static void
fake_disk_geometry(struct lofi_state *lsp)
@@ -1722,6 +2235,7 @@ fake_disk_geometry(struct lofi_state *lsp)
* for a small file, or so many on a big file that you waste space
* for backup superblocks or cylinder group structures.
*/
+ bzero(&lsp->ls_dkg, sizeof (lsp->ls_dkg));
if (dsize < (2 * 1024 * 1024)) /* floppy? */
lsp->ls_dkg.dkg_ncyl = dsize / (100 * 1024);
else
@@ -1729,70 +2243,74 @@ fake_disk_geometry(struct lofi_state *lsp)
/* in case file file is < 100k */
if (lsp->ls_dkg.dkg_ncyl == 0)
lsp->ls_dkg.dkg_ncyl = 1;
- lsp->ls_dkg.dkg_acyl = 0;
- lsp->ls_dkg.dkg_bcyl = 0;
+
+ lsp->ls_dkg.dkg_pcyl = lsp->ls_dkg.dkg_ncyl;
lsp->ls_dkg.dkg_nhead = 1;
- lsp->ls_dkg.dkg_obs1 = 0;
- lsp->ls_dkg.dkg_intrlv = 0;
- lsp->ls_dkg.dkg_obs2 = 0;
- lsp->ls_dkg.dkg_obs3 = 0;
- lsp->ls_dkg.dkg_apc = 0;
lsp->ls_dkg.dkg_rpm = 7200;
- lsp->ls_dkg.dkg_pcyl = lsp->ls_dkg.dkg_ncyl + lsp->ls_dkg.dkg_acyl;
- lsp->ls_dkg.dkg_nsect = dsize / (DEV_BSIZE * lsp->ls_dkg.dkg_ncyl);
- lsp->ls_dkg.dkg_write_reinstruct = 0;
- lsp->ls_dkg.dkg_read_reinstruct = 0;
-
- /* vtoc - see dkio(7I) */
- bzero(&lsp->ls_vtoc, sizeof (struct vtoc));
- lsp->ls_vtoc.v_sanity = VTOC_SANE;
- lsp->ls_vtoc.v_version = V_VERSION;
- (void) strncpy(lsp->ls_vtoc.v_volume, LOFI_DRIVER_NAME,
- sizeof (lsp->ls_vtoc.v_volume));
- lsp->ls_vtoc.v_sectorsz = DEV_BSIZE;
- lsp->ls_vtoc.v_nparts = 1;
- lsp->ls_vtoc.v_part[0].p_tag = V_UNASSIGNED;
+
+ lsp->ls_dkg.dkg_nsect = dsize /
+ (lsp->ls_dkg.dkg_ncyl << lsp->ls_pbshift);
+}
+
+/*
+ * build vtoc - see dkio(7I)
+ *
+ * Fakes one big partition based on the size of the file. This is needed
+ * because we allow newfs'ing the traditional lofi device and newfs will
+ * do several disk ioctls to figure out the geometry and partition information.
+ * It uses that information to determine the parameters to pass to mkfs.
+ */
+static void
+fake_disk_vtoc(struct lofi_state *lsp, struct vtoc *vt)
+{
+ bzero(vt, sizeof (struct vtoc));
+ vt->v_sanity = VTOC_SANE;
+ vt->v_version = V_VERSION;
+ (void) strncpy(vt->v_volume, LOFI_DRIVER_NAME,
+ sizeof (vt->v_volume));
+ vt->v_sectorsz = 1 << lsp->ls_pbshift;
+ vt->v_nparts = 1;
+ vt->v_part[0].p_tag = V_UNASSIGNED;
/*
* A compressed file is read-only, other files can
* be read-write
*/
if (lsp->ls_uncomp_seg_sz > 0) {
- lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT | V_RONLY;
+ vt->v_part[0].p_flag = V_UNMNT | V_RONLY;
} else {
- lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT;
+ vt->v_part[0].p_flag = V_UNMNT;
}
- lsp->ls_vtoc.v_part[0].p_start = (daddr_t)0;
+ vt->v_part[0].p_start = (daddr_t)0;
/*
* The partition size cannot just be the number of sectors, because
* that might not end on a cylinder boundary. And if that's the case,
* newfs/mkfs will print a scary warning. So just figure the size
* based on the number of cylinders and sectors/cylinder.
*/
- lsp->ls_vtoc.v_part[0].p_size = lsp->ls_dkg.dkg_pcyl *
+ vt->v_part[0].p_size = lsp->ls_dkg.dkg_pcyl *
lsp->ls_dkg.dkg_nsect * lsp->ls_dkg.dkg_nhead;
+}
- /* dk_cinfo - see dkio(7I) */
- bzero(&lsp->ls_ci, sizeof (struct dk_cinfo));
- (void) strcpy(lsp->ls_ci.dki_cname, LOFI_DRIVER_NAME);
- lsp->ls_ci.dki_ctype = DKC_MD;
- lsp->ls_ci.dki_flags = 0;
- lsp->ls_ci.dki_cnum = 0;
- lsp->ls_ci.dki_addr = 0;
- lsp->ls_ci.dki_space = 0;
- lsp->ls_ci.dki_prio = 0;
- lsp->ls_ci.dki_vec = 0;
- (void) strcpy(lsp->ls_ci.dki_dname, LOFI_DRIVER_NAME);
- lsp->ls_ci.dki_unit = 0;
- lsp->ls_ci.dki_slave = 0;
- lsp->ls_ci.dki_partition = 0;
+/*
+ * build dk_cinfo - see dkio(7I)
+ */
+static void
+fake_disk_info(dev_t dev, struct dk_cinfo *ci)
+{
+ bzero(ci, sizeof (struct dk_cinfo));
+ (void) strlcpy(ci->dki_cname, LOFI_DRIVER_NAME, sizeof (ci->dki_cname));
+ ci->dki_ctype = DKC_SCSI_CCS;
+ (void) strlcpy(ci->dki_dname, LOFI_DRIVER_NAME, sizeof (ci->dki_dname));
+ ci->dki_unit = LOFI_MINOR2ID(getminor(dev));
+ ci->dki_partition = LOFI_PART(getminor(dev));
/*
* newfs uses this to set maxcontig. Must not be < 16, or it
* will be 0 when newfs multiplies it by DEV_BSIZE and divides
* it by the block size. Then tunefs doesn't work because
* maxcontig is 0.
*/
- lsp->ls_ci.dki_maxtransfer = 16;
+ ci->dki_maxtransfer = 16;
}
/*
@@ -2110,21 +2628,177 @@ lofi_init_compress(struct lofi_state *lsp)
}
/*
+ * Allocate new or proposed id from lofi_id.
+ *
+ * Special cases for proposed id:
+ * 0: not allowed, 0 is id for control device.
+ * -1: allocate first usable id from lofi_id.
+ * any other value is proposed value from userland
+ *
+ * returns DDI_SUCCESS or errno.
+ */
+static int
+lofi_alloc_id(int *idp)
+{
+ int id, error = DDI_SUCCESS;
+
+ if (*idp == -1) {
+ id = id_allocff_nosleep(lofi_id);
+ if (id == -1) {
+ error = EAGAIN;
+ goto err;
+ }
+ } else if (*idp == 0) {
+ error = EINVAL;
+ goto err;
+ } else if (*idp > ((1 << (L_BITSMINOR - LOFI_CMLB_SHIFT)) - 1)) {
+ error = ERANGE;
+ goto err;
+ } else {
+ if (ddi_get_soft_state(lofi_statep, *idp) != NULL) {
+ error = EEXIST;
+ goto err;
+ }
+
+ id = id_alloc_specific_nosleep(lofi_id, *idp);
+ if (id == -1) {
+ error = EAGAIN;
+ goto err;
+ }
+ }
+ *idp = id;
+err:
+ return (error);
+}
+
+static int
+lofi_create_dev(struct lofi_ioctl *klip)
+{
+ dev_info_t *parent, *child;
+ struct lofi_state *lsp = NULL;
+ char namebuf[MAXNAMELEN];
+ int error, circ;
+
+ /* get control device */
+ lsp = ddi_get_soft_state(lofi_statep, 0);
+ parent = ddi_get_parent(lsp->ls_dip);
+
+ if ((error = lofi_alloc_id((int *)&klip->li_id)))
+ return (error);
+
+ (void) snprintf(namebuf, sizeof (namebuf), LOFI_DRIVER_NAME "@%d",
+ klip->li_id);
+
+ ndi_devi_enter(parent, &circ);
+ child = ndi_devi_findchild(parent, namebuf);
+ ndi_devi_exit(parent, circ);
+
+ if (child == NULL) {
+ child = ddi_add_child(parent, LOFI_DRIVER_NAME,
+ (pnode_t)DEVI_SID_NODEID, klip->li_id);
+ if ((error = ddi_prop_update_int(DDI_DEV_T_NONE, child,
+ "instance", klip->li_id)) != DDI_PROP_SUCCESS)
+ goto err;
+
+ if (klip->li_labeled == B_TRUE) {
+ if ((error = ddi_prop_create(DDI_DEV_T_NONE, child,
+ DDI_PROP_CANSLEEP, "labeled", 0, 0))
+ != DDI_PROP_SUCCESS)
+ goto err;
+ }
+
+ if ((error = ndi_devi_online(child, NDI_ONLINE_ATTACH))
+ != NDI_SUCCESS)
+ goto err;
+ } else {
+ id_free(lofi_id, klip->li_id);
+ error = EEXIST;
+ return (error);
+ }
+
+ goto done;
+
+err:
+ ddi_prop_remove_all(child);
+ (void) ndi_devi_offline(child, NDI_DEVI_REMOVE);
+ id_free(lofi_id, klip->li_id);
+done:
+
+ return (error);
+}
+
+static void
+lofi_create_inquiry(struct lofi_state *lsp, struct scsi_inquiry *inq)
+{
+ char *p = NULL;
+
+ (void) strlcpy(inq->inq_vid, LOFI_DRIVER_NAME, sizeof (inq->inq_vid));
+
+ mutex_enter(&lsp->ls_vp_lock);
+ if (lsp->ls_vp != NULL)
+ p = strrchr(lsp->ls_vp->v_path, '/');
+ if (p != NULL)
+ (void) strncpy(inq->inq_pid, p + 1, sizeof (inq->inq_pid));
+ mutex_exit(&lsp->ls_vp_lock);
+ (void) strlcpy(inq->inq_revision, "1.0", sizeof (inq->inq_revision));
+}
+
+/*
+ * copy devlink name from event cache
+ */
+static void
+lofi_copy_devpath(struct lofi_ioctl *klip)
+{
+ int error;
+ char namebuf[MAXNAMELEN], *str;
+ clock_t ticks;
+ nvlist_t *nvl;
+
+ if (klip->li_labeled == B_TRUE)
+ klip->li_devpath[0] = '\0';
+ else {
+ /* no need to wait for messages */
+ (void) snprintf(klip->li_devpath, sizeof (klip->li_devpath),
+ "/dev/" LOFI_CHAR_NAME "/%d", klip->li_id);
+ return;
+ }
+
+ (void) snprintf(namebuf, sizeof (namebuf), "%d", klip->li_id);
+ ticks = ddi_get_lbolt() + LOFI_TIMEOUT * drv_usectohz(1000000);
+
+ nvl = NULL;
+
+ mutex_enter(&lofi_chan_lock);
+ while (nvlist_lookup_nvlist(lofi_devlink_cache, namebuf, &nvl) != 0) {
+ error = cv_timedwait(&lofi_chan_cv, &lofi_chan_lock, ticks);
+ if (error == -1)
+ break;
+ }
+
+ if (nvl != NULL) {
+ if (nvlist_lookup_string(nvl, DEV_NAME, &str) == 0) {
+ (void) strlcpy(klip->li_devpath, str,
+ sizeof (klip->li_devpath));
+ }
+ }
+ mutex_exit(&lofi_chan_lock);
+}
+
+/*
* map a file to a minor number. Return the minor number.
*/
static int
lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor,
int *rvalp, struct cred *credp, int ioctl_flag)
{
- minor_t minor = (minor_t)-1;
+ int id = -1;
struct lofi_state *lsp = NULL;
struct lofi_ioctl *klip;
int error;
struct vnode *vp = NULL;
vattr_t vattr;
int flag;
- dev_t newdev;
- char namebuf[50];
+ char namebuf[MAXNAMELEN];
error = copy_in_lofi_ioctl(ulip, &klip, ioctl_flag);
if (error != 0)
@@ -2132,38 +2806,12 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor,
mutex_enter(&lofi_lock);
- mutex_enter(&curproc->p_lock);
- if ((error = rctl_incr_lofi(curproc, curproc->p_zone, 1)) != 0) {
- mutex_exit(&curproc->p_lock);
- mutex_exit(&lofi_lock);
- free_lofi_ioctl(klip);
- return (error);
- }
- mutex_exit(&curproc->p_lock);
-
if (file_to_lofi_nocheck(klip->li_filename, klip->li_readonly,
NULL) == 0) {
error = EBUSY;
goto err;
}
- if (pickminor) {
- minor = (minor_t)id_allocff_nosleep(lofi_minor_id);
- if (minor == (minor_t)-1) {
- error = EAGAIN;
- goto err;
- }
- } else {
- if (ddi_get_soft_state(lofi_statep, klip->li_minor) != NULL) {
- error = EEXIST;
- goto err;
- }
-
- minor = (minor_t)
- id_alloc_specific_nosleep(lofi_minor_id, klip->li_minor);
- ASSERT(minor != (minor_t)-1);
- }
-
flag = FREAD | FWRITE | FOFFMAX | FEXCL;
error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, &vp, 0, 0);
if (error) {
@@ -2191,35 +2839,22 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor,
goto err;
}
- /* lsp alloc+init */
-
- error = ddi_soft_state_zalloc(lofi_statep, minor);
- if (error == DDI_FAILURE) {
- error = ENOMEM;
- goto err;
+ if (pickminor) {
+ klip->li_id = (uint32_t)-1;
}
+ if ((error = lofi_create_dev(klip)) != 0)
+ goto err;
- lsp = ddi_get_soft_state(lofi_statep, minor);
- list_insert_tail(&lofi_list, lsp);
-
- newdev = makedevice(getmajor(dev), minor);
- lsp->ls_dev = newdev;
- zone_init_ref(&lsp->ls_zone);
- zone_hold_ref(curzone, &lsp->ls_zone, ZONE_REF_LOFI);
- lsp->ls_uncomp_seg_sz = 0;
- lsp->ls_comp_algorithm[0] = '\0';
- lsp->ls_crypto_offset = 0;
-
- cv_init(&lsp->ls_vp_cv, NULL, CV_DRIVER, NULL);
- mutex_init(&lsp->ls_comp_cache_lock, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&lsp->ls_comp_bufs_lock, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&lsp->ls_kstat_lock, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&lsp->ls_vp_lock, NULL, MUTEX_DRIVER, NULL);
+ id = klip->li_id;
+ lsp = ddi_get_soft_state(lofi_statep, id);
+ if (lsp == NULL)
+ goto err;
- (void) snprintf(namebuf, sizeof (namebuf), "%s_taskq_%d",
- LOFI_DRIVER_NAME, minor);
- lsp->ls_taskq = taskq_create_proc(namebuf, lofi_taskq_nthreads,
- minclsyspri, 1, lofi_taskq_maxalloc, curzone->zone_zsched, 0);
+ /*
+ * from this point lofi_destroy() is used to clean up on error
+ * make sure the basic data is set
+ */
+ lsp->ls_dev = makedevice(getmajor(dev), LOFI_ID2MINOR(id));
list_create(&lsp->ls_comp_cache, sizeof (struct lofi_comp_cache),
offsetof(struct lofi_comp_cache, lc_list));
@@ -2232,6 +2867,10 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor,
lsp->ls_vp = vp;
lsp->ls_stacked_vp = vp;
+
+ lsp->ls_vp_size = vattr.va_size;
+ lsp->ls_vp_comp_size = lsp->ls_vp_size;
+
/*
* Try to handle stacked lofs vnodes.
*/
@@ -2249,21 +2888,18 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor,
}
}
- lsp->ls_vp_size = vattr.va_size;
- lsp->ls_vp_comp_size = lsp->ls_vp_size;
-
- lsp->ls_kstat = kstat_create_zone(LOFI_DRIVER_NAME, minor,
- NULL, "disk", KSTAT_TYPE_IO, 1, 0, getzoneid());
-
- if (lsp->ls_kstat == NULL) {
- error = ENOMEM;
- goto err;
- }
-
- lsp->ls_kstat->ks_lock = &lsp->ls_kstat_lock;
- kstat_zone_add(lsp->ls_kstat, GLOBAL_ZONEID);
+ lsp->ls_lbshift = highbit(DEV_BSIZE) - 1;
+ lsp->ls_pbshift = lsp->ls_lbshift;
lsp->ls_readonly = klip->li_readonly;
+ lsp->ls_uncomp_seg_sz = 0;
+ lsp->ls_comp_algorithm[0] = '\0';
+ lsp->ls_crypto_offset = 0;
+
+ (void) snprintf(namebuf, sizeof (namebuf), "%s_taskq_%d",
+ LOFI_DRIVER_NAME, id);
+ lsp->ls_taskq = taskq_create_proc(namebuf, lofi_taskq_nthreads,
+ minclsyspri, 1, lofi_taskq_maxalloc, curzone->zone_zsched, 0);
if ((error = lofi_init_crypto(lsp, klip)) != 0)
goto err;
@@ -2273,74 +2909,46 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor,
fake_disk_geometry(lsp);
- /* create minor nodes */
-
- (void) snprintf(namebuf, sizeof (namebuf), "%d", minor);
- error = ddi_create_minor_node(lofi_dip, namebuf, S_IFBLK, minor,
- DDI_PSEUDO, NULL);
- if (error != DDI_SUCCESS) {
- error = ENXIO;
- goto err;
- }
-
- (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", minor);
- error = ddi_create_minor_node(lofi_dip, namebuf, S_IFCHR, minor,
- DDI_PSEUDO, NULL);
- if (error != DDI_SUCCESS) {
- /* remove block node */
- (void) snprintf(namebuf, sizeof (namebuf), "%d", minor);
- ddi_remove_minor_node(lofi_dip, namebuf);
- error = ENXIO;
- goto err;
- }
-
- /* create DDI properties */
-
- if ((ddi_prop_update_int64(newdev, lofi_dip, SIZE_PROP_NAME,
+ if ((ddi_prop_update_int64(lsp->ls_dev, lsp->ls_dip, SIZE_PROP_NAME,
lsp->ls_vp_size - lsp->ls_crypto_offset)) != DDI_PROP_SUCCESS) {
error = EINVAL;
- goto nodeerr;
+ goto err;
}
- if ((ddi_prop_update_int64(newdev, lofi_dip, NBLOCKS_PROP_NAME,
+ if ((ddi_prop_update_int64(lsp->ls_dev, lsp->ls_dip, NBLOCKS_PROP_NAME,
(lsp->ls_vp_size - lsp->ls_crypto_offset) / DEV_BSIZE))
!= DDI_PROP_SUCCESS) {
error = EINVAL;
- goto nodeerr;
- }
-
- if (ddi_prop_update_string(newdev, lofi_dip, ZONE_PROP_NAME,
- (char *)curproc->p_zone->zone_name) != DDI_PROP_SUCCESS) {
- error = EINVAL;
- goto nodeerr;
+ goto err;
}
- kstat_install(lsp->ls_kstat);
-
+ list_insert_tail(&lofi_list, lsp);
+ /*
+ * Notify we are ready to rock.
+ */
+ mutex_enter(&lsp->ls_vp_lock);
+ lsp->ls_vp_ready = B_TRUE;
+ cv_broadcast(&lsp->ls_vp_cv);
+ mutex_exit(&lsp->ls_vp_lock);
mutex_exit(&lofi_lock);
+ lofi_copy_devpath(klip);
+
if (rvalp)
- *rvalp = (int)minor;
- klip->li_minor = minor;
+ *rvalp = id;
(void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
free_lofi_ioctl(klip);
return (0);
-nodeerr:
- lofi_free_dev(newdev);
err:
if (lsp != NULL) {
lofi_destroy(lsp, credp);
} else {
if (vp != NULL) {
+ (void) VOP_PUTPAGE(vp, 0, 0, B_INVAL, credp, NULL);
(void) VOP_CLOSE(vp, flag, 1, 0, credp, NULL);
VN_RELE(vp);
}
-
- if (minor != (minor_t)-1)
- id_free(lofi_minor_id, minor);
-
- rctl_decr_lofi(curproc->p_zone, 1);
}
mutex_exit(&lofi_lock);
@@ -2357,6 +2965,9 @@ lofi_unmap_file(struct lofi_ioctl *ulip, int byfilename,
{
struct lofi_state *lsp;
struct lofi_ioctl *klip;
+ nvlist_t *nvl = NULL;
+ clock_t ticks;
+ char name[MAXNAMELEN];
int err;
err = copy_in_lofi_ioctl(ulip, &klip, ioctl_flag);
@@ -2370,12 +2981,12 @@ lofi_unmap_file(struct lofi_ioctl *ulip, int byfilename,
mutex_exit(&lofi_lock);
return (err);
}
- } else if (klip->li_minor == 0) {
+ } else if (klip->li_id == 0) {
mutex_exit(&lofi_lock);
free_lofi_ioctl(klip);
return (ENXIO);
} else {
- lsp = ddi_get_soft_state(lofi_statep, klip->li_minor);
+ lsp = ddi_get_soft_state(lofi_statep, klip->li_id);
}
if (lsp == NULL || lsp->ls_vp == NULL || lofi_access(lsp) != 0) {
@@ -2384,7 +2995,7 @@ lofi_unmap_file(struct lofi_ioctl *ulip, int byfilename,
return (ENXIO);
}
- klip->li_minor = getminor(lsp->ls_dev);
+ klip->li_id = LOFI_MINOR2ID(getminor(lsp->ls_dev));
/*
* If it's still held open, we'll do one of three things:
@@ -2432,9 +3043,27 @@ lofi_unmap_file(struct lofi_ioctl *ulip, int byfilename,
}
out:
- lofi_free_dev(lsp->ls_dev);
+ lofi_free_dev(lsp);
lofi_destroy(lsp, credp);
+ /*
+ * check the lofi_devlink_cache if device is really gone.
+ * note: we just wait for timeout here and dont give error if
+ * timer will expire. This check is to try to ensure the unmap is
+ * really done when lofiadm -d completes.
+ * Since lofi_lock is held, also hopefully the lofiadm -a calls
+ * wont interfere the the unmap.
+ */
+ (void) snprintf(name, sizeof (name), "%d", klip->li_id);
+ ticks = ddi_get_lbolt() + LOFI_TIMEOUT * drv_usectohz(1000000);
+ mutex_enter(&lofi_chan_lock);
+ while (nvlist_lookup_nvlist(lofi_devlink_cache, name, &nvl) == 0) {
+ err = cv_timedwait(&lofi_chan_cv, &lofi_chan_lock, ticks);
+ if (err == -1)
+ break;
+ }
+ mutex_exit(&lofi_chan_lock);
+
mutex_exit(&lofi_lock);
(void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
free_lofi_ioctl(klip);
@@ -2460,13 +3089,13 @@ lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which,
switch (which) {
case LOFI_GET_FILENAME:
- if (klip->li_minor == 0) {
+ if (klip->li_id == 0) {
free_lofi_ioctl(klip);
return (EINVAL);
}
mutex_enter(&lofi_lock);
- lsp = ddi_get_soft_state(lofi_statep, klip->li_minor);
+ lsp = ddi_get_soft_state(lofi_statep, klip->li_id);
if (lsp == NULL || lofi_access(lsp) != 0) {
mutex_exit(&lofi_lock);
free_lofi_ioctl(klip);
@@ -2484,11 +3113,14 @@ lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which,
}
klip->li_readonly = lsp->ls_readonly;
+ klip->li_labeled = lsp->ls_cmlbhandle != NULL;
(void) strlcpy(klip->li_algorithm, lsp->ls_comp_algorithm,
sizeof (klip->li_algorithm));
klip->li_crypto_enabled = lsp->ls_crypto_enabled;
mutex_exit(&lofi_lock);
+
+ lofi_copy_devpath(klip);
error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
free_lofi_ioctl(klip);
return (error);
@@ -2496,12 +3128,19 @@ lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which,
mutex_enter(&lofi_lock);
error = file_to_lofi(klip->li_filename,
klip->li_readonly, &lsp);
- if (error == 0)
- klip->li_minor = getminor(lsp->ls_dev);
+ if (error != 0) {
+ mutex_exit(&lofi_lock);
+ free_lofi_ioctl(klip);
+ return (error);
+ }
+ klip->li_id = LOFI_MINOR2ID(getminor(lsp->ls_dev));
+
+ klip->li_readonly = lsp->ls_readonly;
+ klip->li_labeled = lsp->ls_cmlbhandle != NULL;
mutex_exit(&lofi_lock);
- if (error == 0)
- error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
+ lofi_copy_devpath(klip);
+ error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag);
free_lofi_ioctl(klip);
return (error);
@@ -2515,7 +3154,7 @@ lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which,
return (error);
}
- klip->li_minor = getminor(lsp->ls_dev);
+ klip->li_id = LOFI_MINOR2ID(getminor(lsp->ls_dev));
(void) strlcpy(klip->li_algorithm, lsp->ls_comp_algorithm,
sizeof (klip->li_algorithm));
@@ -2530,17 +3169,63 @@ lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which,
}
static int
+uscsi_is_inquiry(intptr_t arg, int flag, union scsi_cdb *cdb,
+ struct uscsi_cmd *uscmd)
+{
+ int rval;
+
+#ifdef _MULTI_DATAMODEL
+ switch (ddi_model_convert_from(flag & FMODELS)) {
+ case DDI_MODEL_ILP32: {
+ struct uscsi_cmd32 ucmd32;
+
+ if (ddi_copyin((void *)arg, &ucmd32, sizeof (ucmd32), flag)) {
+ rval = EFAULT;
+ goto err;
+ }
+ uscsi_cmd32touscsi_cmd((&ucmd32), uscmd);
+ break;
+ }
+ case DDI_MODEL_NONE:
+ if (ddi_copyin((void *)arg, uscmd, sizeof (*uscmd), flag)) {
+ rval = EFAULT;
+ goto err;
+ }
+ break;
+ default:
+ rval = EFAULT;
+ goto err;
+ }
+#else
+ if (ddi_copyin((void *)arg, uscmd, sizeof (*uscmd), flag)) {
+ rval = EFAULT;
+ goto err;
+ }
+#endif /* _MULTI_DATAMODEL */
+ if (ddi_copyin(uscmd->uscsi_cdb, cdb, uscmd->uscsi_cdblen, flag)) {
+ rval = EFAULT;
+ goto err;
+ }
+ if (cdb->scc_cmd == SCMD_INQUIRY) {
+ return (0);
+ }
+err:
+ return (rval);
+}
+
+static int
lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp,
int *rvalp)
{
int error;
enum dkio_state dkstate;
struct lofi_state *lsp;
- minor_t minor;
+ int id;
+
+ id = LOFI_MINOR2ID(getminor(dev));
- minor = getminor(dev);
/* lofi ioctls only apply to the master device */
- if (minor == 0) {
+ if (id == 0) {
struct lofi_ioctl *lip = (struct lofi_ioctl *)arg;
/*
@@ -2576,27 +3261,29 @@ lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp,
* This API made limited sense when this value was fixed
* at LOFI_MAX_FILES. However, its use to iterate
* across all possible devices in lofiadm means we don't
- * want to return L_MAXMIN32, but the highest
- * *allocated* minor.
+ * want to return L_MAXMIN, but the highest
+ * *allocated* id.
*/
case LOFI_GET_MAXMINOR:
- minor = 0;
+ id = 0;
mutex_enter(&lofi_lock);
for (lsp = list_head(&lofi_list); lsp != NULL;
lsp = list_next(&lofi_list, lsp)) {
+ int i;
if (lofi_access(lsp) != 0)
continue;
- if (getminor(lsp->ls_dev) > minor)
- minor = getminor(lsp->ls_dev);
+ i = ddi_get_instance(lsp->ls_dip);
+ if (i > id)
+ id = i;
}
mutex_exit(&lofi_lock);
- error = ddi_copyout(&minor, &lip->li_minor,
- sizeof (minor), flag);
+ error = ddi_copyout(&id, &lip->li_id,
+ sizeof (id), flag);
if (error)
return (EFAULT);
return (0);
@@ -2610,13 +3297,21 @@ lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp,
}
mutex_enter(&lofi_lock);
- lsp = ddi_get_soft_state(lofi_statep, minor);
+ lsp = ddi_get_soft_state(lofi_statep, id);
if (lsp == NULL || lsp->ls_vp_closereq) {
mutex_exit(&lofi_lock);
return (ENXIO);
}
mutex_exit(&lofi_lock);
+ if (ddi_prop_exists(DDI_DEV_T_ANY, lsp->ls_dip, DDI_PROP_DONTPASS,
+ "labeled") == 1) {
+ error = cmlb_ioctl(lsp->ls_cmlbhandle, dev, cmd, arg, flag,
+ credp, rvalp, 0);
+ if (error != ENOTTY)
+ return (error);
+ }
+
/*
* We explicitly allow DKIOCSTATE, but all other ioctls should fail with
* EIO as if the device was no longer present.
@@ -2626,12 +3321,44 @@ lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp,
/* these are for faking out utilities like newfs */
switch (cmd) {
- case DKIOCGVTOC:
+ case DKIOCGMEDIAINFO:
+ case DKIOCGMEDIAINFOEXT: {
+ struct dk_minfo_ext media_info;
+ int shift = lsp->ls_lbshift;
+ int size;
+
+ if (cmd == DKIOCGMEDIAINFOEXT) {
+ media_info.dki_pbsize = 1U << lsp->ls_pbshift;
+ size = sizeof (struct dk_minfo_ext);
+ } else {
+ size = sizeof (struct dk_minfo);
+ }
+
+ media_info.dki_media_type = DK_FIXED_DISK;
+ media_info.dki_lbsize = 1U << shift;
+ media_info.dki_capacity =
+ (lsp->ls_vp_size - lsp->ls_crypto_offset) >> shift;
+
+ if (ddi_copyout(&media_info, (void *)arg, size, flag))
+ return (EFAULT);
+ return (0);
+ }
+ case DKIOCREMOVABLE: {
+ int i = 0;
+ if (ddi_copyout(&i, (caddr_t)arg, sizeof (int), flag))
+ return (EFAULT);
+ return (0);
+ }
+
+ case DKIOCGVTOC: {
+ struct vtoc vt;
+ fake_disk_vtoc(lsp, &vt);
+
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32: {
struct vtoc32 vtoc32;
- vtoctovtoc32(lsp->ls_vtoc, vtoc32);
+ vtoctovtoc32(vt, vtoc32);
if (ddi_copyout(&vtoc32, (void *)arg,
sizeof (struct vtoc32), flag))
return (EFAULT);
@@ -2639,18 +3366,20 @@ lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp,
}
case DDI_MODEL_NONE:
- if (ddi_copyout(&lsp->ls_vtoc, (void *)arg,
+ if (ddi_copyout(&vt, (void *)arg,
sizeof (struct vtoc), flag))
return (EFAULT);
break;
}
return (0);
- case DKIOCINFO:
- error = ddi_copyout(&lsp->ls_ci, (void *)arg,
- sizeof (struct dk_cinfo), flag);
- if (error)
+ }
+ case DKIOCINFO: {
+ struct dk_cinfo ci;
+ fake_disk_info(dev, &ci);
+ if (ddi_copyout(&ci, (void *)arg, sizeof (ci), flag))
return (EFAULT);
return (0);
+ }
case DKIOCG_VIRTGEOM:
case DKIOCG_PHYGEOM:
case DKIOCGGEOM:
@@ -2697,11 +3426,80 @@ lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp,
sizeof (dkstate), flag) != 0)
return (EFAULT);
return (0);
+ case USCSICMD: {
+ struct uscsi_cmd uscmd;
+ union scsi_cdb cdb;
+
+ if (uscsi_is_inquiry(arg, flag, &cdb, &uscmd) == 0) {
+ struct scsi_inquiry inq = {0};
+
+ lofi_create_inquiry(lsp, &inq);
+ if (ddi_copyout(&inq, uscmd.uscsi_bufaddr,
+ uscmd.uscsi_buflen, flag) != 0)
+ return (EFAULT);
+ return (0);
+ } else if (cdb.scc_cmd == SCMD_READ_CAPACITY) {
+ struct scsi_capacity capacity;
+
+ capacity.capacity =
+ BE_32((lsp->ls_vp_size - lsp->ls_crypto_offset) >>
+ lsp->ls_lbshift);
+ capacity.lbasize = BE_32(1 << lsp->ls_lbshift);
+ if (ddi_copyout(&capacity, uscmd.uscsi_bufaddr,
+ uscmd.uscsi_buflen, flag) != 0)
+ return (EFAULT);
+ return (0);
+ }
+
+ uscmd.uscsi_rqstatus = 0xff;
+#ifdef _MULTI_DATAMODEL
+ switch (ddi_model_convert_from(flag & FMODELS)) {
+ case DDI_MODEL_ILP32: {
+ struct uscsi_cmd32 ucmd32;
+ uscsi_cmdtouscsi_cmd32((&uscmd), (&ucmd32));
+ if (ddi_copyout(&ucmd32, (void *)arg, sizeof (ucmd32),
+ flag) != 0)
+ return (EFAULT);
+ break;
+ }
+ case DDI_MODEL_NONE:
+ if (ddi_copyout(&uscmd, (void *)arg, sizeof (uscmd),
+ flag) != 0)
+ return (EFAULT);
+ break;
+ default:
+ return (EFAULT);
+ }
+#else
+ if (ddi_copyout(&uscmd, (void *)arg, sizeof (uscmd), flag) != 0)
+ return (EFAULT);
+#endif /* _MULTI_DATAMODEL */
+ return (0);
+ }
default:
+#ifdef DEBUG
+ cmn_err(CE_WARN, "lofi_ioctl: %d is not implemented\n", cmd);
+#endif /* DEBUG */
return (ENOTTY);
}
}
+static int
+lofi_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
+ char *name, caddr_t valuep, int *lengthp)
+{
+ struct lofi_state *lsp;
+
+ lsp = ddi_get_soft_state(lofi_statep, ddi_get_instance(dip));
+ if (lsp == NULL) {
+ return (ddi_prop_op(dev, dip, prop_op, mod_flags,
+ name, valuep, lengthp));
+ }
+
+ return (cmlb_prop_op(lsp->ls_cmlbhandle, dev, dip, prop_op, mod_flags,
+ name, valuep, lengthp, LOFI_PART(getminor(dev)), NULL));
+}
+
static struct cb_ops lofi_cb_ops = {
lofi_open, /* open */
lofi_close, /* close */
@@ -2715,7 +3513,7 @@ static struct cb_ops lofi_cb_ops = {
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
- ddi_prop_op, /* prop_op */
+ lofi_prop_op, /* prop_op */
0, /* streamtab */
D_64BIT | D_NEW | D_MP, /* Driver compatibility flag */
CB_REV,
@@ -2758,17 +3556,43 @@ _init(void)
list_create(&lofi_list, sizeof (struct lofi_state),
offsetof(struct lofi_state, ls_list));
- error = ddi_soft_state_init(&lofi_statep,
+ error = ddi_soft_state_init((void **)&lofi_statep,
sizeof (struct lofi_state), 0);
- if (error)
+ if (error) {
+ list_destroy(&lofi_list);
return (error);
+ }
+
+ /*
+ * The minor number is stored as id << LOFI_CMLB_SHIFT as
+ * we need to reserve space for cmlb minor numbers.
+ * This will leave out 4096 id values on 32bit kernel, which should
+ * still suffice.
+ */
+ lofi_id = id_space_create("lofi_id", 1,
+ (1 << (L_BITSMINOR - LOFI_CMLB_SHIFT)));
+
+ if (lofi_id == NULL) {
+ ddi_soft_state_fini((void **)&lofi_statep);
+ list_destroy(&lofi_list);
+ return (DDI_FAILURE);
+ }
mutex_init(&lofi_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&lofi_chan_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&lofi_chan_cv, NULL, CV_DRIVER, NULL);
+ error = nvlist_alloc(&lofi_devlink_cache, NV_UNIQUE_NAME, KM_SLEEP);
- error = mod_install(&modlinkage);
+ if (error == 0)
+ error = mod_install(&modlinkage);
if (error) {
+ id_space_destroy(lofi_id);
+ if (lofi_devlink_cache != NULL)
+ nvlist_free(lofi_devlink_cache);
+ mutex_destroy(&lofi_chan_lock);
+ cv_destroy(&lofi_chan_cv);
mutex_destroy(&lofi_lock);
- ddi_soft_state_fini(&lofi_statep);
+ ddi_soft_state_fini((void **)&lofi_statep);
list_destroy(&lofi_list);
}
@@ -2793,8 +3617,16 @@ _fini(void)
if (error)
return (error);
+ mutex_enter(&lofi_chan_lock);
+ nvlist_free(lofi_devlink_cache);
+ lofi_devlink_cache = NULL;
+ mutex_exit(&lofi_chan_lock);
+
+ mutex_destroy(&lofi_chan_lock);
+ cv_destroy(&lofi_chan_cv);
mutex_destroy(&lofi_lock);
- ddi_soft_state_fini(&lofi_statep);
+ id_space_destroy(lofi_id);
+ ddi_soft_state_fini((void **)&lofi_statep);
list_destroy(&lofi_list);
return (error);