/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
*/
/*
* Copyright 2016 Nexenta Systems, Inc.
*/
#include <sys/kmem_impl.h>
#include <sys/sysmacros.h>
#include "pvscsi.h"
#include "pvscsi_var.h"
static void *pvscsi_sstate;
/* HBA DMA attributes */
.dma_attr_addr_lo = 0x0000000000000000ull,
.dma_attr_addr_hi = 0xFFFFFFFFFFFFFFFFull,
.dma_attr_count_max = 0x000000007FFFFFFFull,
.dma_attr_align = 0x0000000000000001ull,
.dma_attr_burstsizes = 0x7ff,
.dma_attr_minxfer = 0x00000001u,
.dma_attr_maxxfer = 0x00000000FFFFFFFFull,
.dma_attr_seg = 0x00000000FFFFFFFFull,
.dma_attr_sgllen = 1,
.dma_attr_granular = 0x00000200u,
.dma_attr_flags = 0
};
.dma_attr_addr_lo = 0x0000000000000000ull,
.dma_attr_addr_hi = 0xFFFFFFFFFFFFFFFFull,
.dma_attr_count_max = 0x000000007FFFFFFFull,
.dma_attr_align = 0x0000000000000001ull,
.dma_attr_burstsizes = 0x7ff,
.dma_attr_minxfer = 0x00000001u,
.dma_attr_maxxfer = 0x00000000FFFFFFFFull,
.dma_attr_seg = 0x00000000FFFFFFFFull,
.dma_attr_sgllen = 1,
.dma_attr_granular = 0x00000001u,
.dma_attr_flags = 0
};
/* DMA attributes for buffer I/O */
.dma_attr_addr_lo = 0x0000000000000000ull,
.dma_attr_addr_hi = 0xFFFFFFFFFFFFFFFFull,
.dma_attr_count_max = 0x000000007FFFFFFFull,
.dma_attr_align = 0x0000000000000001ull,
.dma_attr_burstsizes = 0x7ff,
.dma_attr_minxfer = 0x00000001u,
.dma_attr_maxxfer = 0x00000000FFFFFFFFull,
.dma_attr_seg = 0x00000000FFFFFFFFull,
.dma_attr_granular = 0x00000200u,
.dma_attr_flags = 0
};
};
};
static void
{
pvs->cmd_queue_len++;
}
static void
{
pvs->cmd_queue_len--;
}
}
static uint64_t
{
}
static pvscsi_cmd_ctx_t *
{
return (ctx);
}
return (NULL);
}
static pvscsi_cmd_ctx_t *
{
else
return (NULL);
}
static boolean_t
{
return (B_FALSE);
return (B_TRUE);
}
static void
{
}
static uint32_t
{
return (ret);
}
static void
{
value);
}
static void
{
}
static uint32_t
{
}
static void
{
}
static void
{
}
static void
{
if (--pvs->intr_lock_counter == 0) {
}
}
static void
{
}
static void
{
}
static void
{
}
static void
{
}
static int
{
struct buf *b;
return (-1);
CDB_GROUP0, sizeof (struct scsi_arq_status), 0, 0,
goto free_buf;
cdb[0] = SCMD_INQUIRY;
cdb[1] = 0;
cdb[2] = 0;
cdb[5] = 0;
return (ret);
}
static int
dev_info_t **childp)
{
int inqrc;
int ncompatible = 0;
/* Inquiry target */
/* Find devnode */
break;
}
if (inqrc != 0) {
/* Target disappeared, drop devnode */
char *devname;
/* Get full devname */
/* Clean cache and name */
}
/* Target exists */
}
return (NDI_SUCCESS);
} else if (inqrc != 0) {
/* Target doesn't exist */
return (NDI_FAILURE);
}
goto free_nodename;
&dip) != NDI_SUCCESS) {
goto free_nodename;
}
"lun", 0) != DDI_PROP_SUCCESS ||
"!failed to update props for target %d", target);
goto free_devi;
}
goto free_devi;
target);
goto free_devi;
}
return (NDI_SUCCESS);
(void) ndi_devi_free(dip);
return (NDI_FAILURE);
}
static int
{
int target;
/* ndi_devi_enter is done in pvscsi_bus_config */
}
return (NDI_SUCCESS);
}
static pvscsi_cmd_t *
{
/* Save command status for further processing */
/* Mark this command as arrived from hardware */
} else {
}
} else {
}
}
sdesc->cmpConsIdx++;
}
return (head);
}
static pvscsi_msg_t *
{
return (NULL);
case PVSCSI_MSG_DEV_ADDED:
case PVSCSI_MSG_DEV_REMOVED:
return (NULL);
break;
default:
return (NULL);
}
sdesc->msgConsIdx++;
return (msg);
}
static void
{
int circ;
}
static int
{
pvscsi_cmd_t *c;
/* Check if the cmd was already completed by the HBA */
if (c == cmd)
return (CMD_CMPLT);
}
/* Check if cmd was really scheduled by the HBA */
return (CMD_CMPLT);
/* Abort cmd in the HBA */
/* Check if cmd was completed by the HBA before it could be aborted */
if (c == cmd)
return (CMD_CMPLT);
}
}
/* Release I/O ctx */
/* Remove cmd from the queue */
/* Insert cmd at the beginning of the list */
return (CMD_ABORTED);
}
static void
{
int i;
if (cmd->cmd_dma_count == 0)
return;
for (i = 0; i < cmd->cmd_dmaccount; i++) {
}
} else {
}
}
static void
{
}
static void
{
if (scsi_status != STATUS_GOOD &&
(host_status == BTSTAT_SUCCESS ||
if (scsi_status == STATUS_CHECK) {
int arq_size;
arq_size);
} else {
astat->sts_rqpkt_resid = 0;
}
astat->sts_rqpkt_statistics = 0;
}
return;
}
switch (host_status) {
case BTSTAT_SUCCESS:
break;
case BTSTAT_DATARUN:
break;
case BTSTAT_DATA_UNDERRUN:
break;
case BTSTAT_SELTIMEO:
break;
case BTSTAT_TAGREJECT:
break;
case BTSTAT_BADMSG:
break;
case BTSTAT_SENTRST:
case BTSTAT_RECVRST:
case BTSTAT_BUSRESET:
break;
case BTSTAT_ABORTQUEUE:
break;
case BTSTAT_HAHARDWARE:
case BTSTAT_INVPHASE:
case BTSTAT_HATIMEOUT:
case BTSTAT_NORESPONSE:
case BTSTAT_DISCONNECT:
case BTSTAT_HASOFTWARE:
case BTSTAT_BUSFREE:
case BTSTAT_SENSFAILED:
break;
default:
"!unknown host status code: %d", host_status);
break;
}
}
static void
{
pvscsi_cmd_t *c;
return;
}
/* Release I/O ctx */
/* Remove command from queue */
} else {
0);
(STAT_TIMEOUT | STAT_ABORTED);
(STAT_TIMEOUT | STAT_ABORTED);
} else {
}
}
}
cmd = c;
}
}
static void
{
}
static int
{
int i;
/*
* Make sure we're not missing any commands completed
* concurrently before we have actually disabled interrupts.
*/
/* Disable interrupts from H/W */
/* Wait for interrupt to arrive */
for (i = 0; i < cycles; i++) {
if ((status & PVSCSI_INTR_ALL_SUPPORTED) != 0) {
/* Check completion ring */
break;
} else {
}
}
/* Enable interrupts from H/W */
if (!seen_intr) {
/* No interrupts seen from device during the timeout */
/* Command was cancelled asynchronously */
} else if ((pvscsi_abort_cmd(cmd,
&dcmd)) == CMD_ABORTED) {
/* Command was cancelled in hardware */
}
/*
* Complete commands that might be on completion list.
* Target command can also be on the list in case it was
* completed before it could be actually cancelled.
*/
break;
}
if (!seen_intr)
break;
}
return (TRAN_ACCEPT);
}
static void
{
/*
* Try to abort all queued commands, merging commands waiting
* for completion into a single list to complete them at one
* time when mutex is released.
*/
while (qlen > 0) {
qlen--;
int c = --pvs->cmd_queue_len;
/*
* Assume command is completely cancelled now,
* so mark it as requested.
*/
}
/*
* Now merge current pending commands with
* previous ones.
*/
}
} else {
}
}
}
static void
{
if (pvs->cmd_queue_len == 0 &&
}
}
static int
{
return (TRAN_BUSY);
}
return (TRAN_BUSY);
}
} else {
}
/* Setup tag info */
else
/* Setup I/O direction and map data buffers */
else
} else {
}
sdesc->reqProdIdx++;
case SCMD_READ:
case SCMD_WRITE:
case SCMD_READ_G1:
case SCMD_WRITE_G1:
case SCMD_READ_G4:
case SCMD_WRITE_G4:
case SCMD_READ_G5:
case SCMD_WRITE_G5:
break;
default:
break;
}
return (TRAN_ACCEPT);
}
static int
{
int flags;
/* Try to process pending requests */
/* Abort all pending requests */
/* Reset at hardware level */
if (bus_reset) {
/* Should never happen after bus reset */
} else {
}
return (1);
}
static void
{
}
}
}
}
/* ARGSUSED pvs */
static int
{
void *buf;
return (NULL);
}
goto out;
}
goto out;
}
return (DDI_SUCCESS);
out:
return (NULL);
}
static int
{
return (DDI_FAILURE);
}
"!failed to allocate %ld bytes for DMA buffer", length);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* TODO Support multipart SG regions */
return (DDI_SUCCESS);
}
static void
{
}
static int
{
int i;
goto cleanup;
}
return (DDI_SUCCESS);
for (; i >= 0; --i, --ctx) {
}
return (DDI_FAILURE);
}
static void
{
int i;
}
}
static int
{
/* Allocate DMA buffer for rings state */
return (DDI_FAILURE);
/* Allocate DMA buffer for request ring */
goto free_rings_state;
/* Allocate completion ring */
goto free_req_buf;
/* Allocate message ring */
goto free_cmp_buf;
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static void
{
}
static void
{
int i;
/* Setup request ring */
}
/* Setup completion ring */
}
/* Issue SETUP command */
/* Setup message ring */
}
sizeof (cmd_msg));
}
static int
{
®s_length) != DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
break;
}
}
continue;
if (type != PCI_ADDR_IO) {
®size) != DDI_SUCCESS) {
"!failed to get size of reg %d", rn);
goto out;
}
if (regsize == PVSCSI_MEM_SPACE_SIZE) {
"!failed to map MMIO BAR");
goto out;
}
ret = DDI_SUCCESS;
break;
}
}
}
out:
return (ret);
}
static void
{
}
static int
{
DDI_SUCCESS) {
return (DDI_FAILURE);
}
if ((intr_caps & DDI_INTR_FLAG_BLOCK) != 0) {
"!failed to enable interrupt block");
}
} else {
continue;
"!failed to enable interrupt");
while (--i >= 0)
break;
}
}
/* Unmask interrupts */
if (rc == DDI_SUCCESS) {
}
return (rc);
}
/* ARGSUSED arg2 */
static uint32_t
{
if (pvs->num_pollers > 0) {
return (DDI_INTR_CLAIMED);
}
if (pvscsi_enable_msi) {
} else {
if (handled)
}
if (handled) {
DDI_NOSLEEP) == DDI_FAILURE)
"!failed to process msg type %d for target %d",
}
if (qnotify)
}
}
static int
{
int i;
navail == 0) {
"!failed to get number of available interrupts of type %d",
type);
return (DDI_FAILURE);
}
"!failed to allocate %d bytes for interrupt hashtable",
return (DDI_FAILURE);
}
navail);
goto free_htable;
}
goto free_intrs;
}
for (i = 0; i < nactual; i++) {
"!failed to add interrupt handler");
goto free_intrs;
}
}
return (DDI_SUCCESS);
for (i = 0; i < nactual; i++)
return (DDI_FAILURE);
}
static void
{
int i;
}
}
static int
{
int intr_types;
&intr_types) != DDI_SUCCESS) {
"!failed to get supported interrupt types");
return (DDI_FAILURE);
}
if (pvscsi_register_isr(pvs,
DDI_INTR_TYPE_MSIX) == DDI_SUCCESS) {
} else {
"!failed to install MSI-X interrupt handler");
}
if (pvscsi_register_isr(pvs,
DDI_INTR_TYPE_MSI) == DDI_SUCCESS) {
} else {
"!failed to install MSI interrupt handler");
}
} else if ((intr_types & DDI_INTR_TYPE_FIXED) != 0) {
if (pvscsi_register_isr(pvs,
DDI_INTR_TYPE_FIXED) == DDI_SUCCESS) {
} else {
"!failed to install FIXED interrupt handler");
}
}
}
static void
{
for (;;) {
now = ddi_get_lbolt();
/*
* Commands with 'FLAG_NOINTR' are watched using their
* own timeouts, so we should not touch them.
*/
now > c->timeout_lbolt) {
"!expired command: %p (%ld > %ld)",
(void *)c, now, c->timeout_lbolt);
expired = c;
} else {
*pnext = c;
}
}
c = cn;
}
/* Now cancel all expired commands */
/* Build a fake SCSI address */
expired = c;
}
}
/* Finish job */
break;
}
/* Explicitly woken up, finish job */
break;
}
}
/* Confirm thread termination */
}
static int
{
/* Allocate a DMA handle for data transfers */
return (-1);
}
/* Setup ARQ buffer */
goto free_handle;
}
"!failed to allocate DMA handle for ARQ buffer");
goto free_arqbuf;
}
goto free_arqhdl;
}
return (0);
return (-1);
}
/* ARGSUSED cdrarg */
static void
{
}
}
}
}
/* tran_* entry points and setup */
/* ARGSUSED hba_dip tgt_dip hba_tran */
static int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static int
{
int rc;
/*
* Reinitialize some fields because the packet may
* have been resubmitted.
*/
pkt->pkt_statistics = 0;
/* Zero status byte */
/*
* Consistent packets need to be synced first
* (only for data going out).
*/
}
}
return (TRAN_BUSY);
}
if (poll) {
if (rc == TRAN_ACCEPT)
if (qnotify)
}
return (rc);
}
static int
{
switch (level) {
case RESET_ALL:
case RESET_TARGET:
default:
return (0);
}
}
static int
{
/* Abort single command */
/* Assume command is completely cancelled now */
}
} else {
/* Abort all commands on the bus */
}
if (qnotify)
return (1);
}
/* ARGSUSED tgtonly */
static int
{
return (-1);
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
case SCSI_CAP_UNTAGGED_QING:
return (1);
default:
return (-1);
}
}
/* ARGSUSED tgtonly */
static int
{
return (-1);
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
if (value == 0)
else
return (1);
default:
return (0);
}
}
static void
{
}
}
}
static struct scsi_pkt *
{
int rc, i;
/* Allocate a new SCSI packet */
return (NULL);
sizeof (cmd->cached_cookies));
/* Allocate extended buffers */
"!extent allocation failed");
goto out;
}
}
} else {
}
/* Handle partial DMA transfers */
return (NULL);
return (NULL);
goto handle_dma_cookies;
}
/* Setup data buffer */
int dma_flags;
} else {
}
if ((flags & PKT_CONSISTENT) != 0) {
}
if ((flags & PKT_DMA_PARTIAL) != 0)
&cmd->cmd_dmaccount);
if (rc == DDI_DMA_PARTIAL_MAP) {
cmd->cmd_winindex = 0;
&cmd->cmd_dmaccount);
switch (rc) {
case DDI_DMA_NORESOURCES:
break;
case DDI_DMA_BADATTR:
case DDI_DMA_NOMAPPING:
break;
case DDI_DMA_TOOBIG:
default:
break;
}
goto out;
}
"!invalid cookie count: %d (max %d)",
goto out;
}
/*
* Calculate total amount of bytes for this I/O and
* store cookies for further processing.
*/
}
}
return (pkt);
out:
if (is_new)
return (NULL);
}
/* ARGSUSED ap */
static void
{
}
}
/* ARGSUSED ap */
static void
{
}
}
/* ARGSUSED ap flag callback arg */
static int
{
return (DDI_FAILURE);
}
static int
{
return (-1);
if (!HBA_IS_QUIESCED(pvs))
if (pvs->cmd_queue_len != 0) {
/* Outstanding commands present, wait */
}
/* Suspend taskq delivery and complete all scheduled tasks */
return (0);
}
static int
{
return (-1);
if (!HBA_IS_QUIESCED(pvs)) {
return (0);
}
/* Resume taskq delivery */
return (0);
}
static int
{
char *p;
int circ;
long target = 0;
switch (op) {
case BUS_CONFIG_ONE:
break;
case BUS_CONFIG_DRIVER:
case BUS_CONFIG_ALL:
break;
default:
break;
}
if (ret == NDI_SUCCESS)
return (ret);
}
static int
{
DDI_SUCCESS) {
return (-1);
}
return (0);
}
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
case DDI_RESUME:
break;
default:
return (DDI_FAILURE);
}
/* Allocate softstate information */
"!ddi_soft_state_zalloc() failed for instance %d",
instance);
return (DDI_FAILURE);
}
instance);
goto fail;
}
/*
* Indicate that we are 'sizeof (scsi_*(9S))' clean, we use
* scsi_pkt_size() instead.
*/
/* Setup HBA instance */
MUTEX_DRIVER, NULL);
"!failed to create a cache for SCSI commands");
goto fail;
}
goto free_cache;
}
goto free_io;
}
goto free_rings;
}
goto free_intr;
}
if (pvscsi_hba_setup(pvs) != 0) {
goto free_sg;
}
"!failed to create completion taskq");
goto free_sg;
}
"!failed to create message taskq");
goto free_comp_tq;
}
goto free_msg_tq;
}
/* Launch watchdog thread */
return (DDI_SUCCESS);
fail:
return (DDI_FAILURE);
}
static int
{
int instance;
switch (cmd) {
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
instance);
return (DDI_FAILURE);
}
/* Shutdown message taskq */
/* Shutdown completion taskq */
/* Shutdown watchdog thread */
return (DDI_SUCCESS);
}
static int
int *rval)
{
int ret;
return (ENXIO);
}
/* Try to handle command in a common way */
return (ret);
return (ENXIO);
}
static int
{
return (DDI_SUCCESS);
return (DDI_SUCCESS);
/* Mask all interrupts from device */
/* Reset the HBA */
return (DDI_SUCCESS);
}
/* module */
.cb_open = scsi_hba_open,
.cb_strategy = nodev,
.cb_ioctl = pvscsi_ioctl,
};
.devo_refcnt = 0,
.devo_identify = nulldev,
.devo_probe = nulldev,
.devo_reset = nodev,
.devo_cb_ops = &pvscsi_cb_ops,
.devo_bus_ops = NULL,
.devo_power = NULL,
};
};
&modldrv,
};
int
_init(void)
{
int ret;
sizeof (struct pvscsi_softc), PVSCSI_INITIAL_SSTATE_ITEMS)) != 0) {
return (ret);
}
return (ret);
}
}
return (ret);
}
int
{
}
int
_fini(void)
{
int ret;
}
return (ret);
}