--- /dev/null	Mon Aug  5 17:31:41 2019
+++ new/usr/src/uts/common/io/scsi/adapters/smartpqi/smartpqi_intr.c	Mon Aug  5 17:31:41 2019
@@ -0,0 +1,473 @@
+/*
+ * 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
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
+
+/*
+ * This file sets up the interrupts with the system and also processes
+ * interrupts from the hardware.
+ */
+
+/* ---- Driver specific header ---- */
+#include <smartpqi.h>
+
+/* ---- Forward declarations of private methods ---- */
+static int add_intrs(pqi_state_t s, int type);
+static uint_t intr_handler(caddr_t arg1, caddr_t arg2);
+static void sync_error(pqi_state_t s, pqi_io_request_t *io,
+    pqi_io_response_t *rsp);
+static void process_raid_io_error(pqi_io_request_t *io);
+static void process_aio_io_error(pqi_io_request_t *io);
+static void disable_aio_path(pqi_io_request_t *io);
+
+/*
+ * smartpqi_register_intrs -- Figure out which type of interrupts and register
+ *			      them with the framework.
+ */
+int
+smartpqi_register_intrs(pqi_state_t s)
+{
+	int	intr_types;
+
+	/* ---- Get supported interrupt types ---- */
+	if (ddi_intr_get_supported_types(s->s_dip, &intr_types) !=
+	    DDI_SUCCESS) {
+		dev_err(s->s_dip, CE_NOTE,
+		    "failed to get supported intr types");
+		return (FALSE);
+	}
+
+	if (intr_types & DDI_INTR_TYPE_MSIX) {
+		if (add_intrs(s, DDI_INTR_TYPE_MSIX) == TRUE) {
+			s->s_intr_type = DDI_INTR_TYPE_MSIX;
+			return (TRUE);
+		}
+	} else if (intr_types & DDI_INTR_TYPE_MSI) {
+		if (add_intrs(s, DDI_INTR_TYPE_MSI) == TRUE) {
+			s->s_intr_type = DDI_INTR_TYPE_MSI;
+			return (TRUE);
+		}
+	} else if (intr_types & DDI_INTR_TYPE_FIXED) {
+		if (add_intrs(s, DDI_INTR_TYPE_FIXED) == TRUE) {
+			s->s_intr_type = DDI_INTR_TYPE_FIXED;
+			return (TRUE);
+		}
+	} else {
+		/* ---- Warning since it's a DDI framework error ---- */
+		dev_err(s->s_dip, CE_WARN,
+		    "ddi_intr_get_supported_types returned bogus type of 0x%x",
+		    intr_types);
+	}
+
+	return (FALSE);
+}
+
+/*
+ * smartqpi_unregister_intrs -- Disable and remove interrupt handlers
+ */
+void
+smartpqi_unregister_intrs(pqi_state_t s)
+{
+	int	i;
+
+	/* --- First disable the interrupts ---- */
+	if (s->s_intr_cap & DDI_INTR_FLAG_BLOCK) {
+		(void) ddi_intr_block_disable(s->s_itable, s->s_intr_cnt);
+	} else {
+		for (i = 0; i < s->s_intr_cnt; i++) {
+			(void) ddi_intr_disable(s->s_itable[i]);
+		}
+	}
+
+	/* ---- Next remove the interrupt handlers ---- */
+	for (i = 0; i < s->s_intr_cnt; i++) {
+		(void) ddi_intr_remove_handler(s->s_itable[i]);
+		(void) ddi_intr_free(s->s_itable[i]);
+	}
+
+	kmem_free(s->s_itable, s->s_intr_size);
+	/* ---- Just in case ---- */
+	s->s_itable = NULL;
+	s->s_intr_size = 0;
+}
+
+void
+pqi_process_io_intr(pqi_state_t s, pqi_queue_group_t *qg)
+{
+	pqi_index_t		oq_pi;
+	pqi_index_t		oq_ci;
+	pqi_io_request_t	*io;
+	pqi_io_response_t	*rsp;
+	uint16_t		rqst_id;
+	int			response_cnt = 0;
+	int			qnotify;
+
+	oq_ci = qg->oq_ci_copy;
+	atomic_inc_32(&s->s_intr_count);
+
+	mutex_enter(&s->s_intr_mutex);
+	for (;;) {
+		(void) ddi_dma_sync(s->s_queue_dma->handle,
+		    (uintptr_t)qg->oq_pi -
+		    (uintptr_t)s->s_queue_dma->alloc_memory,
+		    sizeof (oq_pi), DDI_DMA_SYNC_FORCPU);
+
+		oq_pi = *qg->oq_pi;
+		if (oq_pi == oq_ci)
+			break;
+
+		rsp = (pqi_io_response_t *)(qg->oq_element_array +
+		    (oq_ci * PQI_OPERATIONAL_OQ_ELEMENT_LENGTH));
+		(void) ddi_dma_sync(s->s_queue_dma->handle,
+		    (uintptr_t)rsp - (uintptr_t)s->s_queue_dma->alloc_memory,
+		    sizeof (*rsp), DDI_DMA_SYNC_FORCPU);
+		rqst_id = rsp->request_id;
+		ASSERT(rqst_id < s->s_max_io_slots);
+		io = &s->s_io_rqst_pool[rqst_id];
+
+		ASSERT(io->io_refcount == 1);
+
+		if (io->io_cmd != NULL) {
+			pqi_cmd_t	cmd = io->io_cmd;
+
+			mutex_enter(&cmd->pc_device->pd_mutex);
+			if (cmd->pc_flags & PQI_FLAG_ABORTED) {
+				mutex_exit(&cmd->pc_device->pd_mutex);
+				response_cnt++;
+				oq_ci = (oq_ci + 1) % s->s_num_elements_per_oq;
+				continue;
+			}
+			cmd->pc_flags |= PQI_FLAG_FINISHING;
+			mutex_exit(&cmd->pc_device->pd_mutex);
+		}
+
+		io->io_iu_type = rsp->header.iu_type;
+		switch (rsp->header.iu_type) {
+		case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS:
+		case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS:
+		case PQI_RESPONSE_IU_GENERAL_MANAGEMENT:
+			io->io_status = PQI_DATA_IN_OUT_GOOD;
+			break;
+		case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR:
+			io->io_status = PQI_DATA_IN_OUT_ERROR;
+			sync_error(s, io, rsp);
+			process_raid_io_error(io);
+			break;
+		case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR:
+			io->io_status = PQI_DATA_IN_OUT_ERROR;
+			sync_error(s, io, rsp);
+			process_aio_io_error(io);
+			break;
+		case PQI_RESPONSE_IU_AIO_PATH_DISABLED:
+			io->io_status = PQI_DATA_IN_OUT_PROTOCOL_ERROR;
+			disable_aio_path(io);
+			break;
+
+		default:
+			ASSERT(0);
+			break;
+		}
+		io->io_cb(io, io->io_context);
+		response_cnt++;
+		oq_ci = (oq_ci + 1) % s->s_num_elements_per_oq;
+	}
+
+	if (response_cnt) {
+		qg->cmplt_count += response_cnt;
+		qg->oq_ci_copy = oq_ci;
+		ddi_put32(s->s_datap, qg->oq_ci, oq_ci);
+	}
+	mutex_exit(&s->s_intr_mutex);
+
+	mutex_enter(&s->s_mutex);
+	qnotify = HBA_QUIESCED_PENDING(s);
+	mutex_exit(&s->s_mutex);
+
+	if (qnotify)
+		pqi_quiesced_notify(s);
+
+}
+
+/*
+ * add_intrs --
+ */
+static int
+add_intrs(pqi_state_t s, int type)
+{
+	dev_info_t	*dip	= s->s_dip;
+	int		avail;
+	int		actual;
+	int		count	= 0;
+	int		i;
+	int		ret;
+
+	/* ---- Get number of interrupts ---- */
+	ret = ddi_intr_get_nintrs(dip, type, &count);
+	if (ret != DDI_SUCCESS || count <= 0) {
+		dev_err(s->s_dip, CE_NOTE, "ddi_intr_get_nintrs failed, "
+		    "ret=%d, count=%d", ret, count);
+		return (FALSE);
+	}
+
+	/* ---- Get number of available interrupts ---- */
+	ret = ddi_intr_get_navail(dip, type, &avail);
+	if (ret != DDI_SUCCESS || avail == 0) {
+		dev_err(s->s_dip, CE_NOTE, "ddi_intr_get_navail failed, "
+		    "ret=%d, avail=%d", ret, avail);
+		return (FALSE);
+	}
+
+	if (type != DDI_INTR_TYPE_FIXED)
+		count = 1;
+
+	s->s_intr_size = count * sizeof (ddi_intr_handle_t);
+	s->s_itable = kmem_zalloc(s->s_intr_size, KM_SLEEP);
+	ret = ddi_intr_alloc(dip, s->s_itable, type, 0, count, &actual,
+	    DDI_INTR_ALLOC_NORMAL);
+	if (ret != DDI_SUCCESS || actual == 0) {
+		dev_err(s->s_dip, CE_NOTE, "ddi_intr_alloc failed, ret=%d",
+		    ret);
+		return (FALSE);
+	}
+
+	/* ---- Use count return or abort? Make note of at least---- */
+	if (actual < count) {
+		dev_err(s->s_dip, CE_NOTE,
+		    "interrupts: requested=%d, received=%d",
+		    count, actual);
+	}
+	s->s_intr_cnt = actual;
+
+	/* ---- Get priority for first intr, assume rest are the same ---- */
+	if ((ret = ddi_intr_get_pri(s->s_itable[0], &s->s_intr_pri)) !=
+	    DDI_SUCCESS) {
+		dev_err(s->s_dip, CE_NOTE, "ddi_intr_get_pri failed, ret=%d",
+		    ret);
+		goto failure;
+	}
+
+	/* ---- Test for high level mutex ---- */
+	if (s->s_intr_pri >= ddi_intr_get_hilevel_pri()) {
+		dev_err(s->s_dip, CE_NOTE, "Hi level interrupts not supported");
+		goto failure;
+	}
+
+	/* ---- Install interrupt handler ---- */
+	for (i = 0; i < actual; i++) {
+		if ((ret = ddi_intr_add_handler(s->s_itable[i], intr_handler,
+		    (caddr_t)s, (caddr_t)(uintptr_t)i)) != DDI_SUCCESS) {
+			dev_err(s->s_dip, CE_NOTE,
+			    "ddi_intr_add_handler failed, index=%d, ret=%d",
+			    i, ret);
+			goto failure;
+		}
+	}
+
+	if ((ret = ddi_intr_get_cap(s->s_itable[0], &s->s_intr_cap))
+	    != DDI_SUCCESS) {
+		dev_err(s->s_dip, CE_NOTE, "ddi_intr_get_cap failed, ret=%d",
+		    ret);
+		goto failure;
+	}
+
+	/* ---- Enable interrupts ---- */
+	if (s->s_intr_cap & DDI_INTR_FLAG_BLOCK) {
+		(void) ddi_intr_block_enable(s->s_itable, s->s_intr_cnt);
+	} else {
+		/* --- Enable interrupts for either MSI or FIXED ---- */
+		for (i = 0; i < actual; i++)
+			(void) ddi_intr_enable(s->s_itable[i]);
+	}
+
+	return (TRUE);
+
+failure:
+	/* ---- Free allocated interrupts pointers ---- */
+	for (i = 0; i < actual; i++)
+		(void) ddi_intr_free(s->s_itable[i]);
+	kmem_free(s->s_itable, s->s_intr_size);
+	s->s_itable = NULL;
+	s->s_intr_size = 0;
+	return (FALSE);
+}
+
+static void
+disable_aio_path(pqi_io_request_t *io)
+{
+	pqi_device_t	devp;
+
+	devp = io->io_cmd->pc_device;
+	devp->pd_aio_enabled = 0;
+}
+
+static void
+process_raid_io_error(pqi_io_request_t *io)
+{
+	pqi_raid_error_info_t	ei;
+	pqi_cmd_t		cmd;
+	int			sense_len;
+	int			statusbuf_len;
+	int			sense_len_to_copy;
+	struct scsi_arq_status	*arq;
+	struct scsi_pkt		*pkt;
+
+	if ((ei = io->io_error_info) != NULL) {
+		io->io_status = ei->data_out_result;
+		if ((cmd = io->io_cmd) == NULL)
+			return;
+
+		pkt = cmd->pc_pkt;
+		pkt->pkt_resid -= ei->data_out_transferred;
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		arq = (struct scsi_arq_status *)pkt->pkt_scbp;
+		*((uchar_t *)&arq->sts_status) = ei->status;
+		*((uchar_t *)&arq->sts_rqpkt_status) = STATUS_GOOD;
+		arq->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+		    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS |
+		    STATE_ARQ_DONE;
+
+		sense_len = ei->sense_data_length;
+		if (sense_len == 0)
+			sense_len = ei->response_data_length;
+
+		if (sense_len == 0) {
+			/* ---- auto request sense failed ---- */
+			arq->sts_rqpkt_status.sts_chk = 1;
+			arq->sts_rqpkt_resid = cmd->pc_statuslen;
+			return;
+		} else if (sense_len < cmd->pc_statuslen) {
+			/* ---- auto request sense short ---- */
+			arq->sts_rqpkt_resid = cmd->pc_statuslen -
+			    sense_len;
+		} else {
+			/* ---- auto request sense complete ---- */
+			arq->sts_rqpkt_resid = 0;
+		}
+		arq->sts_rqpkt_statistics = 0;
+		pkt->pkt_state |= STATE_ARQ_DONE;
+		if (cmd->pc_statuslen > PQI_ARQ_STATUS_NOSENSE_LEN) {
+			statusbuf_len = cmd->pc_statuslen -
+			    PQI_ARQ_STATUS_NOSENSE_LEN;
+		} else {
+			statusbuf_len = 0;
+		}
+
+		if (sense_len > sizeof (ei->data))
+			sense_len = sizeof (ei->data);
+		sense_len_to_copy = min(sense_len, statusbuf_len);
+
+		if (sense_len_to_copy) {
+			(void) memcpy(&arq->sts_sensedata, ei->data,
+			    sense_len_to_copy);
+		}
+	} else {
+		/*
+		 * sync_error is called before this and sets io_error_info
+		 * which means the value must be non-zero
+		 */
+		ASSERT(0);
+	}
+}
+
+/*ARGSUSED*/
+static void
+process_aio_io_error(pqi_io_request_t *io)
+{
+}
+
+static void
+sync_error(pqi_state_t s, pqi_io_request_t *io, pqi_io_response_t *rsp)
+{
+	(void) ddi_dma_sync(s->s_error_dma->handle,
+	    rsp->error_index * PQI_ERROR_BUFFER_ELEMENT_LENGTH,
+	    PQI_ERROR_BUFFER_ELEMENT_LENGTH, DDI_DMA_SYNC_FORCPU);
+
+	io->io_error_info = s->s_error_dma->alloc_memory +
+	    (rsp->error_index * PQI_ERROR_BUFFER_ELEMENT_LENGTH);
+}
+
+static void
+process_event_intr(pqi_state_t s)
+{
+	pqi_event_queue_t	*q = &s->s_event_queue;
+	pqi_event_response_t	*rsp;
+	int			idx;
+	int			num_events	= 0;
+	pqi_event_t		e;
+	pqi_index_t		oq_ci;
+	pqi_index_t		oq_pi;
+
+	oq_ci = q->oq_ci_copy;
+
+	mutex_enter(&s->s_intr_mutex);
+	for (;;) {
+		(void) ddi_dma_sync(s->s_queue_dma->handle,
+		    (uintptr_t)q->oq_pi -
+		    (uintptr_t)s->s_queue_dma->alloc_memory,
+		    sizeof (oq_pi), DDI_DMA_SYNC_FORCPU);
+		oq_pi = *q->oq_pi;
+
+		if (oq_pi == oq_ci)
+			break;
+
+		num_events++;
+		(void) ddi_dma_sync(s->s_queue_dma->handle,
+		    (uintptr_t)q->oq_element_array +
+		    (oq_ci * PQI_EVENT_OQ_ELEMENT_LENGTH) -
+		    (uintptr_t)s->s_queue_dma->alloc_memory,
+		    sizeof (*rsp),
+		    DDI_DMA_SYNC_FORCPU);
+		rsp = (pqi_event_response_t *)((uintptr_t)q->oq_element_array +
+		    (oq_ci * PQI_EVENT_OQ_ELEMENT_LENGTH));
+		idx = pqi_map_event(rsp->event_type);
+
+		if (idx != -1 && rsp->request_acknowlege) {
+			e = &s->s_events[idx];
+			e->ev_pending = B_TRUE;
+			e->ev_type = rsp->event_type;
+			e->ev_id = rsp->event_id;
+			e->ev_additional = rsp->additional_event_id;
+		}
+		oq_ci = (oq_ci + 1) % PQI_NUM_EVENT_QUEUE_ELEMENTS;
+	}
+
+	if (num_events != 0) {
+		q->oq_ci_copy = oq_ci;
+		ddi_put32(s->s_datap, q->oq_ci, oq_ci);
+		(void) ddi_taskq_dispatch(s->s_events_taskq, pqi_event_worker,
+		    s, 0);
+	}
+	mutex_exit(&s->s_intr_mutex);
+}
+
+static uint_t
+intr_handler(caddr_t arg1, caddr_t arg2)
+{
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	pqi_state_t		s = (pqi_state_t)arg1;
+	int			queue_group_idx = (int)(intptr_t)arg2;
+	pqi_queue_group_t	*qg;
+
+	if (s->s_intr_ready == 0)
+		return (DDI_INTR_CLAIMED);
+
+	qg = &s->s_queue_groups[queue_group_idx];
+	pqi_process_io_intr(s, qg);
+	if (queue_group_idx == s->s_event_queue.int_msg_num)
+		process_event_intr(s);
+
+	pqi_start_io(s, qg, RAID_PATH, NULL);
+	pqi_start_io(s, qg, AIO_PATH, NULL);
+
+	return (DDI_INTR_CLAIMED);
+}
