565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * This file and its contents are supplied under the terms of the
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Common Development and Distribution License ("CDDL"), version 1.0.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * You may only use this file in accordance with the terms of version
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * 1.0 of the CDDL.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * A full copy of the text of the CDDL should have accompanied this
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * source. A copy of the CDDL is also available via the Internet at
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Copyright 2016 Nexenta Systems, Inc.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovint pvscsi_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_PER_RING;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovint pvscsi_msg_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_MSG_RING;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovstatic int pvscsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* HBA DMA attributes */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* DMA attributes for req/comp rings */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* DMA attributes for buffer I/O */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovstatic ddi_device_acc_attr_t pvscsi_mmio_attr = {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovstatic ddi_device_acc_attr_t pvscsi_dma_attrs = {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT(!list_link_active(&(cmd)->cmd_queue_node));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_map_ctx(pvscsi_softc_t *pvs, pvscsi_cmd_ctx_t *io_ctx)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_lookup_ctx(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_resolve_ctx(pvscsi_softc_t *pvs, uint64_t ctx)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_acquire_ctx(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ctx = (pvscsi_cmd_ctx_t *)list_remove_head(&pvs->cmd_ctx_pool);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_reg_read(pvscsi_softc_t *pvs, uint32_t offset)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT((offset & (sizeof (uint32_t) - 1)) == 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_reg_write(pvscsi_softc_t *pvs, uint32_t offset, uint32_t value)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT((offset & (sizeof (uint32_t) - 1)) == 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ddi_put32(pvs->mmio_handle, (uint32_t *)(pvs->mmio_base + offset),
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_write_cmd_desc(pvscsi_softc_t *pvs, uint32_t cmd, void *desc, size_t len)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_COMMAND, cmd);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ddi_rep_put32(pvs->mmio_handle, (uint32_t *)desc,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (uint32_t *)(pvs->mmio_base + PVSCSI_REG_OFFSET_COMMAND_DATA),
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (pvscsi_reg_read(pvs, PVSCSI_REG_OFFSET_INTR_STATUS));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_write_intr_status(pvscsi_softc_t *pvs, uint32_t val)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_STATUS, val);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_MASK, 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_MASK,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_RESET_BUS, NULL, 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_KICK_NON_RW_IO, 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_inquiry_target(pvscsi_softc_t *pvs, int target, struct scsi_inquiry *inq)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((b = scsi_alloc_consistent_buf(&ap, (struct buf *)NULL, len,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pkt = scsi_init_pkt(&ap, (struct scsi_pkt *)NULL, b,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov CDB_GROUP0, sizeof (struct scsi_arq_status), 0, 0,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov bzero((struct scsi_inquiry *)b->b_un.b_addr, sizeof (*inq));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_config_one(dev_info_t *pdip, pvscsi_softc_t *pvs, int target,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Inquiry target */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov inqrc = pvscsi_inquiry_target(pvs, target, &inq);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Find devnode */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov for (devnode = list_head(&pvs->devnodes); devnode != NULL;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Target disappeared, drop devnode */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Get full devname */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Clean cache and name */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (void) devfs_clean(devnode->parent, devname + 1,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (void) ndi_devi_offline(devnode->pdip, NDI_DEVI_REMOVE);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Target exists */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov } else if (inqrc != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Target doesn't exist */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov scsi_hba_nodename_compatible_get(&inq, NULL, inq.inq_dtype, NULL,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ndi_devi_alloc(pdip, nodename, DEVI_SID_NODEID,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to alloc device instance");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "compatible", compatible, ncompatible) != DDI_PROP_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to update props for target %d", target);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((devnode = kmem_zalloc(sizeof (*devnode), KM_NOSLEEP)) == NULL)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ndi_devi_online(dip, NDI_ONLINE_ATTACH) != NDI_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to online target %d",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov scsi_hba_nodename_compatible_free(nodename, compatible);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov scsi_hba_nodename_compatible_free(nodename, compatible);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_config_all(dev_info_t *pdip, pvscsi_softc_t *pvs)
6bd8a07093bddc0edfc07bfda4ca600e31c02c03Yuri Pankov for (target = 0; target < PVSCSI_MAXTGTS; target++) {
6bd8a07093bddc0edfc07bfda4ca600e31c02c03Yuri Pankov /* ndi_devi_enter is done in pvscsi_bus_config */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (void) pvscsi_config_one(pdip, pvs, target, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov struct PVSCSIRingsState *sdesc = RINGS_STATE(pvs);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov while (sdesc->cmpConsIdx != sdesc->cmpProdIdx) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cdesc = CMP_RING(pvs) + (sdesc->cmpConsIdx & MASK(cmp_ne));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Save command status for further processing */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Mark this command as arrived from hardware */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov struct PVSCSIRingsState *sdesc = RINGS_STATE(pvs);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov mdesc = MSG_RING(pvs) + (sdesc->msgConsIdx & MASK(msg_ne));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov desc = (struct PVSCSIMsgDescDevStatusChanged *)mdesc;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov msg = kmem_alloc(sizeof (pvscsi_msg_t), KM_NOSLEEP);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!unknown msg type: %d",
6bd8a07093bddc0edfc07bfda4ca600e31c02c03Yuri Pankov (void) pvscsi_config_one(dip, msg->msg_pvs, msg->target, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_abort_cmd(pvscsi_cmd_t *cmd, pvscsi_cmd_t **pending)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!aborting command %p", (void *)cmd);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Check if the cmd was already completed by the HBA */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov *pending = done = pvscsi_process_comp_ring(pvs);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Check if cmd was really scheduled by the HBA */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Abort cmd in the HBA */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_ABORT_CMD, &acmd, sizeof (acmd));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Check if cmd was completed by the HBA before it could be aborted */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((done = pvscsi_process_comp_ring(pvs)) != NULL) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Release I/O ctx */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Remove cmd from the queue */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Insert cmd at the beginning of the list */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!command %p aborted", (void *)cmd);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_map_buffers(pvscsi_cmd_t *cmd, struct PVSCSIRingReqDesc *rdesc)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT(cmd->cmd_dmaccount > 0 && cmd->cmd_dmaccount <=
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov struct PVSCSISGElement *sgl = CMD_CTX_SGLIST_VA(cmd->ctx);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov sgl[i].addr = cmd->cached_cookies[i].dmac_laddress;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov sgl[i].length = cmd->cached_cookies[i].dmac_size;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov rdesc->dataAddr = (uint64_t)CMD_CTX_SGLIST_PA(cmd->ctx);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov rdesc->dataAddr = cmd->cached_cookies[0].dmac_laddress;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_comp_cmd(pvscsi_cmd_t *cmd, uint8_t status)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov uchar_t scsi_status = cmd->cmp_stat.scsi_status;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov uint32_t host_status = cmd->cmp_stat.host_status;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (host_status == BTSTAT_LINKED_COMMAND_COMPLETED) ||
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (host_status == BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG))) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov struct scsi_arq_status *astat = (void*)(pkt->pkt_scbp);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (*(uint8_t *)&astat->sts_rqpkt_status) = STATUS_GOOD;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_reason = pkt->pkt_state |= (STATE_GOT_BUS |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov STATE_GOT_TARGET | STATE_SENT_CMD | STATE_GOT_STATUS);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_resid = cmd->dma_count - cmd->cmp_stat.data_len;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Release I/O ctx */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Remove command from queue */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAG_HW_STATUS) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT((cmd->flags & PVSCSI_FLAGS_NON_HW_COMPLETION) !=
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAG_TIMED_OUT) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov } else if ((cmd->flags & PVSCSI_FLAG_ABORTED) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov } else if ((cmd->flags & PVSCSI_FLAGS_RESET) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAG_RESET_BUS) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_dev_reset(pvscsi_softc_t *pvs, int target)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_RESET_DEVICE, &cmd, sizeof (cmd));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_poll_cmd(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov int cycles = (cmd->pkt->pkt_time * 1000000) / USECS_TO_WAIT;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Make sure we're not missing any commands completed
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * concurrently before we have actually disabled interrupts.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Disable interrupts from H/W */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Wait for interrupt to arrive */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov for (i = 0; i < cycles; i++) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((status & PVSCSI_INTR_ALL_SUPPORTED) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Check completion ring */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Enable interrupts from H/W */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* No interrupts seen from device during the timeout */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAGS_COMPLETION) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Command was cancelled asynchronously */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Command was cancelled in hardware */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STAT_TIMEOUT | STAT_ABORTED);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Complete commands that might be on completion list.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Target command can also be on the list in case it was
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * completed before it could be actually cancelled.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_abort_all(struct scsi_address *ap, pvscsi_softc_t *pvs,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Try to abort all queued commands, merging commands waiting
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * for completion into a single list to complete them at one
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * time when mutex is released.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov while (qlen > 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ap == NULL || ap->a_target == cmd->cmd_target) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (pvscsi_abort_cmd(cmd, &pcmd) == CMD_ABORTED) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Assume command is completely cancelled now,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * so mark it as requested.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Now merge current pending commands with
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * previous ones.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (pvs->flags & PVSCSI_HBA_QUIESCE_PENDING) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_transport_command(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov struct PVSCSIRingsState *sdesc = RINGS_STATE(pvs);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!no free ctx available");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((sdesc->reqProdIdx - sdesc->cmpConsIdx) >= (1 << req_ne)) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!no free I/O slots available");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov rdesc = REQ_RING(pvs) + (sdesc->reqProdIdx & MASK(req_ne));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov bzero((void*)cmd->arqbuf->b_un.b_addr, SENSE_BUFFER_SIZE);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup tag info */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup I/O direction and map data buffers */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAG_DMA_VALID) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmd->timeout_lbolt = ddi_get_lbolt() + SEC_TO_TICK(pkt->pkt_time);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT((cmd->flags & PVSCSI_FLAG_DMA_VALID) != 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_reset_generic(pvscsi_softc_t *pvs, struct scsi_address *ap)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov flags = bus_reset ? PVSCSI_FLAG_RESET_BUS : PVSCSI_FLAG_RESET_DEV;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Try to process pending requests */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Abort all pending requests */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Reset at hardware level */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Should never happen after bus reset */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED pvs */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_cmd_ext_alloc(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd, int kf)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((buf = kmem_zalloc(cmd->cmdlen, kf)) == NULL)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((buf = kmem_zalloc(cmd->statuslen, kf)) == NULL)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmd->cmd_rqslen = (cmd->statuslen - sizeof (cmd->cmd_scb));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((buf = kmem_zalloc(cmd->tgtlen, kf)) == NULL)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_setup_dma_buffer(pvscsi_softc_t *pvs, size_t length,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((ddi_dma_alloc_handle(pvs->dip, &pvscsi_ring_dma_attr,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov DDI_DMA_SLEEP, NULL, &buf->dma_handle)) != DDI_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to allocate DMA handle");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((ddi_dma_mem_alloc(buf->dma_handle, length, &pvscsi_dma_attrs,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &buf->addr,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov &buf->real_length, &buf->acc_handle)) != DDI_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to allocate %ld bytes for DMA buffer", length);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((ddi_dma_addr_bind_handle(buf->dma_handle, NULL, buf->addr,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov buf->real_length, DDI_DMA_CONSISTENT | DDI_DMA_RDWR, DDI_DMA_SLEEP,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to bind DMA buffer");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* TODO Support multipart SG regions */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov size_t size = pvs->req_depth * sizeof (pvscsi_cmd_ctx_t);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ctx = pvs->cmd_ctx = kmem_zalloc(size, KM_SLEEP);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov for (; i >= 0; --i, --ctx) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov kmem_free(pvs->cmd_ctx, pvs->req_pages << PAGE_SHIFT);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate DMA buffer for rings state */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate DMA buffer for request ring */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvs->req_pages = MIN(pvscsi_ring_pages, PVSCSI_MAX_NUM_PAGES_REQ_RING);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvs->req_depth = pvs->req_pages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (pvscsi_setup_dma_buffer(pvs, pvs->req_pages * PAGE_SIZE,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate completion ring */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvs->cmp_pages = MIN(pvscsi_ring_pages, PVSCSI_MAX_NUM_PAGES_CMP_RING);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (pvscsi_setup_dma_buffer(pvs, pvs->cmp_pages * PAGE_SIZE,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate message ring */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (pvscsi_setup_dma_buffer(pvs, pvs->msg_pages * PAGE_SIZE,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov struct PVSCSICmdDescSetupMsgRing cmd_msg = { 0 };
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmd.ringsStatePPN = pvs->rings_state_buf.pa >> PAGE_SHIFT;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup request ring */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup completion ring */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov bzero(REQ_RING(pvs), pvs->req_pages * PAGE_SIZE);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov bzero(CMP_RING(pvs), pvs->cmp_pages * PAGE_SIZE);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Issue SETUP command */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_SETUP_RINGS, &cmd, sizeof (cmd));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup message ring */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov bzero(MSG_RING(pvs), pvs->msg_pages * PAGE_SIZE);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_SETUP_MSG_RING, &cmd_msg,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pvs->dip,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to lookup 'reg' property");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov rcount = regs_length * sizeof (int) / sizeof (pci_regspec_t);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov for (offset = PCI_CONF_BASE0; offset <= PCI_CONF_BASE5; offset += 4) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (PCI_REG_REG_G(regs[rn].pci_phys_hi) == offset) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to map MMIO BAR");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((rc = ddi_intr_get_cap(pvs->intr_htable[0], &intr_caps)) !=
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to get interrupt caps");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((rc = ddi_intr_block_enable(pvs->intr_htable,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to enable interrupt block");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((rc = ddi_intr_enable(pvs->intr_htable[i])) ==
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to enable interrupt");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov while (--i >= 0)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Unmask interrupts */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_MASK,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED arg2 */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov handled = (status & PVSCSI_INTR_ALL_SUPPORTED) != 0;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (pending != NULL && ddi_taskq_dispatch(pvs->comp_tq,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (msg != NULL && ddi_taskq_dispatch(pvs->msg_tq,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_handle_msg, msg, DDI_NOSLEEP) == DDI_FAILURE) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to process msg type %d for target %d",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (handled ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_register_isr(pvscsi_softc_t *pvs, int type)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_intr_get_navail(pvs->dip, type, &navail) != DDI_SUCCESS ||
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to get number of available interrupts of type %d",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvs->intr_size = navail * sizeof (ddi_intr_handle_t);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pvs->intr_htable = kmem_alloc(pvs->intr_size, KM_SLEEP)) == NULL) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to allocate %d bytes for interrupt hashtable",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_intr_alloc(pvs->dip, pvs->intr_htable, type, 0, navail,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov &nactual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS || nactual == 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to allocate %d interrupts",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to get interrupt priority");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov for (i = 0; i < nactual; i++) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_intr_handler, (caddr_t)pvs, NULL) != DDI_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to add interrupt handler");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov for (i = 0; i < nactual; i++)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (void) ddi_intr_remove_handler(pvs->intr_htable[i]);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to get supported interrupt types");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((intr_types & DDI_INTR_TYPE_MSIX) != 0 && pvscsi_enable_msi) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to install MSI-X interrupt handler");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov } else if ((intr_types & DDI_INTR_TYPE_MSI) != 0 && pvscsi_enable_msi) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to install MSI interrupt handler");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov } else if ((intr_types & DDI_INTR_TYPE_FIXED) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to install FIXED interrupt handler");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (pvs->intr_type == 0 ? DDI_FAILURE : DDI_SUCCESS);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov for (c = list_head(&pvs->cmd_queue); c != NULL; ) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Commands with 'FLAG_NOINTR' are watched using their
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * own timeouts, so we should not touch them.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!expired command: %p (%ld > %ld)",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Now cancel all expired commands */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Build a fake SCSI address */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pvs->flags & PVSCSI_DRIVER_SHUTDOWN) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Finish job */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (cv_reltimedwait(&pvs->wd_condvar, &pvs->mutex,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Explicitly woken up, finish job */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Confirm thread termination */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_ccache_constructor(void *buf, void *cdrarg, int kmflags)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov callback = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate a DMA handle for data transfers */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((ddi_dma_alloc_handle(pvs->dip, &pvs->io_dma_attr, callback,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to allocate DMA handle");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup ARQ buffer */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->arqbuf = scsi_alloc_consistent_buf(&ap, (struct buf *)NULL,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov SENSE_BUFFER_SIZE, B_READ, callback, NULL)) == NULL) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to allocate ARQ buffer");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_dma_alloc_handle(pvs->dip, &pvs->hba_dma_attr,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to allocate DMA handle for ARQ buffer");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_dma_buf_bind_handle(cmd->arqhdl, cmd->arqbuf,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (DDI_DMA_READ | DDI_DMA_CONSISTENT), callback, NULL,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to bind ARQ buffer");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED cdrarg */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_ccache_destructor(void *buf, void *cdrarg)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* tran_* entry points and setup */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED hba_dip tgt_dip hba_tran */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov boolean_t poll = ((pkt->pkt_flags & FLAG_NOINTR) != 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_softc_t *pvs = ap->a_hba_tran->tran_hba_private;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Reinitialize some fields because the packet may
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * have been resubmitted.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Zero status byte */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAG_DMA_VALID) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Consistent packets need to be synced first
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * (only for data going out).
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_softc_t *pvs = ap->a_hba_tran->tran_hba_private;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Abort single command */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (pvscsi_abort_cmd(cmd, &pending) == CMD_ABORTED) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Assume command is completely cancelled now */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Abort all commands on the bus */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_abort_all(ap, pvs, &pending, PVSCSI_FLAG_ABORTED);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED tgtonly */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_getcap(struct scsi_address *ap, char *cap, int tgtonly)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_softc_t *pvs = ap->a_hba_tran->tran_hba_private;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return ((pvs->flags & PVSCSI_HBA_AUTO_REQUEST_SENSE) != 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED tgtonly */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_setcap(struct scsi_address *ap, char *cap, int value, int tgtonly)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_softc_t *pvs = ap->a_hba_tran->tran_hba_private;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_softc_t *pvs = ap->a_hba_tran->tran_hba_private;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAG_DMA_VALID) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovstatic struct scsi_pkt *
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt, struct buf *bp,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov int cmdlen, int statuslen, int tgtlen, int flags, int (*callback)(),
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov int kf = (callback == SLEEP_FUNC) ? KM_SLEEP: KM_NOSLEEP;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate a new SCSI packet */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd = kmem_cache_alloc(pvs->cmd_cache, kf)) == NULL)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate extended buffers */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (pvscsi_cmd_ext_alloc(pvs, cmd, kf) != DDI_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!extent allocation failed");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT((cmd->flags & PVSCSI_FLAG_TRANSPORT) == 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Handle partial DMA transfers */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_dma_getwin(cmd->cmd_dmahdl, cmd->cmd_winindex,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov &cmd->cmd_dmac, &cmd->cmd_dmaccount) == DDI_FAILURE)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup data buffer */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov rc = ddi_dma_buf_bind_handle(cmd->cmd_dmahdl, bp,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!invalid cookie count: %d (max %d)",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmd->cmd_total_dma_count += cmd->cmd_dmac.dmac_size;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Calculate total amount of bytes for this I/O and
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * store cookies for further processing.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ddi_dma_nextcookie(cmd->cmd_dmahdl, &cmd->cmd_dmac);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmd->cmd_total_dma_count += cmd->cmd_dmac.dmac_size;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pkt->pkt_resid = (bp->b_bcount - cmd->cmd_total_dma_count);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED ap */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((cmd->flags & PVSCSI_FLAG_DMA_VALID) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED ap */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov/* ARGSUSED ap flag callback arg */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_reset_notify(struct scsi_address *ap, int flag,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((tran = ddi_get_driver_private(dip)) == NULL ||
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Outstanding commands present, wait */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Suspend taskq delivery and complete all scheduled tasks */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((tran = ddi_get_driver_private(dip)) == NULL ||
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Resume taskq delivery */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_bus_config(dev_info_t *pdip, uint_t flags, ddi_bus_config_op_t op,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ret = pvscsi_config_one(pdip, pvs, (int)target, childp);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ret = ndi_busop_bus_config(pdip, flags, op, arg, childp, 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov hba_tran = pvs->tran = scsi_hba_tran_alloc(pvs->dip,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov hba_tran->tran_destroy_pkt = pvscsi_destroy_pkt;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov hba_tran->tran_reset_notify = pvscsi_reset_notify;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov hba_tran->tran_unquiesce = pvscsi_unquiesce_hba;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov hba_tran->tran_interconnect_type = INTERCONNECT_SAS;
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (scsi_hba_attach_setup(pvs->dip, &pvs->hba_dma_attr, hba_tran,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov SCSI_HBA_TRAN_CDB | SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CLONE) !=
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to attach HBA");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov return (-1);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov ASSERT(scsi_hba_iport_unit_address(dip) == NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Allocate softstate information */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_soft_state_zalloc(pvscsi_sstate, instance) != DDI_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!ddi_soft_state_zalloc() failed for instance %d",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pvs = ddi_get_soft_state(pvscsi_sstate, instance)) == NULL) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmn_err(CE_WARN, "!failed to get soft state for instance %d",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * Indicate that we are 'sizeof (scsi_*(9S))' clean, we use
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov * scsi_pkt_size() instead.
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Setup HBA instance */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov mutex_init(&pvs->mutex, "pvscsi instance mutex", MUTEX_DRIVER, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov mutex_init(&pvs->intr_mutex, "pvscsi instance interrupt mutex",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov mutex_init(&pvs->rx_mutex, "pvscsi rx ring mutex", MUTEX_DRIVER, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov mutex_init(&pvs->tx_mutex, "pvscsi tx ring mutex", MUTEX_DRIVER, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov list_create(&pvs->cmd_ctx_pool, sizeof (pvscsi_cmd_ctx_t),
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov list_create(&pvs->devnodes, sizeof (pvscsi_device_t),
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov list_create(&pvs->cmd_queue, sizeof (pvscsi_cmd_t),
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cv_init(&pvs->syncvar, "pvscsi synchronization cv", CV_DRIVER, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cv_init(&pvs->wd_condvar, "pvscsi watchdog cv", CV_DRIVER, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cv_init(&pvs->quiescevar, "pvscsi quiesce cv", CV_DRIVER, NULL);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov (void) sprintf(buf, "pvscsi%d_cache", instance);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvs->cmd_cache = kmem_cache_create(buf, sizeof (pvscsi_cmd_t), 0,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_ccache_constructor, pvscsi_ccache_destructor, NULL,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to create a cache for SCSI commands");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to setup I/O region");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pvscsi_allocate_rings(pvs)) != DDI_SUCCESS) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to allocate DMA rings");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to setup ISR");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to setup S/G");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to setup HBA");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pvs->comp_tq = ddi_taskq_create(pvs->dip, "comp_tq",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov MIN(UINT16_MAX, ncpus), TASKQ_DEFAULTPRI, 0)) == NULL) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to create completion taskq");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pvs->msg_tq = ddi_taskq_create(pvs->dip, "msg_tq",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov "!failed to create message taskq");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov dev_err(pvs->dip, CE_WARN, "!failed to enable interrupts");
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Launch watchdog thread */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvs->wd_thread = thread_create(NULL, 0, pvscsi_wd_thread, pvs, 0, &p0,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((pvs = ddi_get_soft_state(pvscsi_sstate, instance)) == NULL) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmn_err(CE_WARN, "!failed to get soft state for instance %d",
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Shutdown message taskq */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Shutdown completion taskq */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Shutdown watchdog thread */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankovpvscsi_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp,
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if (ddi_get_soft_state(pvscsi_sstate, getminor(dev)) == NULL) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmn_err(CE_WARN, "!invalid device instance: %d", getminor(dev));
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Try to handle command in a common way */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((ret = scsi_hba_ioctl(dev, cmd, data, mode, credp, rval)) != ENOTTY)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmn_err(CE_WARN, "!unsupported IOCTL command: 0x%X", cmd);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov if ((tran = ddi_get_driver_private(devi)) == NULL)
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Mask all interrupts from device */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_MASK, 0);
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov /* Reset the HBA */
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov sizeof (struct pvscsi_softc), PVSCSI_INITIAL_SSTATE_ITEMS)) != 0) {
565657ca18725b8f8dbe5b93704cc1d173be9d65Yuri Pankov cmn_err(CE_WARN, "!ddi_soft_state_init() failed");