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 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 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 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 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 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 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 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 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 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 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 */ 470*06d7f587SGarrett 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 104099e2a6f8SGarrett D'Amore vioscsi_rescan_luns(void *arg) 104199e2a6f8SGarrett D'Amore { 104299e2a6f8SGarrett D'Amore vioscsi_softc_t *sc = arg; 104399e2a6f8SGarrett D'Amore vioscsi_dev_t *vd; 1044e302d6afSGarrett D'Amore scsi_hba_tgtmap_t *tm = sc->vs_tgtmap; 104599e2a6f8SGarrett 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 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 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 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 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 126699e2a6f8SGarrett D'Amore if (virtio_init_complete(vio, 0) != 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 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 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 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 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 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 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 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 157699e2a6f8SGarrett D'Amore _info(struct modinfo *modinfop) 157799e2a6f8SGarrett D'Amore { 157899e2a6f8SGarrett D'Amore return (mod_info(&modlinkage, modinfop)); 157999e2a6f8SGarrett D'Amore } 1580