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