199e2a6f8SGarrett D'Amore /*
299e2a6f8SGarrett D'Amore  * This file and its contents are supplied under the terms of the
399e2a6f8SGarrett D'Amore  * Common Development and Distribution License ("CDDL"), version 1.0.
499e2a6f8SGarrett D'Amore  * You may only use this file in accordance with the terms of version
599e2a6f8SGarrett D'Amore  * 1.0 of the CDDL.
699e2a6f8SGarrett D'Amore  *
799e2a6f8SGarrett D'Amore  * A full copy of the text of the CDDL should have accompanied this
899e2a6f8SGarrett D'Amore  * source.  A copy of the CDDL is also available via the Internet at
999e2a6f8SGarrett D'Amore  * http://www.illumos.org/license/CDDL.
1099e2a6f8SGarrett D'Amore  */
1199e2a6f8SGarrett D'Amore 
1299e2a6f8SGarrett D'Amore /*
1399e2a6f8SGarrett D'Amore  * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
1499e2a6f8SGarrett D'Amore  * Copyright 2022 RackTop Systems, Inc.
1599e2a6f8SGarrett D'Amore  */
1699e2a6f8SGarrett D'Amore 
1799e2a6f8SGarrett D'Amore #include "vioscsi.h"
1899e2a6f8SGarrett D'Amore 
1999e2a6f8SGarrett D'Amore static char vioscsi_ident[] = "VIRTIO SCSI driver";
2099e2a6f8SGarrett D'Amore 
2199e2a6f8SGarrett D'Amore static uint_t vioscsi_ctl_handler(caddr_t arg1, caddr_t arg2);
2299e2a6f8SGarrett D'Amore static uint_t vioscsi_evt_handler(caddr_t arg1, caddr_t arg2);
2399e2a6f8SGarrett D'Amore static uint_t vioscsi_cmd_handler(caddr_t arg1, caddr_t arg2);
2499e2a6f8SGarrett D'Amore 
2599e2a6f8SGarrett D'Amore static int vioscsi_tran_getcap(struct scsi_address *, char *, int);
2699e2a6f8SGarrett D'Amore static int vioscsi_tran_setcap(struct scsi_address *, char *, int, int);
2799e2a6f8SGarrett D'Amore static int vioscsi_tran_reset(struct scsi_address *, int);
2899e2a6f8SGarrett D'Amore 
2999e2a6f8SGarrett D'Amore static int vioscsi_tran_start(struct scsi_address *, struct scsi_pkt *);
3099e2a6f8SGarrett D'Amore static int vioscsi_tran_abort(struct scsi_address *, struct scsi_pkt *);
3199e2a6f8SGarrett D'Amore 
3299e2a6f8SGarrett D'Amore static int vioscsi_iport_attach(dev_info_t *);
3399e2a6f8SGarrett D'Amore static int vioscsi_iport_detach(dev_info_t *);
3499e2a6f8SGarrett D'Amore 
3599e2a6f8SGarrett D'Amore static int vioscsi_req_init(vioscsi_softc_t *, vioscsi_request_t *,
3699e2a6f8SGarrett D'Amore     virtio_queue_t *, int);
3799e2a6f8SGarrett D'Amore static void vioscsi_req_fini(vioscsi_request_t *);
3899e2a6f8SGarrett D'Amore static boolean_t vioscsi_req_abort(vioscsi_softc_t *, vioscsi_request_t *);
3999e2a6f8SGarrett D'Amore static void vioscsi_lun_changed(vioscsi_softc_t *sc, uint8_t target);
4099e2a6f8SGarrett D'Amore static void vioscsi_discover(void *);
4199e2a6f8SGarrett D'Amore 
4299e2a6f8SGarrett D'Amore /*
4399e2a6f8SGarrett D'Amore  * DMA attributes. We support a linked list, but most of our uses require a
4499e2a6f8SGarrett D'Amore  * single aligned buffer.  The HBA buffers will use a copy of this adjusted for
4599e2a6f8SGarrett D'Amore  * the actual virtio limits.
4699e2a6f8SGarrett D'Amore  */
4799e2a6f8SGarrett D'Amore static ddi_dma_attr_t virtio_dma_attr = {
4899e2a6f8SGarrett D'Amore 	.dma_attr_version =		DMA_ATTR_V0,
4999e2a6f8SGarrett D'Amore 	.dma_attr_addr_lo =		0,
5099e2a6f8SGarrett D'Amore 	.dma_attr_addr_hi =		0xFFFFFFFFFFFFFFFFull,
5199e2a6f8SGarrett D'Amore 	.dma_attr_count_max =		0x00000000FFFFFFFFull,
5299e2a6f8SGarrett D'Amore 	.dma_attr_align =		1,
5399e2a6f8SGarrett D'Amore 	.dma_attr_burstsizes =		1,
5499e2a6f8SGarrett D'Amore 	.dma_attr_minxfer =		1,
5599e2a6f8SGarrett D'Amore 	.dma_attr_maxxfer =		0xFFFFFFFFull,
5699e2a6f8SGarrett D'Amore 	.dma_attr_seg =			0xFFFFFFFFFFFFFFFFull,
5799e2a6f8SGarrett D'Amore 	.dma_attr_sgllen =		1,
5899e2a6f8SGarrett D'Amore 	.dma_attr_granular =		1,
5999e2a6f8SGarrett D'Amore 	.dma_attr_flags =		0,
6099e2a6f8SGarrett D'Amore };
6199e2a6f8SGarrett D'Amore 
6299e2a6f8SGarrett D'Amore /*
6399e2a6f8SGarrett D'Amore  * this avoids calls to drv_usectohz that might be expensive:
6499e2a6f8SGarrett D'Amore  */
6599e2a6f8SGarrett D'Amore static clock_t vioscsi_hz;
6699e2a6f8SGarrett D'Amore 
6799e2a6f8SGarrett D'Amore static boolean_t
vioscsi_poll_until(vioscsi_softc_t * sc,vioscsi_request_t * req,ddi_intr_handler_t func,clock_t until)6899e2a6f8SGarrett D'Amore vioscsi_poll_until(vioscsi_softc_t *sc, vioscsi_request_t *req,
6999e2a6f8SGarrett D'Amore     ddi_intr_handler_t func, clock_t until)
7099e2a6f8SGarrett D'Amore {
7199e2a6f8SGarrett D'Amore 	until *= 1000000; /* convert to usec */
7299e2a6f8SGarrett D'Amore 	while (until > 0) {
7399e2a6f8SGarrett D'Amore 		(void) func((caddr_t)sc, NULL);
7499e2a6f8SGarrett D'Amore 		if (req->vr_done) {
7599e2a6f8SGarrett D'Amore 			return (B_TRUE);
7699e2a6f8SGarrett D'Amore 		}
7799e2a6f8SGarrett D'Amore 		drv_usecwait(10);
7899e2a6f8SGarrett D'Amore 		until -= 10;
7999e2a6f8SGarrett D'Amore 	}
8099e2a6f8SGarrett D'Amore 	atomic_or_8(&req->vr_expired, 1);
8199e2a6f8SGarrett D'Amore 	return (B_FALSE);
8299e2a6f8SGarrett D'Amore }
8399e2a6f8SGarrett D'Amore 
8499e2a6f8SGarrett D'Amore static boolean_t
vioscsi_tmf(vioscsi_softc_t * sc,uint32_t func,uint8_t target,uint16_t lun,vioscsi_request_t * task)8599e2a6f8SGarrett D'Amore vioscsi_tmf(vioscsi_softc_t *sc, uint32_t func, uint8_t target, uint16_t lun,
8699e2a6f8SGarrett D'Amore     vioscsi_request_t *task)
8799e2a6f8SGarrett D'Amore {
8899e2a6f8SGarrett D'Amore 	vioscsi_request_t req;
8999e2a6f8SGarrett D'Amore 	vioscsi_tmf_res_t *res;
9099e2a6f8SGarrett D'Amore 	vioscsi_tmf_req_t *tmf;
9199e2a6f8SGarrett D'Amore 
9299e2a6f8SGarrett D'Amore 	bzero(&req, sizeof (req));
9399e2a6f8SGarrett D'Amore 
9499e2a6f8SGarrett D'Amore 	if (vioscsi_req_init(sc, &req, sc->vs_ctl_vq, KM_NOSLEEP) != 0) {
9599e2a6f8SGarrett D'Amore 		return (B_FALSE);
9699e2a6f8SGarrett D'Amore 	}
9799e2a6f8SGarrett D'Amore 
9899e2a6f8SGarrett D'Amore 	tmf = &req.vr_req->tmf;
9999e2a6f8SGarrett D'Amore 	res = &req.vr_res->tmf;
10099e2a6f8SGarrett D'Amore 
10199e2a6f8SGarrett D'Amore 	tmf->type = VIRTIO_SCSI_T_TMF;
10299e2a6f8SGarrett D'Amore 	tmf->subtype = func;
10399e2a6f8SGarrett D'Amore 	tmf->lun[0] = 1;
10499e2a6f8SGarrett D'Amore 	tmf->lun[1] = target;
10599e2a6f8SGarrett D'Amore 	tmf->lun[2] = 0x40 | (lun >> 8);
10699e2a6f8SGarrett D'Amore 	tmf->lun[3] = lun & 0xff;
10799e2a6f8SGarrett D'Amore 	tmf->tag = (uint64_t)task;
10899e2a6f8SGarrett D'Amore 
10999e2a6f8SGarrett D'Amore 	virtio_chain_clear(req.vr_vic);
11099e2a6f8SGarrett D'Amore 	if (virtio_chain_append(req.vr_vic, req.vr_req_pa, sizeof (*tmf),
11199e2a6f8SGarrett D'Amore 	    VIRTIO_DIR_DEVICE_READS) != DDI_SUCCESS) {
11299e2a6f8SGarrett D'Amore 		return (B_FALSE);
11399e2a6f8SGarrett D'Amore 	}
11499e2a6f8SGarrett D'Amore 
11599e2a6f8SGarrett D'Amore 	if (virtio_chain_append(req.vr_vic, req.vr_res_pa, sizeof (*res),
11699e2a6f8SGarrett D'Amore 	    VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
11799e2a6f8SGarrett D'Amore 		return (B_FALSE);
11899e2a6f8SGarrett D'Amore 	}
11999e2a6f8SGarrett D'Amore 
12099e2a6f8SGarrett D'Amore 	/*
12199e2a6f8SGarrett D'Amore 	 * Make sure the device can see our request:
12299e2a6f8SGarrett D'Amore 	 */
12399e2a6f8SGarrett D'Amore 	virtio_dma_sync(req.vr_dma, DDI_DMA_SYNC_FORDEV);
12499e2a6f8SGarrett D'Amore 
12599e2a6f8SGarrett D'Amore 	/*
12699e2a6f8SGarrett D'Amore 	 * Push chain into the queue:
12799e2a6f8SGarrett D'Amore 	 */
12899e2a6f8SGarrett D'Amore 	virtio_chain_submit(req.vr_vic, B_TRUE);
12999e2a6f8SGarrett D'Amore 
13099e2a6f8SGarrett D'Amore 	/*
13199e2a6f8SGarrett D'Amore 	 * Wait for it to complete -- these should always complete in a tiny
13299e2a6f8SGarrett D'Amore 	 * amount of time.  Give it 5 seconds to be sure.
13399e2a6f8SGarrett D'Amore 	 */
13499e2a6f8SGarrett D'Amore 	if (!vioscsi_poll_until(sc, &req,  vioscsi_ctl_handler, 5)) {
13599e2a6f8SGarrett D'Amore 		/*
13699e2a6f8SGarrett D'Amore 		 * We timed out -- this should *NEVER* happen!
13799e2a6f8SGarrett D'Amore 		 * There is no safe way to deal with this if it occurs, so we
13899e2a6f8SGarrett D'Amore 		 * just warn and leak the resources.  Plan for a reboot soon.
13999e2a6f8SGarrett D'Amore 		 */
14099e2a6f8SGarrett D'Amore 		dev_err(sc->vs_dip, CE_WARN,
14199e2a6f8SGarrett D'Amore 		    "task mgmt timeout! (target %d lun %d)", target, lun);
14299e2a6f8SGarrett D'Amore 		return (B_FALSE);
14399e2a6f8SGarrett D'Amore 	}
14499e2a6f8SGarrett D'Amore 
14599e2a6f8SGarrett D'Amore 	vioscsi_req_fini(&req);
14699e2a6f8SGarrett D'Amore 
14799e2a6f8SGarrett D'Amore 	switch (res->response) {
14899e2a6f8SGarrett D'Amore 	case VIRTIO_SCSI_S_OK:
14999e2a6f8SGarrett D'Amore 	case VIRTIO_SCSI_S_FUNCTION_SUCCEEDED:
15099e2a6f8SGarrett D'Amore 		break;
15199e2a6f8SGarrett D'Amore 	default:
15299e2a6f8SGarrett D'Amore 		return (B_FALSE);
15399e2a6f8SGarrett D'Amore 	}
15499e2a6f8SGarrett D'Amore 	return (B_TRUE);
15599e2a6f8SGarrett D'Amore }
15699e2a6f8SGarrett D'Amore 
15799e2a6f8SGarrett D'Amore static boolean_t
vioscsi_lun_reset(vioscsi_softc_t * sc,uint8_t target,uint16_t lun)15899e2a6f8SGarrett D'Amore vioscsi_lun_reset(vioscsi_softc_t *sc, uint8_t target, uint16_t lun)
15999e2a6f8SGarrett D'Amore {
16099e2a6f8SGarrett D'Amore 	return (vioscsi_tmf(sc, VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET,
16199e2a6f8SGarrett D'Amore 	    target, lun, NULL));
16299e2a6f8SGarrett D'Amore }
16399e2a6f8SGarrett D'Amore 
16499e2a6f8SGarrett D'Amore static boolean_t
vioscsi_target_reset(vioscsi_softc_t * sc,uint8_t target)16599e2a6f8SGarrett D'Amore vioscsi_target_reset(vioscsi_softc_t *sc, uint8_t target)
16699e2a6f8SGarrett D'Amore {
16799e2a6f8SGarrett D'Amore 	return (vioscsi_tmf(sc, VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET,
16899e2a6f8SGarrett D'Amore 	    target, 0, NULL));
16999e2a6f8SGarrett D'Amore }
17099e2a6f8SGarrett D'Amore 
17199e2a6f8SGarrett D'Amore static boolean_t
vioscsi_req_abort(vioscsi_softc_t * sc,vioscsi_request_t * req)17299e2a6f8SGarrett D'Amore vioscsi_req_abort(vioscsi_softc_t *sc, vioscsi_request_t *req)
17399e2a6f8SGarrett D'Amore {
17499e2a6f8SGarrett D'Amore 	return (vioscsi_tmf(sc, VIRTIO_SCSI_T_TMF_ABORT_TASK,
17599e2a6f8SGarrett D'Amore 	    req->vr_target, req->vr_lun, req));
17699e2a6f8SGarrett D'Amore }
17799e2a6f8SGarrett D'Amore 
17899e2a6f8SGarrett D'Amore static void
vioscsi_dev_abort(vioscsi_dev_t * vd)17999e2a6f8SGarrett D'Amore vioscsi_dev_abort(vioscsi_dev_t *vd)
18099e2a6f8SGarrett D'Amore {
18199e2a6f8SGarrett D'Amore 	vioscsi_request_t *req;
18299e2a6f8SGarrett D'Amore 	list_t *l = &vd->vd_reqs;
18399e2a6f8SGarrett D'Amore 
18499e2a6f8SGarrett D'Amore 	mutex_enter(&vd->vd_lock);
18599e2a6f8SGarrett D'Amore 	for (req = list_head(l); req != NULL; req = list_next(l, req)) {
18699e2a6f8SGarrett D'Amore 		(void) vioscsi_tmf(vd->vd_sc, VIRTIO_SCSI_T_TMF_ABORT_TASK,
18799e2a6f8SGarrett D'Amore 		    req->vr_target, req->vr_lun, req);
18899e2a6f8SGarrett D'Amore 	}
18999e2a6f8SGarrett D'Amore 	mutex_exit(&vd->vd_lock);
19099e2a6f8SGarrett D'Amore }
19199e2a6f8SGarrett D'Amore 
19299e2a6f8SGarrett D'Amore static void
vioscsi_dev_timeout(void * arg)19399e2a6f8SGarrett D'Amore vioscsi_dev_timeout(void *arg)
19499e2a6f8SGarrett D'Amore {
19599e2a6f8SGarrett D'Amore 	vioscsi_dev_t *vd = arg;
19699e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = vd->vd_sc;
19799e2a6f8SGarrett D'Amore 	vioscsi_request_t *req;
19899e2a6f8SGarrett D'Amore 	timeout_id_t tid;
19999e2a6f8SGarrett D'Amore 	clock_t now;
20099e2a6f8SGarrett D'Amore 	list_t *l;
20199e2a6f8SGarrett D'Amore 
20299e2a6f8SGarrett D'Amore 	mutex_enter(&vd->vd_lock);
20399e2a6f8SGarrett D'Amore 	if ((tid = vd->vd_timeout) == 0) {
20499e2a6f8SGarrett D'Amore 		/*
20599e2a6f8SGarrett D'Amore 		 * We are shutting down, stop and do not reschedule.
20699e2a6f8SGarrett D'Amore 		 */
20799e2a6f8SGarrett D'Amore 		mutex_exit(&vd->vd_lock);
20899e2a6f8SGarrett D'Amore 		return;
20999e2a6f8SGarrett D'Amore 	}
21099e2a6f8SGarrett D'Amore 	vd->vd_timeout = 0;
21199e2a6f8SGarrett D'Amore 
21299e2a6f8SGarrett D'Amore 	now = ddi_get_lbolt();
21399e2a6f8SGarrett D'Amore 	l = &vd->vd_reqs;
21499e2a6f8SGarrett D'Amore 
21599e2a6f8SGarrett D'Amore 	for (req = list_head(l); req != NULL; req = list_next(l, req)) {
21699e2a6f8SGarrett D'Amore 		/*
21799e2a6f8SGarrett D'Amore 		 * The list is sorted by expiration time, so if we reach an
21899e2a6f8SGarrett D'Amore 		 * item that hasn't expired yet, we're done.
21999e2a6f8SGarrett D'Amore 		 */
22099e2a6f8SGarrett D'Amore 		if (now < req->vr_expire) {
22199e2a6f8SGarrett D'Amore 			break;
22299e2a6f8SGarrett D'Amore 		}
22399e2a6f8SGarrett D'Amore 		atomic_or_8(&req->vr_expired, 1);
22499e2a6f8SGarrett D'Amore 
22599e2a6f8SGarrett D'Amore 		/*
22699e2a6f8SGarrett D'Amore 		 * This command timed out, so send an abort.
22799e2a6f8SGarrett D'Amore 		 */
22899e2a6f8SGarrett D'Amore 		dev_err(sc->vs_dip, CE_WARN, "cmd timed out (%ds)",
22999e2a6f8SGarrett D'Amore 		    (int)req->vr_time);
23099e2a6f8SGarrett D'Amore 		(void) vioscsi_req_abort(sc, req);
23199e2a6f8SGarrett D'Amore 	}
23299e2a6f8SGarrett D'Amore 
23399e2a6f8SGarrett D'Amore 	if (!list_is_empty(l)) {
23499e2a6f8SGarrett D'Amore 		/*
23599e2a6f8SGarrett D'Amore 		 * Check again in a second.
23699e2a6f8SGarrett D'Amore 		 * If these wake ups are too expensive, we could
23799e2a6f8SGarrett D'Amore 		 * calculate other timeouts, but that would require
23899e2a6f8SGarrett D'Amore 		 * doing untimeout if we want to wake up earlier.
23999e2a6f8SGarrett D'Amore 		 * This is probably cheaper, and certainly simpler.
24099e2a6f8SGarrett D'Amore 		 */
24199e2a6f8SGarrett D'Amore 		vd->vd_timeout = timeout(vioscsi_dev_timeout, vd, vioscsi_hz);
24299e2a6f8SGarrett D'Amore 	}
24399e2a6f8SGarrett D'Amore 	mutex_exit(&vd->vd_lock);
24499e2a6f8SGarrett D'Amore }
24599e2a6f8SGarrett D'Amore 
24699e2a6f8SGarrett D'Amore static void
vioscsi_poll(vioscsi_softc_t * sc,vioscsi_request_t * req)24799e2a6f8SGarrett D'Amore vioscsi_poll(vioscsi_softc_t *sc, vioscsi_request_t *req)
24899e2a6f8SGarrett D'Amore {
24999e2a6f8SGarrett D'Amore 	if (vioscsi_poll_until(sc, req, vioscsi_cmd_handler, req->vr_time)) {
25099e2a6f8SGarrett D'Amore 		return;
25199e2a6f8SGarrett D'Amore 	}
25299e2a6f8SGarrett D'Amore 
25399e2a6f8SGarrett D'Amore 	/*
25499e2a6f8SGarrett D'Amore 	 * Try a "gentle" task abort -- timeouts may be quasi-normal for some
25599e2a6f8SGarrett D'Amore 	 * types of requests and devices.
25699e2a6f8SGarrett D'Amore 	 */
25799e2a6f8SGarrett D'Amore 	if (vioscsi_req_abort(sc, req) &&
25899e2a6f8SGarrett D'Amore 	    vioscsi_poll_until(sc, req, vioscsi_cmd_handler, 1)) {
25999e2a6f8SGarrett D'Amore 		return;
26099e2a6f8SGarrett D'Amore 	}
26199e2a6f8SGarrett D'Amore 
26299e2a6f8SGarrett D'Amore 	/*
26399e2a6f8SGarrett D'Amore 	 * A little more forceful with a lun reset:
26499e2a6f8SGarrett D'Amore 	 */
26599e2a6f8SGarrett D'Amore 	if (vioscsi_lun_reset(sc, req->vr_target, req->vr_lun) &&
26699e2a6f8SGarrett D'Amore 	    vioscsi_poll_until(sc, req, vioscsi_cmd_handler, 1)) {
26799e2a6f8SGarrett D'Amore 		return;
26899e2a6f8SGarrett D'Amore 	}
26999e2a6f8SGarrett D'Amore 
27099e2a6f8SGarrett D'Amore 	/*
27199e2a6f8SGarrett D'Amore 	 * If all else fails, reset the target, and keep trying.
27299e2a6f8SGarrett D'Amore 	 * This can wind up blocking forever, but if it does it means we are in
27399e2a6f8SGarrett D'Amore 	 * a very bad situation (and the virtio device is busted).
27499e2a6f8SGarrett D'Amore 	 * We may also be leaking request structures at this point, but only at
27599e2a6f8SGarrett D'Amore 	 * the maximum rate of one per minute.
27699e2a6f8SGarrett D'Amore 	 */
27799e2a6f8SGarrett D'Amore 	for (;;) {
27899e2a6f8SGarrett D'Amore 		dev_err(sc->vs_dip, CE_WARN, "request stuck, resetting target");
27999e2a6f8SGarrett D'Amore 		(void) vioscsi_target_reset(sc, req->vr_target);
28099e2a6f8SGarrett D'Amore 		if (vioscsi_poll_until(sc, req, vioscsi_cmd_handler, 60)) {
28199e2a6f8SGarrett D'Amore 			return;
28299e2a6f8SGarrett D'Amore 		}
28399e2a6f8SGarrett D'Amore 	}
28499e2a6f8SGarrett D'Amore }
28599e2a6f8SGarrett D'Amore 
28699e2a6f8SGarrett D'Amore static void
vioscsi_start(vioscsi_softc_t * sc,vioscsi_request_t * req)28799e2a6f8SGarrett D'Amore vioscsi_start(vioscsi_softc_t *sc, vioscsi_request_t *req)
28899e2a6f8SGarrett D'Amore {
28999e2a6f8SGarrett D'Amore 	vioscsi_cmd_req_t *cmd = &req->vr_req->cmd;
29099e2a6f8SGarrett D'Amore 
29199e2a6f8SGarrett D'Amore 	req->vr_done = 0;
29299e2a6f8SGarrett D'Amore 	req->vr_expired = 0;
29399e2a6f8SGarrett D'Amore 	cmd->lun[0] = 1;
29499e2a6f8SGarrett D'Amore 	cmd->lun[1] = req->vr_target;
29599e2a6f8SGarrett D'Amore 	cmd->lun[2] = 0x40 | ((req->vr_lun >> 8) & 0xff);
29699e2a6f8SGarrett D'Amore 	cmd->lun[3] = req->vr_lun & 0xff;
29799e2a6f8SGarrett D'Amore 	cmd->lun[4] = 0;
29899e2a6f8SGarrett D'Amore 	cmd->lun[5] = 0;
29999e2a6f8SGarrett D'Amore 	cmd->lun[6] = 0;
30099e2a6f8SGarrett D'Amore 	cmd->lun[7] = 0;
30199e2a6f8SGarrett D'Amore 	cmd->tag = (uint64_t)req;
30299e2a6f8SGarrett D'Amore 	cmd->prio = 0;
30399e2a6f8SGarrett D'Amore 	cmd->crn = 0;
30499e2a6f8SGarrett D'Amore 	cmd->task_attr = req->vr_task_attr;
30599e2a6f8SGarrett D'Amore 
30699e2a6f8SGarrett D'Amore 	/*
30799e2a6f8SGarrett D'Amore 	 * Make sure the device can see our CDB data:
30899e2a6f8SGarrett D'Amore 	 */
30999e2a6f8SGarrett D'Amore 	virtio_dma_sync(req->vr_dma, DDI_DMA_SYNC_FORDEV);
31099e2a6f8SGarrett D'Amore 
31199e2a6f8SGarrett D'Amore 	/*
31299e2a6f8SGarrett D'Amore 	 * Determine whether we expect to poll before submitting (because we
31399e2a6f8SGarrett D'Amore 	 * cannot touch the request after submission if we are not polling).
31499e2a6f8SGarrett D'Amore 	 */
31599e2a6f8SGarrett D'Amore 	if (req->vr_poll) {
31699e2a6f8SGarrett D'Amore 		/*
31799e2a6f8SGarrett D'Amore 		 * Push chain into the queue:
31899e2a6f8SGarrett D'Amore 		 */
31999e2a6f8SGarrett D'Amore 		virtio_chain_submit(req->vr_vic, B_TRUE);
32099e2a6f8SGarrett D'Amore 
32199e2a6f8SGarrett D'Amore 		/*
32299e2a6f8SGarrett D'Amore 		 * NB: Interrupts may be enabled, or might not be.  It is fine
32399e2a6f8SGarrett D'Amore 		 * either way.
32499e2a6f8SGarrett D'Amore 		 */
32599e2a6f8SGarrett D'Amore 		vioscsi_poll(sc, req);
32699e2a6f8SGarrett D'Amore 	} else {
32799e2a6f8SGarrett D'Amore 		/*
32899e2a6f8SGarrett D'Amore 		 * Push chain into the queue:
32999e2a6f8SGarrett D'Amore 		 */
33099e2a6f8SGarrett D'Amore 		virtio_chain_submit(req->vr_vic, B_TRUE);
33199e2a6f8SGarrett D'Amore 	}
33299e2a6f8SGarrett D'Amore }
33399e2a6f8SGarrett D'Amore 
33499e2a6f8SGarrett D'Amore static int
vioscsi_tran_start(struct scsi_address * ap,struct scsi_pkt * pkt)33599e2a6f8SGarrett D'Amore vioscsi_tran_start(struct scsi_address *ap, struct scsi_pkt *pkt)
33699e2a6f8SGarrett D'Amore {
33799e2a6f8SGarrett D'Amore 	struct scsi_device *sd = scsi_address_device(ap);
33899e2a6f8SGarrett D'Amore 	vioscsi_dev_t *vd = scsi_device_hba_private_get(sd);
33999e2a6f8SGarrett D'Amore 	vioscsi_request_t *req = pkt->pkt_ha_private;
34099e2a6f8SGarrett D'Amore 	virtio_chain_t *vic = req->vr_vic;
34199e2a6f8SGarrett D'Amore 	vioscsi_cmd_req_t *cmd = &req->vr_req->cmd;
34299e2a6f8SGarrett D'Amore 	vioscsi_cmd_res_t *res = &req->vr_res->cmd;
34399e2a6f8SGarrett D'Amore 
34499e2a6f8SGarrett D'Amore 	if (pkt->pkt_cdbp == NULL) {
34599e2a6f8SGarrett D'Amore 		return (TRAN_BADPKT);
34699e2a6f8SGarrett D'Amore 	}
34799e2a6f8SGarrett D'Amore 
34899e2a6f8SGarrett D'Amore 	bzero(cmd, sizeof (*cmd));
34999e2a6f8SGarrett D'Amore 	bcopy(pkt->pkt_cdbp, cmd->cdb, pkt->pkt_cdblen);
35099e2a6f8SGarrett D'Amore 
35199e2a6f8SGarrett D'Amore 	/*
35299e2a6f8SGarrett D'Amore 	 * Default expiration is 10 seconds, clip at an hour.
35399e2a6f8SGarrett D'Amore 	 * (order of operations here is to avoid wrapping, if run in a 32-bit
35499e2a6f8SGarrett D'Amore 	 * kernel)
35599e2a6f8SGarrett D'Amore 	 */
35699e2a6f8SGarrett D'Amore 	req->vr_time = min(pkt->pkt_time ? pkt->pkt_time : 10, 3600);
35799e2a6f8SGarrett D'Amore 	req->vr_dev = vd;
35899e2a6f8SGarrett D'Amore 	req->vr_poll = ((pkt->pkt_flags & FLAG_NOINTR) != 0);
35999e2a6f8SGarrett D'Amore 	req->vr_target = vd->vd_target;
36099e2a6f8SGarrett D'Amore 	req->vr_lun = vd->vd_lun;
36199e2a6f8SGarrett D'Amore 	req->vr_start = ddi_get_lbolt();
36299e2a6f8SGarrett D'Amore 	req->vr_expire = req->vr_start + req->vr_time * vioscsi_hz;
36399e2a6f8SGarrett D'Amore 
36499e2a6f8SGarrett D'Amore 	/*
36599e2a6f8SGarrett D'Amore 	 * Configure task queuing behavior:
36699e2a6f8SGarrett D'Amore 	 */
36799e2a6f8SGarrett D'Amore 	if (pkt->pkt_flags & (FLAG_HTAG|FLAG_HEAD)) {
36899e2a6f8SGarrett D'Amore 		req->vr_task_attr = VIRTIO_SCSI_S_HEAD;
36999e2a6f8SGarrett D'Amore 	} else if (pkt->pkt_flags & FLAG_OTAG) {
37099e2a6f8SGarrett D'Amore 		req->vr_task_attr = VIRTIO_SCSI_S_ORDERED;
37199e2a6f8SGarrett D'Amore 	} else if (pkt->pkt_flags & FLAG_SENSING) {
37299e2a6f8SGarrett D'Amore 		req->vr_task_attr = VIRTIO_SCSI_S_ACA;
37399e2a6f8SGarrett D'Amore 	} else { /* FLAG_STAG is also our default */
37499e2a6f8SGarrett D'Amore 		req->vr_task_attr = VIRTIO_SCSI_S_SIMPLE;
37599e2a6f8SGarrett D'Amore 	}
37699e2a6f8SGarrett D'Amore 
37799e2a6f8SGarrett D'Amore 	/*
37899e2a6f8SGarrett D'Amore 	 * Make sure we start with a clear chain:
37999e2a6f8SGarrett D'Amore 	 */
38099e2a6f8SGarrett D'Amore 	virtio_chain_clear(vic);
38199e2a6f8SGarrett D'Amore 
38299e2a6f8SGarrett D'Amore 	/*
38399e2a6f8SGarrett D'Amore 	 * The KVM SCSI emulation requires that all outgoing buffers are added
38499e2a6f8SGarrett D'Amore 	 * first with the request header being the first entry.  After the
38599e2a6f8SGarrett D'Amore 	 * outgoing have been added then the incoming buffers with the response
38699e2a6f8SGarrett D'Amore 	 * buffer being the first of the incoming.  This requirement is
38799e2a6f8SGarrett D'Amore 	 * independent of using chained ring entries or one ring entry with
38899e2a6f8SGarrett D'Amore 	 * indirect buffers.
38999e2a6f8SGarrett D'Amore 	 */
39099e2a6f8SGarrett D'Amore 
39199e2a6f8SGarrett D'Amore 	/*
39299e2a6f8SGarrett D'Amore 	 * Add request header:
39399e2a6f8SGarrett D'Amore 	 */
39499e2a6f8SGarrett D'Amore 	if (virtio_chain_append(vic, req->vr_req_pa, sizeof (*cmd),
39599e2a6f8SGarrett D'Amore 	    VIRTIO_DIR_DEVICE_READS) != DDI_SUCCESS) {
39699e2a6f8SGarrett D'Amore 		return (TRAN_BUSY);
39799e2a6f8SGarrett D'Amore 	}
39899e2a6f8SGarrett D'Amore 
39999e2a6f8SGarrett D'Amore 	/*
40099e2a6f8SGarrett D'Amore 	 * Add write buffers:
40199e2a6f8SGarrett D'Amore 	 */
40299e2a6f8SGarrett D'Amore 	if (pkt->pkt_dma_flags & DDI_DMA_WRITE) {
40399e2a6f8SGarrett D'Amore 		for (int i = 0; i < pkt->pkt_numcookies; i++) {
40499e2a6f8SGarrett D'Amore 			if (virtio_chain_append(vic,
40599e2a6f8SGarrett D'Amore 			    pkt->pkt_cookies[i].dmac_laddress,
40699e2a6f8SGarrett D'Amore 			    pkt->pkt_cookies[i].dmac_size,
40799e2a6f8SGarrett D'Amore 			    VIRTIO_DIR_DEVICE_READS) != DDI_SUCCESS) {
40899e2a6f8SGarrett D'Amore 				return (TRAN_BUSY);
40999e2a6f8SGarrett D'Amore 			}
41099e2a6f8SGarrett D'Amore 		}
41199e2a6f8SGarrett D'Amore 	}
41299e2a6f8SGarrett D'Amore 
41399e2a6f8SGarrett D'Amore 	/*
41499e2a6f8SGarrett D'Amore 	 * Add response header:
41599e2a6f8SGarrett D'Amore 	 */
41699e2a6f8SGarrett D'Amore 	if (virtio_chain_append(vic, req->vr_res_pa, sizeof (*res),
41799e2a6f8SGarrett D'Amore 	    VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
41899e2a6f8SGarrett D'Amore 		return (TRAN_BUSY);
41999e2a6f8SGarrett D'Amore 	}
42099e2a6f8SGarrett D'Amore 
42199e2a6f8SGarrett D'Amore 	/*
42299e2a6f8SGarrett D'Amore 	 * Add read buffers:
42399e2a6f8SGarrett D'Amore 	 */
42499e2a6f8SGarrett D'Amore 	if (pkt->pkt_dma_flags & DDI_DMA_READ) {
42599e2a6f8SGarrett D'Amore 		for (int i = 0; i < pkt->pkt_numcookies; i++) {
42699e2a6f8SGarrett D'Amore 			if (virtio_chain_append(vic,
42799e2a6f8SGarrett D'Amore 			    pkt->pkt_cookies[i].dmac_laddress,
42899e2a6f8SGarrett D'Amore 			    pkt->pkt_cookies[i].dmac_size,
42999e2a6f8SGarrett D'Amore 			    VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
43099e2a6f8SGarrett D'Amore 				return (TRAN_BUSY);
43199e2a6f8SGarrett D'Amore 			}
43299e2a6f8SGarrett D'Amore 		}
43399e2a6f8SGarrett D'Amore 	}
43499e2a6f8SGarrett D'Amore 
43599e2a6f8SGarrett D'Amore 	/*
43699e2a6f8SGarrett D'Amore 	 * Check for queue depth, and add to the timeout list:
43799e2a6f8SGarrett D'Amore 	 */
43899e2a6f8SGarrett D'Amore 	mutex_enter(&vd->vd_lock);
43999e2a6f8SGarrett D'Amore 	if (vd->vd_num_cmd >= vd->vd_max_cmd) {
44099e2a6f8SGarrett D'Amore 		mutex_exit(&vd->vd_lock);
44199e2a6f8SGarrett D'Amore 		return (TRAN_BUSY);
44299e2a6f8SGarrett D'Amore 	}
44399e2a6f8SGarrett D'Amore 	vd->vd_num_cmd++;
44499e2a6f8SGarrett D'Amore 
44599e2a6f8SGarrett D'Amore 	if (!req->vr_poll) {
44699e2a6f8SGarrett D'Amore 		/*
44799e2a6f8SGarrett D'Amore 		 * Add the request to the timeout list.
44899e2a6f8SGarrett D'Amore 		 *
44999e2a6f8SGarrett D'Amore 		 * In order to minimize the work done during timeout handling,
45099e2a6f8SGarrett D'Amore 		 * we keep requests sorted.  This assumes that requests mostly
45199e2a6f8SGarrett D'Amore 		 * have the same timeout, and requests with long timeouts are
45299e2a6f8SGarrett D'Amore 		 * infrequent.
45399e2a6f8SGarrett D'Amore 		 */
45499e2a6f8SGarrett D'Amore 		list_t *l = &vd->vd_reqs;
45599e2a6f8SGarrett D'Amore 		vioscsi_request_t *r;
45699e2a6f8SGarrett D'Amore 
45799e2a6f8SGarrett D'Amore 		for (r = list_tail(l); r != NULL; r = list_prev(l, r)) {
45899e2a6f8SGarrett D'Amore 			/*
45999e2a6f8SGarrett D'Amore 			 * Avoids wrapping lbolt:
46099e2a6f8SGarrett D'Amore 			 */
46199e2a6f8SGarrett D'Amore 			if ((req->vr_expire - r->vr_expire) >= 0) {
46299e2a6f8SGarrett D'Amore 				list_insert_after(l, r, req);
46399e2a6f8SGarrett D'Amore 				break;
46499e2a6f8SGarrett D'Amore 			}
46599e2a6f8SGarrett D'Amore 		}
46699e2a6f8SGarrett D'Amore 		if (r == NULL) {
46799e2a6f8SGarrett D'Amore 			/*
46899e2a6f8SGarrett D'Amore 			 * List empty, or this one expires before others:
46999e2a6f8SGarrett D'Amore 			 */
47006d7f587SGarrett D'Amore 			list_insert_head(l, req);
47199e2a6f8SGarrett D'Amore 		}
47299e2a6f8SGarrett D'Amore 		if (vd->vd_timeout == 0) {
47399e2a6f8SGarrett D'Amore 			vd->vd_timeout = timeout(vioscsi_dev_timeout, vd,
47499e2a6f8SGarrett D'Amore 			    vioscsi_hz);
47599e2a6f8SGarrett D'Amore 		}
47699e2a6f8SGarrett D'Amore 	}
47799e2a6f8SGarrett D'Amore 
47899e2a6f8SGarrett D'Amore 	mutex_exit(&vd->vd_lock);
47999e2a6f8SGarrett D'Amore 
48099e2a6f8SGarrett D'Amore 	vioscsi_start(vd->vd_sc, req);
48199e2a6f8SGarrett D'Amore 	return (TRAN_ACCEPT);
48299e2a6f8SGarrett D'Amore }
48399e2a6f8SGarrett D'Amore 
48499e2a6f8SGarrett D'Amore static int
vioscsi_tran_abort(struct scsi_address * ap,struct scsi_pkt * pkt)48599e2a6f8SGarrett D'Amore vioscsi_tran_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
48699e2a6f8SGarrett D'Amore {
48799e2a6f8SGarrett D'Amore 	struct scsi_device *sd;
48899e2a6f8SGarrett D'Amore 	vioscsi_dev_t *vd;
48999e2a6f8SGarrett D'Amore 	vioscsi_request_t *req;
49099e2a6f8SGarrett D'Amore 
49199e2a6f8SGarrett D'Amore 	if ((ap == NULL) ||
49299e2a6f8SGarrett D'Amore 	    ((sd = scsi_address_device(ap)) == NULL) ||
49399e2a6f8SGarrett D'Amore 	    ((vd = scsi_device_hba_private_get(sd)) == NULL)) {
49499e2a6f8SGarrett D'Amore 		return (0);
49599e2a6f8SGarrett D'Amore 	}
49699e2a6f8SGarrett D'Amore 	if (pkt == NULL) {
49799e2a6f8SGarrett D'Amore 		/*
49899e2a6f8SGarrett D'Amore 		 * Abort all requests for the LUN.
49999e2a6f8SGarrett D'Amore 		 */
50099e2a6f8SGarrett D'Amore 		vioscsi_dev_abort(vd);
50199e2a6f8SGarrett D'Amore 		return (1);
50299e2a6f8SGarrett D'Amore 	}
50399e2a6f8SGarrett D'Amore 	if ((req = pkt->pkt_ha_private) != NULL) {
50499e2a6f8SGarrett D'Amore 		return (vioscsi_req_abort(vd->vd_sc, req) ? 1 : 0);
50599e2a6f8SGarrett D'Amore 	}
50699e2a6f8SGarrett D'Amore 
50799e2a6f8SGarrett D'Amore 	return (0);
50899e2a6f8SGarrett D'Amore }
50999e2a6f8SGarrett D'Amore 
51099e2a6f8SGarrett D'Amore static void
vioscsi_req_fini(vioscsi_request_t * req)51199e2a6f8SGarrett D'Amore vioscsi_req_fini(vioscsi_request_t *req)
51299e2a6f8SGarrett D'Amore {
51399e2a6f8SGarrett D'Amore 	if (req->vr_dma != NULL) {
51499e2a6f8SGarrett D'Amore 		virtio_dma_free(req->vr_dma);
51599e2a6f8SGarrett D'Amore 		req->vr_dma = NULL;
51699e2a6f8SGarrett D'Amore 	}
51799e2a6f8SGarrett D'Amore 	if (req->vr_vic != NULL) {
51899e2a6f8SGarrett D'Amore 		virtio_chain_free(req->vr_vic);
51999e2a6f8SGarrett D'Amore 		req->vr_vic = NULL;
52099e2a6f8SGarrett D'Amore 	}
52199e2a6f8SGarrett D'Amore }
52299e2a6f8SGarrett D'Amore 
52399e2a6f8SGarrett D'Amore static int
vioscsi_req_init(vioscsi_softc_t * sc,vioscsi_request_t * req,virtio_queue_t * vq,int sleep)52499e2a6f8SGarrett D'Amore vioscsi_req_init(vioscsi_softc_t *sc, vioscsi_request_t *req,
52599e2a6f8SGarrett D'Amore     virtio_queue_t *vq, int sleep)
52699e2a6f8SGarrett D'Amore {
52799e2a6f8SGarrett D'Amore 	uint64_t pa;
52899e2a6f8SGarrett D'Amore 
52999e2a6f8SGarrett D'Amore 	bzero(req, sizeof (*req));
53099e2a6f8SGarrett D'Amore 	list_link_init(&req->vr_node);
53199e2a6f8SGarrett D'Amore 	req->vr_vq = vq;
53299e2a6f8SGarrett D'Amore 	req->vr_dma = virtio_dma_alloc(sc->vs_virtio, sizeof (vioscsi_op_t),
53399e2a6f8SGarrett D'Amore 	    &virtio_dma_attr, DDI_DMA_STREAMING | DDI_DMA_READ | DDI_DMA_WRITE,
53499e2a6f8SGarrett D'Amore 	    sleep);
53599e2a6f8SGarrett D'Amore 	req->vr_vic = virtio_chain_alloc(vq, sleep);
53699e2a6f8SGarrett D'Amore 	if ((req->vr_dma == NULL) || (req->vr_vic == NULL)) {
53799e2a6f8SGarrett D'Amore 		return (-1);
53899e2a6f8SGarrett D'Amore 	}
53999e2a6f8SGarrett D'Amore 	virtio_chain_data_set(req->vr_vic, req);
54099e2a6f8SGarrett D'Amore 	req->vr_req = virtio_dma_va(req->vr_dma, VIOSCSI_REQ_OFFSET);
54199e2a6f8SGarrett D'Amore 	req->vr_res = virtio_dma_va(req->vr_dma, VIOSCSI_RES_OFFSET);
54299e2a6f8SGarrett D'Amore 	pa = virtio_dma_cookie_pa(req->vr_dma, 0);
54399e2a6f8SGarrett D'Amore 	req->vr_req_pa = pa + VIOSCSI_REQ_OFFSET;
54499e2a6f8SGarrett D'Amore 	req->vr_res_pa = pa + VIOSCSI_RES_OFFSET;
54599e2a6f8SGarrett D'Amore 	return (0);
54699e2a6f8SGarrett D'Amore }
54799e2a6f8SGarrett D'Amore 
54899e2a6f8SGarrett D'Amore static void
vioscsi_tran_pkt_destructor(struct scsi_pkt * pkt,scsi_hba_tran_t * tran)54999e2a6f8SGarrett D'Amore vioscsi_tran_pkt_destructor(struct scsi_pkt *pkt, scsi_hba_tran_t *tran)
55099e2a6f8SGarrett D'Amore {
55199e2a6f8SGarrett D'Amore 	vioscsi_request_t *req = pkt->pkt_ha_private;
55299e2a6f8SGarrett D'Amore 
55399e2a6f8SGarrett D'Amore 	vioscsi_req_fini(req);
55499e2a6f8SGarrett D'Amore }
55599e2a6f8SGarrett D'Amore 
55699e2a6f8SGarrett D'Amore static int
vioscsi_tran_pkt_constructor(struct scsi_pkt * pkt,scsi_hba_tran_t * tran,int sleep)55799e2a6f8SGarrett D'Amore vioscsi_tran_pkt_constructor(struct scsi_pkt *pkt, scsi_hba_tran_t *tran,
55899e2a6f8SGarrett D'Amore     int sleep)
55999e2a6f8SGarrett D'Amore {
56099e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = tran->tran_hba_private;
56199e2a6f8SGarrett D'Amore 	vioscsi_request_t *req = pkt->pkt_ha_private;
56299e2a6f8SGarrett D'Amore 
56399e2a6f8SGarrett D'Amore 	if (vioscsi_req_init(sc, req, sc->vs_cmd_vq, sleep) != 0) {
56499e2a6f8SGarrett D'Amore 		vioscsi_req_fini(req);
56599e2a6f8SGarrett D'Amore 		return (-1);
56699e2a6f8SGarrett D'Amore 	}
56799e2a6f8SGarrett D'Amore 	req->vr_pkt = pkt;
56899e2a6f8SGarrett D'Amore 	return (0);
56999e2a6f8SGarrett D'Amore }
57099e2a6f8SGarrett D'Amore 
57199e2a6f8SGarrett D'Amore static int
vioscsi_tran_setup_pkt(struct scsi_pkt * pkt,int (* cb)(caddr_t),caddr_t arg)57299e2a6f8SGarrett D'Amore vioscsi_tran_setup_pkt(struct scsi_pkt *pkt, int (*cb)(caddr_t), caddr_t arg)
57399e2a6f8SGarrett D'Amore {
57499e2a6f8SGarrett D'Amore 	if ((pkt->pkt_dma_flags & DDI_DMA_RDWR) == DDI_DMA_RDWR) {
57599e2a6f8SGarrett D'Amore 		/*
57699e2a6f8SGarrett D'Amore 		 * We can do read, or write, but not both.
57799e2a6f8SGarrett D'Amore 		 */
57899e2a6f8SGarrett D'Amore 		return (-1);
57999e2a6f8SGarrett D'Amore 	}
58099e2a6f8SGarrett D'Amore 
58199e2a6f8SGarrett D'Amore 	return (0);
58299e2a6f8SGarrett D'Amore }
58399e2a6f8SGarrett D'Amore 
58499e2a6f8SGarrett D'Amore static void
vioscsi_tran_teardown_pkt(struct scsi_pkt * pkt)58599e2a6f8SGarrett D'Amore vioscsi_tran_teardown_pkt(struct scsi_pkt *pkt)
58699e2a6f8SGarrett D'Amore {
58799e2a6f8SGarrett D'Amore 	vioscsi_request_t *req = pkt->pkt_ha_private;
58899e2a6f8SGarrett D'Amore 	virtio_chain_t *vic = req->vr_vic;
58999e2a6f8SGarrett D'Amore 
59099e2a6f8SGarrett D'Amore 	virtio_chain_clear(vic);
59199e2a6f8SGarrett D'Amore }
59299e2a6f8SGarrett D'Amore 
59399e2a6f8SGarrett D'Amore static int
vioscsi_tran_getcap(struct scsi_address * ap,char * cap,int whom)59499e2a6f8SGarrett D'Amore vioscsi_tran_getcap(struct scsi_address *ap, char *cap, int whom)
59599e2a6f8SGarrett D'Amore {
59699e2a6f8SGarrett D'Amore 	int rval = 0;
59799e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = ap->a_hba_tran->tran_hba_private;
59899e2a6f8SGarrett D'Amore 
59999e2a6f8SGarrett D'Amore 	if (cap == NULL)
60099e2a6f8SGarrett D'Amore 		return (-1);
60199e2a6f8SGarrett D'Amore 
60299e2a6f8SGarrett D'Amore 	switch (scsi_hba_lookup_capstr(cap)) {
60399e2a6f8SGarrett D'Amore 	case SCSI_CAP_CDB_LEN:
60499e2a6f8SGarrett D'Amore 		rval = sc->vs_cdb_size;
60599e2a6f8SGarrett D'Amore 		break;
60699e2a6f8SGarrett D'Amore 
60799e2a6f8SGarrett D'Amore 	case SCSI_CAP_ARQ:
60899e2a6f8SGarrett D'Amore 	case SCSI_CAP_LUN_RESET:
60999e2a6f8SGarrett D'Amore 	case SCSI_CAP_TAGGED_QING:
61099e2a6f8SGarrett D'Amore 	case SCSI_CAP_UNTAGGED_QING:
61199e2a6f8SGarrett D'Amore 		rval = 1;
61299e2a6f8SGarrett D'Amore 		break;
61399e2a6f8SGarrett D'Amore 
61499e2a6f8SGarrett D'Amore 	default:
61599e2a6f8SGarrett D'Amore 		rval = -1;
61699e2a6f8SGarrett D'Amore 	}
61799e2a6f8SGarrett D'Amore 	return (rval);
61899e2a6f8SGarrett D'Amore }
61999e2a6f8SGarrett D'Amore 
62099e2a6f8SGarrett D'Amore static int
vioscsi_tran_setcap(struct scsi_address * ap,char * cap,int value,int whom)62199e2a6f8SGarrett D'Amore vioscsi_tran_setcap(struct scsi_address *ap, char *cap, int value, int whom)
62299e2a6f8SGarrett D'Amore {
62399e2a6f8SGarrett D'Amore 	int rval = 1;
62499e2a6f8SGarrett D'Amore 
62599e2a6f8SGarrett D'Amore 	if (cap == NULL || whom == 0) {
62699e2a6f8SGarrett D'Amore 		return (-1);
62799e2a6f8SGarrett D'Amore 	}
62899e2a6f8SGarrett D'Amore 
62999e2a6f8SGarrett D'Amore 	switch (scsi_hba_lookup_capstr(cap)) {
63099e2a6f8SGarrett D'Amore 	default:
63199e2a6f8SGarrett D'Amore 		rval = 1;
63299e2a6f8SGarrett D'Amore 	}
63399e2a6f8SGarrett D'Amore 	return (rval);
63499e2a6f8SGarrett D'Amore }
63599e2a6f8SGarrett D'Amore 
63699e2a6f8SGarrett D'Amore static int
vioscsi_tran_reset(struct scsi_address * ap,int level)63799e2a6f8SGarrett D'Amore vioscsi_tran_reset(struct scsi_address *ap, int level)
63899e2a6f8SGarrett D'Amore {
63999e2a6f8SGarrett D'Amore 	struct scsi_device *sd;
64099e2a6f8SGarrett D'Amore 	vioscsi_dev_t *vd;
64199e2a6f8SGarrett D'Amore 
64299e2a6f8SGarrett D'Amore 	if ((ap == NULL) ||
64399e2a6f8SGarrett D'Amore 	    ((sd = scsi_address_device(ap)) == NULL) ||
64499e2a6f8SGarrett D'Amore 	    ((vd = scsi_device_hba_private_get(sd)) == NULL)) {
64599e2a6f8SGarrett D'Amore 		return (0);
64699e2a6f8SGarrett D'Amore 	}
64799e2a6f8SGarrett D'Amore 
64899e2a6f8SGarrett D'Amore 	switch (level) {
64999e2a6f8SGarrett D'Amore 	case RESET_LUN:
65099e2a6f8SGarrett D'Amore 		if (vioscsi_lun_reset(vd->vd_sc, vd->vd_target, vd->vd_lun)) {
65199e2a6f8SGarrett D'Amore 			return (1);
65299e2a6f8SGarrett D'Amore 		}
65399e2a6f8SGarrett D'Amore 		break;
65499e2a6f8SGarrett D'Amore 	case RESET_TARGET:
65599e2a6f8SGarrett D'Amore 		if (vioscsi_target_reset(vd->vd_sc, vd->vd_target)) {
65699e2a6f8SGarrett D'Amore 			return (1);
65799e2a6f8SGarrett D'Amore 		}
65899e2a6f8SGarrett D'Amore 		break;
65999e2a6f8SGarrett D'Amore 	case RESET_ALL:
66099e2a6f8SGarrett D'Amore 	default:
66199e2a6f8SGarrett D'Amore 		break;
66299e2a6f8SGarrett D'Amore 	}
66399e2a6f8SGarrett D'Amore 	return (0);
66499e2a6f8SGarrett D'Amore }
66599e2a6f8SGarrett D'Amore 
66699e2a6f8SGarrett D'Amore static boolean_t
vioscsi_parse_unit_address(const char * ua,int * tgt,int * lun)66799e2a6f8SGarrett D'Amore vioscsi_parse_unit_address(const char *ua, int *tgt, int *lun)
66899e2a6f8SGarrett D'Amore {
66999e2a6f8SGarrett D'Amore 	long num;
67099e2a6f8SGarrett D'Amore 	char *end;
67199e2a6f8SGarrett D'Amore 
67299e2a6f8SGarrett D'Amore 	if ((ddi_strtol(ua, &end, 16, &num) != 0) ||
67399e2a6f8SGarrett D'Amore 	    ((*end != ',') && (*end != 0))) {
67499e2a6f8SGarrett D'Amore 		return (B_FALSE);
67599e2a6f8SGarrett D'Amore 	}
67699e2a6f8SGarrett D'Amore 	*tgt = (int)num;
67799e2a6f8SGarrett D'Amore 	if (*end == 0) {
67899e2a6f8SGarrett D'Amore 		*lun = 0;
67999e2a6f8SGarrett D'Amore 		return (B_TRUE);
68099e2a6f8SGarrett D'Amore 	}
68199e2a6f8SGarrett D'Amore 	end++; /* skip comma */
68299e2a6f8SGarrett D'Amore 	if ((ddi_strtol(end, &end, 16, &num) != 0) || (*end != 0)) {
68399e2a6f8SGarrett D'Amore 		return (B_FALSE);
68499e2a6f8SGarrett D'Amore 	}
68599e2a6f8SGarrett D'Amore 	*lun = (int)num;
68699e2a6f8SGarrett D'Amore 	return (B_TRUE);
68799e2a6f8SGarrett D'Amore }
68899e2a6f8SGarrett D'Amore 
68999e2a6f8SGarrett D'Amore uint_t
vioscsi_ctl_handler(caddr_t arg1,caddr_t arg2)69099e2a6f8SGarrett D'Amore vioscsi_ctl_handler(caddr_t arg1, caddr_t arg2)
69199e2a6f8SGarrett D'Amore {
69299e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = (vioscsi_softc_t *)arg1;
69399e2a6f8SGarrett D'Amore 	virtio_chain_t *vic;
69499e2a6f8SGarrett D'Amore 
69599e2a6f8SGarrett D'Amore 	while ((vic = virtio_queue_poll(sc->vs_ctl_vq)) != NULL) {
69699e2a6f8SGarrett D'Amore 		vioscsi_request_t *req;
69799e2a6f8SGarrett D'Amore 
69899e2a6f8SGarrett D'Amore 		if ((req = virtio_chain_data(vic)) == NULL) {
69999e2a6f8SGarrett D'Amore 			dev_err(sc->vs_dip, CE_WARN, "missing ctl chain data");
70099e2a6f8SGarrett D'Amore 			continue;
70199e2a6f8SGarrett D'Amore 		}
70299e2a6f8SGarrett D'Amore 		atomic_or_8(&req->vr_done, 1);
70399e2a6f8SGarrett D'Amore 	}
70499e2a6f8SGarrett D'Amore 	return (DDI_INTR_CLAIMED);
70599e2a6f8SGarrett D'Amore }
70699e2a6f8SGarrett D'Amore 
70799e2a6f8SGarrett D'Amore uint_t
vioscsi_evt_handler(caddr_t arg1,caddr_t arg2)70899e2a6f8SGarrett D'Amore vioscsi_evt_handler(caddr_t arg1, caddr_t arg2)
70999e2a6f8SGarrett D'Amore {
71099e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = (vioscsi_softc_t *)arg1;
71199e2a6f8SGarrett D'Amore 	virtio_chain_t *vic;
71299e2a6f8SGarrett D'Amore 	boolean_t missed = B_FALSE;
71399e2a6f8SGarrett D'Amore 
71499e2a6f8SGarrett D'Amore 	while ((vic = virtio_queue_poll(sc->vs_evt_vq)) != NULL) {
71599e2a6f8SGarrett D'Amore 		vioscsi_evt_t *evt;
71699e2a6f8SGarrett D'Amore 		vioscsi_event_t *ve;
71799e2a6f8SGarrett D'Amore 		uint8_t target;
71899e2a6f8SGarrett D'Amore 
71999e2a6f8SGarrett D'Amore 		if ((ve = virtio_chain_data(vic)) == NULL) {
72099e2a6f8SGarrett D'Amore 			/*
72199e2a6f8SGarrett D'Amore 			 * This should never occur, it's a bug if it does.
72299e2a6f8SGarrett D'Amore 			 */
72399e2a6f8SGarrett D'Amore 			dev_err(sc->vs_dip, CE_WARN, "missing evt chain data");
72499e2a6f8SGarrett D'Amore 			continue;
72599e2a6f8SGarrett D'Amore 		}
72699e2a6f8SGarrett D'Amore 		evt = ve->ve_evt;
72799e2a6f8SGarrett D'Amore 
72899e2a6f8SGarrett D'Amore 		virtio_dma_sync(ve->ve_dma, DDI_DMA_SYNC_FORKERNEL);
72999e2a6f8SGarrett D'Amore 
73099e2a6f8SGarrett D'Amore 		target = evt->lun[1];
73199e2a6f8SGarrett D'Amore 		switch (evt->event & 0x7FFFFFFF) {
73299e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_T_TRANSPORT_RESET:
73399e2a6f8SGarrett D'Amore 			switch (evt->reason) {
73499e2a6f8SGarrett D'Amore 			case VIRTIO_SCSI_EVT_RESET_HARD:
73599e2a6f8SGarrett D'Amore 				/*
73699e2a6f8SGarrett D'Amore 				 * We could reset-notify, but this doesn't seem
73799e2a6f8SGarrett D'Amore 				 * to get fired for targets initiated from
73899e2a6f8SGarrett D'Amore 				 * host.
73999e2a6f8SGarrett D'Amore 				 */
74099e2a6f8SGarrett D'Amore 				break;
74199e2a6f8SGarrett D'Amore 			case VIRTIO_SCSI_EVT_RESET_REMOVED:
74299e2a6f8SGarrett D'Amore 			case VIRTIO_SCSI_EVT_RESET_RESCAN:
74399e2a6f8SGarrett D'Amore 				/*
74499e2a6f8SGarrett D'Amore 				 * We can treat these the same for the target,
74599e2a6f8SGarrett D'Amore 				 * and not worry about the actual LUN id here.
74699e2a6f8SGarrett D'Amore 				 */
74799e2a6f8SGarrett D'Amore 				vioscsi_lun_changed(sc, target);
74899e2a6f8SGarrett D'Amore 				break;
74999e2a6f8SGarrett D'Amore 			default:
75099e2a6f8SGarrett D'Amore 				/*
75199e2a6f8SGarrett D'Amore 				 * Some other event we don't know about.
75299e2a6f8SGarrett D'Amore 				 */
75399e2a6f8SGarrett D'Amore 				break;
75499e2a6f8SGarrett D'Amore 			}
75599e2a6f8SGarrett D'Amore 			break;
75699e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_T_NO_EVENT:
75799e2a6f8SGarrett D'Amore 			/*
75899e2a6f8SGarrett D'Amore 			 * If this happens, we missed some event(s).
75999e2a6f8SGarrett D'Amore 			 */
76099e2a6f8SGarrett D'Amore 			missed = B_TRUE;
76199e2a6f8SGarrett D'Amore 			break;
76299e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_T_ASYNC_NOTIFY:
76399e2a6f8SGarrett D'Amore 			/*
76499e2a6f8SGarrett D'Amore 			 * We don't register for these, so we don't expect
76599e2a6f8SGarrett D'Amore 			 * them.
76699e2a6f8SGarrett D'Amore 			 */
76799e2a6f8SGarrett D'Amore 			break;
76899e2a6f8SGarrett D'Amore 		}
76999e2a6f8SGarrett D'Amore 
77099e2a6f8SGarrett D'Amore 		if (evt->event & VIRTIO_SCSI_T_EVENTS_MISSED) {
77199e2a6f8SGarrett D'Amore 			missed = B_TRUE;
77299e2a6f8SGarrett D'Amore 		}
77399e2a6f8SGarrett D'Amore 
77499e2a6f8SGarrett D'Amore 		/*
77599e2a6f8SGarrett D'Amore 		 * Resubmit the chain for the next event.
77699e2a6f8SGarrett D'Amore 		 */
77799e2a6f8SGarrett D'Amore 		virtio_chain_submit(vic, B_TRUE);
77899e2a6f8SGarrett D'Amore 	}
77999e2a6f8SGarrett D'Amore 
78099e2a6f8SGarrett D'Amore 	if (missed) {
78199e2a6f8SGarrett D'Amore 		(void) ddi_taskq_dispatch(sc->vs_tq, vioscsi_discover, sc,
78299e2a6f8SGarrett D'Amore 		    DDI_NOSLEEP);
78399e2a6f8SGarrett D'Amore 	}
78499e2a6f8SGarrett D'Amore 
78599e2a6f8SGarrett D'Amore 	return (DDI_INTR_CLAIMED);
78699e2a6f8SGarrett D'Amore }
78799e2a6f8SGarrett D'Amore 
78899e2a6f8SGarrett D'Amore uint_t
vioscsi_cmd_handler(caddr_t arg1,caddr_t arg2)78999e2a6f8SGarrett D'Amore vioscsi_cmd_handler(caddr_t arg1, caddr_t arg2)
79099e2a6f8SGarrett D'Amore {
79199e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = (vioscsi_softc_t *)arg1;
79299e2a6f8SGarrett D'Amore 	virtio_chain_t *vic;
79399e2a6f8SGarrett D'Amore 
79499e2a6f8SGarrett D'Amore 	while ((vic = virtio_queue_poll(sc->vs_cmd_vq)) != NULL) {
79599e2a6f8SGarrett D'Amore 
79699e2a6f8SGarrett D'Amore 		vioscsi_request_t *req;
79799e2a6f8SGarrett D'Amore 		vioscsi_dev_t *vd;
79899e2a6f8SGarrett D'Amore 		struct scsi_pkt *pkt;
79999e2a6f8SGarrett D'Amore 		struct virtio_scsi_cmd_resp *res;
80099e2a6f8SGarrett D'Amore 
80199e2a6f8SGarrett D'Amore 		if ((req = virtio_chain_data(vic)) == NULL) {
80299e2a6f8SGarrett D'Amore 			/*
80399e2a6f8SGarrett D'Amore 			 * This should never occur, it's a bug if it does.
80499e2a6f8SGarrett D'Amore 			 */
80599e2a6f8SGarrett D'Amore 			dev_err(sc->vs_dip, CE_WARN, "missing cmd chain data");
80699e2a6f8SGarrett D'Amore 			continue;
80799e2a6f8SGarrett D'Amore 		}
80899e2a6f8SGarrett D'Amore 
80999e2a6f8SGarrett D'Amore 		virtio_dma_sync(req->vr_dma, DDI_DMA_SYNC_FORKERNEL);
81099e2a6f8SGarrett D'Amore 		res = &req->vr_res->cmd;
81199e2a6f8SGarrett D'Amore 		pkt = req->vr_pkt;
81299e2a6f8SGarrett D'Amore 
81399e2a6f8SGarrett D'Amore 		if (pkt == NULL) {
81499e2a6f8SGarrett D'Amore 			/*
81599e2a6f8SGarrett D'Amore 			 * This is an internal request (from discovery), and
81699e2a6f8SGarrett D'Amore 			 * doesn't have an associated SCSI pkt structure.  In
81799e2a6f8SGarrett D'Amore 			 * this case, the notification we've done is
81899e2a6f8SGarrett D'Amore 			 * sufficient, and the submitter will examine the
81999e2a6f8SGarrett D'Amore 			 * response field directly.
82099e2a6f8SGarrett D'Amore 			 */
82199e2a6f8SGarrett D'Amore 			if (req->vr_poll) {
82299e2a6f8SGarrett D'Amore 				atomic_or_8(&req->vr_done, 1);
82399e2a6f8SGarrett D'Amore 			}
82499e2a6f8SGarrett D'Amore 			continue;
82599e2a6f8SGarrett D'Amore 		}
82699e2a6f8SGarrett D'Amore 
82799e2a6f8SGarrett D'Amore 		if ((vd = req->vr_dev) != NULL) {
82899e2a6f8SGarrett D'Amore 			mutex_enter(&vd->vd_lock);
82999e2a6f8SGarrett D'Amore 			vd->vd_num_cmd--;
83099e2a6f8SGarrett D'Amore 			list_remove(&vd->vd_reqs, req);
83199e2a6f8SGarrett D'Amore 			mutex_exit(&vd->vd_lock);
83299e2a6f8SGarrett D'Amore 		}
83399e2a6f8SGarrett D'Amore 
83499e2a6f8SGarrett D'Amore 		switch (res->response) {
83599e2a6f8SGarrett D'Amore 
83699e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_S_OK:
83799e2a6f8SGarrett D'Amore 			/*
83899e2a6f8SGarrett D'Amore 			 * Request processed successfully, check SCSI status.
83999e2a6f8SGarrett D'Amore 			 */
84099e2a6f8SGarrett D'Amore 			pkt->pkt_scbp[0] = res->status;
84199e2a6f8SGarrett D'Amore 			pkt->pkt_resid = 0;
84299e2a6f8SGarrett D'Amore 			pkt->pkt_reason = CMD_CMPLT;
84399e2a6f8SGarrett D'Amore 			pkt->pkt_state =
84499e2a6f8SGarrett D'Amore 			    STATE_GOT_BUS | STATE_GOT_TARGET |
84599e2a6f8SGarrett D'Amore 			    STATE_SENT_CMD | STATE_GOT_STATUS;
84699e2a6f8SGarrett D'Amore 			if ((pkt->pkt_numcookies > 0) &&
84799e2a6f8SGarrett D'Amore 			    (pkt->pkt_cookies[0].dmac_size > 0)) {
84899e2a6f8SGarrett D'Amore 				pkt->pkt_state |= STATE_XFERRED_DATA;
84999e2a6f8SGarrett D'Amore 			}
85099e2a6f8SGarrett D'Amore 
85199e2a6f8SGarrett D'Amore 			/*
85299e2a6f8SGarrett D'Amore 			 * For CHECK_CONDITION, fill out the ARQ details:
85399e2a6f8SGarrett D'Amore 			 */
85499e2a6f8SGarrett D'Amore 			if (res->status == STATUS_CHECK) {
85599e2a6f8SGarrett D'Amore 				/*
85699e2a6f8SGarrett D'Amore 				 * ARQ status and arq structure:
85799e2a6f8SGarrett D'Amore 				 */
85899e2a6f8SGarrett D'Amore 				pkt->pkt_state |= STATE_ARQ_DONE;
85999e2a6f8SGarrett D'Amore 				pkt->pkt_scbp[1] = STATUS_GOOD;
86099e2a6f8SGarrett D'Amore 				struct scsi_arq_status *ars =
86199e2a6f8SGarrett D'Amore 				    (void *)pkt->pkt_scbp;
86299e2a6f8SGarrett D'Amore 				ars->sts_rqpkt_reason = CMD_CMPLT;
86399e2a6f8SGarrett D'Amore 				ars->sts_rqpkt_resid = 0;
86499e2a6f8SGarrett D'Amore 				ars->sts_rqpkt_state =
86599e2a6f8SGarrett D'Amore 				    STATE_GOT_BUS | STATE_GOT_TARGET |
86699e2a6f8SGarrett D'Amore 				    STATE_GOT_STATUS | STATE_SENT_CMD |
86799e2a6f8SGarrett D'Amore 				    STATE_XFERRED_DATA;
86899e2a6f8SGarrett D'Amore 				bcopy(res->sense, &ars->sts_sensedata,
86999e2a6f8SGarrett D'Amore 				    res->sense_len);
87099e2a6f8SGarrett D'Amore 			}
87199e2a6f8SGarrett D'Amore 			break;
87299e2a6f8SGarrett D'Amore 
87399e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_S_BAD_TARGET:
87499e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_S_INCORRECT_LUN:
87599e2a6f8SGarrett D'Amore 			pkt->pkt_reason = CMD_DEV_GONE;
87699e2a6f8SGarrett D'Amore 			break;
87799e2a6f8SGarrett D'Amore 
87899e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_S_OVERRUN:
87999e2a6f8SGarrett D'Amore 			dev_err(sc->vs_dip, CE_WARN, "OVERRUN");
88099e2a6f8SGarrett D'Amore 			pkt->pkt_reason = CMD_DATA_OVR;
88199e2a6f8SGarrett D'Amore 			break;
88299e2a6f8SGarrett D'Amore 
88399e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_S_RESET:
88499e2a6f8SGarrett D'Amore 			pkt->pkt_reason = CMD_RESET;
88599e2a6f8SGarrett D'Amore 			pkt->pkt_statistics |= STAT_DEV_RESET;
88699e2a6f8SGarrett D'Amore 			break;
88799e2a6f8SGarrett D'Amore 
88899e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_S_ABORTED:
88999e2a6f8SGarrett D'Amore 			if (req->vr_expired) {
89099e2a6f8SGarrett D'Amore 				pkt->pkt_statistics |= STAT_TIMEOUT;
89199e2a6f8SGarrett D'Amore 				pkt->pkt_reason = CMD_TIMEOUT;
89299e2a6f8SGarrett D'Amore 			} else {
89399e2a6f8SGarrett D'Amore 				pkt->pkt_reason = CMD_ABORTED;
89499e2a6f8SGarrett D'Amore 				pkt->pkt_statistics |= STAT_ABORTED;
89599e2a6f8SGarrett D'Amore 			}
89699e2a6f8SGarrett D'Amore 			break;
89799e2a6f8SGarrett D'Amore 
89899e2a6f8SGarrett D'Amore 		case VIRTIO_SCSI_S_BUSY:
89999e2a6f8SGarrett D'Amore 			/*
90099e2a6f8SGarrett D'Amore 			 * Busy, should have been caught at submission:
90199e2a6f8SGarrett D'Amore 			 */
90299e2a6f8SGarrett D'Amore 			pkt->pkt_reason = CMD_TRAN_ERR;
90399e2a6f8SGarrett D'Amore 			break;
90499e2a6f8SGarrett D'Amore 
90599e2a6f8SGarrett D'Amore 		default:
90699e2a6f8SGarrett D'Amore 			dev_err(sc->vs_dip, CE_WARN, "Unknown response: 0x%x",
90799e2a6f8SGarrett D'Amore 			    res->response);
90899e2a6f8SGarrett D'Amore 			pkt->pkt_reason = CMD_TRAN_ERR;
90999e2a6f8SGarrett D'Amore 			break;
91099e2a6f8SGarrett D'Amore 		}
91199e2a6f8SGarrett D'Amore 
91299e2a6f8SGarrett D'Amore 
91399e2a6f8SGarrett D'Amore 		if (!req->vr_poll) {
91499e2a6f8SGarrett D'Amore 			scsi_hba_pkt_comp(pkt);
91599e2a6f8SGarrett D'Amore 		} else {
91699e2a6f8SGarrett D'Amore 			atomic_or_8(&req->vr_done, 1);
91799e2a6f8SGarrett D'Amore 		}
91899e2a6f8SGarrett D'Amore 	}
91999e2a6f8SGarrett D'Amore 	return (DDI_INTR_CLAIMED);
92099e2a6f8SGarrett D'Amore }
92199e2a6f8SGarrett D'Amore 
92299e2a6f8SGarrett D'Amore static int
vioscsi_tran_tgt_init(dev_info_t * hdip,dev_info_t * tdip,scsi_hba_tran_t * tran,struct scsi_device * sd)92399e2a6f8SGarrett D'Amore vioscsi_tran_tgt_init(dev_info_t *hdip, dev_info_t *tdip, scsi_hba_tran_t *tran,
92499e2a6f8SGarrett D'Amore     struct scsi_device *sd)
92599e2a6f8SGarrett D'Amore {
92699e2a6f8SGarrett D'Amore 	const char *ua;
92799e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc;
92899e2a6f8SGarrett D'Amore 	int target;
92999e2a6f8SGarrett D'Amore 	int lun;
93099e2a6f8SGarrett D'Amore 	vioscsi_dev_t *vd;
93199e2a6f8SGarrett D'Amore 
93299e2a6f8SGarrett D'Amore 	if (scsi_hba_iport_unit_address(hdip) == NULL) {
93399e2a6f8SGarrett D'Amore 		return (DDI_FAILURE); /* only iport has targets */
93499e2a6f8SGarrett D'Amore 	}
93599e2a6f8SGarrett D'Amore 	if ((sc = tran->tran_hba_private) == NULL) {
93699e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
93799e2a6f8SGarrett D'Amore 	}
93899e2a6f8SGarrett D'Amore 
93999e2a6f8SGarrett D'Amore 	if (((ua = scsi_device_unit_address(sd)) == NULL) ||
94099e2a6f8SGarrett D'Amore 	    (!vioscsi_parse_unit_address(ua, &target, &lun))) {
94199e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
94299e2a6f8SGarrett D'Amore 	}
94399e2a6f8SGarrett D'Amore 
94499e2a6f8SGarrett D'Amore 	vd = kmem_zalloc(sizeof (*vd), KM_SLEEP);
94599e2a6f8SGarrett D'Amore 	list_create(&vd->vd_reqs, sizeof (vioscsi_request_t),
94699e2a6f8SGarrett D'Amore 	    offsetof(vioscsi_request_t, vr_node));
94799e2a6f8SGarrett D'Amore 	mutex_init(&vd->vd_lock, NULL, MUTEX_DRIVER,
94899e2a6f8SGarrett D'Amore 	    virtio_intr_pri(sc->vs_virtio));
94999e2a6f8SGarrett D'Amore 
95099e2a6f8SGarrett D'Amore 	vd->vd_target = (uint8_t)target;
95199e2a6f8SGarrett D'Amore 	vd->vd_lun = (uint16_t)lun;
95299e2a6f8SGarrett D'Amore 	vd->vd_sc = sc;
95399e2a6f8SGarrett D'Amore 	vd->vd_sd = sd;
95499e2a6f8SGarrett D'Amore 	vd->vd_max_cmd = sc->vs_cmd_per_lun;
95599e2a6f8SGarrett D'Amore 	vd->vd_num_cmd = 0;
95699e2a6f8SGarrett D'Amore 
95799e2a6f8SGarrett D'Amore 	scsi_device_hba_private_set(sd, vd);
95899e2a6f8SGarrett D'Amore 
95999e2a6f8SGarrett D'Amore 	mutex_enter(&sc->vs_lock);
96099e2a6f8SGarrett D'Amore 	list_insert_tail(&sc->vs_devs, vd);
96199e2a6f8SGarrett D'Amore 	mutex_exit(&sc->vs_lock);
96299e2a6f8SGarrett D'Amore 
96399e2a6f8SGarrett D'Amore 	return (DDI_SUCCESS);
96499e2a6f8SGarrett D'Amore }
96599e2a6f8SGarrett D'Amore 
96699e2a6f8SGarrett D'Amore static void
vioscsi_tran_tgt_free(dev_info_t * hdip,dev_info_t * tdip,scsi_hba_tran_t * tran,struct scsi_device * sd)96799e2a6f8SGarrett D'Amore vioscsi_tran_tgt_free(dev_info_t *hdip, dev_info_t *tdip, scsi_hba_tran_t *tran,
96899e2a6f8SGarrett D'Amore     struct scsi_device *sd)
96999e2a6f8SGarrett D'Amore {
97099e2a6f8SGarrett D'Amore 	vioscsi_dev_t *vd = scsi_device_hba_private_get(sd);
97199e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = vd->vd_sc;
97299e2a6f8SGarrett D'Amore 	timeout_id_t tid;
97399e2a6f8SGarrett D'Amore 
97499e2a6f8SGarrett D'Amore 	scsi_device_hba_private_set(sd, NULL);
97599e2a6f8SGarrett D'Amore 
97699e2a6f8SGarrett D'Amore 	mutex_enter(&vd->vd_lock);
97799e2a6f8SGarrett D'Amore 	tid = vd->vd_timeout;
97899e2a6f8SGarrett D'Amore 	vd->vd_timeout = 0;
97999e2a6f8SGarrett D'Amore 	mutex_exit(&vd->vd_lock);
98099e2a6f8SGarrett D'Amore 
98199e2a6f8SGarrett D'Amore 	if (tid != 0) {
98299e2a6f8SGarrett D'Amore 		(void) untimeout(tid);
98399e2a6f8SGarrett D'Amore 	}
98499e2a6f8SGarrett D'Amore 
98599e2a6f8SGarrett D'Amore 	mutex_enter(&sc->vs_lock);
98699e2a6f8SGarrett D'Amore 	list_remove(&sc->vs_devs, vd);
98799e2a6f8SGarrett D'Amore 	mutex_exit(&sc->vs_lock);
98899e2a6f8SGarrett D'Amore 
98999e2a6f8SGarrett D'Amore 	list_destroy(&vd->vd_reqs);
99099e2a6f8SGarrett D'Amore 	mutex_destroy(&vd->vd_lock);
99199e2a6f8SGarrett D'Amore 	kmem_free(vd, sizeof (*vd));
99299e2a6f8SGarrett D'Amore }
99399e2a6f8SGarrett D'Amore 
99499e2a6f8SGarrett D'Amore /*
99599e2a6f8SGarrett D'Amore  * vioscsi_probe_target probes for existence of a valid target (LUN 0).
99699e2a6f8SGarrett D'Amore  * It utilizes the supplied request, and sends TEST UNIT READY.
99799e2a6f8SGarrett D'Amore  * (This command is used because it requires no data.)
99899e2a6f8SGarrett D'Amore  * It returns 1 if the target is found, 0 if not, and -1 on error.
99999e2a6f8SGarrett D'Amore  * It is expected additional LUNs will be discovered by the HBA framework using
100099e2a6f8SGarrett D'Amore  * REPORT LUNS on LUN 0.
100199e2a6f8SGarrett D'Amore  */
100299e2a6f8SGarrett D'Amore static int
vioscsi_probe_target(vioscsi_softc_t * sc,vioscsi_request_t * req,uint8_t target)100399e2a6f8SGarrett D'Amore vioscsi_probe_target(vioscsi_softc_t *sc, vioscsi_request_t *req,
100499e2a6f8SGarrett D'Amore     uint8_t target)
100599e2a6f8SGarrett D'Amore {
100699e2a6f8SGarrett D'Amore 	struct virtio_scsi_cmd_req *cmd = &req->vr_req->cmd;
100799e2a6f8SGarrett D'Amore 	struct virtio_scsi_cmd_resp *res = &req->vr_res->cmd;
100899e2a6f8SGarrett D'Amore 
100999e2a6f8SGarrett D'Amore 	bzero(cmd, sizeof (*cmd));
101099e2a6f8SGarrett D'Amore 	cmd->cdb[0] = SCMD_TEST_UNIT_READY;
101199e2a6f8SGarrett D'Amore 
101299e2a6f8SGarrett D'Amore 	virtio_chain_clear(req->vr_vic);
101399e2a6f8SGarrett D'Amore 	if (virtio_chain_append(req->vr_vic, req->vr_req_pa,
101499e2a6f8SGarrett D'Amore 	    sizeof (*cmd), VIRTIO_DIR_DEVICE_READS) != DDI_SUCCESS) {
101599e2a6f8SGarrett D'Amore 		return (-1);
101699e2a6f8SGarrett D'Amore 	}
101799e2a6f8SGarrett D'Amore 	if (virtio_chain_append(req->vr_vic, req->vr_res_pa,
101899e2a6f8SGarrett D'Amore 	    sizeof (*res), VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
101999e2a6f8SGarrett D'Amore 		return (-1);
102099e2a6f8SGarrett D'Amore 	}
102199e2a6f8SGarrett D'Amore 	req->vr_poll = B_TRUE;
102299e2a6f8SGarrett D'Amore 	req->vr_start = ddi_get_lbolt();
102399e2a6f8SGarrett D'Amore 	req->vr_time = 10; /* seconds */
102499e2a6f8SGarrett D'Amore 	req->vr_target = target;
102599e2a6f8SGarrett D'Amore 	req->vr_lun = 0;
102699e2a6f8SGarrett D'Amore 	req->vr_task_attr = VIRTIO_SCSI_S_HEAD;
102799e2a6f8SGarrett D'Amore 	vioscsi_start(sc, req);
102899e2a6f8SGarrett D'Amore 	switch (res->response) {
102999e2a6f8SGarrett D'Amore 	case VIRTIO_SCSI_S_OK:
103099e2a6f8SGarrett D'Amore 		return (1);
103199e2a6f8SGarrett D'Amore 	case VIRTIO_SCSI_S_INCORRECT_LUN:
103299e2a6f8SGarrett D'Amore 	case VIRTIO_SCSI_S_BAD_TARGET:
103399e2a6f8SGarrett D'Amore 		return (0);
103499e2a6f8SGarrett D'Amore 	default:
103599e2a6f8SGarrett D'Amore 		return (-1);
103699e2a6f8SGarrett D'Amore 	}
103799e2a6f8SGarrett D'Amore }
103899e2a6f8SGarrett D'Amore 
103999e2a6f8SGarrett D'Amore static void
vioscsi_rescan_luns(void * arg)104099e2a6f8SGarrett D'Amore vioscsi_rescan_luns(void *arg)
104199e2a6f8SGarrett D'Amore {
1042e302d6afSGarrett D'Amore 	vioscsi_softc_t		*sc = arg;
1043e302d6afSGarrett D'Amore 	vioscsi_dev_t		*vd;
1044e302d6afSGarrett D'Amore 	scsi_hba_tgtmap_t	*tm = sc->vs_tgtmap;
1045e302d6afSGarrett D'Amore 	list_t			*l;
1046e302d6afSGarrett D'Amore 	char			addr[16];
104799e2a6f8SGarrett D'Amore 
104899e2a6f8SGarrett D'Amore 	l = &sc->vs_devs;
104999e2a6f8SGarrett D'Amore 	mutex_enter(&sc->vs_lock);
105099e2a6f8SGarrett D'Amore 	for (vd = list_head(l); vd != NULL; vd = list_next(l, vd)) {
1051e302d6afSGarrett D'Amore 		if (!vd->vd_rescan) {
1052e302d6afSGarrett D'Amore 			continue;
105399e2a6f8SGarrett D'Amore 		}
1054e302d6afSGarrett D'Amore 
1055e302d6afSGarrett D'Amore 		vd->vd_rescan = B_FALSE;
1056e302d6afSGarrett D'Amore 		(void) snprintf(addr, sizeof (addr), "%x", vd->vd_target);
1057e302d6afSGarrett D'Amore 		scsi_hba_tgtmap_scan_luns(tm, addr);
105899e2a6f8SGarrett D'Amore 	}
105999e2a6f8SGarrett D'Amore 	mutex_exit(&sc->vs_lock);
106099e2a6f8SGarrett D'Amore }
106199e2a6f8SGarrett D'Amore 
106299e2a6f8SGarrett D'Amore static void
vioscsi_lun_changed(vioscsi_softc_t * sc,uint8_t target)106399e2a6f8SGarrett D'Amore vioscsi_lun_changed(vioscsi_softc_t *sc, uint8_t target)
106499e2a6f8SGarrett D'Amore {
106599e2a6f8SGarrett D'Amore 	vioscsi_dev_t *vd;
106699e2a6f8SGarrett D'Amore 	list_t *l = &sc->vs_devs;
106799e2a6f8SGarrett D'Amore 	boolean_t found = B_FALSE;
106899e2a6f8SGarrett D'Amore 
106999e2a6f8SGarrett D'Amore 	mutex_enter(&sc->vs_lock);
107099e2a6f8SGarrett D'Amore 	for (vd = list_head(l); vd != NULL; vd = list_next(l, vd)) {
107199e2a6f8SGarrett D'Amore 		if ((vd->vd_target == target) && (vd->vd_lun == 0)) {
107299e2a6f8SGarrett D'Amore 			vd->vd_rescan = B_TRUE;
107399e2a6f8SGarrett D'Amore 			found = B_TRUE;
107499e2a6f8SGarrett D'Amore 			break;
107599e2a6f8SGarrett D'Amore 		}
107699e2a6f8SGarrett D'Amore 	}
107799e2a6f8SGarrett D'Amore 	mutex_exit(&sc->vs_lock);
107899e2a6f8SGarrett D'Amore 
107999e2a6f8SGarrett D'Amore 	if (found) {
108099e2a6f8SGarrett D'Amore 		/*
108199e2a6f8SGarrett D'Amore 		 * We have lun 0 already, so report luns changed:
108299e2a6f8SGarrett D'Amore 		 */
108399e2a6f8SGarrett D'Amore 		(void) ddi_taskq_dispatch(sc->vs_tq, vioscsi_rescan_luns,
108499e2a6f8SGarrett D'Amore 		    sc, DDI_NOSLEEP);
108599e2a6f8SGarrett D'Amore 	} else {
108699e2a6f8SGarrett D'Amore 		/*
108799e2a6f8SGarrett D'Amore 		 * We didn't find lun 0, so issue a new discovery:
108899e2a6f8SGarrett D'Amore 		 */
108999e2a6f8SGarrett D'Amore 		(void) ddi_taskq_dispatch(sc->vs_tq, vioscsi_discover,
109099e2a6f8SGarrett D'Amore 		    sc, DDI_NOSLEEP);
109199e2a6f8SGarrett D'Amore 	}
109299e2a6f8SGarrett D'Amore }
109399e2a6f8SGarrett D'Amore 
109499e2a6f8SGarrett D'Amore /*
109599e2a6f8SGarrett D'Amore  * vioscsi_discover is our task function for performing target and lun
109699e2a6f8SGarrett D'Amore  * discovery.  This is done using active SCSI probes.
109799e2a6f8SGarrett D'Amore  */
109899e2a6f8SGarrett D'Amore static void
vioscsi_discover(void * arg)109999e2a6f8SGarrett D'Amore vioscsi_discover(void *arg)
110099e2a6f8SGarrett D'Amore {
110199e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc = arg;
110299e2a6f8SGarrett D'Amore 	scsi_hba_tgtmap_t *tm = sc->vs_tgtmap;
110399e2a6f8SGarrett D'Amore 	vioscsi_request_t req;
110499e2a6f8SGarrett D'Amore 
110599e2a6f8SGarrett D'Amore 	if (vioscsi_req_init(sc, &req, sc->vs_cmd_vq, KM_SLEEP) != 0) {
110699e2a6f8SGarrett D'Amore 		vioscsi_req_fini(&req);
110799e2a6f8SGarrett D'Amore 		return;
110899e2a6f8SGarrett D'Amore 	}
110999e2a6f8SGarrett D'Amore 
111099e2a6f8SGarrett D'Amore 	if (scsi_hba_tgtmap_set_begin(tm) != DDI_SUCCESS) {
111199e2a6f8SGarrett D'Amore 		vioscsi_req_fini(&req);
111299e2a6f8SGarrett D'Amore 		return;
111399e2a6f8SGarrett D'Amore 	}
111499e2a6f8SGarrett D'Amore 	for (uint8_t target = 0; target < sc->vs_max_target; target++) {
111599e2a6f8SGarrett D'Amore 		char ua[10];
111699e2a6f8SGarrett D'Amore 		switch (vioscsi_probe_target(sc, &req, target)) {
111799e2a6f8SGarrett D'Amore 		case 1:
111899e2a6f8SGarrett D'Amore 			(void) snprintf(ua, sizeof (ua), "%x", target);
111999e2a6f8SGarrett D'Amore 			if (scsi_hba_tgtmap_set_add(tm, SCSI_TGT_SCSI_DEVICE,
112099e2a6f8SGarrett D'Amore 			    ua, NULL) != DDI_SUCCESS) {
112199e2a6f8SGarrett D'Amore 				(void) scsi_hba_tgtmap_set_flush(tm);
112299e2a6f8SGarrett D'Amore 				vioscsi_req_fini(&req);
112399e2a6f8SGarrett D'Amore 				return;
112499e2a6f8SGarrett D'Amore 			}
112599e2a6f8SGarrett D'Amore 			break;
112699e2a6f8SGarrett D'Amore 		case 0:
112799e2a6f8SGarrett D'Amore 			continue;
112899e2a6f8SGarrett D'Amore 		case -1:
112999e2a6f8SGarrett D'Amore 			(void) scsi_hba_tgtmap_set_flush(tm);
113099e2a6f8SGarrett D'Amore 			vioscsi_req_fini(&req);
113199e2a6f8SGarrett D'Amore 			return;
113299e2a6f8SGarrett D'Amore 		}
113399e2a6f8SGarrett D'Amore 	}
113499e2a6f8SGarrett D'Amore 	(void) scsi_hba_tgtmap_set_end(tm, 0);
113599e2a6f8SGarrett D'Amore 	vioscsi_req_fini(&req);
113699e2a6f8SGarrett D'Amore }
113799e2a6f8SGarrett D'Amore 
113899e2a6f8SGarrett D'Amore static void
vioscsi_teardown(vioscsi_softc_t * sc,boolean_t failed)113999e2a6f8SGarrett D'Amore vioscsi_teardown(vioscsi_softc_t *sc, boolean_t failed)
114099e2a6f8SGarrett D'Amore {
114199e2a6f8SGarrett D'Amore 	if (sc->vs_virtio != NULL) {
114299e2a6f8SGarrett D'Amore 		virtio_fini(sc->vs_virtio, failed);
114399e2a6f8SGarrett D'Amore 	}
114499e2a6f8SGarrett D'Amore 
114599e2a6f8SGarrett D'Amore 	/*
114699e2a6f8SGarrett D'Amore 	 * Free up the event resources:
114799e2a6f8SGarrett D'Amore 	 */
114899e2a6f8SGarrett D'Amore 	for (int i = 0; i < VIOSCSI_NUM_EVENTS; i++) {
114999e2a6f8SGarrett D'Amore 		vioscsi_event_t *ve = &sc->vs_events[i];
115099e2a6f8SGarrett D'Amore 		if (ve->ve_vic != NULL) {
115199e2a6f8SGarrett D'Amore 			virtio_chain_free(ve->ve_vic);
115299e2a6f8SGarrett D'Amore 		}
115399e2a6f8SGarrett D'Amore 		if (ve->ve_dma != NULL) {
115499e2a6f8SGarrett D'Amore 			virtio_dma_free(ve->ve_dma);
115599e2a6f8SGarrett D'Amore 		}
115699e2a6f8SGarrett D'Amore 	}
115799e2a6f8SGarrett D'Amore 
115899e2a6f8SGarrett D'Amore 	if (sc->vs_tran != NULL) {
115999e2a6f8SGarrett D'Amore 		scsi_hba_tran_free(sc->vs_tran);
116099e2a6f8SGarrett D'Amore 	}
116199e2a6f8SGarrett D'Amore 	if (sc->vs_tq != NULL) {
116299e2a6f8SGarrett D'Amore 		ddi_taskq_destroy(sc->vs_tq);
116399e2a6f8SGarrett D'Amore 	}
116499e2a6f8SGarrett D'Amore 	if (sc->vs_intr_pri != NULL) {
116599e2a6f8SGarrett D'Amore 		mutex_destroy(&sc->vs_lock);
116699e2a6f8SGarrett D'Amore 	}
116799e2a6f8SGarrett D'Amore 	kmem_free(sc, sizeof (*sc));
116899e2a6f8SGarrett D'Amore }
116999e2a6f8SGarrett D'Amore 
117099e2a6f8SGarrett D'Amore static int
vioscsi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)117199e2a6f8SGarrett D'Amore vioscsi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
117299e2a6f8SGarrett D'Amore {
117399e2a6f8SGarrett D'Amore 	scsi_hba_tran_t *tran = NULL;
117499e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc;
117599e2a6f8SGarrett D'Amore 	virtio_t *vio;
117699e2a6f8SGarrett D'Amore 	ddi_dma_attr_t attr;
117799e2a6f8SGarrett D'Amore 
117899e2a6f8SGarrett D'Amore 	if (cmd != DDI_ATTACH) { /* no suspend/resume support */
117999e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
118099e2a6f8SGarrett D'Amore 	}
118199e2a6f8SGarrett D'Amore 
118299e2a6f8SGarrett D'Amore 	if (scsi_hba_iport_unit_address(dip) != NULL) {
118399e2a6f8SGarrett D'Amore 		return (vioscsi_iport_attach(dip));
118499e2a6f8SGarrett D'Amore 	}
118599e2a6f8SGarrett D'Amore 
118699e2a6f8SGarrett D'Amore 	sc = kmem_zalloc(sizeof (*sc), KM_SLEEP);
118799e2a6f8SGarrett D'Amore 	sc->vs_dip = dip;
118899e2a6f8SGarrett D'Amore 
118999e2a6f8SGarrett D'Amore 	list_create(&sc->vs_devs, sizeof (vioscsi_dev_t),
119099e2a6f8SGarrett D'Amore 	    offsetof(vioscsi_dev_t, vd_node));
119199e2a6f8SGarrett D'Amore 
119299e2a6f8SGarrett D'Amore 	tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
119399e2a6f8SGarrett D'Amore 	sc->vs_tran = tran;
119499e2a6f8SGarrett D'Amore 
119599e2a6f8SGarrett D'Amore 	tran->tran_hba_len = sizeof (vioscsi_request_t);
119699e2a6f8SGarrett D'Amore 	tran->tran_hba_private = sc;
119799e2a6f8SGarrett D'Amore 
119899e2a6f8SGarrett D'Amore 	/*
119999e2a6f8SGarrett D'Amore 	 * We don't use WWN addressing, so advertise parallel.  The underlying
120099e2a6f8SGarrett D'Amore 	 * device might still be using a different transport, even in a
120199e2a6f8SGarrett D'Amore 	 * pass-through, but we cannot discriminate that at this layer.
120299e2a6f8SGarrett D'Amore 	 */
120399e2a6f8SGarrett D'Amore 	tran->tran_interconnect_type = INTERCONNECT_PARALLEL;
120499e2a6f8SGarrett D'Amore 
120599e2a6f8SGarrett D'Amore 	tran->tran_start = vioscsi_tran_start;
120699e2a6f8SGarrett D'Amore 	tran->tran_abort = vioscsi_tran_abort;
120799e2a6f8SGarrett D'Amore 	tran->tran_reset = vioscsi_tran_reset;
120899e2a6f8SGarrett D'Amore 	tran->tran_getcap = vioscsi_tran_getcap;
120999e2a6f8SGarrett D'Amore 	tran->tran_setcap = vioscsi_tran_setcap;
121099e2a6f8SGarrett D'Amore 
121199e2a6f8SGarrett D'Amore 	tran->tran_tgt_init = vioscsi_tran_tgt_init;
121299e2a6f8SGarrett D'Amore 	tran->tran_tgt_free = vioscsi_tran_tgt_free;
121399e2a6f8SGarrett D'Amore 
121499e2a6f8SGarrett D'Amore 	tran->tran_setup_pkt = vioscsi_tran_setup_pkt;
121599e2a6f8SGarrett D'Amore 	tran->tran_teardown_pkt = vioscsi_tran_teardown_pkt;
121699e2a6f8SGarrett D'Amore 	tran->tran_pkt_constructor = vioscsi_tran_pkt_constructor;
121799e2a6f8SGarrett D'Amore 	tran->tran_pkt_destructor = vioscsi_tran_pkt_destructor;
121899e2a6f8SGarrett D'Amore 
121999e2a6f8SGarrett D'Amore 	/*
122099e2a6f8SGarrett D'Amore 	 * We need to determine some device settings here, so we initialize the
122199e2a6f8SGarrett D'Amore 	 * virtio in order to access those values.  The rest of the setup we do
122299e2a6f8SGarrett D'Amore 	 * in the iport attach.  Note that this driver cannot support
122399e2a6f8SGarrett D'Amore 	 * reattaching a child iport once it is removed -- the entire driver
122499e2a6f8SGarrett D'Amore 	 * will need to be reset for that.
122599e2a6f8SGarrett D'Amore 	 */
122699e2a6f8SGarrett D'Amore 	vio = virtio_init(dip, VIOSCSI_WANTED_FEATURES, B_TRUE);
122799e2a6f8SGarrett D'Amore 	if ((sc->vs_virtio = vio) == NULL) {
122899e2a6f8SGarrett D'Amore 		dev_err(dip, CE_WARN, "failed to init virtio");
122999e2a6f8SGarrett D'Amore 		vioscsi_teardown(sc, B_TRUE);
123099e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
123199e2a6f8SGarrett D'Amore 	}
123299e2a6f8SGarrett D'Amore 
123399e2a6f8SGarrett D'Amore 	/*
123499e2a6f8SGarrett D'Amore 	 * Get virtio parameters:
123599e2a6f8SGarrett D'Amore 	 */
123699e2a6f8SGarrett D'Amore 	sc->vs_max_target = virtio_dev_get16(vio, VIRTIO_SCSI_CFG_MAX_TARGET);
123799e2a6f8SGarrett D'Amore 	sc->vs_max_lun = virtio_dev_get32(vio, VIRTIO_SCSI_CFG_MAX_LUN);
123899e2a6f8SGarrett D'Amore 	sc->vs_cdb_size = virtio_dev_get32(vio, VIRTIO_SCSI_CFG_CDB_SIZE);
123999e2a6f8SGarrett D'Amore 	sc->vs_max_seg = virtio_dev_get32(vio, VIRTIO_SCSI_CFG_SEG_MAX);
124099e2a6f8SGarrett D'Amore 	sc->vs_cmd_per_lun = virtio_dev_get32(vio, VIRTIO_SCSI_CFG_CMD_PER_LUN);
124199e2a6f8SGarrett D'Amore 
124299e2a6f8SGarrett D'Amore 	/*
124399e2a6f8SGarrett D'Amore 	 * Adjust operating parameters to functional limits:
124499e2a6f8SGarrett D'Amore 	 */
124599e2a6f8SGarrett D'Amore 	sc->vs_max_target = min(VIOSCSI_MAX_TARGET, sc->vs_max_target);
124699e2a6f8SGarrett D'Amore 	sc->vs_cmd_per_lun = max(1, sc->vs_max_target);
124799e2a6f8SGarrett D'Amore 	sc->vs_max_seg = max(VIOSCSI_MIN_SEGS, sc->vs_max_seg);
124899e2a6f8SGarrett D'Amore 
124999e2a6f8SGarrett D'Amore 	/*
125099e2a6f8SGarrett D'Amore 	 * Allocate queues:
125199e2a6f8SGarrett D'Amore 	 */
125299e2a6f8SGarrett D'Amore 	sc->vs_ctl_vq = virtio_queue_alloc(vio, 0, "ctl",
125399e2a6f8SGarrett D'Amore 	    vioscsi_ctl_handler, sc, B_FALSE, sc->vs_max_seg);
125499e2a6f8SGarrett D'Amore 	sc->vs_evt_vq = virtio_queue_alloc(vio, 1, "evt",
125599e2a6f8SGarrett D'Amore 	    vioscsi_evt_handler, sc, B_FALSE, sc->vs_max_seg);
125699e2a6f8SGarrett D'Amore 	sc->vs_cmd_vq = virtio_queue_alloc(vio, 2, "cmd",
125799e2a6f8SGarrett D'Amore 	    vioscsi_cmd_handler, sc, B_FALSE, sc->vs_max_seg);
125899e2a6f8SGarrett D'Amore 
125999e2a6f8SGarrett D'Amore 	if ((sc->vs_ctl_vq == NULL) || (sc->vs_evt_vq == NULL) ||
126099e2a6f8SGarrett D'Amore 	    (sc->vs_cmd_vq == NULL)) {
126199e2a6f8SGarrett D'Amore 		dev_err(dip, CE_WARN, "failed allocating queue(s)");
126299e2a6f8SGarrett D'Amore 		vioscsi_teardown(sc, B_TRUE);
126399e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
126499e2a6f8SGarrett D'Amore 	}
126599e2a6f8SGarrett D'Amore 
1266*64439ec0SJoshua M. Clulow 	if (virtio_init_complete(vio, VIRTIO_ANY_INTR_TYPE) != DDI_SUCCESS) {
126799e2a6f8SGarrett D'Amore 		dev_err(dip, CE_WARN, "virtio_init_complete failed");
126899e2a6f8SGarrett D'Amore 		vioscsi_teardown(sc, B_TRUE);
126999e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
127099e2a6f8SGarrett D'Amore 	}
127199e2a6f8SGarrett D'Amore 
127299e2a6f8SGarrett D'Amore 	/*
127399e2a6f8SGarrett D'Amore 	 * We cannot initialize this mutex before virtio_init_complete:
127499e2a6f8SGarrett D'Amore 	 */
127599e2a6f8SGarrett D'Amore 	sc->vs_intr_pri = virtio_intr_pri(vio);
127699e2a6f8SGarrett D'Amore 	mutex_init(&sc->vs_lock, NULL, MUTEX_DRIVER, sc->vs_intr_pri);
127799e2a6f8SGarrett D'Amore 
127899e2a6f8SGarrett D'Amore 	/*
127999e2a6f8SGarrett D'Amore 	 * Allocate events, but do not submit yet:
128099e2a6f8SGarrett D'Amore 	 */
128199e2a6f8SGarrett D'Amore 	for (int i = 0; i < VIOSCSI_NUM_EVENTS; i++) {
128299e2a6f8SGarrett D'Amore 		vioscsi_event_t *ve = &sc->vs_events[i];
128399e2a6f8SGarrett D'Amore 		ve->ve_vic = virtio_chain_alloc(sc->vs_evt_vq, KM_SLEEP);
128499e2a6f8SGarrett D'Amore 		ve->ve_dma = virtio_dma_alloc(sc->vs_virtio,
128599e2a6f8SGarrett D'Amore 		    sizeof (vioscsi_evt_t), &virtio_dma_attr,
128699e2a6f8SGarrett D'Amore 		    DDI_DMA_STREAMING | DDI_DMA_READ, KM_SLEEP);
128799e2a6f8SGarrett D'Amore 		if ((ve->ve_vic == NULL) || (ve->ve_dma == NULL)) {
128899e2a6f8SGarrett D'Amore 			vioscsi_teardown(sc, B_TRUE);
128999e2a6f8SGarrett D'Amore 			return (DDI_FAILURE);
129099e2a6f8SGarrett D'Amore 		}
129199e2a6f8SGarrett D'Amore 		if (virtio_chain_append(ve->ve_vic,
129299e2a6f8SGarrett D'Amore 		    virtio_dma_cookie_pa(ve->ve_dma, 0), sizeof (*ve->ve_evt),
129399e2a6f8SGarrett D'Amore 		    VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
129499e2a6f8SGarrett D'Amore 			vioscsi_teardown(sc, B_TRUE);
129599e2a6f8SGarrett D'Amore 			return (DDI_FAILURE);
129699e2a6f8SGarrett D'Amore 		}
129799e2a6f8SGarrett D'Amore 		ve->ve_evt = virtio_dma_va(ve->ve_dma, 0);
129899e2a6f8SGarrett D'Amore 		virtio_chain_data_set(ve->ve_vic, ve);
129999e2a6f8SGarrett D'Amore 	}
130099e2a6f8SGarrett D'Amore 
130199e2a6f8SGarrett D'Amore 	sc->vs_tq = ddi_taskq_create(dip, "task", 1, TASKQ_DEFAULTPRI, 0);
130299e2a6f8SGarrett D'Amore 	if (sc->vs_tq == NULL) {
130399e2a6f8SGarrett D'Amore 		dev_err(dip, CE_WARN, "failed to create taskq");
130499e2a6f8SGarrett D'Amore 		vioscsi_teardown(sc, B_TRUE);
130599e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
130699e2a6f8SGarrett D'Amore 	}
130799e2a6f8SGarrett D'Amore 
130899e2a6f8SGarrett D'Amore 	/*
130999e2a6f8SGarrett D'Amore 	 * Maximum number of segments, subtract two needed for headers:
131099e2a6f8SGarrett D'Amore 	 */
131199e2a6f8SGarrett D'Amore 	attr = virtio_dma_attr;
131299e2a6f8SGarrett D'Amore 	attr.dma_attr_sgllen = sc->vs_max_seg - 2;
131399e2a6f8SGarrett D'Amore 
131499e2a6f8SGarrett D'Amore 	if (scsi_hba_attach_setup(dip, &attr, tran,
131599e2a6f8SGarrett D'Amore 	    SCSI_HBA_ADDR_COMPLEX | SCSI_HBA_HBA |
131699e2a6f8SGarrett D'Amore 	    SCSI_HBA_TRAN_CDB | SCSI_HBA_TRAN_SCB) !=
131799e2a6f8SGarrett D'Amore 	    DDI_SUCCESS) {
131899e2a6f8SGarrett D'Amore 		vioscsi_teardown(sc, B_TRUE);
131999e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
132099e2a6f8SGarrett D'Amore 	}
132199e2a6f8SGarrett D'Amore 
132299e2a6f8SGarrett D'Amore 	if (scsi_hba_iport_register(dip, "iport0") != DDI_SUCCESS) {
132399e2a6f8SGarrett D'Amore 		vioscsi_teardown(sc, B_TRUE);
132499e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
132599e2a6f8SGarrett D'Amore 	}
132699e2a6f8SGarrett D'Amore 
132799e2a6f8SGarrett D'Amore 	ddi_report_dev(dip);
132899e2a6f8SGarrett D'Amore 
132999e2a6f8SGarrett D'Amore 	return (DDI_SUCCESS);
133099e2a6f8SGarrett D'Amore }
133199e2a6f8SGarrett D'Amore 
133299e2a6f8SGarrett D'Amore static void
vioscsi_iport_teardown(vioscsi_softc_t * sc)133399e2a6f8SGarrett D'Amore vioscsi_iport_teardown(vioscsi_softc_t *sc)
133499e2a6f8SGarrett D'Amore {
133599e2a6f8SGarrett D'Amore 	/*
133699e2a6f8SGarrett D'Amore 	 * Stop the taskq -- ensures we don't try to access resources from a
133799e2a6f8SGarrett D'Amore 	 * task while we are tearing down.
133899e2a6f8SGarrett D'Amore 	 */
133999e2a6f8SGarrett D'Amore 	ddi_taskq_suspend(sc->vs_tq);
134099e2a6f8SGarrett D'Amore 	ddi_taskq_wait(sc->vs_tq);
134199e2a6f8SGarrett D'Amore 
134299e2a6f8SGarrett D'Amore 	/*
134399e2a6f8SGarrett D'Amore 	 * Shutdown all interrupts and device transfers:
134499e2a6f8SGarrett D'Amore 	 */
134599e2a6f8SGarrett D'Amore 	virtio_interrupts_disable(sc->vs_virtio);
134699e2a6f8SGarrett D'Amore 	virtio_shutdown(sc->vs_virtio);
134799e2a6f8SGarrett D'Amore 
134899e2a6f8SGarrett D'Amore 	/*
134999e2a6f8SGarrett D'Amore 	 * Common resources:
135099e2a6f8SGarrett D'Amore 	 */
135199e2a6f8SGarrett D'Amore 	if (sc->vs_tgtmap != NULL) {
135299e2a6f8SGarrett D'Amore 		scsi_hba_tgtmap_destroy(sc->vs_tgtmap);
135399e2a6f8SGarrett D'Amore 		sc->vs_tgtmap = NULL;
135499e2a6f8SGarrett D'Amore 	}
135599e2a6f8SGarrett D'Amore }
135699e2a6f8SGarrett D'Amore 
135799e2a6f8SGarrett D'Amore /*
135899e2a6f8SGarrett D'Amore  * vioscsi_iport_attach implements the attach of the iport.  We do the final
135999e2a6f8SGarrett D'Amore  * set up of interrupts, and posting of event buffers here, as we do not want
136099e2a6f8SGarrett D'Amore  * any activity unless the iport is attached.  This matches detach, and makes
136199e2a6f8SGarrett D'Amore  * teardown safer.
136299e2a6f8SGarrett D'Amore  */
136399e2a6f8SGarrett D'Amore static int
vioscsi_iport_attach(dev_info_t * dip)136499e2a6f8SGarrett D'Amore vioscsi_iport_attach(dev_info_t *dip)
136599e2a6f8SGarrett D'Amore {
136699e2a6f8SGarrett D'Amore 	const char *ua = scsi_hba_iport_unit_address(dip);
136799e2a6f8SGarrett D'Amore 	scsi_hba_tran_t *tran;
136899e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc;
136999e2a6f8SGarrett D'Amore 
137099e2a6f8SGarrett D'Amore 	/*
137199e2a6f8SGarrett D'Amore 	 * We only support a single iport -- all disks are virtual and all
137299e2a6f8SGarrett D'Amore 	 * disks use target/lun addresses.
137399e2a6f8SGarrett D'Amore 	 */
137499e2a6f8SGarrett D'Amore 	if ((ua == NULL) || (strcmp(ua, "iport0") != 0)) {
137599e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
137699e2a6f8SGarrett D'Amore 	}
137799e2a6f8SGarrett D'Amore 
137899e2a6f8SGarrett D'Amore 	/*
137999e2a6f8SGarrett D'Amore 	 * Get our parent's tran, and look up the sc from that:
138099e2a6f8SGarrett D'Amore 	 */
138199e2a6f8SGarrett D'Amore 	tran = ddi_get_driver_private(ddi_get_parent(dip));
138299e2a6f8SGarrett D'Amore 	if ((tran == NULL) ||
138399e2a6f8SGarrett D'Amore 	    ((sc = tran->tran_hba_private) == NULL)) {
138499e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
138599e2a6f8SGarrett D'Amore 	}
138699e2a6f8SGarrett D'Amore 
138799e2a6f8SGarrett D'Amore 	/*
138899e2a6f8SGarrett D'Amore 	 * Save a copy of the soft state in our tran private area.
138999e2a6f8SGarrett D'Amore 	 * (The framework clears this after cloning from parent.)
139099e2a6f8SGarrett D'Amore 	 */
139199e2a6f8SGarrett D'Amore 	tran = ddi_get_driver_private(dip);
139299e2a6f8SGarrett D'Amore 	tran->tran_hba_private = sc;
139399e2a6f8SGarrett D'Amore 
139499e2a6f8SGarrett D'Amore 	/*
139599e2a6f8SGarrett D'Amore 	 * We don't want interrupts on the control queue -- strictly polled
139699e2a6f8SGarrett D'Amore 	 * (however if this handler is called from an interrupt, it should
139799e2a6f8SGarrett D'Amore 	 * still be absolutely fine).
139899e2a6f8SGarrett D'Amore 	 */
139999e2a6f8SGarrett D'Amore 	virtio_queue_no_interrupt(sc->vs_ctl_vq, B_TRUE);
140099e2a6f8SGarrett D'Amore 
140199e2a6f8SGarrett D'Amore 	if (scsi_hba_tgtmap_create(dip, SCSI_TM_FULLSET, MICROSEC,
140299e2a6f8SGarrett D'Amore 	    2 * MICROSEC, sc, NULL, NULL, &sc->vs_tgtmap) != DDI_SUCCESS) {
140399e2a6f8SGarrett D'Amore 		vioscsi_iport_teardown(sc);
140499e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
140599e2a6f8SGarrett D'Amore 	}
140699e2a6f8SGarrett D'Amore 
140799e2a6f8SGarrett D'Amore 	/*
140899e2a6f8SGarrett D'Amore 	 * Post events:
140999e2a6f8SGarrett D'Amore 	 */
141099e2a6f8SGarrett D'Amore 	for (int i = 0; i < VIOSCSI_NUM_EVENTS; i++) {
141199e2a6f8SGarrett D'Amore 		virtio_chain_submit(sc->vs_events[i].ve_vic, B_FALSE);
141299e2a6f8SGarrett D'Amore 	}
141399e2a6f8SGarrett D'Amore 	virtio_queue_flush(sc->vs_evt_vq);
141499e2a6f8SGarrett D'Amore 
141599e2a6f8SGarrett D'Amore 	/*
141699e2a6f8SGarrett D'Amore 	 * Start interrupts going now:
141799e2a6f8SGarrett D'Amore 	 */
141899e2a6f8SGarrett D'Amore 	if (virtio_interrupts_enable(sc->vs_virtio) != DDI_SUCCESS) {
141999e2a6f8SGarrett D'Amore 		vioscsi_iport_teardown(sc);
142099e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
142199e2a6f8SGarrett D'Amore 	}
142299e2a6f8SGarrett D'Amore 
142399e2a6f8SGarrett D'Amore 	/*
142499e2a6f8SGarrett D'Amore 	 * Start a discovery:
142599e2a6f8SGarrett D'Amore 	 */
142699e2a6f8SGarrett D'Amore 	(void) ddi_taskq_dispatch(sc->vs_tq, vioscsi_discover, sc, DDI_SLEEP);
142799e2a6f8SGarrett D'Amore 
142899e2a6f8SGarrett D'Amore 	return (DDI_SUCCESS);
142999e2a6f8SGarrett D'Amore }
143099e2a6f8SGarrett D'Amore 
143199e2a6f8SGarrett D'Amore static int
vioscsi_quiesce(dev_info_t * dip)143299e2a6f8SGarrett D'Amore vioscsi_quiesce(dev_info_t *dip)
143399e2a6f8SGarrett D'Amore {
143499e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc;
143599e2a6f8SGarrett D'Amore 	scsi_hba_tran_t *tran;
143699e2a6f8SGarrett D'Amore 
143799e2a6f8SGarrett D'Amore 	if (((tran = ddi_get_driver_private(dip)) == NULL) ||
143899e2a6f8SGarrett D'Amore 	    ((sc = tran->tran_hba_private) == NULL)) {
143999e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
144099e2a6f8SGarrett D'Amore 	}
144199e2a6f8SGarrett D'Amore 	if (sc->vs_virtio == NULL) {
144299e2a6f8SGarrett D'Amore 		return (DDI_SUCCESS); /* not initialized yet */
144399e2a6f8SGarrett D'Amore 	}
144499e2a6f8SGarrett D'Amore 
144599e2a6f8SGarrett D'Amore 	return (virtio_quiesce(sc->vs_virtio));
144699e2a6f8SGarrett D'Amore }
144799e2a6f8SGarrett D'Amore 
144899e2a6f8SGarrett D'Amore /*
144999e2a6f8SGarrett D'Amore  * vioscsi_iport_detach is used to perform the detach of the iport.  It
145099e2a6f8SGarrett D'Amore  * disables interrupts and the device, but does not free resources, other than
145199e2a6f8SGarrett D'Amore  * the target map.  Note that due to lack of a way to start virtio after
145299e2a6f8SGarrett D'Amore  * virtio_shutdown(), it is not possible to reattach the iport after this is
145399e2a6f8SGarrett D'Amore  * called, unless the underlying HBA is also detached and then re-attached.
145499e2a6f8SGarrett D'Amore  */
145599e2a6f8SGarrett D'Amore static int
vioscsi_iport_detach(dev_info_t * dip)145699e2a6f8SGarrett D'Amore vioscsi_iport_detach(dev_info_t *dip)
145799e2a6f8SGarrett D'Amore {
145899e2a6f8SGarrett D'Amore 	const char *ua = scsi_hba_iport_unit_address(dip);
145999e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc;
146099e2a6f8SGarrett D'Amore 	scsi_hba_tran_t *tran;
146199e2a6f8SGarrett D'Amore 
146299e2a6f8SGarrett D'Amore 	if ((ua == NULL) || (strcmp(ua, "iport0") != 0)) {
146399e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
146499e2a6f8SGarrett D'Amore 	}
146599e2a6f8SGarrett D'Amore 
146699e2a6f8SGarrett D'Amore 	if (((tran = ddi_get_driver_private(dip)) == NULL) ||
146799e2a6f8SGarrett D'Amore 	    ((sc = tran->tran_hba_private) == NULL)) {
146899e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
146999e2a6f8SGarrett D'Amore 	}
147099e2a6f8SGarrett D'Amore 
147199e2a6f8SGarrett D'Amore 	mutex_enter(&sc->vs_lock);
147299e2a6f8SGarrett D'Amore 	if (!list_is_empty(&sc->vs_devs)) {
147399e2a6f8SGarrett D'Amore 		/*
147499e2a6f8SGarrett D'Amore 		 * Cannot detach while we have target children.
147599e2a6f8SGarrett D'Amore 		 */
147699e2a6f8SGarrett D'Amore 		mutex_exit(&sc->vs_lock);
147799e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
147899e2a6f8SGarrett D'Amore 	}
147999e2a6f8SGarrett D'Amore 
148099e2a6f8SGarrett D'Amore 	vioscsi_iport_teardown(sc);
148199e2a6f8SGarrett D'Amore 
148299e2a6f8SGarrett D'Amore 	return (DDI_SUCCESS);
148399e2a6f8SGarrett D'Amore }
148499e2a6f8SGarrett D'Amore 
148599e2a6f8SGarrett D'Amore static int
vioscsi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)148699e2a6f8SGarrett D'Amore vioscsi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
148799e2a6f8SGarrett D'Amore {
148899e2a6f8SGarrett D'Amore 	vioscsi_softc_t *sc;
148999e2a6f8SGarrett D'Amore 	scsi_hba_tran_t *tran;
149099e2a6f8SGarrett D'Amore 
149199e2a6f8SGarrett D'Amore 	if (cmd != DDI_DETACH)  {
149299e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
149399e2a6f8SGarrett D'Amore 	}
149499e2a6f8SGarrett D'Amore 
149599e2a6f8SGarrett D'Amore 	if (scsi_hba_iport_unit_address(dip) != NULL) {
149699e2a6f8SGarrett D'Amore 		return (vioscsi_iport_detach(dip));
149799e2a6f8SGarrett D'Amore 	}
149899e2a6f8SGarrett D'Amore 
149999e2a6f8SGarrett D'Amore 	if (((tran = ddi_get_driver_private(dip)) == NULL) ||
150099e2a6f8SGarrett D'Amore 	    ((sc = tran->tran_hba_private) == NULL)) {
150199e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
150299e2a6f8SGarrett D'Amore 	}
150399e2a6f8SGarrett D'Amore 
150499e2a6f8SGarrett D'Amore 	if (scsi_hba_detach(dip) != DDI_SUCCESS) {
150599e2a6f8SGarrett D'Amore 		return (DDI_FAILURE);
150699e2a6f8SGarrett D'Amore 	}
150799e2a6f8SGarrett D'Amore 	vioscsi_teardown(sc, B_FALSE);
150899e2a6f8SGarrett D'Amore 
150999e2a6f8SGarrett D'Amore 	return (DDI_SUCCESS);
151099e2a6f8SGarrett D'Amore }
151199e2a6f8SGarrett D'Amore 
151299e2a6f8SGarrett D'Amore static struct dev_ops vioscsi_dev_ops = {
151399e2a6f8SGarrett D'Amore 	.devo_rev =		DEVO_REV,
151499e2a6f8SGarrett D'Amore 	.devo_refcnt =		0,
151599e2a6f8SGarrett D'Amore 	.devo_getinfo =		nodev,
151699e2a6f8SGarrett D'Amore 	.devo_identify =	nulldev,
151799e2a6f8SGarrett D'Amore 	.devo_probe =		nulldev,
151899e2a6f8SGarrett D'Amore 	.devo_attach =		vioscsi_attach,
151999e2a6f8SGarrett D'Amore 	.devo_detach =		vioscsi_detach,
152099e2a6f8SGarrett D'Amore 	.devo_reset =		nodev,
152199e2a6f8SGarrett D'Amore 	.devo_cb_ops =		NULL,
152299e2a6f8SGarrett D'Amore 	.devo_bus_ops =		NULL,
152399e2a6f8SGarrett D'Amore 	.devo_power =		NULL,
152499e2a6f8SGarrett D'Amore 	.devo_quiesce =		vioscsi_quiesce,
152599e2a6f8SGarrett D'Amore };
152699e2a6f8SGarrett D'Amore 
152799e2a6f8SGarrett D'Amore static struct modldrv modldrv = {
152899e2a6f8SGarrett D'Amore 	.drv_modops =		&mod_driverops,
152999e2a6f8SGarrett D'Amore 	.drv_linkinfo =		vioscsi_ident,
153099e2a6f8SGarrett D'Amore 	.drv_dev_ops =		&vioscsi_dev_ops,
153199e2a6f8SGarrett D'Amore };
153299e2a6f8SGarrett D'Amore 
153399e2a6f8SGarrett D'Amore static struct modlinkage modlinkage = {
153499e2a6f8SGarrett D'Amore 	.ml_rev =		MODREV_1,
153599e2a6f8SGarrett D'Amore 	.ml_linkage =		{ &modldrv, NULL, },
153699e2a6f8SGarrett D'Amore };
153799e2a6f8SGarrett D'Amore 
153899e2a6f8SGarrett D'Amore 
153999e2a6f8SGarrett D'Amore int
_init(void)154099e2a6f8SGarrett D'Amore _init(void)
154199e2a6f8SGarrett D'Amore {
154299e2a6f8SGarrett D'Amore 	int err;
154399e2a6f8SGarrett D'Amore 
154499e2a6f8SGarrett D'Amore 	/*
154599e2a6f8SGarrett D'Amore 	 * Initialize this unconditionally:
154699e2a6f8SGarrett D'Amore 	 */
154799e2a6f8SGarrett D'Amore 	vioscsi_hz = drv_usectohz(1000000);
154899e2a6f8SGarrett D'Amore 
154999e2a6f8SGarrett D'Amore 	if ((err = scsi_hba_init(&modlinkage)) != 0) {
155099e2a6f8SGarrett D'Amore 		return (err);
155199e2a6f8SGarrett D'Amore 	}
155299e2a6f8SGarrett D'Amore 
155399e2a6f8SGarrett D'Amore 	if ((err = mod_install(&modlinkage)) != 0) {
155499e2a6f8SGarrett D'Amore 		scsi_hba_fini(&modlinkage);
155599e2a6f8SGarrett D'Amore 		return (err);
155699e2a6f8SGarrett D'Amore 	}
155799e2a6f8SGarrett D'Amore 
155899e2a6f8SGarrett D'Amore 	return (err);
155999e2a6f8SGarrett D'Amore }
156099e2a6f8SGarrett D'Amore 
156199e2a6f8SGarrett D'Amore int
_fini(void)156299e2a6f8SGarrett D'Amore _fini(void)
156399e2a6f8SGarrett D'Amore {
156499e2a6f8SGarrett D'Amore 	int err;
156599e2a6f8SGarrett D'Amore 
156699e2a6f8SGarrett D'Amore 	if ((err = mod_remove(&modlinkage)) != 0) {
156799e2a6f8SGarrett D'Amore 		return (err);
156899e2a6f8SGarrett D'Amore 	}
156999e2a6f8SGarrett D'Amore 
157099e2a6f8SGarrett D'Amore 	scsi_hba_fini(&modlinkage);
157199e2a6f8SGarrett D'Amore 
157299e2a6f8SGarrett D'Amore 	return (DDI_SUCCESS);
157399e2a6f8SGarrett D'Amore }
157499e2a6f8SGarrett D'Amore 
157599e2a6f8SGarrett D'Amore int
_info(struct modinfo * modinfop)157699e2a6f8SGarrett D'Amore _info(struct modinfo *modinfop)
157799e2a6f8SGarrett D'Amore {
157899e2a6f8SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
157999e2a6f8SGarrett D'Amore }
1580