1*8226594fSRick McNeal /*
2*8226594fSRick McNeal  * This file and its contents are supplied under the terms of the
3*8226594fSRick McNeal  * Common Development and Distribution License ("CDDL"), version 1.0.
4*8226594fSRick McNeal  * You may only use this file in accordance with the terms of version
5*8226594fSRick McNeal  * 1.0 of the CDDL.
6*8226594fSRick McNeal  *
7*8226594fSRick McNeal  * A full copy of the text of the CDDL should have accompanied this
8*8226594fSRick McNeal  * source.  A copy of the CDDL is also available via the Internet at
9*8226594fSRick McNeal  * http://www.illumos.org/license/CDDL.
10*8226594fSRick McNeal  */
11*8226594fSRick McNeal 
12*8226594fSRick McNeal /*
13*8226594fSRick McNeal  * Copyright 2023 Tintri by DDN, Inc. All rights reserved.
14*8226594fSRick McNeal  */
15*8226594fSRick McNeal 
16*8226594fSRick McNeal /*
17*8226594fSRick McNeal  * This file contains code necessary to send SCSI commands to HBA.
18*8226594fSRick McNeal  */
19*8226594fSRick McNeal #include <smartpqi.h>
20*8226594fSRick McNeal 
21*8226594fSRick McNeal /*
22*8226594fSRick McNeal  * []------------------------------------------------------------------[]
23*8226594fSRick McNeal  * | Forward declarations for support/utility functions			|
24*8226594fSRick McNeal  * []------------------------------------------------------------------[]
25*8226594fSRick McNeal  */
26*8226594fSRick McNeal static void aio_io_complete(pqi_io_request_t *io, void *context);
27*8226594fSRick McNeal static void raid_io_complete(pqi_io_request_t *io, void *context);
28*8226594fSRick McNeal static void build_aio_sg_list(pqi_state_t *s,
29*8226594fSRick McNeal 	pqi_aio_path_request_t *rqst, pqi_cmd_t *cmd, pqi_io_request_t *);
30*8226594fSRick McNeal static void build_raid_sg_list(pqi_state_t *s,
31*8226594fSRick McNeal 	pqi_raid_path_request_t *rqst, pqi_cmd_t *cmd, pqi_io_request_t *);
32*8226594fSRick McNeal static pqi_io_request_t *setup_aio_request(pqi_state_t *s, pqi_cmd_t *cmd);
33*8226594fSRick McNeal static pqi_io_request_t *setup_raid_request(pqi_state_t *s, pqi_cmd_t *cmd);
34*8226594fSRick McNeal static uint32_t read_heartbeat_counter(pqi_state_t *s);
35*8226594fSRick McNeal static void take_ctlr_offline(pqi_state_t *s);
36*8226594fSRick McNeal static uint32_t free_elem_count(pqi_index_t pi, pqi_index_t ci,
37*8226594fSRick McNeal 	uint32_t per_iq);
38*8226594fSRick McNeal static void ack_event(pqi_state_t *s, pqi_event_t *e);
39*8226594fSRick McNeal static boolean_t is_aio_enabled(pqi_device_t *d);
40*8226594fSRick McNeal static void lun_reset_worker(void *v);
41*8226594fSRick McNeal static void lun_reset_complete(pqi_io_request_t *io, void *ctx);
42*8226594fSRick McNeal 
43*8226594fSRick McNeal #define	DIV_UP(n, d) ((n + (d - 1)) / d)
44*8226594fSRick McNeal 
45*8226594fSRick McNeal /*
46*8226594fSRick McNeal  * []------------------------------------------------------------------[]
47*8226594fSRick McNeal  * | Main entry points in file.						|
48*8226594fSRick McNeal  * []------------------------------------------------------------------[]
49*8226594fSRick McNeal  */
50*8226594fSRick McNeal 
51*8226594fSRick McNeal int pqi_do_reset_lun = -1;
52*8226594fSRick McNeal int pqi_do_reset_ctlr = -1;
53*8226594fSRick McNeal /*
54*8226594fSRick McNeal  * pqi_watchdog -- interrupt count and/or heartbeat must increase over time.
55*8226594fSRick McNeal  */
56*8226594fSRick McNeal void
pqi_watchdog(void * v)57*8226594fSRick McNeal pqi_watchdog(void *v)
58*8226594fSRick McNeal {
59*8226594fSRick McNeal 	pqi_state_t	*s = v;
60*8226594fSRick McNeal 	uint32_t	hb;
61*8226594fSRick McNeal 
62*8226594fSRick McNeal 	if (pqi_is_offline(s))
63*8226594fSRick McNeal 		return;
64*8226594fSRick McNeal 
65*8226594fSRick McNeal 	hb = read_heartbeat_counter(s);
66*8226594fSRick McNeal 	if ((s->s_last_intr_count == s->s_intr_count) &&
67*8226594fSRick McNeal 	    (s->s_last_heartbeat_count == hb)) {
68*8226594fSRick McNeal 		dev_err(s->s_dip, CE_NOTE, "No heartbeat");
69*8226594fSRick McNeal 		pqi_show_dev_state(s);
70*8226594fSRick McNeal 		take_ctlr_offline(s);
71*8226594fSRick McNeal 	} else {
72*8226594fSRick McNeal 		if (pqi_do_reset_ctlr == s->s_instance) {
73*8226594fSRick McNeal 			pqi_do_reset_ctlr = -1;
74*8226594fSRick McNeal 			take_ctlr_offline(s);
75*8226594fSRick McNeal 		} else {
76*8226594fSRick McNeal 			s->s_last_intr_count = s->s_intr_count;
77*8226594fSRick McNeal 			s->s_last_heartbeat_count = hb;
78*8226594fSRick McNeal 			s->s_watchdog = timeout(pqi_watchdog, s,
79*8226594fSRick McNeal 			    drv_usectohz(WATCHDOG));
80*8226594fSRick McNeal 		}
81*8226594fSRick McNeal 	}
82*8226594fSRick McNeal }
83*8226594fSRick McNeal 
84*8226594fSRick McNeal /*
85*8226594fSRick McNeal  * pqi_start_io -- queues command to HBA.
86*8226594fSRick McNeal  *
87*8226594fSRick McNeal  * This method can be called either from the upper layer with a non-zero
88*8226594fSRick McNeal  * io argument or called during an interrupt to load the outgoing queue
89*8226594fSRick McNeal  * with more commands.
90*8226594fSRick McNeal  */
91*8226594fSRick McNeal void
pqi_start_io(pqi_state_t * s,pqi_queue_group_t * qg,pqi_path_t path,pqi_io_request_t * io)92*8226594fSRick McNeal pqi_start_io(pqi_state_t *s, pqi_queue_group_t *qg, pqi_path_t path,
93*8226594fSRick McNeal     pqi_io_request_t *io)
94*8226594fSRick McNeal {
95*8226594fSRick McNeal 	pqi_iu_header_t	*rqst;
96*8226594fSRick McNeal 	size_t		iu_len;
97*8226594fSRick McNeal 	size_t		copy_to_end;
98*8226594fSRick McNeal 	pqi_index_t	iq_pi;
99*8226594fSRick McNeal 	pqi_index_t	iq_ci;
100*8226594fSRick McNeal 	uint32_t	elem_needed;
101*8226594fSRick McNeal 	uint32_t	elem_to_end;
102*8226594fSRick McNeal 	caddr_t		next_elem;
103*8226594fSRick McNeal 	int		sending		= 0;
104*8226594fSRick McNeal 
105*8226594fSRick McNeal 	mutex_enter(&qg->submit_lock[path]);
106*8226594fSRick McNeal 	if (io != NULL) {
107*8226594fSRick McNeal 		io->io_queue_group = qg;
108*8226594fSRick McNeal 		io->io_queue_path = path;
109*8226594fSRick McNeal 		list_insert_tail(&qg->request_list[path], io);
110*8226594fSRick McNeal 	}
111*8226594fSRick McNeal 
112*8226594fSRick McNeal 
113*8226594fSRick McNeal 	iq_pi = qg->iq_pi_copy[path];
114*8226594fSRick McNeal 	while ((io = list_remove_head(&qg->request_list[path])) != NULL) {
115*8226594fSRick McNeal 
116*8226594fSRick McNeal 		/* ---- Primary cause for !active is controller failure ---- */
117*8226594fSRick McNeal 		if (qg->qg_active == B_FALSE) {
118*8226594fSRick McNeal 			pqi_cmd_t	*c = io->io_cmd;
119*8226594fSRick McNeal 
120*8226594fSRick McNeal 			mutex_enter(&c->pc_device->pd_mutex);
121*8226594fSRick McNeal 			/*
122*8226594fSRick McNeal 			 * When a command is failed it will be removed from
123*8226594fSRick McNeal 			 * the queue group if pc_io_rqst is not NULL. Since
124*8226594fSRick McNeal 			 * we have already removed the command from the list
125*8226594fSRick McNeal 			 * would shouldn't attempt to do so a second time.
126*8226594fSRick McNeal 			 */
127*8226594fSRick McNeal 			c->pc_io_rqst = NULL;
128*8226594fSRick McNeal 			(void) pqi_fail_cmd(io->io_cmd, CMD_DEV_GONE,
129*8226594fSRick McNeal 			    STAT_TERMINATED);
130*8226594fSRick McNeal 			mutex_exit(&c->pc_device->pd_mutex);
131*8226594fSRick McNeal 			continue;
132*8226594fSRick McNeal 		}
133*8226594fSRick McNeal 
134*8226594fSRick McNeal 		rqst = io->io_iu;
135*8226594fSRick McNeal 		iu_len = rqst->iu_length + PQI_REQUEST_HEADER_LENGTH;
136*8226594fSRick McNeal 		elem_needed = DIV_UP(iu_len, PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
137*8226594fSRick McNeal 		(void) ddi_dma_sync(s->s_queue_dma->handle,
138*8226594fSRick McNeal 		    (uintptr_t)qg->iq_ci[path] -
139*8226594fSRick McNeal 		    (uintptr_t)s->s_queue_dma->alloc_memory, sizeof (iq_ci),
140*8226594fSRick McNeal 		    DDI_DMA_SYNC_FORCPU);
141*8226594fSRick McNeal 		iq_ci = *qg->iq_ci[path];
142*8226594fSRick McNeal 
143*8226594fSRick McNeal 		if (elem_needed > free_elem_count(iq_pi, iq_ci,
144*8226594fSRick McNeal 		    s->s_num_elements_per_iq)) {
145*8226594fSRick McNeal 			list_insert_head(&qg->request_list[path], io);
146*8226594fSRick McNeal 			break;
147*8226594fSRick McNeal 		}
148*8226594fSRick McNeal 
149*8226594fSRick McNeal 		if (pqi_cmd_action(io->io_cmd, PQI_CMD_START) == PQI_CMD_FAIL)
150*8226594fSRick McNeal 			continue;
151*8226594fSRick McNeal 
152*8226594fSRick McNeal 		io->io_pi = iq_pi;
153*8226594fSRick McNeal 		rqst->iu_id = qg->oq_id;
154*8226594fSRick McNeal 		next_elem = qg->iq_element_array[path] +
155*8226594fSRick McNeal 		    (iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
156*8226594fSRick McNeal 		elem_to_end = s->s_num_elements_per_iq - iq_pi;
157*8226594fSRick McNeal 		if (elem_needed <= elem_to_end) {
158*8226594fSRick McNeal 			(void) memcpy(next_elem, rqst, iu_len);
159*8226594fSRick McNeal 			(void) ddi_dma_sync(s->s_queue_dma->handle,
160*8226594fSRick McNeal 			    (uintptr_t)next_elem -
161*8226594fSRick McNeal 			    (uintptr_t)s->s_queue_dma->alloc_memory, iu_len,
162*8226594fSRick McNeal 			    DDI_DMA_SYNC_FORDEV);
163*8226594fSRick McNeal 		} else {
164*8226594fSRick McNeal 			copy_to_end = elem_to_end *
165*8226594fSRick McNeal 			    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH;
166*8226594fSRick McNeal 			(void) memcpy(next_elem, rqst, copy_to_end);
167*8226594fSRick McNeal 			(void) ddi_dma_sync(s->s_queue_dma->handle,
168*8226594fSRick McNeal 			    (uintptr_t)next_elem -
169*8226594fSRick McNeal 			    (uintptr_t)s->s_queue_dma->alloc_memory,
170*8226594fSRick McNeal 			    copy_to_end, DDI_DMA_SYNC_FORDEV);
171*8226594fSRick McNeal 			(void) memcpy(qg->iq_element_array[path],
172*8226594fSRick McNeal 			    (caddr_t)rqst + copy_to_end,
173*8226594fSRick McNeal 			    iu_len - copy_to_end);
174*8226594fSRick McNeal 			(void) ddi_dma_sync(s->s_queue_dma->handle,
175*8226594fSRick McNeal 			    0, iu_len - copy_to_end, DDI_DMA_SYNC_FORDEV);
176*8226594fSRick McNeal 		}
177*8226594fSRick McNeal 		sending += elem_needed;
178*8226594fSRick McNeal 
179*8226594fSRick McNeal 		iq_pi = (iq_pi + elem_needed) % s->s_num_elements_per_iq;
180*8226594fSRick McNeal 	}
181*8226594fSRick McNeal 
182*8226594fSRick McNeal 	qg->submit_count += sending;
183*8226594fSRick McNeal 	if (iq_pi != qg->iq_pi_copy[path]) {
184*8226594fSRick McNeal 		qg->iq_pi_copy[path] = iq_pi;
185*8226594fSRick McNeal 		ddi_put32(s->s_datap, qg->iq_pi[path], iq_pi);
186*8226594fSRick McNeal 	} else {
187*8226594fSRick McNeal 		ASSERT0(sending);
188*8226594fSRick McNeal 	}
189*8226594fSRick McNeal 	mutex_exit(&qg->submit_lock[path]);
190*8226594fSRick McNeal }
191*8226594fSRick McNeal 
192*8226594fSRick McNeal int
pqi_transport_command(pqi_state_t * s,pqi_cmd_t * cmd)193*8226594fSRick McNeal pqi_transport_command(pqi_state_t *s, pqi_cmd_t *cmd)
194*8226594fSRick McNeal {
195*8226594fSRick McNeal 	pqi_device_t		*devp = cmd->pc_device;
196*8226594fSRick McNeal 	int			path;
197*8226594fSRick McNeal 	pqi_io_request_t	*io;
198*8226594fSRick McNeal 
199*8226594fSRick McNeal 	if (is_aio_enabled(devp) == B_TRUE) {
200*8226594fSRick McNeal 		path = AIO_PATH;
201*8226594fSRick McNeal 		io = setup_aio_request(s, cmd);
202*8226594fSRick McNeal 	} else {
203*8226594fSRick McNeal 		path = RAID_PATH;
204*8226594fSRick McNeal 		io = setup_raid_request(s, cmd);
205*8226594fSRick McNeal 	}
206*8226594fSRick McNeal 
207*8226594fSRick McNeal 	if (io == NULL)
208*8226594fSRick McNeal 		return (TRAN_BUSY);
209*8226594fSRick McNeal 
210*8226594fSRick McNeal 	cmd->pc_io_rqst = io;
211*8226594fSRick McNeal 	(void) pqi_cmd_action(cmd, PQI_CMD_QUEUE);
212*8226594fSRick McNeal 
213*8226594fSRick McNeal 	pqi_start_io(s, &s->s_queue_groups[PQI_DEFAULT_QUEUE_GROUP],
214*8226594fSRick McNeal 	    path, io);
215*8226594fSRick McNeal 
216*8226594fSRick McNeal 	return (TRAN_ACCEPT);
217*8226594fSRick McNeal }
218*8226594fSRick McNeal 
219*8226594fSRick McNeal void
pqi_do_rescan(void * v)220*8226594fSRick McNeal pqi_do_rescan(void *v)
221*8226594fSRick McNeal {
222*8226594fSRick McNeal 	pqi_state_t	*s	= v;
223*8226594fSRick McNeal 
224*8226594fSRick McNeal 	ndi_devi_enter(scsi_vhci_dip);
225*8226594fSRick McNeal 	ndi_devi_enter(s->s_dip);
226*8226594fSRick McNeal 	pqi_rescan_devices(s);
227*8226594fSRick McNeal 	(void) pqi_config_all(s->s_dip, s);
228*8226594fSRick McNeal 	ndi_devi_exit(s->s_dip);
229*8226594fSRick McNeal 	ndi_devi_exit(scsi_vhci_dip);
230*8226594fSRick McNeal }
231*8226594fSRick McNeal 
232*8226594fSRick McNeal void
pqi_event_worker(void * v)233*8226594fSRick McNeal pqi_event_worker(void *v)
234*8226594fSRick McNeal {
235*8226594fSRick McNeal 	pqi_state_t	*s		= v;
236*8226594fSRick McNeal 	int		i;
237*8226594fSRick McNeal 	pqi_event_t	*e;
238*8226594fSRick McNeal 	boolean_t	non_heartbeat	= B_FALSE;
239*8226594fSRick McNeal 
240*8226594fSRick McNeal 	if (pqi_is_offline(s))
241*8226594fSRick McNeal 		return;
242*8226594fSRick McNeal 
243*8226594fSRick McNeal 	e = s->s_events;
244*8226594fSRick McNeal 	for (i = 0; i < PQI_NUM_SUPPORTED_EVENTS; i++) {
245*8226594fSRick McNeal 		if (e->ev_pending == B_TRUE) {
246*8226594fSRick McNeal 			e->ev_pending = B_FALSE;
247*8226594fSRick McNeal 			ack_event(s, e);
248*8226594fSRick McNeal 			if (pqi_map_event(PQI_EVENT_TYPE_HEARTBEAT) != i)
249*8226594fSRick McNeal 				non_heartbeat = B_TRUE;
250*8226594fSRick McNeal 		}
251*8226594fSRick McNeal 		e++;
252*8226594fSRick McNeal 	}
253*8226594fSRick McNeal 
254*8226594fSRick McNeal 	if (non_heartbeat == B_TRUE)
255*8226594fSRick McNeal 		pqi_do_rescan(s);
256*8226594fSRick McNeal }
257*8226594fSRick McNeal 
258*8226594fSRick McNeal /*
259*8226594fSRick McNeal  * pqi_fail_cmd -- given a reason and stats the command is failed.
260*8226594fSRick McNeal  */
261*8226594fSRick McNeal pqi_cmd_action_t
pqi_fail_cmd(pqi_cmd_t * cmd,uchar_t reason,uint_t stats)262*8226594fSRick McNeal pqi_fail_cmd(pqi_cmd_t *cmd, uchar_t reason, uint_t stats)
263*8226594fSRick McNeal {
264*8226594fSRick McNeal 	struct scsi_pkt		*pkt	= CMD2PKT(cmd);
265*8226594fSRick McNeal 
266*8226594fSRick McNeal 	pkt->pkt_reason = reason;
267*8226594fSRick McNeal 	pkt->pkt_statistics = stats;
268*8226594fSRick McNeal 
269*8226594fSRick McNeal 	return (pqi_cmd_action_nolock(cmd, PQI_CMD_FAIL));
270*8226594fSRick McNeal }
271*8226594fSRick McNeal 
272*8226594fSRick McNeal void
pqi_fail_drive_cmds(pqi_device_t * d,uchar_t reason)273*8226594fSRick McNeal pqi_fail_drive_cmds(pqi_device_t *d, uchar_t reason)
274*8226594fSRick McNeal {
275*8226594fSRick McNeal 	pqi_cmd_t	*c, *next_c;
276*8226594fSRick McNeal 
277*8226594fSRick McNeal 	mutex_enter(&d->pd_mutex);
278*8226594fSRick McNeal 
279*8226594fSRick McNeal 	c = list_head(&d->pd_cmd_list);
280*8226594fSRick McNeal 	while (c != NULL) {
281*8226594fSRick McNeal 		next_c = list_next(&d->pd_cmd_list, c);
282*8226594fSRick McNeal 		if (pqi_fail_cmd(c, reason, STAT_BUS_RESET) !=
283*8226594fSRick McNeal 		    PQI_CMD_START) {
284*8226594fSRick McNeal 			/*
285*8226594fSRick McNeal 			 * The command can't be terminated in the driver because
286*8226594fSRick McNeal 			 * it was already handed off to the HBA and the driver
287*8226594fSRick McNeal 			 * will have to wait for completion. The reason is
288*8226594fSRick McNeal 			 * that the HBA indicates slots are complete, not a
289*8226594fSRick McNeal 			 * pointer to a command. If the code were to cancel
290*8226594fSRick McNeal 			 * an outstanding command that slot could be reused
291*8226594fSRick McNeal 			 * by another command and when the completion interrupt
292*8226594fSRick McNeal 			 * arrives the driver would signal that a command had
293*8226594fSRick McNeal 			 * completed when in fact it was a prior command that
294*8226594fSRick McNeal 			 * had been canceled.
295*8226594fSRick McNeal 			 *
296*8226594fSRick McNeal 			 * Should the command fail to complete due to an HBA
297*8226594fSRick McNeal 			 * error the command will be forced through to
298*8226594fSRick McNeal 			 * completion during a timeout scan that occurs on
299*8226594fSRick McNeal 			 * another thread.
300*8226594fSRick McNeal 			 */
301*8226594fSRick McNeal 			d->pd_killed++;
302*8226594fSRick McNeal 		} else {
303*8226594fSRick McNeal 			d->pd_posted++;
304*8226594fSRick McNeal 		}
305*8226594fSRick McNeal 		c = next_c;
306*8226594fSRick McNeal 	}
307*8226594fSRick McNeal 
308*8226594fSRick McNeal 	mutex_exit(&d->pd_mutex);
309*8226594fSRick McNeal }
310*8226594fSRick McNeal 
311*8226594fSRick McNeal uint32_t
pqi_disable_intr(pqi_state_t * s)312*8226594fSRick McNeal pqi_disable_intr(pqi_state_t *s)
313*8226594fSRick McNeal {
314*8226594fSRick McNeal 	uint32_t	db;
315*8226594fSRick McNeal 	uint32_t	rval;
316*8226594fSRick McNeal 
317*8226594fSRick McNeal 	rval = db = G32(s, sis_host_to_ctrl_doorbell);
318*8226594fSRick McNeal 	db &= ~(SIS_ENABLE_MSIX | SIS_ENABLE_INTX);
319*8226594fSRick McNeal 	S32(s, sis_host_to_ctrl_doorbell, db);
320*8226594fSRick McNeal 	return (rval);
321*8226594fSRick McNeal }
322*8226594fSRick McNeal 
323*8226594fSRick McNeal void
pqi_enable_intr(pqi_state_t * s,uint32_t old_state)324*8226594fSRick McNeal pqi_enable_intr(pqi_state_t *s, uint32_t old_state)
325*8226594fSRick McNeal {
326*8226594fSRick McNeal 	S32(s, sis_host_to_ctrl_doorbell, old_state);
327*8226594fSRick McNeal }
328*8226594fSRick McNeal 
329*8226594fSRick McNeal typedef struct reset_closure {
330*8226594fSRick McNeal 	pqi_state_t	*rc_s;
331*8226594fSRick McNeal 	pqi_device_t	*rc_d;
332*8226594fSRick McNeal } *reset_closure_t;
333*8226594fSRick McNeal 
334*8226594fSRick McNeal /*
335*8226594fSRick McNeal  * pqi_lun_reset -- set up callback to reset the device
336*8226594fSRick McNeal  *
337*8226594fSRick McNeal  * Dispatch queue is used here because the call tree can come from the interrupt
338*8226594fSRick McNeal  * routine. (pqi_process_io_intr -> aio_io_complete -> SCSA -> tran_reset ->
339*8226594fSRick McNeal  * pqi_lun_reset). If pqi_lun_reset were to actually do the reset work it would
340*8226594fSRick McNeal  * then wait for an interrupt which would never arrive since the current thread
341*8226594fSRick McNeal  * would be the interrupt thread. So, start a task to reset the device and
342*8226594fSRick McNeal  * wait for completion.
343*8226594fSRick McNeal  */
344*8226594fSRick McNeal void
pqi_lun_reset(pqi_state_t * s,pqi_device_t * d)345*8226594fSRick McNeal pqi_lun_reset(pqi_state_t *s, pqi_device_t *d)
346*8226594fSRick McNeal {
347*8226594fSRick McNeal 	reset_closure_t	r = kmem_alloc(sizeof (struct reset_closure), KM_SLEEP);
348*8226594fSRick McNeal 
349*8226594fSRick McNeal 	r->rc_s = s;
350*8226594fSRick McNeal 	r->rc_d = d;
351*8226594fSRick McNeal 	(void) ddi_taskq_dispatch(s->s_events_taskq, lun_reset_worker, r, 0);
352*8226594fSRick McNeal }
353*8226594fSRick McNeal 
354*8226594fSRick McNeal /*
355*8226594fSRick McNeal  * []------------------------------------------------------------------[]
356*8226594fSRick McNeal  * | Support/utility functions for main entry points			|
357*8226594fSRick McNeal  * []------------------------------------------------------------------[]
358*8226594fSRick McNeal  */
359*8226594fSRick McNeal 
360*8226594fSRick McNeal static uint32_t
count_drive_cmds(pqi_device_t * d)361*8226594fSRick McNeal count_drive_cmds(pqi_device_t *d)
362*8226594fSRick McNeal {
363*8226594fSRick McNeal 	pqi_cmd_t	*c;
364*8226594fSRick McNeal 	uint32_t	count = 0;
365*8226594fSRick McNeal 
366*8226594fSRick McNeal 	mutex_enter(&d->pd_mutex);
367*8226594fSRick McNeal 	c = list_head(&d->pd_cmd_list);
368*8226594fSRick McNeal 	while (c != NULL) {
369*8226594fSRick McNeal 		c = list_next(&d->pd_cmd_list, c);
370*8226594fSRick McNeal 		count++;
371*8226594fSRick McNeal 	}
372*8226594fSRick McNeal 	mutex_exit(&d->pd_mutex);
373*8226594fSRick McNeal 
374*8226594fSRick McNeal 	return (count);
375*8226594fSRick McNeal }
376*8226594fSRick McNeal 
377*8226594fSRick McNeal static uint32_t
count_oustanding_cmds(pqi_state_t * s)378*8226594fSRick McNeal count_oustanding_cmds(pqi_state_t *s)
379*8226594fSRick McNeal {
380*8226594fSRick McNeal 	uint32_t	count = 0;
381*8226594fSRick McNeal 	pqi_device_t	*d;
382*8226594fSRick McNeal 
383*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
384*8226594fSRick McNeal 	d = list_head(&s->s_devnodes);
385*8226594fSRick McNeal 	while (d != NULL) {
386*8226594fSRick McNeal 		count += count_drive_cmds(d);
387*8226594fSRick McNeal 		d = list_next(&s->s_devnodes, d);
388*8226594fSRick McNeal 	}
389*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
390*8226594fSRick McNeal 
391*8226594fSRick McNeal 	return (count);
392*8226594fSRick McNeal }
393*8226594fSRick McNeal 
394*8226594fSRick McNeal static void
lun_reset_worker(void * v)395*8226594fSRick McNeal lun_reset_worker(void *v)
396*8226594fSRick McNeal {
397*8226594fSRick McNeal 	reset_closure_t			r = v;
398*8226594fSRick McNeal 	pqi_state_t			*s;
399*8226594fSRick McNeal 	pqi_device_t			*d;
400*8226594fSRick McNeal 	pqi_io_request_t		*io;
401*8226594fSRick McNeal 	ksema_t				sema;
402*8226594fSRick McNeal 	pqi_task_management_rqst_t	*rqst;
403*8226594fSRick McNeal 	struct pqi_cmd			cmd;
404*8226594fSRick McNeal 
405*8226594fSRick McNeal 	s = r->rc_s;
406*8226594fSRick McNeal 	d = r->rc_d;
407*8226594fSRick McNeal 
408*8226594fSRick McNeal 	pqi_fail_drive_cmds(d, CMD_RESET);
409*8226594fSRick McNeal 	sema_init(&sema, 0, NULL, SEMA_DRIVER, NULL);
410*8226594fSRick McNeal 
411*8226594fSRick McNeal 	bzero(&cmd, sizeof (cmd));
412*8226594fSRick McNeal 	mutex_init(&cmd.pc_mutex, NULL, MUTEX_DRIVER, NULL);
413*8226594fSRick McNeal 
414*8226594fSRick McNeal 	if ((io = pqi_alloc_io(s)) == NULL) {
415*8226594fSRick McNeal 		mutex_destroy(&cmd.pc_mutex);
416*8226594fSRick McNeal 		kmem_free(r, sizeof (*r));
417*8226594fSRick McNeal 		return;
418*8226594fSRick McNeal 	}
419*8226594fSRick McNeal 	io->io_cb = lun_reset_complete;
420*8226594fSRick McNeal 	io->io_context = &sema;
421*8226594fSRick McNeal 	io->io_cmd = &cmd;
422*8226594fSRick McNeal 	cmd.pc_io_rqst = io;
423*8226594fSRick McNeal 	cmd.pc_softc = s;
424*8226594fSRick McNeal 	cmd.pc_device = &s->s_special_device;
425*8226594fSRick McNeal 
426*8226594fSRick McNeal 	(void) pqi_cmd_action(&cmd, PQI_CMD_QUEUE);
427*8226594fSRick McNeal 
428*8226594fSRick McNeal 	rqst = io->io_iu;
429*8226594fSRick McNeal 	(void) memset(rqst, 0, sizeof (*rqst));
430*8226594fSRick McNeal 
431*8226594fSRick McNeal 	rqst->header.iu_type = PQI_REQUEST_IU_TASK_MANAGEMENT;
432*8226594fSRick McNeal 	rqst->header.iu_length = sizeof (*rqst) - PQI_REQUEST_HEADER_LENGTH;
433*8226594fSRick McNeal 	rqst->request_id = PQI_MAKE_REQID(io->io_index, io->io_gen);
434*8226594fSRick McNeal 	(void) memcpy(rqst->lun_number, d->pd_scsi3addr,
435*8226594fSRick McNeal 	    sizeof (rqst->lun_number));
436*8226594fSRick McNeal 	rqst->task_management_function = SOP_TASK_MANAGEMENT_LUN_RESET;
437*8226594fSRick McNeal 
438*8226594fSRick McNeal 	pqi_start_io(s, &s->s_queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH,
439*8226594fSRick McNeal 	    io);
440*8226594fSRick McNeal 
441*8226594fSRick McNeal 	sema_p(&sema);
442*8226594fSRick McNeal 
443*8226594fSRick McNeal 	(void) pqi_cmd_action(&cmd, PQI_CMD_CMPLT);
444*8226594fSRick McNeal 	mutex_destroy(&cmd.pc_mutex);
445*8226594fSRick McNeal 	kmem_free(r, sizeof (*r));
446*8226594fSRick McNeal }
447*8226594fSRick McNeal 
448*8226594fSRick McNeal static void
lun_reset_complete(pqi_io_request_t * io __unused,void * ctx)449*8226594fSRick McNeal lun_reset_complete(pqi_io_request_t *io __unused, void *ctx)
450*8226594fSRick McNeal {
451*8226594fSRick McNeal 	sema_v((ksema_t *)ctx);
452*8226594fSRick McNeal }
453*8226594fSRick McNeal 
454*8226594fSRick McNeal static void
send_event_ack(pqi_state_t * s,pqi_event_acknowledge_request_t * rqst)455*8226594fSRick McNeal send_event_ack(pqi_state_t *s, pqi_event_acknowledge_request_t *rqst)
456*8226594fSRick McNeal {
457*8226594fSRick McNeal 	pqi_queue_group_t	*qg;
458*8226594fSRick McNeal 	caddr_t			next_element;
459*8226594fSRick McNeal 	pqi_index_t		iq_ci;
460*8226594fSRick McNeal 	pqi_index_t		iq_pi;
461*8226594fSRick McNeal 	int			ms_timeo = 1000 * 10;
462*8226594fSRick McNeal 
463*8226594fSRick McNeal 	qg = &s->s_queue_groups[PQI_DEFAULT_QUEUE_GROUP];
464*8226594fSRick McNeal 	rqst->header.iu_id = qg->oq_id;
465*8226594fSRick McNeal 
466*8226594fSRick McNeal 	for (;;) {
467*8226594fSRick McNeal 		mutex_enter(&qg->submit_lock[RAID_PATH]);
468*8226594fSRick McNeal 		iq_pi = qg->iq_pi_copy[RAID_PATH];
469*8226594fSRick McNeal 		iq_ci = ddi_get32(s->s_queue_dma->acc, qg->iq_ci[RAID_PATH]);
470*8226594fSRick McNeal 
471*8226594fSRick McNeal 		if (free_elem_count(iq_pi, iq_ci, s->s_num_elements_per_iq))
472*8226594fSRick McNeal 			break;
473*8226594fSRick McNeal 
474*8226594fSRick McNeal 		mutex_exit(&qg->submit_lock[RAID_PATH]);
475*8226594fSRick McNeal 		if (pqi_is_offline(s))
476*8226594fSRick McNeal 			return;
477*8226594fSRick McNeal 	}
478*8226594fSRick McNeal 	next_element = qg->iq_element_array[RAID_PATH] +
479*8226594fSRick McNeal 	    (iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH);
480*8226594fSRick McNeal 
481*8226594fSRick McNeal 	(void) memcpy(next_element, rqst, sizeof (*rqst));
482*8226594fSRick McNeal 	(void) ddi_dma_sync(s->s_queue_dma->handle, 0, 0, DDI_DMA_SYNC_FORDEV);
483*8226594fSRick McNeal 
484*8226594fSRick McNeal 	iq_pi = (iq_pi + 1) % s->s_num_elements_per_iq;
485*8226594fSRick McNeal 	qg->iq_pi_copy[RAID_PATH] = iq_pi;
486*8226594fSRick McNeal 
487*8226594fSRick McNeal 	ddi_put32(s->s_datap, qg->iq_pi[RAID_PATH], iq_pi);
488*8226594fSRick McNeal 
489*8226594fSRick McNeal 	/*
490*8226594fSRick McNeal 	 * Special case processing for events required. The driver must
491*8226594fSRick McNeal 	 * wait until the acknowledgement is processed before proceeding.
492*8226594fSRick McNeal 	 * Unfortunately, the HBA doesn't provide an interrupt which means
493*8226594fSRick McNeal 	 * the code must busy wait.
494*8226594fSRick McNeal 	 * Code will wait up to 10 seconds.
495*8226594fSRick McNeal 	 */
496*8226594fSRick McNeal 	while (ms_timeo--) {
497*8226594fSRick McNeal 		drv_usecwait(1000);
498*8226594fSRick McNeal 		iq_ci = ddi_get32(s->s_queue_dma->acc, qg->iq_ci[RAID_PATH]);
499*8226594fSRick McNeal 		if (iq_pi == iq_ci)
500*8226594fSRick McNeal 			break;
501*8226594fSRick McNeal 	}
502*8226594fSRick McNeal 
503*8226594fSRick McNeal 	mutex_exit(&qg->submit_lock[RAID_PATH]);
504*8226594fSRick McNeal }
505*8226594fSRick McNeal 
506*8226594fSRick McNeal static void
ack_event(pqi_state_t * s,pqi_event_t * e)507*8226594fSRick McNeal ack_event(pqi_state_t *s, pqi_event_t *e)
508*8226594fSRick McNeal {
509*8226594fSRick McNeal 	pqi_event_acknowledge_request_t	rqst;
510*8226594fSRick McNeal 
511*8226594fSRick McNeal 	(void) memset(&rqst, 0, sizeof (rqst));
512*8226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_ACKNOWLEDGE_VENDOR_EVENT;
513*8226594fSRick McNeal 	rqst.header.iu_length = sizeof (rqst) - PQI_REQUEST_HEADER_LENGTH;
514*8226594fSRick McNeal 	rqst.event_type = e->ev_type;
515*8226594fSRick McNeal 	rqst.event_id = e->ev_id;
516*8226594fSRick McNeal 	rqst.additional_event_id = e->ev_additional;
517*8226594fSRick McNeal 
518*8226594fSRick McNeal 	send_event_ack(s, &rqst);
519*8226594fSRick McNeal }
520*8226594fSRick McNeal 
521*8226594fSRick McNeal static pqi_io_request_t *
setup_aio_request(pqi_state_t * s,pqi_cmd_t * cmd)522*8226594fSRick McNeal setup_aio_request(pqi_state_t *s, pqi_cmd_t *cmd)
523*8226594fSRick McNeal {
524*8226594fSRick McNeal 	pqi_io_request_t	*io;
525*8226594fSRick McNeal 	pqi_aio_path_request_t	*rqst;
526*8226594fSRick McNeal 	pqi_device_t		*devp = cmd->pc_device;
527*8226594fSRick McNeal 
528*8226594fSRick McNeal 	/* ---- Most likely received a signal during a cv_wait ---- */
529*8226594fSRick McNeal 	if ((io = pqi_alloc_io(s)) == NULL)
530*8226594fSRick McNeal 		return (NULL);
531*8226594fSRick McNeal 
532*8226594fSRick McNeal 	io->io_cb = aio_io_complete;
533*8226594fSRick McNeal 	io->io_cmd = cmd;
534*8226594fSRick McNeal 	io->io_raid_bypass = 0;
535*8226594fSRick McNeal 
536*8226594fSRick McNeal 	rqst = io->io_iu;
537*8226594fSRick McNeal 	(void) memset(rqst, 0, sizeof (*rqst));
538*8226594fSRick McNeal 
539*8226594fSRick McNeal 	rqst->header.iu_type = PQI_REQUEST_IU_AIO_PATH_IO;
540*8226594fSRick McNeal 	rqst->nexus_id = devp->pd_aio_handle;
541*8226594fSRick McNeal 	rqst->buffer_length = cmd->pc_dma_count;
542*8226594fSRick McNeal 	rqst->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
543*8226594fSRick McNeal 	rqst->request_id = PQI_MAKE_REQID(io->io_index, io->io_gen);
544*8226594fSRick McNeal 	rqst->error_index = io->io_index;
545*8226594fSRick McNeal 	rqst->cdb_length = cmd->pc_cmdlen;
546*8226594fSRick McNeal 	(void) memcpy(rqst->cdb, cmd->pc_cdb, cmd->pc_cmdlen);
547*8226594fSRick McNeal 	(void) memcpy(rqst->lun_number, devp->pd_scsi3addr,
548*8226594fSRick McNeal 	    sizeof (rqst->lun_number));
549*8226594fSRick McNeal 
550*8226594fSRick McNeal 	if (cmd->pc_flags & PQI_FLAG_DMA_VALID) {
551*8226594fSRick McNeal 		if (cmd->pc_flags & PQI_FLAG_IO_READ)
552*8226594fSRick McNeal 			rqst->data_direction = SOP_READ_FLAG;
553*8226594fSRick McNeal 		else
554*8226594fSRick McNeal 			rqst->data_direction = SOP_WRITE_FLAG;
555*8226594fSRick McNeal 	} else {
556*8226594fSRick McNeal 		rqst->data_direction = SOP_NO_DIRECTION_FLAG;
557*8226594fSRick McNeal 	}
558*8226594fSRick McNeal 
559*8226594fSRick McNeal 	build_aio_sg_list(s, rqst, cmd, io);
560*8226594fSRick McNeal 	return (io);
561*8226594fSRick McNeal }
562*8226594fSRick McNeal 
563*8226594fSRick McNeal static pqi_io_request_t *
setup_raid_request(pqi_state_t * s,pqi_cmd_t * cmd)564*8226594fSRick McNeal setup_raid_request(pqi_state_t *s, pqi_cmd_t *cmd)
565*8226594fSRick McNeal {
566*8226594fSRick McNeal 	pqi_io_request_t	*io;
567*8226594fSRick McNeal 	pqi_raid_path_request_t	*rqst;
568*8226594fSRick McNeal 	pqi_device_t		*devp = cmd->pc_device;
569*8226594fSRick McNeal 
570*8226594fSRick McNeal 	/* ---- Most likely received a signal during a cv_wait ---- */
571*8226594fSRick McNeal 	if ((io = pqi_alloc_io(s)) == NULL)
572*8226594fSRick McNeal 		return (NULL);
573*8226594fSRick McNeal 
574*8226594fSRick McNeal 	io->io_cb = raid_io_complete;
575*8226594fSRick McNeal 	io->io_cmd = cmd;
576*8226594fSRick McNeal 	io->io_raid_bypass = 0;
577*8226594fSRick McNeal 
578*8226594fSRick McNeal 	rqst = io->io_iu;
579*8226594fSRick McNeal 	(void) memset(rqst, 0, sizeof (*rqst));
580*8226594fSRick McNeal 	rqst->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO;
581*8226594fSRick McNeal 	rqst->rp_data_len = cmd->pc_dma_count;
582*8226594fSRick McNeal 	rqst->rp_task_attr = SOP_TASK_ATTRIBUTE_SIMPLE;
583*8226594fSRick McNeal 	rqst->rp_id = PQI_MAKE_REQID(io->io_index, io->io_gen);
584*8226594fSRick McNeal 	rqst->rp_error_index = io->io_index;
585*8226594fSRick McNeal 	(void) memcpy(rqst->rp_lun, devp->pd_scsi3addr, sizeof (rqst->rp_lun));
586*8226594fSRick McNeal 	(void) memcpy(rqst->rp_cdb, cmd->pc_cdb, cmd->pc_cmdlen);
587*8226594fSRick McNeal 
588*8226594fSRick McNeal 	ASSERT(cmd->pc_cmdlen <= 16);
589*8226594fSRick McNeal 	rqst->rp_additional_cdb = SOP_ADDITIONAL_CDB_BYTES_0;
590*8226594fSRick McNeal 
591*8226594fSRick McNeal 	if (cmd->pc_flags & PQI_FLAG_DMA_VALID) {
592*8226594fSRick McNeal 		if (cmd->pc_flags & PQI_FLAG_IO_READ)
593*8226594fSRick McNeal 			rqst->rp_data_dir = SOP_READ_FLAG;
594*8226594fSRick McNeal 		else
595*8226594fSRick McNeal 			rqst->rp_data_dir = SOP_WRITE_FLAG;
596*8226594fSRick McNeal 	} else {
597*8226594fSRick McNeal 		rqst->rp_data_dir = SOP_NO_DIRECTION_FLAG;
598*8226594fSRick McNeal 	}
599*8226594fSRick McNeal 
600*8226594fSRick McNeal 	build_raid_sg_list(s, rqst, cmd, io);
601*8226594fSRick McNeal 	return (io);
602*8226594fSRick McNeal }
603*8226594fSRick McNeal 
604*8226594fSRick McNeal pqi_cmd_t *
pqi_process_comp_ring(pqi_state_t * s __unused)605*8226594fSRick McNeal pqi_process_comp_ring(pqi_state_t *s __unused)
606*8226594fSRick McNeal {
607*8226594fSRick McNeal 	return (NULL);
608*8226594fSRick McNeal }
609*8226594fSRick McNeal 
610*8226594fSRick McNeal static void
raid_io_complete(pqi_io_request_t * io,void * context)611*8226594fSRick McNeal raid_io_complete(pqi_io_request_t *io, void *context)
612*8226594fSRick McNeal {
613*8226594fSRick McNeal 	/*
614*8226594fSRick McNeal 	 * ---- XXX Not sure if this complete function will be the same
615*8226594fSRick McNeal 	 * or different in the end. If it's the same this will be removed
616*8226594fSRick McNeal 	 * and aio_io_complete will have it's named changed to something
617*8226594fSRick McNeal 	 * more generic.
618*8226594fSRick McNeal 	 */
619*8226594fSRick McNeal 	aio_io_complete(io, context);
620*8226594fSRick McNeal }
621*8226594fSRick McNeal 
622*8226594fSRick McNeal /*
623*8226594fSRick McNeal  * special_error_check -- See if sense buffer matches "offline" status.
624*8226594fSRick McNeal  *
625*8226594fSRick McNeal  * spc3r23 section 4.5.6 -- Sense key and sense code definitions.
626*8226594fSRick McNeal  * Sense key == 5 (KEY_ILLEGAL_REQUEST) indicates one of several conditions
627*8226594fSRick McNeal  * a) Command addressed to incorrect logical unit.
628*8226594fSRick McNeal  * b) Command had an invalid task attribute.
629*8226594fSRick McNeal  * ...
630*8226594fSRick McNeal  * Table 28 also shows that ASC 0x26 and ASCQ of 0x00 is an INVALID FIELD
631*8226594fSRick McNeal  * IN PARAMETER LIST.
632*8226594fSRick McNeal  * At no other time does this combination of KEY/ASC/ASCQ occur except when
633*8226594fSRick McNeal  * a device or cable is pulled from the system along with a Hotplug event.
634*8226594fSRick McNeal  * Without documentation it's only a guess, but it's the best that's available.
635*8226594fSRick McNeal  * So, if the conditions are true the command packet pkt_reason will be changed
636*8226594fSRick McNeal  * to CMD_DEV_GONE which causes MPxIO to switch to the other path and the
637*8226594fSRick McNeal  * Hotplug event will cause a scan to occur which removes other inactive
638*8226594fSRick McNeal  * devices in case of a cable pull.
639*8226594fSRick McNeal  */
640*8226594fSRick McNeal boolean_t
special_error_check(pqi_cmd_t * cmd)641*8226594fSRick McNeal special_error_check(pqi_cmd_t *cmd)
642*8226594fSRick McNeal {
643*8226594fSRick McNeal 	struct scsi_arq_status *arq;
644*8226594fSRick McNeal 
645*8226594fSRick McNeal 	/* LINTED E_BAD_PTR_CAST_ALIGN */
646*8226594fSRick McNeal 	arq = (struct scsi_arq_status *)cmd->pc_pkt->pkt_scbp;
647*8226594fSRick McNeal 
648*8226594fSRick McNeal 	if (((*cmd->pc_pkt->pkt_scbp & STATUS_MASK) == STATUS_CHECK) &&
649*8226594fSRick McNeal 	    (arq->sts_sensedata.es_key == KEY_ILLEGAL_REQUEST) &&
650*8226594fSRick McNeal 	    (arq->sts_sensedata.es_add_code == 0x26) &&
651*8226594fSRick McNeal 	    (arq->sts_sensedata.es_qual_code == 0)) {
652*8226594fSRick McNeal 		return (B_TRUE);
653*8226594fSRick McNeal 	} else {
654*8226594fSRick McNeal 		return (B_FALSE);
655*8226594fSRick McNeal 	}
656*8226594fSRick McNeal }
657*8226594fSRick McNeal 
658*8226594fSRick McNeal static void
aio_io_complete(pqi_io_request_t * io,void * context __unused)659*8226594fSRick McNeal aio_io_complete(pqi_io_request_t *io, void *context __unused)
660*8226594fSRick McNeal {
661*8226594fSRick McNeal 	pqi_cmd_t	*cmd = io->io_cmd;
662*8226594fSRick McNeal 	struct scsi_pkt	*pkt = CMD2PKT(cmd);
663*8226594fSRick McNeal 	boolean_t	pkt_ok = B_FALSE;
664*8226594fSRick McNeal 
665*8226594fSRick McNeal 	if (cmd->pc_flags & (PQI_FLAG_IO_READ | PQI_FLAG_IO_IOPB))
666*8226594fSRick McNeal 		(void) ddi_dma_sync(cmd->pc_dmahdl, 0, 0, DDI_DMA_SYNC_FORCPU);
667*8226594fSRick McNeal 
668*8226594fSRick McNeal 	switch (io->io_status) {
669*8226594fSRick McNeal 	case PQI_DATA_IN_OUT_UNDERFLOW:
670*8226594fSRick McNeal 		pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
671*8226594fSRick McNeal 		    STATE_SENT_CMD | STATE_GOT_STATUS;
672*8226594fSRick McNeal 		if (pkt->pkt_resid == cmd->pc_dma_count) {
673*8226594fSRick McNeal 			pkt->pkt_reason = CMD_INCOMPLETE;
674*8226594fSRick McNeal 		} else {
675*8226594fSRick McNeal 			pkt->pkt_state |= STATE_XFERRED_DATA;
676*8226594fSRick McNeal 			pkt->pkt_reason = CMD_CMPLT;
677*8226594fSRick McNeal 		}
678*8226594fSRick McNeal 		break;
679*8226594fSRick McNeal 
680*8226594fSRick McNeal 	case PQI_DATA_IN_OUT_GOOD:
681*8226594fSRick McNeal 		pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
682*8226594fSRick McNeal 		    STATE_SENT_CMD | STATE_GOT_STATUS;
683*8226594fSRick McNeal 		if (cmd->pc_flags & PQI_FLAG_DMA_VALID)
684*8226594fSRick McNeal 			pkt->pkt_state |= STATE_XFERRED_DATA;
685*8226594fSRick McNeal 		pkt->pkt_reason = CMD_CMPLT;
686*8226594fSRick McNeal 		pkt->pkt_resid = 0;
687*8226594fSRick McNeal 		pkt->pkt_statistics = 0;
688*8226594fSRick McNeal 		pkt_ok = B_TRUE;
689*8226594fSRick McNeal 		break;
690*8226594fSRick McNeal 
691*8226594fSRick McNeal 	case PQI_DATA_IN_OUT_ERROR:
692*8226594fSRick McNeal 		pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
693*8226594fSRick McNeal 		    STATE_SENT_CMD;
694*8226594fSRick McNeal 		if (pkt->pkt_resid != cmd->pc_dma_count) {
695*8226594fSRick McNeal 			pkt->pkt_state |= STATE_XFERRED_DATA;
696*8226594fSRick McNeal 			pkt->pkt_reason = CMD_CMPLT;
697*8226594fSRick McNeal 		} else {
698*8226594fSRick McNeal 			pkt->pkt_reason = CMD_CMPLT;
699*8226594fSRick McNeal 		}
700*8226594fSRick McNeal 		break;
701*8226594fSRick McNeal 
702*8226594fSRick McNeal 	case PQI_DATA_IN_OUT_PROTOCOL_ERROR:
703*8226594fSRick McNeal 		pkt->pkt_reason = CMD_TERMINATED;
704*8226594fSRick McNeal 		pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET;
705*8226594fSRick McNeal 		break;
706*8226594fSRick McNeal 
707*8226594fSRick McNeal 	case PQI_DATA_IN_OUT_HARDWARE_ERROR:
708*8226594fSRick McNeal 		pkt->pkt_reason = CMD_CMPLT;
709*8226594fSRick McNeal 		pkt->pkt_state |= STATE_GOT_BUS;
710*8226594fSRick McNeal 		break;
711*8226594fSRick McNeal 
712*8226594fSRick McNeal 	default:
713*8226594fSRick McNeal 		pkt->pkt_reason = CMD_INCOMPLETE;
714*8226594fSRick McNeal 		break;
715*8226594fSRick McNeal 	}
716*8226594fSRick McNeal 
717*8226594fSRick McNeal 	if (pkt_ok == B_FALSE)
718*8226594fSRick McNeal 		atomic_inc_32(&cmd->pc_device->pd_sense_errors);
719*8226594fSRick McNeal 
720*8226594fSRick McNeal 	if (special_error_check(cmd) == B_TRUE) {
721*8226594fSRick McNeal 		pkt->pkt_reason = CMD_DEV_GONE;
722*8226594fSRick McNeal 		pkt->pkt_statistics = STAT_TERMINATED;
723*8226594fSRick McNeal 	}
724*8226594fSRick McNeal 	(void) pqi_cmd_action(cmd, PQI_CMD_CMPLT);
725*8226594fSRick McNeal }
726*8226594fSRick McNeal 
727*8226594fSRick McNeal static void
fail_outstanding_cmds(pqi_state_t * s)728*8226594fSRick McNeal fail_outstanding_cmds(pqi_state_t *s)
729*8226594fSRick McNeal {
730*8226594fSRick McNeal 	pqi_device_t		*devp;
731*8226594fSRick McNeal 
732*8226594fSRick McNeal 	ASSERT(MUTEX_HELD(&s->s_mutex));
733*8226594fSRick McNeal 
734*8226594fSRick McNeal 	pqi_fail_drive_cmds(&s->s_special_device, CMD_TRAN_ERR);
735*8226594fSRick McNeal 	for (devp = list_head(&s->s_devnodes); devp != NULL;
736*8226594fSRick McNeal 	    devp = list_next(&s->s_devnodes, devp)) {
737*8226594fSRick McNeal 		pqi_fail_drive_cmds(devp, CMD_TRAN_ERR);
738*8226594fSRick McNeal 	}
739*8226594fSRick McNeal }
740*8226594fSRick McNeal 
741*8226594fSRick McNeal static void
set_sg_descriptor(pqi_sg_entry_t * sg,ddi_dma_cookie_t * cookie)742*8226594fSRick McNeal set_sg_descriptor(pqi_sg_entry_t *sg, ddi_dma_cookie_t *cookie)
743*8226594fSRick McNeal {
744*8226594fSRick McNeal 	sg->sg_addr = cookie->dmac_laddress;
745*8226594fSRick McNeal 	sg->sg_len = cookie->dmac_size;
746*8226594fSRick McNeal 	sg->sg_flags = 0;
747*8226594fSRick McNeal }
748*8226594fSRick McNeal 
749*8226594fSRick McNeal static void
build_aio_sg_list(pqi_state_t * s,pqi_aio_path_request_t * rqst,pqi_cmd_t * cmd,pqi_io_request_t * io)750*8226594fSRick McNeal build_aio_sg_list(pqi_state_t *s, pqi_aio_path_request_t *rqst,
751*8226594fSRick McNeal     pqi_cmd_t *cmd, pqi_io_request_t *io)
752*8226594fSRick McNeal {
753*8226594fSRick McNeal 	int			i;
754*8226594fSRick McNeal 	int			max_sg_per_iu;
755*8226594fSRick McNeal 	uint16_t		iu_length;
756*8226594fSRick McNeal 	uint8_t			chained;
757*8226594fSRick McNeal 	uint8_t			num_sg_in_iu	= 0;
758*8226594fSRick McNeal 	ddi_dma_cookie_t	*cookies;
759*8226594fSRick McNeal 	pqi_sg_entry_t		*sg;
760*8226594fSRick McNeal 
761*8226594fSRick McNeal 	iu_length = offsetof(struct pqi_aio_path_request, ap_sglist) -
762*8226594fSRick McNeal 	    PQI_REQUEST_HEADER_LENGTH;
763*8226594fSRick McNeal 
764*8226594fSRick McNeal 	if (cmd->pc_dmaccount == 0)
765*8226594fSRick McNeal 		goto out;
766*8226594fSRick McNeal 	sg = rqst->ap_sglist;
767*8226594fSRick McNeal 	cookies = cmd->pc_cached_cookies;
768*8226594fSRick McNeal 	max_sg_per_iu = s->s_max_sg_per_iu - 1;
769*8226594fSRick McNeal 	i = 0;
770*8226594fSRick McNeal 	chained = 0;
771*8226594fSRick McNeal 
772*8226594fSRick McNeal 	for (;;) {
773*8226594fSRick McNeal 		set_sg_descriptor(sg, cookies);
774*8226594fSRick McNeal 		if (!chained)
775*8226594fSRick McNeal 			num_sg_in_iu++;
776*8226594fSRick McNeal 		i++;
777*8226594fSRick McNeal 		if (i == cmd->pc_dmaccount)
778*8226594fSRick McNeal 			break;
779*8226594fSRick McNeal 		sg++;
780*8226594fSRick McNeal 		cookies++;
781*8226594fSRick McNeal 		if (i == max_sg_per_iu) {
782*8226594fSRick McNeal 			sg->sg_addr = io->io_sg_chain_dma->dma_addr;
783*8226594fSRick McNeal 			sg->sg_len = (cmd->pc_dmaccount - num_sg_in_iu) *
784*8226594fSRick McNeal 			    sizeof (*sg);
785*8226594fSRick McNeal 			sg->sg_flags = CISS_SG_CHAIN;
786*8226594fSRick McNeal 			chained = 1;
787*8226594fSRick McNeal 			num_sg_in_iu++;
788*8226594fSRick McNeal 			sg = (pqi_sg_entry_t *)
789*8226594fSRick McNeal 			    io->io_sg_chain_dma->alloc_memory;
790*8226594fSRick McNeal 		}
791*8226594fSRick McNeal 	}
792*8226594fSRick McNeal 	sg->sg_flags = CISS_SG_LAST;
793*8226594fSRick McNeal 	rqst->partial = chained;
794*8226594fSRick McNeal 	if (chained) {
795*8226594fSRick McNeal 		(void) ddi_dma_sync(io->io_sg_chain_dma->handle, 0, 0,
796*8226594fSRick McNeal 		    DDI_DMA_SYNC_FORDEV);
797*8226594fSRick McNeal 	}
798*8226594fSRick McNeal 	iu_length += num_sg_in_iu * sizeof (*sg);
799*8226594fSRick McNeal 
800*8226594fSRick McNeal out:
801*8226594fSRick McNeal 	rqst->header.iu_length = iu_length;
802*8226594fSRick McNeal 	rqst->num_sg_descriptors = num_sg_in_iu;
803*8226594fSRick McNeal }
804*8226594fSRick McNeal 
805*8226594fSRick McNeal static void
build_raid_sg_list(pqi_state_t * s,pqi_raid_path_request_t * rqst,pqi_cmd_t * cmd,pqi_io_request_t * io)806*8226594fSRick McNeal build_raid_sg_list(pqi_state_t *s, pqi_raid_path_request_t *rqst,
807*8226594fSRick McNeal     pqi_cmd_t *cmd, pqi_io_request_t *io)
808*8226594fSRick McNeal {
809*8226594fSRick McNeal 	int			i		= 0;
810*8226594fSRick McNeal 	int			max_sg_per_iu;
811*8226594fSRick McNeal 	int			num_sg_in_iu	= 0;
812*8226594fSRick McNeal 	uint16_t		iu_length;
813*8226594fSRick McNeal 	uint8_t			chained		= 0;
814*8226594fSRick McNeal 	ddi_dma_cookie_t	*cookies;
815*8226594fSRick McNeal 	pqi_sg_entry_t		*sg;
816*8226594fSRick McNeal 
817*8226594fSRick McNeal 	iu_length = offsetof(struct pqi_raid_path_request, rp_sglist) -
818*8226594fSRick McNeal 	    PQI_REQUEST_HEADER_LENGTH;
819*8226594fSRick McNeal 
820*8226594fSRick McNeal 	if (cmd->pc_dmaccount == 0)
821*8226594fSRick McNeal 		goto out;
822*8226594fSRick McNeal 
823*8226594fSRick McNeal 	sg = rqst->rp_sglist;
824*8226594fSRick McNeal 	cookies = cmd->pc_cached_cookies;
825*8226594fSRick McNeal 	max_sg_per_iu = s->s_max_sg_per_iu - 1;
826*8226594fSRick McNeal 
827*8226594fSRick McNeal 	for (;;) {
828*8226594fSRick McNeal 		set_sg_descriptor(sg, cookies);
829*8226594fSRick McNeal 		if (!chained)
830*8226594fSRick McNeal 			num_sg_in_iu++;
831*8226594fSRick McNeal 		i++;
832*8226594fSRick McNeal 		if (i == cmd->pc_dmaccount)
833*8226594fSRick McNeal 			break;
834*8226594fSRick McNeal 		sg++;
835*8226594fSRick McNeal 		cookies++;
836*8226594fSRick McNeal 		if (i == max_sg_per_iu) {
837*8226594fSRick McNeal 			ASSERT(io->io_sg_chain_dma != NULL);
838*8226594fSRick McNeal 			sg->sg_addr = io->io_sg_chain_dma->dma_addr;
839*8226594fSRick McNeal 			sg->sg_len = (cmd->pc_dmaccount - num_sg_in_iu) *
840*8226594fSRick McNeal 			    sizeof (*sg);
841*8226594fSRick McNeal 			sg->sg_flags = CISS_SG_CHAIN;
842*8226594fSRick McNeal 			chained = 1;
843*8226594fSRick McNeal 			num_sg_in_iu++;
844*8226594fSRick McNeal 			sg = (pqi_sg_entry_t *)
845*8226594fSRick McNeal 			    io->io_sg_chain_dma->alloc_memory;
846*8226594fSRick McNeal 		}
847*8226594fSRick McNeal 	}
848*8226594fSRick McNeal 	sg->sg_flags = CISS_SG_LAST;
849*8226594fSRick McNeal 	rqst->rp_partial = chained;
850*8226594fSRick McNeal 	if (chained) {
851*8226594fSRick McNeal 		(void) ddi_dma_sync(io->io_sg_chain_dma->handle, 0, 0,
852*8226594fSRick McNeal 		    DDI_DMA_SYNC_FORDEV);
853*8226594fSRick McNeal 	}
854*8226594fSRick McNeal 	iu_length += num_sg_in_iu * sizeof (*sg);
855*8226594fSRick McNeal 
856*8226594fSRick McNeal out:
857*8226594fSRick McNeal 	rqst->header.iu_length = iu_length;
858*8226594fSRick McNeal }
859*8226594fSRick McNeal 
860*8226594fSRick McNeal static uint32_t
read_heartbeat_counter(pqi_state_t * s)861*8226594fSRick McNeal read_heartbeat_counter(pqi_state_t *s)
862*8226594fSRick McNeal {
863*8226594fSRick McNeal 	return (ddi_get32(s->s_datap, s->s_heartbeat_counter));
864*8226594fSRick McNeal }
865*8226594fSRick McNeal 
866*8226594fSRick McNeal static void
take_ctlr_offline(pqi_state_t * s)867*8226594fSRick McNeal take_ctlr_offline(pqi_state_t *s)
868*8226594fSRick McNeal {
869*8226594fSRick McNeal 	int			num_passes = 5;
870*8226594fSRick McNeal 	int			i;
871*8226594fSRick McNeal 	pqi_device_t		*d;
872*8226594fSRick McNeal 	pqi_cmd_t		*c, *nc;
873*8226594fSRick McNeal 	pqi_io_request_t	*io;
874*8226594fSRick McNeal 	uint32_t		active_count;
875*8226594fSRick McNeal 
876*8226594fSRick McNeal 	/*
877*8226594fSRick McNeal 	 * 1) Why always panic here?
878*8226594fSRick McNeal 	 * Firmware resets don't work on the Microsemi HBA when the firmware
879*8226594fSRick McNeal 	 * is hung. The code as written fails outstanding commands and tries
880*8226594fSRick McNeal 	 * to reset the HBA. Since the reset don't work the HBA is left in an
881*8226594fSRick McNeal 	 * offline state and further commands sent (retries and new commands)
882*8226594fSRick McNeal 	 * are also failed. Eventually ZFS will panic with a deadman timer,
883*8226594fSRick McNeal 	 * but before that COMSTAR will see I/O requests error out and send
884*8226594fSRick McNeal 	 * I/O errors back to the client which causes corruption since these
885*8226594fSRick McNeal 	 * errors are no different than a device that starts to fail. So,
886*8226594fSRick McNeal 	 * instead of trying to play nice the driver now panics which will
887*8226594fSRick McNeal 	 * allow HA to fail fast to the other node.
888*8226594fSRick McNeal 	 *
889*8226594fSRick McNeal 	 * 2) Why not just remove this routine can call panic from the heartbeat
890*8226594fSRick McNeal 	 * routine?
891*8226594fSRick McNeal 	 * I'm hoping this is a temporary work around. We have been asking
892*8226594fSRick McNeal 	 * for more documentation on the product and we've been told there isn't
893*8226594fSRick McNeal 	 * any available.  It has been implied that some HBA's do support
894*8226594fSRick McNeal 	 * firmware resets. Therefore documentation would enable the driver
895*8226594fSRick McNeal 	 * to determine model number and adjust parameters such as panic on
896*8226594fSRick McNeal 	 * firmware hang or try a reset.
897*8226594fSRick McNeal 	 */
898*8226594fSRick McNeal 	if (1)
899*8226594fSRick McNeal 		panic("Firmware hung");
900*8226594fSRick McNeal 
901*8226594fSRick McNeal 	d = &s->s_special_device;
902*8226594fSRick McNeal 	mutex_enter(&d->pd_mutex);
903*8226594fSRick McNeal 	while ((c = list_remove_head(&d->pd_cmd_list)) != NULL) {
904*8226594fSRick McNeal 		io = c->pc_io_rqst;
905*8226594fSRick McNeal 		io->io_status = PQI_DATA_IN_OUT_ERROR;
906*8226594fSRick McNeal 
907*8226594fSRick McNeal 		mutex_exit(&d->pd_mutex);
908*8226594fSRick McNeal 		(io->io_cb)(io, io->io_context);
909*8226594fSRick McNeal 		mutex_enter(&d->pd_mutex);
910*8226594fSRick McNeal 	}
911*8226594fSRick McNeal 	mutex_exit(&d->pd_mutex);
912*8226594fSRick McNeal 
913*8226594fSRick McNeal 	/*
914*8226594fSRick McNeal 	 * If pqi_reset_ctl() completes successfully the queues will be marked
915*8226594fSRick McNeal 	 * B_TRUE and the controller will be marked online again.
916*8226594fSRick McNeal 	 */
917*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
918*8226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++)
919*8226594fSRick McNeal 		s->s_queue_groups[i].qg_active = B_FALSE;
920*8226594fSRick McNeal 	s->s_offline = B_TRUE;
921*8226594fSRick McNeal 	fail_outstanding_cmds(s);
922*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
923*8226594fSRick McNeal 
924*8226594fSRick McNeal 	/*
925*8226594fSRick McNeal 	 * Commands have been canceled that can be. It's possible there are
926*8226594fSRick McNeal 	 * commands currently running that are about to complete. Give them
927*8226594fSRick McNeal 	 * up to 5 seconds to finish. If those haven't completed by then they
928*8226594fSRick McNeal 	 * are most likely hung in the firmware of the HBA so go ahead and
929*8226594fSRick McNeal 	 * reset the firmware.
930*8226594fSRick McNeal 	 */
931*8226594fSRick McNeal 	while (num_passes-- > 0) {
932*8226594fSRick McNeal 		active_count = count_oustanding_cmds(s);
933*8226594fSRick McNeal 		if (active_count == 0)
934*8226594fSRick McNeal 			break;
935*8226594fSRick McNeal 		drv_usecwait(MICROSEC);
936*8226594fSRick McNeal 	}
937*8226594fSRick McNeal 
938*8226594fSRick McNeal 	/*
939*8226594fSRick McNeal 	 * Any commands remaining are hung in the controller firmware so
940*8226594fSRick McNeal 	 * go ahead time them out so that the upper layers know what's
941*8226594fSRick McNeal 	 * happening.
942*8226594fSRick McNeal 	 */
943*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
944*8226594fSRick McNeal 	for (d = list_head(&s->s_devnodes); d != NULL;
945*8226594fSRick McNeal 	    d = list_next(&s->s_devnodes, d)) {
946*8226594fSRick McNeal 		mutex_enter(&d->pd_mutex);
947*8226594fSRick McNeal 		while ((c = list_head(&d->pd_cmd_list)) != NULL) {
948*8226594fSRick McNeal 			struct scsi_pkt *pkt = CMD2PKT(c);
949*8226594fSRick McNeal 
950*8226594fSRick McNeal 			nc = list_next(&d->pd_cmd_list, c);
951*8226594fSRick McNeal 			ASSERT(pkt);
952*8226594fSRick McNeal 			if (pkt != NULL) {
953*8226594fSRick McNeal 				pkt->pkt_reason = CMD_TIMEOUT;
954*8226594fSRick McNeal 				pkt->pkt_statistics = STAT_TIMEOUT;
955*8226594fSRick McNeal 			}
956*8226594fSRick McNeal 			(void) pqi_cmd_action_nolock(c, PQI_CMD_TIMEOUT);
957*8226594fSRick McNeal 			c = nc;
958*8226594fSRick McNeal 		}
959*8226594fSRick McNeal 		mutex_exit(&d->pd_mutex);
960*8226594fSRick McNeal 	}
961*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
962*8226594fSRick McNeal 
963*8226594fSRick McNeal 	cmn_err(CE_WARN, "Firmware Status: 0x%x", G32(s, sis_firmware_status));
964*8226594fSRick McNeal 
965*8226594fSRick McNeal 	if (pqi_reset_ctl(s) == B_FALSE) {
966*8226594fSRick McNeal 		cmn_err(CE_WARN, "Failed to reset controller");
967*8226594fSRick McNeal 		return;
968*8226594fSRick McNeal 	}
969*8226594fSRick McNeal 
970*8226594fSRick McNeal 	/*
971*8226594fSRick McNeal 	 * This will have the effect of releasing the device's dip
972*8226594fSRick McNeal 	 * structure from the NDI layer do to s_offline == B_TRUE.
973*8226594fSRick McNeal 	 */
974*8226594fSRick McNeal 	ndi_devi_enter(scsi_vhci_dip);
975*8226594fSRick McNeal 	ndi_devi_enter(s->s_dip);
976*8226594fSRick McNeal 	(void) pqi_config_all(s->s_dip, s);
977*8226594fSRick McNeal 	ndi_devi_exit(s->s_dip);
978*8226594fSRick McNeal 	ndi_devi_exit(scsi_vhci_dip);
979*8226594fSRick McNeal }
980*8226594fSRick McNeal 
981*8226594fSRick McNeal static uint32_t
free_elem_count(pqi_index_t pi,pqi_index_t ci,uint32_t per_iq)982*8226594fSRick McNeal free_elem_count(pqi_index_t pi, pqi_index_t ci, uint32_t per_iq)
983*8226594fSRick McNeal {
984*8226594fSRick McNeal 	pqi_index_t	used;
985*8226594fSRick McNeal 	if (pi >= ci) {
986*8226594fSRick McNeal 		used = pi - ci;
987*8226594fSRick McNeal 	} else {
988*8226594fSRick McNeal 		used = per_iq - ci + pi;
989*8226594fSRick McNeal 	}
990*8226594fSRick McNeal 	return (per_iq - used - 1);
991*8226594fSRick McNeal }
992*8226594fSRick McNeal 
993*8226594fSRick McNeal static boolean_t
is_aio_enabled(pqi_device_t * d)994*8226594fSRick McNeal is_aio_enabled(pqi_device_t *d)
995*8226594fSRick McNeal {
996*8226594fSRick McNeal 	return (d->pd_aio_enabled ? B_TRUE : B_FALSE);
997*8226594fSRick McNeal }
998