1*b8aa3defSJoshua M. Clulow /*
2*b8aa3defSJoshua M. Clulow * This file and its contents are supplied under the terms of the
3*b8aa3defSJoshua M. Clulow * Common Development and Distribution License ("CDDL"), version 1.0.
4*b8aa3defSJoshua M. Clulow * You may only use this file in accordance with the terms of version
5*b8aa3defSJoshua M. Clulow * 1.0 of the CDDL.
6*b8aa3defSJoshua M. Clulow *
7*b8aa3defSJoshua M. Clulow * A full copy of the text of the CDDL should have accompanied this
8*b8aa3defSJoshua M. Clulow * source. A copy of the CDDL is also available via the Internet at
9*b8aa3defSJoshua M. Clulow * http://www.illumos.org/license/CDDL.
10*b8aa3defSJoshua M. Clulow */
11*b8aa3defSJoshua M. Clulow
12*b8aa3defSJoshua M. Clulow /*
13*b8aa3defSJoshua M. Clulow * Copyright (c) 2017, Joyent, Inc.
14*b8aa3defSJoshua M. Clulow */
15*b8aa3defSJoshua M. Clulow
16*b8aa3defSJoshua M. Clulow #include <sys/scsi/adapters/smrt/smrt.h>
17*b8aa3defSJoshua M. Clulow
18*b8aa3defSJoshua M. Clulow /*
19*b8aa3defSJoshua M. Clulow * Discovery, Resets, Periodics, and Events
20*b8aa3defSJoshua M. Clulow * ----------------------------------------
21*b8aa3defSJoshua M. Clulow *
22*b8aa3defSJoshua M. Clulow * Discovery is the act of figuring out what logical and physical volumes exist
23*b8aa3defSJoshua M. Clulow * under the controller. Discovery happens in response to the following events:
24*b8aa3defSJoshua M. Clulow *
25*b8aa3defSJoshua M. Clulow * o iports for virtual and physical devices being attached
26*b8aa3defSJoshua M. Clulow * o Controller event notifications indicating potential topology changes
27*b8aa3defSJoshua M. Clulow * o After a reset of the controller, before we can perform I/O again
28*b8aa3defSJoshua M. Clulow *
29*b8aa3defSJoshua M. Clulow * Because we have to perform discovery after a reset, which can happen during
30*b8aa3defSJoshua M. Clulow * panic(), that also means that discovery may be run in panic context. We
31*b8aa3defSJoshua M. Clulow * also need to emphasize the need for discovery to happen after a controller
32*b8aa3defSJoshua M. Clulow * reset. Once a reset is initiated, we cannot be certain about the addresses
33*b8aa3defSJoshua M. Clulow * of any of the existing targets until the reset has completed. The driver
34*b8aa3defSJoshua M. Clulow * performs I/Os to addresses that the controller provides. The controller
35*b8aa3defSJoshua M. Clulow * specification says that these addresses may change after a controller reset.
36*b8aa3defSJoshua M. Clulow *
37*b8aa3defSJoshua M. Clulow * Unfortunately, all of this combined means that making sure we can correctly
38*b8aa3defSJoshua M. Clulow * run discovery is somewhat complicated. In non-panic contexts, discovery is
39*b8aa3defSJoshua M. Clulow * always run from a taskq. We'll kick off the discovery in the taskq if
40*b8aa3defSJoshua M. Clulow * nothing is pending at that time. The state is managed by bits in the
41*b8aa3defSJoshua M. Clulow * smrt_status member of the smrt_t. There are four bits at this time:
42*b8aa3defSJoshua M. Clulow *
43*b8aa3defSJoshua M. Clulow * SMRT_CTLR_DISCOVERY_REQUESTED This flag indicates that something has
44*b8aa3defSJoshua M. Clulow * requested that a discovery be performed.
45*b8aa3defSJoshua M. Clulow * If no flags are set when this is set,
46*b8aa3defSJoshua M. Clulow * then we will kick off discovery. All
47*b8aa3defSJoshua M. Clulow * discovery requests are initiated via the
48*b8aa3defSJoshua M. Clulow * smrt_discover_request() function.
49*b8aa3defSJoshua M. Clulow *
50*b8aa3defSJoshua M. Clulow * SMRT_CTLR_DISCOVERY_RUNNING This flag is set at the start of us
51*b8aa3defSJoshua M. Clulow * running a discovery. It is removed when
52*b8aa3defSJoshua M. Clulow * discovery finishes.
53*b8aa3defSJoshua M. Clulow *
54*b8aa3defSJoshua M. Clulow * SMRT_CTLR_DISCOVERY_PERIODIC This flag is set in a number of
55*b8aa3defSJoshua M. Clulow * circumstances, which will be described
56*b8aa3defSJoshua M. Clulow * in a subsequent section. This indicates
57*b8aa3defSJoshua M. Clulow * that the periodic must kick off the
58*b8aa3defSJoshua M. Clulow * discovery process.
59*b8aa3defSJoshua M. Clulow *
60*b8aa3defSJoshua M. Clulow * SMRT_CTLR_DISCOVERY_REQUIRED This flag indicates that at some point a
61*b8aa3defSJoshua M. Clulow * controller reset occurred and we need to
62*b8aa3defSJoshua M. Clulow * have a successful discovery to finish
63*b8aa3defSJoshua M. Clulow * the act of resetting and allowing I/O to
64*b8aa3defSJoshua M. Clulow * continue.
65*b8aa3defSJoshua M. Clulow *
66*b8aa3defSJoshua M. Clulow * In general, a request to discover kicks off the taskq to discover entries, if
67*b8aa3defSJoshua M. Clulow * it hasn't already been requested or started. This also allows us to coalesce
68*b8aa3defSJoshua M. Clulow * multiple requests, if needed. Note that if a request comes in when a
69*b8aa3defSJoshua M. Clulow * discovery is ongoing, we do not kick off discovery again. Instead, we set
70*b8aa3defSJoshua M. Clulow * the SMRT_CTLR_DISCOVERY_REQUESTED flag which will rerun discovery after the
71*b8aa3defSJoshua M. Clulow * initial pass has completed.
72*b8aa3defSJoshua M. Clulow *
73*b8aa3defSJoshua M. Clulow * When a discovery starts, the first thing it does is clear the
74*b8aa3defSJoshua M. Clulow * SMRT_CTLR_DISCOVERY_REQUESTED flag. This is important, because any
75*b8aa3defSJoshua M. Clulow * additional requests for discovery that come in after this has started likely
76*b8aa3defSJoshua M. Clulow * indicate that we've missed something. As such, when the discovery process
77*b8aa3defSJoshua M. Clulow * finishes, if it sees the REQUESTED flag, then it will need to set the
78*b8aa3defSJoshua M. Clulow * PERIODIC flag. The PERIODIC flag is used to indicate that we should run
79*b8aa3defSJoshua M. Clulow * discovery again, but not kick if off immediately. Instead, it should be
80*b8aa3defSJoshua M. Clulow * driven by the normal periodic behavior.
81*b8aa3defSJoshua M. Clulow *
82*b8aa3defSJoshua M. Clulow * If for some reason the act of discovery fails, or we fail to dispatch
83*b8aa3defSJoshua M. Clulow * discovery due to a transient error, then we will flag PERIODIC so that the
84*b8aa3defSJoshua M. Clulow * periodic tick will try and run things again.
85*b8aa3defSJoshua M. Clulow *
86*b8aa3defSJoshua M. Clulow * Now, we need to talk about SMRT_CTLR_DISCOVERY_REQUIRED. This flag is set
87*b8aa3defSJoshua M. Clulow * after a reset occurs. The reset thread will be blocked on this.
88*b8aa3defSJoshua M. Clulow * Importantly, none of the code in the discovery path can ask for a controller
89*b8aa3defSJoshua M. Clulow * reset at this time. If at the end of a discovery, this flag is set, then we
90*b8aa3defSJoshua M. Clulow * will signal the reset thread that it should check on its status by
91*b8aa3defSJoshua M. Clulow * broadcasting on the smrt_cv_finishq. At that point, the reset thread will
92*b8aa3defSJoshua M. Clulow * continue.
93*b8aa3defSJoshua M. Clulow *
94*b8aa3defSJoshua M. Clulow * Panic Context
95*b8aa3defSJoshua M. Clulow * -------------
96*b8aa3defSJoshua M. Clulow *
97*b8aa3defSJoshua M. Clulow * All of this talk of threads and taskqs is well and good, but as an HBA
98*b8aa3defSJoshua M. Clulow * driver, we have a serious responsibility to try and deal with panic sanely.
99*b8aa3defSJoshua M. Clulow * In panic context, we will directly call the discovery functions and not poll
100*b8aa3defSJoshua M. Clulow * for them to occur.
101*b8aa3defSJoshua M. Clulow *
102*b8aa3defSJoshua M. Clulow * However, because our discovery relies on the target maps, which aren't safe
103*b8aa3defSJoshua M. Clulow * for panic context at this time, we have to take a different approach. We
104*b8aa3defSJoshua M. Clulow * leverage the fact that we have a generation number stored with every
105*b8aa3defSJoshua M. Clulow * discovery. If we try to do an I/O to a device where the generation doesn't
106*b8aa3defSJoshua M. Clulow * match, then we know that it disappeared and should not be used. We also
107*b8aa3defSJoshua M. Clulow * sanity check the model, serial numbers, and WWNs to make sure that these are
108*b8aa3defSJoshua M. Clulow * the same devices. If they are, then we'll end up updating the address
109*b8aa3defSJoshua M. Clulow * structures.
110*b8aa3defSJoshua M. Clulow *
111*b8aa3defSJoshua M. Clulow * Now, it is possible that when we were panicking, we had a thread that was in
112*b8aa3defSJoshua M. Clulow * the process of running a discovery or even resetting the system. Once we're
113*b8aa3defSJoshua M. Clulow * in panic, those threads aren't running, so if they didn't end up producing a
114*b8aa3defSJoshua M. Clulow * new view of the world that the SCSI framework is using, then it shouldn't
115*b8aa3defSJoshua M. Clulow * really matter, as we won't have updated the list of devices. Importantly,
116*b8aa3defSJoshua M. Clulow * once we're in that context, we're not going to be attaching or detaching
117*b8aa3defSJoshua M. Clulow * targets. If we get a request for one of these targets which has disappeared,
118*b8aa3defSJoshua M. Clulow * we're going to have to end up giving up.
119*b8aa3defSJoshua M. Clulow *
120*b8aa3defSJoshua M. Clulow * Request Attributes
121*b8aa3defSJoshua M. Clulow * ------------------
122*b8aa3defSJoshua M. Clulow *
123*b8aa3defSJoshua M. Clulow * The CISS specification allows for three different kinds of attributes that
124*b8aa3defSJoshua M. Clulow * describe how requests are queued to the controller. These are:
125*b8aa3defSJoshua M. Clulow *
126*b8aa3defSJoshua M. Clulow * HEAD OF QUEUE The request should go to the head of the
127*b8aa3defSJoshua M. Clulow * controller queue. This is used for resets and
128*b8aa3defSJoshua M. Clulow * aborts to ensure that they're not blocked behind
129*b8aa3defSJoshua M. Clulow * additional I/O.
130*b8aa3defSJoshua M. Clulow *
131*b8aa3defSJoshua M. Clulow * SIMPLE This queues the request for normal processing.
132*b8aa3defSJoshua M. Clulow * Commands queued this way are not special with
133*b8aa3defSJoshua M. Clulow * respect to one another. We use this for all I/O
134*b8aa3defSJoshua M. Clulow * and discovery commands.
135*b8aa3defSJoshua M. Clulow *
136*b8aa3defSJoshua M. Clulow * ORDERED This attribute is used to indicate that commands
137*b8aa3defSJoshua M. Clulow * should be submitted and processed in some order.
138*b8aa3defSJoshua M. Clulow * This is used primarily for the event
139*b8aa3defSJoshua M. Clulow * notification bits so we can ensure that at the
140*b8aa3defSJoshua M. Clulow * return of a cancellation of the event
141*b8aa3defSJoshua M. Clulow * notification, that any outstanding request has
142*b8aa3defSJoshua M. Clulow * been honored.
143*b8aa3defSJoshua M. Clulow */
144*b8aa3defSJoshua M. Clulow
145*b8aa3defSJoshua M. Clulow static int smrt_ctlr_versions(smrt_t *, uint16_t, smrt_versions_t *);
146*b8aa3defSJoshua M. Clulow static void smrt_discover(void *);
147*b8aa3defSJoshua M. Clulow
148*b8aa3defSJoshua M. Clulow /*
149*b8aa3defSJoshua M. Clulow * The maximum number of seconds to wait for the controller to come online.
150*b8aa3defSJoshua M. Clulow */
151*b8aa3defSJoshua M. Clulow unsigned smrt_ciss_init_time = 90;
152*b8aa3defSJoshua M. Clulow
153*b8aa3defSJoshua M. Clulow /*
154*b8aa3defSJoshua M. Clulow * A tunable that determines the number of events per tick that we'll process
155*b8aa3defSJoshua M. Clulow * via asynchronous event notification. If this rate is very high, then we will
156*b8aa3defSJoshua M. Clulow * not submit the event and it will be picked up at the next tick of the
157*b8aa3defSJoshua M. Clulow * periodic.
158*b8aa3defSJoshua M. Clulow */
159*b8aa3defSJoshua M. Clulow uint_t smrt_event_intervention_threshold = 1000;
160*b8aa3defSJoshua M. Clulow
161*b8aa3defSJoshua M. Clulow /*
162*b8aa3defSJoshua M. Clulow * Converts a LUN Address to a BMIC Identifier. The BMIC Identifier is used
163*b8aa3defSJoshua M. Clulow * when performing various physical commands and generally should stay the same
164*b8aa3defSJoshua M. Clulow * for a given device across inserts and removals; however, not across
165*b8aa3defSJoshua M. Clulow * controller resets. These are calculated based on what the CISS specification
166*b8aa3defSJoshua M. Clulow * calls the 'Level 2' target and bus, which don't have a real meaning in the
167*b8aa3defSJoshua M. Clulow * SAS world otherwise.
168*b8aa3defSJoshua M. Clulow */
169*b8aa3defSJoshua M. Clulow uint16_t
smrt_lun_addr_to_bmic(PhysDevAddr_t * paddr)170*b8aa3defSJoshua M. Clulow smrt_lun_addr_to_bmic(PhysDevAddr_t *paddr)
171*b8aa3defSJoshua M. Clulow {
172*b8aa3defSJoshua M. Clulow uint16_t id;
173*b8aa3defSJoshua M. Clulow
174*b8aa3defSJoshua M. Clulow id = (paddr->Target[1].PeripDev.Bus - 1) << 8;
175*b8aa3defSJoshua M. Clulow id += paddr->Target[1].PeripDev.Dev;
176*b8aa3defSJoshua M. Clulow
177*b8aa3defSJoshua M. Clulow return (id);
178*b8aa3defSJoshua M. Clulow }
179*b8aa3defSJoshua M. Clulow
180*b8aa3defSJoshua M. Clulow void
smrt_write_lun_addr_phys(LUNAddr_t * lun,boolean_t masked,unsigned bus,unsigned target)181*b8aa3defSJoshua M. Clulow smrt_write_lun_addr_phys(LUNAddr_t *lun, boolean_t masked, unsigned bus,
182*b8aa3defSJoshua M. Clulow unsigned target)
183*b8aa3defSJoshua M. Clulow {
184*b8aa3defSJoshua M. Clulow lun->PhysDev.Mode = masked ? MASK_PERIPHERIAL_DEV_ADDR :
185*b8aa3defSJoshua M. Clulow PERIPHERIAL_DEV_ADDR;
186*b8aa3defSJoshua M. Clulow
187*b8aa3defSJoshua M. Clulow lun->PhysDev.TargetId = target;
188*b8aa3defSJoshua M. Clulow lun->PhysDev.Bus = bus;
189*b8aa3defSJoshua M. Clulow
190*b8aa3defSJoshua M. Clulow bzero(&lun->PhysDev.Target, sizeof (lun->PhysDev.Target));
191*b8aa3defSJoshua M. Clulow }
192*b8aa3defSJoshua M. Clulow
193*b8aa3defSJoshua M. Clulow /*
194*b8aa3defSJoshua M. Clulow * According to the CISS Specification, the controller is always addressed in
195*b8aa3defSJoshua M. Clulow * Mask Perhiperhal mode with a bus and target ID of zero. This is used by
196*b8aa3defSJoshua M. Clulow * commands that need to write to the controller itself, which is generally
197*b8aa3defSJoshua M. Clulow * discovery and other commands.
198*b8aa3defSJoshua M. Clulow */
199*b8aa3defSJoshua M. Clulow void
smrt_write_controller_lun_addr(LUNAddr_t * lun)200*b8aa3defSJoshua M. Clulow smrt_write_controller_lun_addr(LUNAddr_t *lun)
201*b8aa3defSJoshua M. Clulow {
202*b8aa3defSJoshua M. Clulow smrt_write_lun_addr_phys(lun, B_TRUE, 0, 0);
203*b8aa3defSJoshua M. Clulow }
204*b8aa3defSJoshua M. Clulow
205*b8aa3defSJoshua M. Clulow void
smrt_write_message_common(smrt_command_t * smcm,uint8_t type,int timeout_secs)206*b8aa3defSJoshua M. Clulow smrt_write_message_common(smrt_command_t *smcm, uint8_t type, int timeout_secs)
207*b8aa3defSJoshua M. Clulow {
208*b8aa3defSJoshua M. Clulow switch (type) {
209*b8aa3defSJoshua M. Clulow case CISS_MSG_ABORT:
210*b8aa3defSJoshua M. Clulow case CISS_MSG_RESET:
211*b8aa3defSJoshua M. Clulow case CISS_MSG_NOP:
212*b8aa3defSJoshua M. Clulow break;
213*b8aa3defSJoshua M. Clulow
214*b8aa3defSJoshua M. Clulow default:
215*b8aa3defSJoshua M. Clulow panic("unknown message type");
216*b8aa3defSJoshua M. Clulow }
217*b8aa3defSJoshua M. Clulow
218*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_MSG;
219*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
220*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_NONE;
221*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Timeout = LE_16(timeout_secs);
222*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDBLen = CISS_CDBLEN;
223*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDB[0] = type;
224*b8aa3defSJoshua M. Clulow }
225*b8aa3defSJoshua M. Clulow
226*b8aa3defSJoshua M. Clulow void
smrt_write_message_abort_one(smrt_command_t * smcm,uint32_t tag)227*b8aa3defSJoshua M. Clulow smrt_write_message_abort_one(smrt_command_t *smcm, uint32_t tag)
228*b8aa3defSJoshua M. Clulow {
229*b8aa3defSJoshua M. Clulow smrt_tag_t cisstag;
230*b8aa3defSJoshua M. Clulow
231*b8aa3defSJoshua M. Clulow /*
232*b8aa3defSJoshua M. Clulow * When aborting a particular command, the request is addressed
233*b8aa3defSJoshua M. Clulow * to the controller.
234*b8aa3defSJoshua M. Clulow */
235*b8aa3defSJoshua M. Clulow smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN,
236*b8aa3defSJoshua M. Clulow B_TRUE, 0, 0);
237*b8aa3defSJoshua M. Clulow
238*b8aa3defSJoshua M. Clulow smrt_write_message_common(smcm, CISS_MSG_ABORT, 0);
239*b8aa3defSJoshua M. Clulow
240*b8aa3defSJoshua M. Clulow /*
241*b8aa3defSJoshua M. Clulow * Abort a single command.
242*b8aa3defSJoshua M. Clulow */
243*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDB[1] = CISS_ABORT_TASK;
244*b8aa3defSJoshua M. Clulow
245*b8aa3defSJoshua M. Clulow /*
246*b8aa3defSJoshua M. Clulow * The CISS Specification says that the tag value for a task-level
247*b8aa3defSJoshua M. Clulow * abort should be in the CDB in bytes 4-11.
248*b8aa3defSJoshua M. Clulow */
249*b8aa3defSJoshua M. Clulow bzero(&cisstag, sizeof (cisstag));
250*b8aa3defSJoshua M. Clulow cisstag.tag_value = tag;
251*b8aa3defSJoshua M. Clulow bcopy(&cisstag, &smcm->smcm_va_cmd->Request.CDB[4],
252*b8aa3defSJoshua M. Clulow sizeof (cisstag));
253*b8aa3defSJoshua M. Clulow }
254*b8aa3defSJoshua M. Clulow
255*b8aa3defSJoshua M. Clulow void
smrt_write_message_abort_all(smrt_command_t * smcm,LUNAddr_t * addr)256*b8aa3defSJoshua M. Clulow smrt_write_message_abort_all(smrt_command_t *smcm, LUNAddr_t *addr)
257*b8aa3defSJoshua M. Clulow {
258*b8aa3defSJoshua M. Clulow /*
259*b8aa3defSJoshua M. Clulow * When aborting all tasks for a particular Logical Volume,
260*b8aa3defSJoshua M. Clulow * the command is addressed not to the controller but to
261*b8aa3defSJoshua M. Clulow * the Volume itself.
262*b8aa3defSJoshua M. Clulow */
263*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Header.LUN = *addr;
264*b8aa3defSJoshua M. Clulow
265*b8aa3defSJoshua M. Clulow smrt_write_message_common(smcm, CISS_MSG_ABORT, 0);
266*b8aa3defSJoshua M. Clulow
267*b8aa3defSJoshua M. Clulow /*
268*b8aa3defSJoshua M. Clulow * Abort all commands for a particular Logical Volume.
269*b8aa3defSJoshua M. Clulow */
270*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDB[1] = CISS_ABORT_TASKSET;
271*b8aa3defSJoshua M. Clulow }
272*b8aa3defSJoshua M. Clulow
273*b8aa3defSJoshua M. Clulow void
smrt_write_message_event_notify(smrt_command_t * smcm)274*b8aa3defSJoshua M. Clulow smrt_write_message_event_notify(smrt_command_t *smcm)
275*b8aa3defSJoshua M. Clulow {
276*b8aa3defSJoshua M. Clulow smrt_event_notify_req_t senr;
277*b8aa3defSJoshua M. Clulow
278*b8aa3defSJoshua M. Clulow smrt_write_controller_lun_addr(&smcm->smcm_va_cmd->Header.LUN);
279*b8aa3defSJoshua M. Clulow
280*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD;
281*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_ORDERED;
282*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_READ;
283*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Timeout = 0;
284*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDBLen = sizeof (senr);
285*b8aa3defSJoshua M. Clulow
286*b8aa3defSJoshua M. Clulow bzero(&senr, sizeof (senr));
287*b8aa3defSJoshua M. Clulow senr.senr_opcode = CISS_SCMD_READ;
288*b8aa3defSJoshua M. Clulow senr.senr_subcode = CISS_BMIC_NOTIFY_ON_EVENT;
289*b8aa3defSJoshua M. Clulow senr.senr_flags = BE_32(0);
290*b8aa3defSJoshua M. Clulow senr.senr_size = BE_32(SMRT_EVENT_NOTIFY_BUFLEN);
291*b8aa3defSJoshua M. Clulow
292*b8aa3defSJoshua M. Clulow bcopy(&senr, &smcm->smcm_va_cmd->Request.CDB[0],
293*b8aa3defSJoshua M. Clulow MIN(CISS_CDBLEN, sizeof (senr)));
294*b8aa3defSJoshua M. Clulow }
295*b8aa3defSJoshua M. Clulow
296*b8aa3defSJoshua M. Clulow void
smrt_write_message_cancel_event_notify(smrt_command_t * smcm)297*b8aa3defSJoshua M. Clulow smrt_write_message_cancel_event_notify(smrt_command_t *smcm)
298*b8aa3defSJoshua M. Clulow {
299*b8aa3defSJoshua M. Clulow smrt_event_notify_req_t senr;
300*b8aa3defSJoshua M. Clulow
301*b8aa3defSJoshua M. Clulow smrt_write_controller_lun_addr(&smcm->smcm_va_cmd->Header.LUN);
302*b8aa3defSJoshua M. Clulow
303*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD;
304*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_ORDERED;
305*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_WRITE;
306*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Timeout = LE_16(SMRT_ASYNC_CANCEL_TIMEOUT);
307*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDBLen = sizeof (senr);
308*b8aa3defSJoshua M. Clulow
309*b8aa3defSJoshua M. Clulow bzero(&senr, sizeof (senr));
310*b8aa3defSJoshua M. Clulow senr.senr_opcode = CISS_SCMD_WRITE;
311*b8aa3defSJoshua M. Clulow senr.senr_subcode = CISS_BMIC_NOTIFY_ON_EVENT_CANCEL;
312*b8aa3defSJoshua M. Clulow senr.senr_size = BE_32(SMRT_EVENT_NOTIFY_BUFLEN);
313*b8aa3defSJoshua M. Clulow
314*b8aa3defSJoshua M. Clulow bcopy(&senr, &smcm->smcm_va_cmd->Request.CDB[0],
315*b8aa3defSJoshua M. Clulow MIN(CISS_CDBLEN, sizeof (senr)));
316*b8aa3defSJoshua M. Clulow }
317*b8aa3defSJoshua M. Clulow
318*b8aa3defSJoshua M. Clulow void
smrt_write_message_reset_ctlr(smrt_command_t * smcm)319*b8aa3defSJoshua M. Clulow smrt_write_message_reset_ctlr(smrt_command_t *smcm)
320*b8aa3defSJoshua M. Clulow {
321*b8aa3defSJoshua M. Clulow smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN,
322*b8aa3defSJoshua M. Clulow B_TRUE, 0, 0);
323*b8aa3defSJoshua M. Clulow
324*b8aa3defSJoshua M. Clulow smrt_write_message_common(smcm, CISS_MSG_RESET, 0);
325*b8aa3defSJoshua M. Clulow
326*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDB[1] = CISS_RESET_CTLR;
327*b8aa3defSJoshua M. Clulow }
328*b8aa3defSJoshua M. Clulow
329*b8aa3defSJoshua M. Clulow void
smrt_write_message_nop(smrt_command_t * smcm,int timeout_secs)330*b8aa3defSJoshua M. Clulow smrt_write_message_nop(smrt_command_t *smcm, int timeout_secs)
331*b8aa3defSJoshua M. Clulow {
332*b8aa3defSJoshua M. Clulow /*
333*b8aa3defSJoshua M. Clulow * No-op messages are always sent to the controller.
334*b8aa3defSJoshua M. Clulow */
335*b8aa3defSJoshua M. Clulow smrt_write_lun_addr_phys(&smcm->smcm_va_cmd->Header.LUN,
336*b8aa3defSJoshua M. Clulow B_TRUE, 0, 0);
337*b8aa3defSJoshua M. Clulow
338*b8aa3defSJoshua M. Clulow smrt_write_message_common(smcm, CISS_MSG_NOP, timeout_secs);
339*b8aa3defSJoshua M. Clulow }
340*b8aa3defSJoshua M. Clulow
341*b8aa3defSJoshua M. Clulow /*
342*b8aa3defSJoshua M. Clulow * This routine is executed regularly by ddi_periodic_add(9F). It checks the
343*b8aa3defSJoshua M. Clulow * health of the controller and looks for submitted commands that have timed
344*b8aa3defSJoshua M. Clulow * out.
345*b8aa3defSJoshua M. Clulow */
346*b8aa3defSJoshua M. Clulow void
smrt_periodic(void * arg)347*b8aa3defSJoshua M. Clulow smrt_periodic(void *arg)
348*b8aa3defSJoshua M. Clulow {
349*b8aa3defSJoshua M. Clulow smrt_t *smrt = arg;
350*b8aa3defSJoshua M. Clulow
351*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
352*b8aa3defSJoshua M. Clulow
353*b8aa3defSJoshua M. Clulow /*
354*b8aa3defSJoshua M. Clulow * Before we even check if the controller is running to process
355*b8aa3defSJoshua M. Clulow * everything else, we must first check if we had a request to kick off
356*b8aa3defSJoshua M. Clulow * discovery. We do this before the check if the controller is running,
357*b8aa3defSJoshua M. Clulow * as this may be required to finish a discovery.
358*b8aa3defSJoshua M. Clulow */
359*b8aa3defSJoshua M. Clulow if ((smrt->smrt_status & SMRT_CTLR_DISCOVERY_PERIODIC) != 0 &&
360*b8aa3defSJoshua M. Clulow (smrt->smrt_status & SMRT_CTLR_DISCOVERY_RUNNING) == 0 &&
361*b8aa3defSJoshua M. Clulow (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) == 0) {
362*b8aa3defSJoshua M. Clulow if (ddi_taskq_dispatch(smrt->smrt_discover_taskq,
363*b8aa3defSJoshua M. Clulow smrt_discover, smrt, DDI_NOSLEEP) != DDI_SUCCESS) {
364*b8aa3defSJoshua M. Clulow smrt->smrt_stats.smrts_discovery_tq_errors++;
365*b8aa3defSJoshua M. Clulow } else {
366*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_DISCOVERY_PERIODIC;
367*b8aa3defSJoshua M. Clulow }
368*b8aa3defSJoshua M. Clulow }
369*b8aa3defSJoshua M. Clulow
370*b8aa3defSJoshua M. Clulow if (!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING)) {
371*b8aa3defSJoshua M. Clulow /*
372*b8aa3defSJoshua M. Clulow * The device is currently not active, e.g. due to an
373*b8aa3defSJoshua M. Clulow * in-progress controller reset.
374*b8aa3defSJoshua M. Clulow */
375*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
376*b8aa3defSJoshua M. Clulow return;
377*b8aa3defSJoshua M. Clulow }
378*b8aa3defSJoshua M. Clulow
379*b8aa3defSJoshua M. Clulow /*
380*b8aa3defSJoshua M. Clulow * Check on the health of the controller firmware. Note that if the
381*b8aa3defSJoshua M. Clulow * controller has locked up, this routine will panic the system.
382*b8aa3defSJoshua M. Clulow */
383*b8aa3defSJoshua M. Clulow smrt_lockup_check(smrt);
384*b8aa3defSJoshua M. Clulow
385*b8aa3defSJoshua M. Clulow /*
386*b8aa3defSJoshua M. Clulow * Reset the event notification threshold counter.
387*b8aa3defSJoshua M. Clulow */
388*b8aa3defSJoshua M. Clulow smrt->smrt_event_count = 0;
389*b8aa3defSJoshua M. Clulow
390*b8aa3defSJoshua M. Clulow /*
391*b8aa3defSJoshua M. Clulow * Check inflight commands to see if they have timed out.
392*b8aa3defSJoshua M. Clulow */
393*b8aa3defSJoshua M. Clulow for (smrt_command_t *smcm = avl_first(&smrt->smrt_inflight);
394*b8aa3defSJoshua M. Clulow smcm != NULL; smcm = AVL_NEXT(&smrt->smrt_inflight, smcm)) {
395*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_POLLED) {
396*b8aa3defSJoshua M. Clulow /*
397*b8aa3defSJoshua M. Clulow * Polled commands are timed out by the polling
398*b8aa3defSJoshua M. Clulow * routine.
399*b8aa3defSJoshua M. Clulow */
400*b8aa3defSJoshua M. Clulow continue;
401*b8aa3defSJoshua M. Clulow }
402*b8aa3defSJoshua M. Clulow
403*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_ABORT_SENT) {
404*b8aa3defSJoshua M. Clulow /*
405*b8aa3defSJoshua M. Clulow * This command has been aborted; either it will
406*b8aa3defSJoshua M. Clulow * complete or the controller will be reset.
407*b8aa3defSJoshua M. Clulow */
408*b8aa3defSJoshua M. Clulow continue;
409*b8aa3defSJoshua M. Clulow }
410*b8aa3defSJoshua M. Clulow
411*b8aa3defSJoshua M. Clulow if (list_link_active(&smcm->smcm_link_abort)) {
412*b8aa3defSJoshua M. Clulow /*
413*b8aa3defSJoshua M. Clulow * Already on the abort queue.
414*b8aa3defSJoshua M. Clulow */
415*b8aa3defSJoshua M. Clulow continue;
416*b8aa3defSJoshua M. Clulow }
417*b8aa3defSJoshua M. Clulow
418*b8aa3defSJoshua M. Clulow if (smcm->smcm_expiry == 0) {
419*b8aa3defSJoshua M. Clulow /*
420*b8aa3defSJoshua M. Clulow * This command has no expiry time.
421*b8aa3defSJoshua M. Clulow */
422*b8aa3defSJoshua M. Clulow continue;
423*b8aa3defSJoshua M. Clulow }
424*b8aa3defSJoshua M. Clulow
425*b8aa3defSJoshua M. Clulow if (gethrtime() > smcm->smcm_expiry) {
426*b8aa3defSJoshua M. Clulow list_insert_tail(&smrt->smrt_abortq, smcm);
427*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_TIMEOUT;
428*b8aa3defSJoshua M. Clulow }
429*b8aa3defSJoshua M. Clulow }
430*b8aa3defSJoshua M. Clulow
431*b8aa3defSJoshua M. Clulow /*
432*b8aa3defSJoshua M. Clulow * Process the abort queue.
433*b8aa3defSJoshua M. Clulow */
434*b8aa3defSJoshua M. Clulow (void) smrt_process_abortq(smrt);
435*b8aa3defSJoshua M. Clulow
436*b8aa3defSJoshua M. Clulow /*
437*b8aa3defSJoshua M. Clulow * Check if we have an outstanding event intervention request. Note,
438*b8aa3defSJoshua M. Clulow * the command in question should always be in a state such that it is
439*b8aa3defSJoshua M. Clulow * usable by the system here. The command is always prepared again by
440*b8aa3defSJoshua M. Clulow * the normal event notification path, even if a reset has occurred.
441*b8aa3defSJoshua M. Clulow * The reset will be processed before we'd ever consider running an
442*b8aa3defSJoshua M. Clulow * event again. Note, if we fail to submit this, then we leave this for
443*b8aa3defSJoshua M. Clulow * the next occurrence of the periodic.
444*b8aa3defSJoshua M. Clulow */
445*b8aa3defSJoshua M. Clulow if (smrt->smrt_status & SMRT_CTLR_ASYNC_INTERVENTION) {
446*b8aa3defSJoshua M. Clulow smrt->smrt_stats.smrts_events_intervened++;
447*b8aa3defSJoshua M. Clulow
448*b8aa3defSJoshua M. Clulow if (smrt_submit(smrt, smrt->smrt_event_cmd) == 0) {
449*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_ASYNC_INTERVENTION;
450*b8aa3defSJoshua M. Clulow }
451*b8aa3defSJoshua M. Clulow }
452*b8aa3defSJoshua M. Clulow
453*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
454*b8aa3defSJoshua M. Clulow }
455*b8aa3defSJoshua M. Clulow
456*b8aa3defSJoshua M. Clulow int
smrt_retrieve(smrt_t * smrt)457*b8aa3defSJoshua M. Clulow smrt_retrieve(smrt_t *smrt)
458*b8aa3defSJoshua M. Clulow {
459*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
460*b8aa3defSJoshua M. Clulow
461*b8aa3defSJoshua M. Clulow switch (smrt->smrt_ctlr_mode) {
462*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_SIMPLE:
463*b8aa3defSJoshua M. Clulow smrt_retrieve_simple(smrt);
464*b8aa3defSJoshua M. Clulow return (DDI_SUCCESS);
465*b8aa3defSJoshua M. Clulow
466*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_UNKNOWN:
467*b8aa3defSJoshua M. Clulow break;
468*b8aa3defSJoshua M. Clulow }
469*b8aa3defSJoshua M. Clulow
470*b8aa3defSJoshua M. Clulow panic("unknown controller mode");
471*b8aa3defSJoshua M. Clulow /* LINTED: E_FUNC_NO_RET_VAL */
472*b8aa3defSJoshua M. Clulow }
473*b8aa3defSJoshua M. Clulow
474*b8aa3defSJoshua M. Clulow /*
475*b8aa3defSJoshua M. Clulow * Grab a new tag number for this command. We aim to avoid reusing tag numbers
476*b8aa3defSJoshua M. Clulow * as much as possible, so as to avoid spurious double completion from the
477*b8aa3defSJoshua M. Clulow * controller.
478*b8aa3defSJoshua M. Clulow */
479*b8aa3defSJoshua M. Clulow static void
smrt_set_new_tag(smrt_t * smrt,smrt_command_t * smcm)480*b8aa3defSJoshua M. Clulow smrt_set_new_tag(smrt_t *smrt, smrt_command_t *smcm)
481*b8aa3defSJoshua M. Clulow {
482*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
483*b8aa3defSJoshua M. Clulow
484*b8aa3defSJoshua M. Clulow /*
485*b8aa3defSJoshua M. Clulow * Loop until we find a tag that is not in use. The tag space is
486*b8aa3defSJoshua M. Clulow * very large (~30 bits) and the maximum number of inflight commands
487*b8aa3defSJoshua M. Clulow * is comparatively small (~1024 in current controllers).
488*b8aa3defSJoshua M. Clulow */
489*b8aa3defSJoshua M. Clulow for (;;) {
490*b8aa3defSJoshua M. Clulow uint32_t new_tag = smrt->smrt_next_tag;
491*b8aa3defSJoshua M. Clulow
492*b8aa3defSJoshua M. Clulow if (++smrt->smrt_next_tag > SMRT_MAX_TAG_NUMBER) {
493*b8aa3defSJoshua M. Clulow smrt->smrt_next_tag = SMRT_MIN_TAG_NUMBER;
494*b8aa3defSJoshua M. Clulow }
495*b8aa3defSJoshua M. Clulow
496*b8aa3defSJoshua M. Clulow if (smrt_lookup_inflight(smrt, new_tag) != NULL) {
497*b8aa3defSJoshua M. Clulow /*
498*b8aa3defSJoshua M. Clulow * This tag is already used on an inflight command.
499*b8aa3defSJoshua M. Clulow * Choose another.
500*b8aa3defSJoshua M. Clulow */
501*b8aa3defSJoshua M. Clulow continue;
502*b8aa3defSJoshua M. Clulow }
503*b8aa3defSJoshua M. Clulow
504*b8aa3defSJoshua M. Clulow /*
505*b8aa3defSJoshua M. Clulow * Set the tag for the command and also write it into the
506*b8aa3defSJoshua M. Clulow * appropriate part of the request block.
507*b8aa3defSJoshua M. Clulow */
508*b8aa3defSJoshua M. Clulow smcm->smcm_tag = new_tag;
509*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Header.Tag.tag_value = new_tag;
510*b8aa3defSJoshua M. Clulow return;
511*b8aa3defSJoshua M. Clulow }
512*b8aa3defSJoshua M. Clulow }
513*b8aa3defSJoshua M. Clulow
514*b8aa3defSJoshua M. Clulow /*
515*b8aa3defSJoshua M. Clulow * Submit a command to the controller.
516*b8aa3defSJoshua M. Clulow */
517*b8aa3defSJoshua M. Clulow int
smrt_submit(smrt_t * smrt,smrt_command_t * smcm)518*b8aa3defSJoshua M. Clulow smrt_submit(smrt_t *smrt, smrt_command_t *smcm)
519*b8aa3defSJoshua M. Clulow {
520*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
521*b8aa3defSJoshua M. Clulow VERIFY(smcm->smcm_type != SMRT_CMDTYPE_PREINIT);
522*b8aa3defSJoshua M. Clulow
523*b8aa3defSJoshua M. Clulow /*
524*b8aa3defSJoshua M. Clulow * Anything that asks us to ignore the running state of the controller
525*b8aa3defSJoshua M. Clulow * must be wired up to poll for completion.
526*b8aa3defSJoshua M. Clulow */
527*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_IGNORE_RUNNING) {
528*b8aa3defSJoshua M. Clulow VERIFY(smcm->smcm_status & SMRT_CMD_STATUS_POLLED);
529*b8aa3defSJoshua M. Clulow }
530*b8aa3defSJoshua M. Clulow
531*b8aa3defSJoshua M. Clulow /*
532*b8aa3defSJoshua M. Clulow * If the controller is currently being reset, do not allow command
533*b8aa3defSJoshua M. Clulow * submission. However, if this is one of the commands needed to finish
534*b8aa3defSJoshua M. Clulow * reset, as indicated on the command structure, allow it.
535*b8aa3defSJoshua M. Clulow */
536*b8aa3defSJoshua M. Clulow if (!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING) &&
537*b8aa3defSJoshua M. Clulow !(smcm->smcm_status & SMRT_CMD_IGNORE_RUNNING)) {
538*b8aa3defSJoshua M. Clulow return (EIO);
539*b8aa3defSJoshua M. Clulow }
540*b8aa3defSJoshua M. Clulow
541*b8aa3defSJoshua M. Clulow /*
542*b8aa3defSJoshua M. Clulow * Do not allow submission of more concurrent commands than the
543*b8aa3defSJoshua M. Clulow * controller supports.
544*b8aa3defSJoshua M. Clulow */
545*b8aa3defSJoshua M. Clulow if (avl_numnodes(&smrt->smrt_inflight) >= smrt->smrt_maxcmds) {
546*b8aa3defSJoshua M. Clulow return (EAGAIN);
547*b8aa3defSJoshua M. Clulow }
548*b8aa3defSJoshua M. Clulow
549*b8aa3defSJoshua M. Clulow /*
550*b8aa3defSJoshua M. Clulow * Synchronise the Command Block DMA resources to ensure that the
551*b8aa3defSJoshua M. Clulow * device has a consistent view before we pass it the command.
552*b8aa3defSJoshua M. Clulow */
553*b8aa3defSJoshua M. Clulow if (ddi_dma_sync(smcm->smcm_contig.smdma_dma_handle, 0, 0,
554*b8aa3defSJoshua M. Clulow DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
555*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "DMA sync failure");
556*b8aa3defSJoshua M. Clulow return (EIO);
557*b8aa3defSJoshua M. Clulow }
558*b8aa3defSJoshua M. Clulow
559*b8aa3defSJoshua M. Clulow /*
560*b8aa3defSJoshua M. Clulow * Ensure that this command is not re-used without issuing a new
561*b8aa3defSJoshua M. Clulow * tag number and performing any appropriate cleanup.
562*b8aa3defSJoshua M. Clulow */
563*b8aa3defSJoshua M. Clulow VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_USED));
564*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_USED;
565*b8aa3defSJoshua M. Clulow
566*b8aa3defSJoshua M. Clulow /*
567*b8aa3defSJoshua M. Clulow * Assign a tag that is not currently in use
568*b8aa3defSJoshua M. Clulow */
569*b8aa3defSJoshua M. Clulow smrt_set_new_tag(smrt, smcm);
570*b8aa3defSJoshua M. Clulow
571*b8aa3defSJoshua M. Clulow /*
572*b8aa3defSJoshua M. Clulow * Insert this command into the inflight AVL.
573*b8aa3defSJoshua M. Clulow */
574*b8aa3defSJoshua M. Clulow avl_index_t where;
575*b8aa3defSJoshua M. Clulow if (avl_find(&smrt->smrt_inflight, smcm, &where) != NULL) {
576*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "duplicate submit tag %x",
577*b8aa3defSJoshua M. Clulow smcm->smcm_tag);
578*b8aa3defSJoshua M. Clulow }
579*b8aa3defSJoshua M. Clulow avl_insert(&smrt->smrt_inflight, smcm, where);
580*b8aa3defSJoshua M. Clulow if (smrt->smrt_stats.smrts_max_inflight <
581*b8aa3defSJoshua M. Clulow avl_numnodes(&smrt->smrt_inflight)) {
582*b8aa3defSJoshua M. Clulow smrt->smrt_stats.smrts_max_inflight =
583*b8aa3defSJoshua M. Clulow avl_numnodes(&smrt->smrt_inflight);
584*b8aa3defSJoshua M. Clulow }
585*b8aa3defSJoshua M. Clulow
586*b8aa3defSJoshua M. Clulow VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT));
587*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_INFLIGHT;
588*b8aa3defSJoshua M. Clulow
589*b8aa3defSJoshua M. Clulow smcm->smcm_time_submit = gethrtime();
590*b8aa3defSJoshua M. Clulow
591*b8aa3defSJoshua M. Clulow switch (smrt->smrt_ctlr_mode) {
592*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_SIMPLE:
593*b8aa3defSJoshua M. Clulow smrt_submit_simple(smrt, smcm);
594*b8aa3defSJoshua M. Clulow return (0);
595*b8aa3defSJoshua M. Clulow
596*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_UNKNOWN:
597*b8aa3defSJoshua M. Clulow break;
598*b8aa3defSJoshua M. Clulow }
599*b8aa3defSJoshua M. Clulow panic("unknown controller mode");
600*b8aa3defSJoshua M. Clulow /* LINTED: E_FUNC_NO_RET_VAL */
601*b8aa3defSJoshua M. Clulow }
602*b8aa3defSJoshua M. Clulow
603*b8aa3defSJoshua M. Clulow static void
smrt_process_finishq_sync(smrt_command_t * smcm)604*b8aa3defSJoshua M. Clulow smrt_process_finishq_sync(smrt_command_t *smcm)
605*b8aa3defSJoshua M. Clulow {
606*b8aa3defSJoshua M. Clulow smrt_t *smrt = smcm->smcm_ctlr;
607*b8aa3defSJoshua M. Clulow
608*b8aa3defSJoshua M. Clulow if (ddi_dma_sync(smcm->smcm_contig.smdma_dma_handle, 0, 0,
609*b8aa3defSJoshua M. Clulow DDI_DMA_SYNC_FORCPU) != DDI_SUCCESS) {
610*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "finishq DMA sync failure");
611*b8aa3defSJoshua M. Clulow }
612*b8aa3defSJoshua M. Clulow }
613*b8aa3defSJoshua M. Clulow
614*b8aa3defSJoshua M. Clulow static void
smrt_process_finishq_one(smrt_command_t * smcm)615*b8aa3defSJoshua M. Clulow smrt_process_finishq_one(smrt_command_t *smcm)
616*b8aa3defSJoshua M. Clulow {
617*b8aa3defSJoshua M. Clulow smrt_t *smrt = smcm->smcm_ctlr;
618*b8aa3defSJoshua M. Clulow
619*b8aa3defSJoshua M. Clulow VERIFY(!(smcm->smcm_status & SMRT_CMD_STATUS_COMPLETE));
620*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_COMPLETE;
621*b8aa3defSJoshua M. Clulow
622*b8aa3defSJoshua M. Clulow switch (smcm->smcm_type) {
623*b8aa3defSJoshua M. Clulow case SMRT_CMDTYPE_INTERNAL:
624*b8aa3defSJoshua M. Clulow cv_broadcast(&smcm->smcm_ctlr->smrt_cv_finishq);
625*b8aa3defSJoshua M. Clulow return;
626*b8aa3defSJoshua M. Clulow
627*b8aa3defSJoshua M. Clulow case SMRT_CMDTYPE_SCSA:
628*b8aa3defSJoshua M. Clulow smrt_hba_complete(smcm);
629*b8aa3defSJoshua M. Clulow return;
630*b8aa3defSJoshua M. Clulow
631*b8aa3defSJoshua M. Clulow case SMRT_CMDTYPE_EVENT:
632*b8aa3defSJoshua M. Clulow smrt_event_complete(smcm);
633*b8aa3defSJoshua M. Clulow return;
634*b8aa3defSJoshua M. Clulow
635*b8aa3defSJoshua M. Clulow case SMRT_CMDTYPE_ABORTQ:
636*b8aa3defSJoshua M. Clulow /*
637*b8aa3defSJoshua M. Clulow * Abort messages sent as part of abort queue processing
638*b8aa3defSJoshua M. Clulow * do not require any completion activity.
639*b8aa3defSJoshua M. Clulow */
640*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
641*b8aa3defSJoshua M. Clulow smrt_command_free(smcm);
642*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
643*b8aa3defSJoshua M. Clulow return;
644*b8aa3defSJoshua M. Clulow
645*b8aa3defSJoshua M. Clulow case SMRT_CMDTYPE_PREINIT:
646*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "preinit command "
647*b8aa3defSJoshua M. Clulow "completed after initialisation");
648*b8aa3defSJoshua M. Clulow return;
649*b8aa3defSJoshua M. Clulow }
650*b8aa3defSJoshua M. Clulow
651*b8aa3defSJoshua M. Clulow panic("unknown command type");
652*b8aa3defSJoshua M. Clulow }
653*b8aa3defSJoshua M. Clulow
654*b8aa3defSJoshua M. Clulow /*
655*b8aa3defSJoshua M. Clulow * Process commands in the completion queue.
656*b8aa3defSJoshua M. Clulow */
657*b8aa3defSJoshua M. Clulow void
smrt_process_finishq(smrt_t * smrt)658*b8aa3defSJoshua M. Clulow smrt_process_finishq(smrt_t *smrt)
659*b8aa3defSJoshua M. Clulow {
660*b8aa3defSJoshua M. Clulow smrt_command_t *smcm;
661*b8aa3defSJoshua M. Clulow
662*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
663*b8aa3defSJoshua M. Clulow
664*b8aa3defSJoshua M. Clulow while ((smcm = list_remove_head(&smrt->smrt_finishq)) != NULL) {
665*b8aa3defSJoshua M. Clulow /*
666*b8aa3defSJoshua M. Clulow * Synchronise the Command Block before we read from it or
667*b8aa3defSJoshua M. Clulow * free it, to ensure that any writes from the controller are
668*b8aa3defSJoshua M. Clulow * visible.
669*b8aa3defSJoshua M. Clulow */
670*b8aa3defSJoshua M. Clulow smrt_process_finishq_sync(smcm);
671*b8aa3defSJoshua M. Clulow
672*b8aa3defSJoshua M. Clulow /*
673*b8aa3defSJoshua M. Clulow * Check if this command was in line to be aborted.
674*b8aa3defSJoshua M. Clulow */
675*b8aa3defSJoshua M. Clulow if (list_link_active(&smcm->smcm_link_abort)) {
676*b8aa3defSJoshua M. Clulow /*
677*b8aa3defSJoshua M. Clulow * This command was in line, but the controller
678*b8aa3defSJoshua M. Clulow * subsequently completed the command before we
679*b8aa3defSJoshua M. Clulow * were able to do so.
680*b8aa3defSJoshua M. Clulow */
681*b8aa3defSJoshua M. Clulow list_remove(&smrt->smrt_abortq, smcm);
682*b8aa3defSJoshua M. Clulow smcm->smcm_status &= ~SMRT_CMD_STATUS_TIMEOUT;
683*b8aa3defSJoshua M. Clulow }
684*b8aa3defSJoshua M. Clulow
685*b8aa3defSJoshua M. Clulow /*
686*b8aa3defSJoshua M. Clulow * Check if this command has been abandoned by the original
687*b8aa3defSJoshua M. Clulow * submitter. If it has, free it now to avoid a leak.
688*b8aa3defSJoshua M. Clulow */
689*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_ABANDONED) {
690*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
691*b8aa3defSJoshua M. Clulow smrt_command_free(smcm);
692*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
693*b8aa3defSJoshua M. Clulow continue;
694*b8aa3defSJoshua M. Clulow }
695*b8aa3defSJoshua M. Clulow
696*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_POLLED) {
697*b8aa3defSJoshua M. Clulow /*
698*b8aa3defSJoshua M. Clulow * This command will be picked up and processed
699*b8aa3defSJoshua M. Clulow * by "smrt_poll_for()" once the CV is triggered
700*b8aa3defSJoshua M. Clulow * at the end of processing.
701*b8aa3defSJoshua M. Clulow */
702*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_POLL_COMPLETE;
703*b8aa3defSJoshua M. Clulow continue;
704*b8aa3defSJoshua M. Clulow }
705*b8aa3defSJoshua M. Clulow
706*b8aa3defSJoshua M. Clulow smrt_process_finishq_one(smcm);
707*b8aa3defSJoshua M. Clulow }
708*b8aa3defSJoshua M. Clulow
709*b8aa3defSJoshua M. Clulow cv_broadcast(&smrt->smrt_cv_finishq);
710*b8aa3defSJoshua M. Clulow }
711*b8aa3defSJoshua M. Clulow
712*b8aa3defSJoshua M. Clulow /*
713*b8aa3defSJoshua M. Clulow * Process commands in the abort queue.
714*b8aa3defSJoshua M. Clulow */
715*b8aa3defSJoshua M. Clulow void
smrt_process_abortq(smrt_t * smrt)716*b8aa3defSJoshua M. Clulow smrt_process_abortq(smrt_t *smrt)
717*b8aa3defSJoshua M. Clulow {
718*b8aa3defSJoshua M. Clulow smrt_command_t *smcm;
719*b8aa3defSJoshua M. Clulow smrt_command_t *abort_smcm = NULL;
720*b8aa3defSJoshua M. Clulow
721*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
722*b8aa3defSJoshua M. Clulow
723*b8aa3defSJoshua M. Clulow if (list_is_empty(&smrt->smrt_abortq)) {
724*b8aa3defSJoshua M. Clulow goto out;
725*b8aa3defSJoshua M. Clulow }
726*b8aa3defSJoshua M. Clulow
727*b8aa3defSJoshua M. Clulow another:
728*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
729*b8aa3defSJoshua M. Clulow if ((abort_smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_ABORTQ,
730*b8aa3defSJoshua M. Clulow KM_NOSLEEP)) == NULL) {
731*b8aa3defSJoshua M. Clulow /*
732*b8aa3defSJoshua M. Clulow * No resources available to send abort messages. We will
733*b8aa3defSJoshua M. Clulow * try again the next time around.
734*b8aa3defSJoshua M. Clulow */
735*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
736*b8aa3defSJoshua M. Clulow goto out;
737*b8aa3defSJoshua M. Clulow }
738*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
739*b8aa3defSJoshua M. Clulow
740*b8aa3defSJoshua M. Clulow while ((smcm = list_remove_head(&smrt->smrt_abortq)) != NULL) {
741*b8aa3defSJoshua M. Clulow if (!(smcm->smcm_status & SMRT_CMD_STATUS_INFLIGHT)) {
742*b8aa3defSJoshua M. Clulow /*
743*b8aa3defSJoshua M. Clulow * This message is not currently inflight, so
744*b8aa3defSJoshua M. Clulow * no abort is needed.
745*b8aa3defSJoshua M. Clulow */
746*b8aa3defSJoshua M. Clulow continue;
747*b8aa3defSJoshua M. Clulow }
748*b8aa3defSJoshua M. Clulow
749*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_ABORT_SENT) {
750*b8aa3defSJoshua M. Clulow /*
751*b8aa3defSJoshua M. Clulow * An abort message has already been sent for
752*b8aa3defSJoshua M. Clulow * this command.
753*b8aa3defSJoshua M. Clulow */
754*b8aa3defSJoshua M. Clulow continue;
755*b8aa3defSJoshua M. Clulow }
756*b8aa3defSJoshua M. Clulow
757*b8aa3defSJoshua M. Clulow /*
758*b8aa3defSJoshua M. Clulow * Send an abort message for the command.
759*b8aa3defSJoshua M. Clulow */
760*b8aa3defSJoshua M. Clulow smrt_write_message_abort_one(abort_smcm, smcm->smcm_tag);
761*b8aa3defSJoshua M. Clulow if (smrt_submit(smrt, abort_smcm) != 0) {
762*b8aa3defSJoshua M. Clulow /*
763*b8aa3defSJoshua M. Clulow * The command could not be submitted to the
764*b8aa3defSJoshua M. Clulow * controller. Put it back in the abort queue
765*b8aa3defSJoshua M. Clulow * and give up for now.
766*b8aa3defSJoshua M. Clulow */
767*b8aa3defSJoshua M. Clulow list_insert_head(&smrt->smrt_abortq, smcm);
768*b8aa3defSJoshua M. Clulow goto out;
769*b8aa3defSJoshua M. Clulow }
770*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_ABORT_SENT;
771*b8aa3defSJoshua M. Clulow
772*b8aa3defSJoshua M. Clulow /*
773*b8aa3defSJoshua M. Clulow * Record some debugging information about the abort we
774*b8aa3defSJoshua M. Clulow * sent:
775*b8aa3defSJoshua M. Clulow */
776*b8aa3defSJoshua M. Clulow smcm->smcm_abort_time = gethrtime();
777*b8aa3defSJoshua M. Clulow smcm->smcm_abort_tag = abort_smcm->smcm_tag;
778*b8aa3defSJoshua M. Clulow
779*b8aa3defSJoshua M. Clulow /*
780*b8aa3defSJoshua M. Clulow * The abort message was sent. Release it and
781*b8aa3defSJoshua M. Clulow * allocate another command.
782*b8aa3defSJoshua M. Clulow */
783*b8aa3defSJoshua M. Clulow abort_smcm = NULL;
784*b8aa3defSJoshua M. Clulow goto another;
785*b8aa3defSJoshua M. Clulow }
786*b8aa3defSJoshua M. Clulow
787*b8aa3defSJoshua M. Clulow out:
788*b8aa3defSJoshua M. Clulow cv_broadcast(&smrt->smrt_cv_finishq);
789*b8aa3defSJoshua M. Clulow if (abort_smcm != NULL) {
790*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
791*b8aa3defSJoshua M. Clulow smrt_command_free(abort_smcm);
792*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
793*b8aa3defSJoshua M. Clulow }
794*b8aa3defSJoshua M. Clulow }
795*b8aa3defSJoshua M. Clulow
796*b8aa3defSJoshua M. Clulow int
smrt_poll_for(smrt_t * smrt,smrt_command_t * smcm)797*b8aa3defSJoshua M. Clulow smrt_poll_for(smrt_t *smrt, smrt_command_t *smcm)
798*b8aa3defSJoshua M. Clulow {
799*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
800*b8aa3defSJoshua M. Clulow VERIFY(smcm->smcm_status & SMRT_CMD_STATUS_POLLED);
801*b8aa3defSJoshua M. Clulow
802*b8aa3defSJoshua M. Clulow while (!(smcm->smcm_status & SMRT_CMD_STATUS_POLL_COMPLETE)) {
803*b8aa3defSJoshua M. Clulow if (smcm->smcm_expiry != 0) {
804*b8aa3defSJoshua M. Clulow /*
805*b8aa3defSJoshua M. Clulow * This command has an expiry time. Check to see
806*b8aa3defSJoshua M. Clulow * if it has already passed:
807*b8aa3defSJoshua M. Clulow */
808*b8aa3defSJoshua M. Clulow if (smcm->smcm_expiry < gethrtime()) {
809*b8aa3defSJoshua M. Clulow return (ETIMEDOUT);
810*b8aa3defSJoshua M. Clulow }
811*b8aa3defSJoshua M. Clulow }
812*b8aa3defSJoshua M. Clulow
813*b8aa3defSJoshua M. Clulow if (ddi_in_panic()) {
814*b8aa3defSJoshua M. Clulow /*
815*b8aa3defSJoshua M. Clulow * When the system is panicking, there are no
816*b8aa3defSJoshua M. Clulow * interrupts or other threads. Drive the polling loop
817*b8aa3defSJoshua M. Clulow * on our own, but with a small delay to avoid
818*b8aa3defSJoshua M. Clulow * aggrevating the controller while we're trying to
819*b8aa3defSJoshua M. Clulow * dump.
820*b8aa3defSJoshua M. Clulow */
821*b8aa3defSJoshua M. Clulow (void) smrt_retrieve(smrt);
822*b8aa3defSJoshua M. Clulow smrt_process_finishq(smrt);
823*b8aa3defSJoshua M. Clulow drv_usecwait(100);
824*b8aa3defSJoshua M. Clulow continue;
825*b8aa3defSJoshua M. Clulow }
826*b8aa3defSJoshua M. Clulow
827*b8aa3defSJoshua M. Clulow /*
828*b8aa3defSJoshua M. Clulow * Wait for command completion to return through the regular
829*b8aa3defSJoshua M. Clulow * interrupt handling path.
830*b8aa3defSJoshua M. Clulow */
831*b8aa3defSJoshua M. Clulow if (smcm->smcm_expiry == 0) {
832*b8aa3defSJoshua M. Clulow cv_wait(&smrt->smrt_cv_finishq, &smrt->smrt_mutex);
833*b8aa3defSJoshua M. Clulow } else {
834*b8aa3defSJoshua M. Clulow /*
835*b8aa3defSJoshua M. Clulow * Wait only until the expiry time for this command.
836*b8aa3defSJoshua M. Clulow */
837*b8aa3defSJoshua M. Clulow (void) cv_timedwait_sig_hrtime(&smrt->smrt_cv_finishq,
838*b8aa3defSJoshua M. Clulow &smrt->smrt_mutex, smcm->smcm_expiry);
839*b8aa3defSJoshua M. Clulow }
840*b8aa3defSJoshua M. Clulow }
841*b8aa3defSJoshua M. Clulow
842*b8aa3defSJoshua M. Clulow /*
843*b8aa3defSJoshua M. Clulow * Fire the completion callback for this command. The callback
844*b8aa3defSJoshua M. Clulow * is responsible for freeing the command, so it may not be
845*b8aa3defSJoshua M. Clulow * referenced again once this call returns.
846*b8aa3defSJoshua M. Clulow */
847*b8aa3defSJoshua M. Clulow smrt_process_finishq_one(smcm);
848*b8aa3defSJoshua M. Clulow
849*b8aa3defSJoshua M. Clulow return (0);
850*b8aa3defSJoshua M. Clulow }
851*b8aa3defSJoshua M. Clulow
852*b8aa3defSJoshua M. Clulow void
smrt_intr_set(smrt_t * smrt,boolean_t enabled)853*b8aa3defSJoshua M. Clulow smrt_intr_set(smrt_t *smrt, boolean_t enabled)
854*b8aa3defSJoshua M. Clulow {
855*b8aa3defSJoshua M. Clulow /*
856*b8aa3defSJoshua M. Clulow * Read the Interrupt Mask Register.
857*b8aa3defSJoshua M. Clulow */
858*b8aa3defSJoshua M. Clulow uint32_t imr = smrt_get32(smrt, CISS_I2O_INTERRUPT_MASK);
859*b8aa3defSJoshua M. Clulow
860*b8aa3defSJoshua M. Clulow switch (smrt->smrt_ctlr_mode) {
861*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_SIMPLE:
862*b8aa3defSJoshua M. Clulow if (enabled) {
863*b8aa3defSJoshua M. Clulow imr &= ~CISS_IMR_BIT_SIMPLE_INTR_DISABLE;
864*b8aa3defSJoshua M. Clulow } else {
865*b8aa3defSJoshua M. Clulow imr |= CISS_IMR_BIT_SIMPLE_INTR_DISABLE;
866*b8aa3defSJoshua M. Clulow }
867*b8aa3defSJoshua M. Clulow smrt_put32(smrt, CISS_I2O_INTERRUPT_MASK, imr);
868*b8aa3defSJoshua M. Clulow return;
869*b8aa3defSJoshua M. Clulow
870*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_UNKNOWN:
871*b8aa3defSJoshua M. Clulow break;
872*b8aa3defSJoshua M. Clulow }
873*b8aa3defSJoshua M. Clulow panic("unknown controller mode");
874*b8aa3defSJoshua M. Clulow }
875*b8aa3defSJoshua M. Clulow
876*b8aa3defSJoshua M. Clulow /*
877*b8aa3defSJoshua M. Clulow * Signal to the controller that we have updated the Configuration Table by
878*b8aa3defSJoshua M. Clulow * writing to the Inbound Doorbell Register. The controller will, after some
879*b8aa3defSJoshua M. Clulow * number of seconds, acknowledge this by clearing the bit.
880*b8aa3defSJoshua M. Clulow *
881*b8aa3defSJoshua M. Clulow * If successful, return DDI_SUCCESS. If the controller takes too long to
882*b8aa3defSJoshua M. Clulow * acknowledge, return DDI_FAILURE.
883*b8aa3defSJoshua M. Clulow */
884*b8aa3defSJoshua M. Clulow int
smrt_cfgtbl_flush(smrt_t * smrt)885*b8aa3defSJoshua M. Clulow smrt_cfgtbl_flush(smrt_t *smrt)
886*b8aa3defSJoshua M. Clulow {
887*b8aa3defSJoshua M. Clulow /*
888*b8aa3defSJoshua M. Clulow * Read the current value of the Inbound Doorbell Register.
889*b8aa3defSJoshua M. Clulow */
890*b8aa3defSJoshua M. Clulow uint32_t idr = smrt_get32(smrt, CISS_I2O_INBOUND_DOORBELL);
891*b8aa3defSJoshua M. Clulow
892*b8aa3defSJoshua M. Clulow /*
893*b8aa3defSJoshua M. Clulow * Signal the Configuration Table change to the controller.
894*b8aa3defSJoshua M. Clulow */
895*b8aa3defSJoshua M. Clulow idr |= CISS_IDR_BIT_CFGTBL_CHANGE;
896*b8aa3defSJoshua M. Clulow smrt_put32(smrt, CISS_I2O_INBOUND_DOORBELL, idr);
897*b8aa3defSJoshua M. Clulow
898*b8aa3defSJoshua M. Clulow /*
899*b8aa3defSJoshua M. Clulow * Wait for the controller to acknowledge the change.
900*b8aa3defSJoshua M. Clulow */
901*b8aa3defSJoshua M. Clulow for (unsigned i = 0; i < smrt_ciss_init_time; i++) {
902*b8aa3defSJoshua M. Clulow idr = smrt_get32(smrt, CISS_I2O_INBOUND_DOORBELL);
903*b8aa3defSJoshua M. Clulow
904*b8aa3defSJoshua M. Clulow if ((idr & CISS_IDR_BIT_CFGTBL_CHANGE) == 0) {
905*b8aa3defSJoshua M. Clulow return (DDI_SUCCESS);
906*b8aa3defSJoshua M. Clulow }
907*b8aa3defSJoshua M. Clulow
908*b8aa3defSJoshua M. Clulow /*
909*b8aa3defSJoshua M. Clulow * Wait for one second before trying again.
910*b8aa3defSJoshua M. Clulow */
911*b8aa3defSJoshua M. Clulow delay(drv_usectohz(1000000));
912*b8aa3defSJoshua M. Clulow }
913*b8aa3defSJoshua M. Clulow
914*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "time out expired before controller "
915*b8aa3defSJoshua M. Clulow "configuration completed");
916*b8aa3defSJoshua M. Clulow return (DDI_FAILURE);
917*b8aa3defSJoshua M. Clulow }
918*b8aa3defSJoshua M. Clulow
919*b8aa3defSJoshua M. Clulow int
smrt_cfgtbl_transport_has_support(smrt_t * smrt,int xport)920*b8aa3defSJoshua M. Clulow smrt_cfgtbl_transport_has_support(smrt_t *smrt, int xport)
921*b8aa3defSJoshua M. Clulow {
922*b8aa3defSJoshua M. Clulow VERIFY(xport == CISS_CFGTBL_XPORT_SIMPLE);
923*b8aa3defSJoshua M. Clulow
924*b8aa3defSJoshua M. Clulow /*
925*b8aa3defSJoshua M. Clulow * Read the current value of the "Supported Transport Methods" field in
926*b8aa3defSJoshua M. Clulow * the Configuration Table.
927*b8aa3defSJoshua M. Clulow */
928*b8aa3defSJoshua M. Clulow uint32_t xport_active = ddi_get32(smrt->smrt_ct_handle,
929*b8aa3defSJoshua M. Clulow &smrt->smrt_ct->TransportSupport);
930*b8aa3defSJoshua M. Clulow
931*b8aa3defSJoshua M. Clulow /*
932*b8aa3defSJoshua M. Clulow * Check that the desired transport method is supported by the
933*b8aa3defSJoshua M. Clulow * controller:
934*b8aa3defSJoshua M. Clulow */
935*b8aa3defSJoshua M. Clulow if ((xport_active & xport) == 0) {
936*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "controller does not support "
937*b8aa3defSJoshua M. Clulow "method \"%s\"", xport == CISS_CFGTBL_XPORT_SIMPLE ?
938*b8aa3defSJoshua M. Clulow "simple" : "performant");
939*b8aa3defSJoshua M. Clulow return (DDI_FAILURE);
940*b8aa3defSJoshua M. Clulow }
941*b8aa3defSJoshua M. Clulow
942*b8aa3defSJoshua M. Clulow return (DDI_SUCCESS);
943*b8aa3defSJoshua M. Clulow }
944*b8aa3defSJoshua M. Clulow
945*b8aa3defSJoshua M. Clulow void
smrt_cfgtbl_transport_set(smrt_t * smrt,int xport)946*b8aa3defSJoshua M. Clulow smrt_cfgtbl_transport_set(smrt_t *smrt, int xport)
947*b8aa3defSJoshua M. Clulow {
948*b8aa3defSJoshua M. Clulow VERIFY(xport == CISS_CFGTBL_XPORT_SIMPLE);
949*b8aa3defSJoshua M. Clulow
950*b8aa3defSJoshua M. Clulow ddi_put32(smrt->smrt_ct_handle, &smrt->smrt_ct->TransportRequest,
951*b8aa3defSJoshua M. Clulow xport);
952*b8aa3defSJoshua M. Clulow }
953*b8aa3defSJoshua M. Clulow
954*b8aa3defSJoshua M. Clulow int
smrt_cfgtbl_transport_confirm(smrt_t * smrt,int xport)955*b8aa3defSJoshua M. Clulow smrt_cfgtbl_transport_confirm(smrt_t *smrt, int xport)
956*b8aa3defSJoshua M. Clulow {
957*b8aa3defSJoshua M. Clulow VERIFY(xport == CISS_CFGTBL_XPORT_SIMPLE);
958*b8aa3defSJoshua M. Clulow
959*b8aa3defSJoshua M. Clulow /*
960*b8aa3defSJoshua M. Clulow * Read the current value of the TransportActive field in the
961*b8aa3defSJoshua M. Clulow * Configuration Table.
962*b8aa3defSJoshua M. Clulow */
963*b8aa3defSJoshua M. Clulow uint32_t xport_active = ddi_get32(smrt->smrt_ct_handle,
964*b8aa3defSJoshua M. Clulow &smrt->smrt_ct->TransportActive);
965*b8aa3defSJoshua M. Clulow
966*b8aa3defSJoshua M. Clulow /*
967*b8aa3defSJoshua M. Clulow * Check that the desired transport method is now active:
968*b8aa3defSJoshua M. Clulow */
969*b8aa3defSJoshua M. Clulow if ((xport_active & xport) == 0) {
970*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "failed to enable transport "
971*b8aa3defSJoshua M. Clulow "method \"%s\"", xport == CISS_CFGTBL_XPORT_SIMPLE ?
972*b8aa3defSJoshua M. Clulow "simple" : "performant");
973*b8aa3defSJoshua M. Clulow return (DDI_FAILURE);
974*b8aa3defSJoshua M. Clulow }
975*b8aa3defSJoshua M. Clulow
976*b8aa3defSJoshua M. Clulow /*
977*b8aa3defSJoshua M. Clulow * Ensure that the controller is now ready to accept commands.
978*b8aa3defSJoshua M. Clulow */
979*b8aa3defSJoshua M. Clulow if ((xport_active & CISS_CFGTBL_READY_FOR_COMMANDS) == 0) {
980*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "controller not ready to "
981*b8aa3defSJoshua M. Clulow "accept commands");
982*b8aa3defSJoshua M. Clulow return (DDI_FAILURE);
983*b8aa3defSJoshua M. Clulow }
984*b8aa3defSJoshua M. Clulow
985*b8aa3defSJoshua M. Clulow return (DDI_SUCCESS);
986*b8aa3defSJoshua M. Clulow }
987*b8aa3defSJoshua M. Clulow
988*b8aa3defSJoshua M. Clulow uint32_t
smrt_ctlr_get_maxsgelements(smrt_t * smrt)989*b8aa3defSJoshua M. Clulow smrt_ctlr_get_maxsgelements(smrt_t *smrt)
990*b8aa3defSJoshua M. Clulow {
991*b8aa3defSJoshua M. Clulow return (ddi_get32(smrt->smrt_ct_handle, &smrt->smrt_ct->MaxSGElements));
992*b8aa3defSJoshua M. Clulow }
993*b8aa3defSJoshua M. Clulow
994*b8aa3defSJoshua M. Clulow uint32_t
smrt_ctlr_get_cmdsoutmax(smrt_t * smrt)995*b8aa3defSJoshua M. Clulow smrt_ctlr_get_cmdsoutmax(smrt_t *smrt)
996*b8aa3defSJoshua M. Clulow {
997*b8aa3defSJoshua M. Clulow return (ddi_get32(smrt->smrt_ct_handle, &smrt->smrt_ct->CmdsOutMax));
998*b8aa3defSJoshua M. Clulow }
999*b8aa3defSJoshua M. Clulow
1000*b8aa3defSJoshua M. Clulow static uint32_t
smrt_ctlr_get_hostdrvsup(smrt_t * smrt)1001*b8aa3defSJoshua M. Clulow smrt_ctlr_get_hostdrvsup(smrt_t *smrt)
1002*b8aa3defSJoshua M. Clulow {
1003*b8aa3defSJoshua M. Clulow return (ddi_get32(smrt->smrt_ct_handle,
1004*b8aa3defSJoshua M. Clulow &smrt->smrt_ct->HostDrvrSupport));
1005*b8aa3defSJoshua M. Clulow }
1006*b8aa3defSJoshua M. Clulow
1007*b8aa3defSJoshua M. Clulow int
smrt_ctlr_init(smrt_t * smrt)1008*b8aa3defSJoshua M. Clulow smrt_ctlr_init(smrt_t *smrt)
1009*b8aa3defSJoshua M. Clulow {
1010*b8aa3defSJoshua M. Clulow uint8_t signature[4] = { 'C', 'I', 'S', 'S' };
1011*b8aa3defSJoshua M. Clulow int e;
1012*b8aa3defSJoshua M. Clulow
1013*b8aa3defSJoshua M. Clulow if ((e = smrt_ctlr_wait_for_state(smrt,
1014*b8aa3defSJoshua M. Clulow SMRT_WAIT_STATE_READY)) != DDI_SUCCESS) {
1015*b8aa3defSJoshua M. Clulow return (e);
1016*b8aa3defSJoshua M. Clulow }
1017*b8aa3defSJoshua M. Clulow
1018*b8aa3defSJoshua M. Clulow /*
1019*b8aa3defSJoshua M. Clulow * The configuration table contains an ASCII signature ("CISS") which
1020*b8aa3defSJoshua M. Clulow * should be checked as we initialise the controller.
1021*b8aa3defSJoshua M. Clulow * See: "9.1 Configuration Table" in CISS Specification.
1022*b8aa3defSJoshua M. Clulow */
1023*b8aa3defSJoshua M. Clulow for (unsigned i = 0; i < 4; i++) {
1024*b8aa3defSJoshua M. Clulow if (ddi_get8(smrt->smrt_ct_handle,
1025*b8aa3defSJoshua M. Clulow &smrt->smrt_ct->Signature[i]) != signature[i]) {
1026*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "invalid signature "
1027*b8aa3defSJoshua M. Clulow "detected");
1028*b8aa3defSJoshua M. Clulow return (DDI_FAILURE);
1029*b8aa3defSJoshua M. Clulow }
1030*b8aa3defSJoshua M. Clulow }
1031*b8aa3defSJoshua M. Clulow
1032*b8aa3defSJoshua M. Clulow /*
1033*b8aa3defSJoshua M. Clulow * Initialise an appropriate Transport Method. For now, this driver
1034*b8aa3defSJoshua M. Clulow * only supports the "Simple" method.
1035*b8aa3defSJoshua M. Clulow */
1036*b8aa3defSJoshua M. Clulow if ((e = smrt_ctlr_init_simple(smrt)) != DDI_SUCCESS) {
1037*b8aa3defSJoshua M. Clulow return (e);
1038*b8aa3defSJoshua M. Clulow }
1039*b8aa3defSJoshua M. Clulow
1040*b8aa3defSJoshua M. Clulow /*
1041*b8aa3defSJoshua M. Clulow * Save some common feature support bitfields.
1042*b8aa3defSJoshua M. Clulow */
1043*b8aa3defSJoshua M. Clulow smrt->smrt_host_support = smrt_ctlr_get_hostdrvsup(smrt);
1044*b8aa3defSJoshua M. Clulow smrt->smrt_bus_support = ddi_get32(smrt->smrt_ct_handle,
1045*b8aa3defSJoshua M. Clulow &smrt->smrt_ct->BusTypes);
1046*b8aa3defSJoshua M. Clulow
1047*b8aa3defSJoshua M. Clulow /*
1048*b8aa3defSJoshua M. Clulow * Read initial controller heartbeat value and mark the current
1049*b8aa3defSJoshua M. Clulow * reading time.
1050*b8aa3defSJoshua M. Clulow */
1051*b8aa3defSJoshua M. Clulow smrt->smrt_last_heartbeat = ddi_get32(smrt->smrt_ct_handle,
1052*b8aa3defSJoshua M. Clulow &smrt->smrt_ct->HeartBeat);
1053*b8aa3defSJoshua M. Clulow smrt->smrt_last_heartbeat_time = gethrtime();
1054*b8aa3defSJoshua M. Clulow
1055*b8aa3defSJoshua M. Clulow /*
1056*b8aa3defSJoshua M. Clulow * Determine the firmware version of the controller so that we can
1057*b8aa3defSJoshua M. Clulow * select which type of interrupts to use.
1058*b8aa3defSJoshua M. Clulow */
1059*b8aa3defSJoshua M. Clulow if ((e = smrt_ctlr_versions(smrt, SMRT_DISCOVER_TIMEOUT,
1060*b8aa3defSJoshua M. Clulow &smrt->smrt_versions)) != 0) {
1061*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "could not identify "
1062*b8aa3defSJoshua M. Clulow "controller (%d)", e);
1063*b8aa3defSJoshua M. Clulow return (DDI_FAILURE);
1064*b8aa3defSJoshua M. Clulow }
1065*b8aa3defSJoshua M. Clulow
1066*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_NOTE, "!firmware rev %s",
1067*b8aa3defSJoshua M. Clulow smrt->smrt_versions.smrtv_firmware_rev);
1068*b8aa3defSJoshua M. Clulow
1069*b8aa3defSJoshua M. Clulow return (DDI_SUCCESS);
1070*b8aa3defSJoshua M. Clulow }
1071*b8aa3defSJoshua M. Clulow
1072*b8aa3defSJoshua M. Clulow void
smrt_ctlr_teardown(smrt_t * smrt)1073*b8aa3defSJoshua M. Clulow smrt_ctlr_teardown(smrt_t *smrt)
1074*b8aa3defSJoshua M. Clulow {
1075*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_STATUS_RUNNING;
1076*b8aa3defSJoshua M. Clulow
1077*b8aa3defSJoshua M. Clulow switch (smrt->smrt_ctlr_mode) {
1078*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_SIMPLE:
1079*b8aa3defSJoshua M. Clulow smrt_ctlr_teardown_simple(smrt);
1080*b8aa3defSJoshua M. Clulow return;
1081*b8aa3defSJoshua M. Clulow
1082*b8aa3defSJoshua M. Clulow case SMRT_CTLR_MODE_UNKNOWN:
1083*b8aa3defSJoshua M. Clulow return;
1084*b8aa3defSJoshua M. Clulow }
1085*b8aa3defSJoshua M. Clulow
1086*b8aa3defSJoshua M. Clulow panic("unknown controller mode");
1087*b8aa3defSJoshua M. Clulow }
1088*b8aa3defSJoshua M. Clulow
1089*b8aa3defSJoshua M. Clulow int
smrt_ctlr_wait_for_state(smrt_t * smrt,smrt_wait_state_t state)1090*b8aa3defSJoshua M. Clulow smrt_ctlr_wait_for_state(smrt_t *smrt, smrt_wait_state_t state)
1091*b8aa3defSJoshua M. Clulow {
1092*b8aa3defSJoshua M. Clulow unsigned wait_usec = 100 * 1000;
1093*b8aa3defSJoshua M. Clulow unsigned wait_count = SMRT_WAIT_DELAY_SECONDS * 1000000 / wait_usec;
1094*b8aa3defSJoshua M. Clulow
1095*b8aa3defSJoshua M. Clulow VERIFY(state == SMRT_WAIT_STATE_READY ||
1096*b8aa3defSJoshua M. Clulow state == SMRT_WAIT_STATE_UNREADY);
1097*b8aa3defSJoshua M. Clulow
1098*b8aa3defSJoshua M. Clulow /*
1099*b8aa3defSJoshua M. Clulow * Read from the Scratchpad Register until the expected ready signature
1100*b8aa3defSJoshua M. Clulow * is detected. This behaviour is not described in the CISS
1101*b8aa3defSJoshua M. Clulow * specification.
1102*b8aa3defSJoshua M. Clulow *
1103*b8aa3defSJoshua M. Clulow * If the device is not in the desired state immediately, sleep for a
1104*b8aa3defSJoshua M. Clulow * second and try again. If the device has not become ready in 300
1105*b8aa3defSJoshua M. Clulow * seconds, give up.
1106*b8aa3defSJoshua M. Clulow */
1107*b8aa3defSJoshua M. Clulow for (unsigned i = 0; i < wait_count; i++) {
1108*b8aa3defSJoshua M. Clulow uint32_t spr = smrt_get32(smrt, CISS_I2O_SCRATCHPAD);
1109*b8aa3defSJoshua M. Clulow
1110*b8aa3defSJoshua M. Clulow switch (state) {
1111*b8aa3defSJoshua M. Clulow case SMRT_WAIT_STATE_READY:
1112*b8aa3defSJoshua M. Clulow if (spr == CISS_SCRATCHPAD_INITIALISED) {
1113*b8aa3defSJoshua M. Clulow return (DDI_SUCCESS);
1114*b8aa3defSJoshua M. Clulow }
1115*b8aa3defSJoshua M. Clulow break;
1116*b8aa3defSJoshua M. Clulow
1117*b8aa3defSJoshua M. Clulow case SMRT_WAIT_STATE_UNREADY:
1118*b8aa3defSJoshua M. Clulow if (spr != CISS_SCRATCHPAD_INITIALISED) {
1119*b8aa3defSJoshua M. Clulow return (DDI_SUCCESS);
1120*b8aa3defSJoshua M. Clulow }
1121*b8aa3defSJoshua M. Clulow break;
1122*b8aa3defSJoshua M. Clulow }
1123*b8aa3defSJoshua M. Clulow
1124*b8aa3defSJoshua M. Clulow if (ddi_in_panic()) {
1125*b8aa3defSJoshua M. Clulow /*
1126*b8aa3defSJoshua M. Clulow * There is no sleep for the panicking, so we
1127*b8aa3defSJoshua M. Clulow * must spin wait:
1128*b8aa3defSJoshua M. Clulow */
1129*b8aa3defSJoshua M. Clulow drv_usecwait(wait_usec);
1130*b8aa3defSJoshua M. Clulow } else {
1131*b8aa3defSJoshua M. Clulow /*
1132*b8aa3defSJoshua M. Clulow * Wait for a quarter second and try again.
1133*b8aa3defSJoshua M. Clulow */
1134*b8aa3defSJoshua M. Clulow delay(drv_usectohz(wait_usec));
1135*b8aa3defSJoshua M. Clulow }
1136*b8aa3defSJoshua M. Clulow }
1137*b8aa3defSJoshua M. Clulow
1138*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "time out waiting for controller "
1139*b8aa3defSJoshua M. Clulow "to enter state \"%s\"", state == SMRT_WAIT_STATE_READY ?
1140*b8aa3defSJoshua M. Clulow "ready": "unready");
1141*b8aa3defSJoshua M. Clulow return (DDI_FAILURE);
1142*b8aa3defSJoshua M. Clulow }
1143*b8aa3defSJoshua M. Clulow
1144*b8aa3defSJoshua M. Clulow void
smrt_lockup_check(smrt_t * smrt)1145*b8aa3defSJoshua M. Clulow smrt_lockup_check(smrt_t *smrt)
1146*b8aa3defSJoshua M. Clulow {
1147*b8aa3defSJoshua M. Clulow /*
1148*b8aa3defSJoshua M. Clulow * Read the current controller heartbeat value.
1149*b8aa3defSJoshua M. Clulow */
1150*b8aa3defSJoshua M. Clulow uint32_t heartbeat = ddi_get32(smrt->smrt_ct_handle,
1151*b8aa3defSJoshua M. Clulow &smrt->smrt_ct->HeartBeat);
1152*b8aa3defSJoshua M. Clulow
1153*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
1154*b8aa3defSJoshua M. Clulow
1155*b8aa3defSJoshua M. Clulow /*
1156*b8aa3defSJoshua M. Clulow * Check to see if the value is the same as last time we looked:
1157*b8aa3defSJoshua M. Clulow */
1158*b8aa3defSJoshua M. Clulow if (heartbeat != smrt->smrt_last_heartbeat) {
1159*b8aa3defSJoshua M. Clulow /*
1160*b8aa3defSJoshua M. Clulow * The heartbeat value has changed, which suggests that the
1161*b8aa3defSJoshua M. Clulow * firmware in the controller has not yet come to a complete
1162*b8aa3defSJoshua M. Clulow * stop. Record the new value, as well as the current time.
1163*b8aa3defSJoshua M. Clulow */
1164*b8aa3defSJoshua M. Clulow smrt->smrt_last_heartbeat = heartbeat;
1165*b8aa3defSJoshua M. Clulow smrt->smrt_last_heartbeat_time = gethrtime();
1166*b8aa3defSJoshua M. Clulow return;
1167*b8aa3defSJoshua M. Clulow }
1168*b8aa3defSJoshua M. Clulow
1169*b8aa3defSJoshua M. Clulow /*
1170*b8aa3defSJoshua M. Clulow * The controller _might_ have been able to signal to us that is
1171*b8aa3defSJoshua M. Clulow * has locked up. This is a truly unfathomable state of affairs:
1172*b8aa3defSJoshua M. Clulow * If the firmware can tell it has flown off the rails, why not
1173*b8aa3defSJoshua M. Clulow * simply reset the controller?
1174*b8aa3defSJoshua M. Clulow */
1175*b8aa3defSJoshua M. Clulow uint32_t odr = smrt_get32(smrt, CISS_I2O_OUTBOUND_DOORBELL_STATUS);
1176*b8aa3defSJoshua M. Clulow uint32_t spr = smrt_get32(smrt, CISS_I2O_SCRATCHPAD);
1177*b8aa3defSJoshua M. Clulow if ((odr & CISS_ODR_BIT_LOCKUP) != 0) {
1178*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "HP SmartArray firmware has "
1179*b8aa3defSJoshua M. Clulow "reported a critical fault (odr %08x spr %08x)",
1180*b8aa3defSJoshua M. Clulow odr, spr);
1181*b8aa3defSJoshua M. Clulow }
1182*b8aa3defSJoshua M. Clulow
1183*b8aa3defSJoshua M. Clulow if (gethrtime() > smrt->smrt_last_heartbeat_time + 60 * NANOSEC) {
1184*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "HP SmartArray firmware has "
1185*b8aa3defSJoshua M. Clulow "stopped responding (odr %08x spr %08x)",
1186*b8aa3defSJoshua M. Clulow odr, spr);
1187*b8aa3defSJoshua M. Clulow }
1188*b8aa3defSJoshua M. Clulow }
1189*b8aa3defSJoshua M. Clulow
1190*b8aa3defSJoshua M. Clulow /*
1191*b8aa3defSJoshua M. Clulow * Probe the controller with the IDENTIFY CONTROLLER request. This is a BMIC
1192*b8aa3defSJoshua M. Clulow * command, so it must be submitted to the controller and we must poll for its
1193*b8aa3defSJoshua M. Clulow * completion. This functionality is only presently used during controller
1194*b8aa3defSJoshua M. Clulow * initialisation, so it uses the special pre-initialisation path for command
1195*b8aa3defSJoshua M. Clulow * allocation and submission.
1196*b8aa3defSJoshua M. Clulow */
1197*b8aa3defSJoshua M. Clulow static int
smrt_ctlr_identify(smrt_t * smrt,uint16_t timeout,smrt_identify_controller_t * resp)1198*b8aa3defSJoshua M. Clulow smrt_ctlr_identify(smrt_t *smrt, uint16_t timeout,
1199*b8aa3defSJoshua M. Clulow smrt_identify_controller_t *resp)
1200*b8aa3defSJoshua M. Clulow {
1201*b8aa3defSJoshua M. Clulow smrt_command_t *smcm;
1202*b8aa3defSJoshua M. Clulow smrt_identify_controller_req_t smicr;
1203*b8aa3defSJoshua M. Clulow int r;
1204*b8aa3defSJoshua M. Clulow size_t sz;
1205*b8aa3defSJoshua M. Clulow
1206*b8aa3defSJoshua M. Clulow /*
1207*b8aa3defSJoshua M. Clulow * Allocate a command with a data buffer; the controller will fill it
1208*b8aa3defSJoshua M. Clulow * with identification information. There is some suggestion in the
1209*b8aa3defSJoshua M. Clulow * firmware-level specification that the buffer length should be a
1210*b8aa3defSJoshua M. Clulow * multiple of 512 bytes for some controllers, so we round up.
1211*b8aa3defSJoshua M. Clulow */
1212*b8aa3defSJoshua M. Clulow sz = P2ROUNDUP_TYPED(sizeof (*resp), 512, size_t);
1213*b8aa3defSJoshua M. Clulow if ((smcm = smrt_command_alloc_preinit(smrt, sz, KM_SLEEP)) == NULL) {
1214*b8aa3defSJoshua M. Clulow return (ENOMEM);
1215*b8aa3defSJoshua M. Clulow }
1216*b8aa3defSJoshua M. Clulow
1217*b8aa3defSJoshua M. Clulow smrt_write_controller_lun_addr(&smcm->smcm_va_cmd->Header.LUN);
1218*b8aa3defSJoshua M. Clulow
1219*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.CDBLen = sizeof (smicr);
1220*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Timeout = timeout;
1221*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD;
1222*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_SIMPLE;
1223*b8aa3defSJoshua M. Clulow smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_READ;
1224*b8aa3defSJoshua M. Clulow
1225*b8aa3defSJoshua M. Clulow /*
1226*b8aa3defSJoshua M. Clulow * Construct the IDENTIFY CONTROLLER request CDB. Note that any
1227*b8aa3defSJoshua M. Clulow * reserved fields in the request must be filled with zeroes.
1228*b8aa3defSJoshua M. Clulow */
1229*b8aa3defSJoshua M. Clulow bzero(&smicr, sizeof (smicr));
1230*b8aa3defSJoshua M. Clulow smicr.smicr_opcode = CISS_SCMD_BMIC_READ;
1231*b8aa3defSJoshua M. Clulow smicr.smicr_lun = 0;
1232*b8aa3defSJoshua M. Clulow smicr.smicr_command = CISS_BMIC_IDENTIFY_CONTROLLER;
1233*b8aa3defSJoshua M. Clulow bcopy(&smicr, &smcm->smcm_va_cmd->Request.CDB[0],
1234*b8aa3defSJoshua M. Clulow MIN(CISS_CDBLEN, sizeof (smicr)));
1235*b8aa3defSJoshua M. Clulow
1236*b8aa3defSJoshua M. Clulow /*
1237*b8aa3defSJoshua M. Clulow * Send the command to the device and poll for its completion.
1238*b8aa3defSJoshua M. Clulow */
1239*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
1240*b8aa3defSJoshua M. Clulow smcm->smcm_expiry = gethrtime() + timeout * NANOSEC;
1241*b8aa3defSJoshua M. Clulow if ((r = smrt_preinit_command_simple(smrt, smcm)) != 0) {
1242*b8aa3defSJoshua M. Clulow VERIFY3S(r, ==, ETIMEDOUT);
1243*b8aa3defSJoshua M. Clulow VERIFY0(smcm->smcm_status & SMRT_CMD_STATUS_POLL_COMPLETE);
1244*b8aa3defSJoshua M. Clulow
1245*b8aa3defSJoshua M. Clulow /*
1246*b8aa3defSJoshua M. Clulow * This command timed out, but the driver is not presently
1247*b8aa3defSJoshua M. Clulow * initialised to the point where we can try to abort it.
1248*b8aa3defSJoshua M. Clulow * The command was created with the PREINIT type, so it
1249*b8aa3defSJoshua M. Clulow * does not appear in the global command tracking list.
1250*b8aa3defSJoshua M. Clulow * In order to avoid problems with DMA from the controller,
1251*b8aa3defSJoshua M. Clulow * we have to leak the command allocation.
1252*b8aa3defSJoshua M. Clulow */
1253*b8aa3defSJoshua M. Clulow smcm = NULL;
1254*b8aa3defSJoshua M. Clulow goto out;
1255*b8aa3defSJoshua M. Clulow }
1256*b8aa3defSJoshua M. Clulow
1257*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_RESET_SENT) {
1258*b8aa3defSJoshua M. Clulow /*
1259*b8aa3defSJoshua M. Clulow * The controller was reset while we were trying to identify
1260*b8aa3defSJoshua M. Clulow * it. Report failure.
1261*b8aa3defSJoshua M. Clulow */
1262*b8aa3defSJoshua M. Clulow r = EIO;
1263*b8aa3defSJoshua M. Clulow goto out;
1264*b8aa3defSJoshua M. Clulow }
1265*b8aa3defSJoshua M. Clulow
1266*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_ERROR) {
1267*b8aa3defSJoshua M. Clulow ErrorInfo_t *ei = smcm->smcm_va_err;
1268*b8aa3defSJoshua M. Clulow
1269*b8aa3defSJoshua M. Clulow if (ei->CommandStatus != CISS_CMD_DATA_UNDERRUN) {
1270*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "identify "
1271*b8aa3defSJoshua M. Clulow "controller error: status 0x%x",
1272*b8aa3defSJoshua M. Clulow ei->CommandStatus);
1273*b8aa3defSJoshua M. Clulow r = EIO;
1274*b8aa3defSJoshua M. Clulow goto out;
1275*b8aa3defSJoshua M. Clulow }
1276*b8aa3defSJoshua M. Clulow }
1277*b8aa3defSJoshua M. Clulow
1278*b8aa3defSJoshua M. Clulow if (resp != NULL) {
1279*b8aa3defSJoshua M. Clulow /*
1280*b8aa3defSJoshua M. Clulow * Copy the identify response out for the caller.
1281*b8aa3defSJoshua M. Clulow */
1282*b8aa3defSJoshua M. Clulow bcopy(smcm->smcm_internal->smcmi_va, resp, sizeof (*resp));
1283*b8aa3defSJoshua M. Clulow }
1284*b8aa3defSJoshua M. Clulow
1285*b8aa3defSJoshua M. Clulow r = 0;
1286*b8aa3defSJoshua M. Clulow
1287*b8aa3defSJoshua M. Clulow out:
1288*b8aa3defSJoshua M. Clulow if (smcm != NULL) {
1289*b8aa3defSJoshua M. Clulow smrt_command_free(smcm);
1290*b8aa3defSJoshua M. Clulow }
1291*b8aa3defSJoshua M. Clulow return (r);
1292*b8aa3defSJoshua M. Clulow }
1293*b8aa3defSJoshua M. Clulow
1294*b8aa3defSJoshua M. Clulow /*
1295*b8aa3defSJoshua M. Clulow * The firmware versions in an IDENTIFY CONTROLLER response generally take
1296*b8aa3defSJoshua M. Clulow * the form of a four byte ASCII string containing a dotted decimal version
1297*b8aa3defSJoshua M. Clulow * number; e.g., "8.00".
1298*b8aa3defSJoshua M. Clulow *
1299*b8aa3defSJoshua M. Clulow * This function sanitises the firmware version, replacing unexpected
1300*b8aa3defSJoshua M. Clulow * values with a question mark.
1301*b8aa3defSJoshua M. Clulow */
1302*b8aa3defSJoshua M. Clulow static void
smrt_copy_firmware_version(uint8_t * src,char * dst)1303*b8aa3defSJoshua M. Clulow smrt_copy_firmware_version(uint8_t *src, char *dst)
1304*b8aa3defSJoshua M. Clulow {
1305*b8aa3defSJoshua M. Clulow for (unsigned i = 0; i < 4; i++) {
1306*b8aa3defSJoshua M. Clulow /*
1307*b8aa3defSJoshua M. Clulow * Make sure that this is a 7-bit clean ASCII value.
1308*b8aa3defSJoshua M. Clulow */
1309*b8aa3defSJoshua M. Clulow char c = src[i] <= 0x7f ? (char)(src[i] & 0x7f) : '?';
1310*b8aa3defSJoshua M. Clulow
1311*b8aa3defSJoshua M. Clulow if (isalnum(c) || c == '.' || c == ' ') {
1312*b8aa3defSJoshua M. Clulow dst[i] = c;
1313*b8aa3defSJoshua M. Clulow } else {
1314*b8aa3defSJoshua M. Clulow dst[i] = '?';
1315*b8aa3defSJoshua M. Clulow }
1316*b8aa3defSJoshua M. Clulow }
1317*b8aa3defSJoshua M. Clulow dst[4] = '\0';
1318*b8aa3defSJoshua M. Clulow }
1319*b8aa3defSJoshua M. Clulow
1320*b8aa3defSJoshua M. Clulow /*
1321*b8aa3defSJoshua M. Clulow * Using an IDENTIFY CONTROLLER request, determine firmware and controller
1322*b8aa3defSJoshua M. Clulow * version details. See the comments for "smrt_ctlr_identify()" for more
1323*b8aa3defSJoshua M. Clulow * details about calling context.
1324*b8aa3defSJoshua M. Clulow */
1325*b8aa3defSJoshua M. Clulow static int
smrt_ctlr_versions(smrt_t * smrt,uint16_t timeout,smrt_versions_t * smrtv)1326*b8aa3defSJoshua M. Clulow smrt_ctlr_versions(smrt_t *smrt, uint16_t timeout, smrt_versions_t *smrtv)
1327*b8aa3defSJoshua M. Clulow {
1328*b8aa3defSJoshua M. Clulow smrt_identify_controller_t smic;
1329*b8aa3defSJoshua M. Clulow int r;
1330*b8aa3defSJoshua M. Clulow
1331*b8aa3defSJoshua M. Clulow if ((r = smrt_ctlr_identify(smrt, timeout, &smic)) != 0) {
1332*b8aa3defSJoshua M. Clulow return (r);
1333*b8aa3defSJoshua M. Clulow }
1334*b8aa3defSJoshua M. Clulow
1335*b8aa3defSJoshua M. Clulow smrtv->smrtv_hardware_version = smic.smic_hardware_version;
1336*b8aa3defSJoshua M. Clulow smrt_copy_firmware_version(smic.smic_firmware_rev,
1337*b8aa3defSJoshua M. Clulow smrtv->smrtv_firmware_rev);
1338*b8aa3defSJoshua M. Clulow smrt_copy_firmware_version(smic.smic_recovery_rev,
1339*b8aa3defSJoshua M. Clulow smrtv->smrtv_recovery_rev);
1340*b8aa3defSJoshua M. Clulow smrt_copy_firmware_version(smic.smic_bootblock_rev,
1341*b8aa3defSJoshua M. Clulow smrtv->smrtv_bootblock_rev);
1342*b8aa3defSJoshua M. Clulow
1343*b8aa3defSJoshua M. Clulow return (0);
1344*b8aa3defSJoshua M. Clulow }
1345*b8aa3defSJoshua M. Clulow
1346*b8aa3defSJoshua M. Clulow int
smrt_ctlr_reset(smrt_t * smrt)1347*b8aa3defSJoshua M. Clulow smrt_ctlr_reset(smrt_t *smrt)
1348*b8aa3defSJoshua M. Clulow {
1349*b8aa3defSJoshua M. Clulow smrt_command_t *smcm, *smcm_nop;
1350*b8aa3defSJoshua M. Clulow int r;
1351*b8aa3defSJoshua M. Clulow
1352*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
1353*b8aa3defSJoshua M. Clulow
1354*b8aa3defSJoshua M. Clulow if (ddi_in_panic()) {
1355*b8aa3defSJoshua M. Clulow goto skip_check;
1356*b8aa3defSJoshua M. Clulow }
1357*b8aa3defSJoshua M. Clulow
1358*b8aa3defSJoshua M. Clulow if (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) {
1359*b8aa3defSJoshua M. Clulow /*
1360*b8aa3defSJoshua M. Clulow * Don't pile on. One reset is enough. Wait until
1361*b8aa3defSJoshua M. Clulow * it's complete, and then return success.
1362*b8aa3defSJoshua M. Clulow */
1363*b8aa3defSJoshua M. Clulow while (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) {
1364*b8aa3defSJoshua M. Clulow cv_wait(&smrt->smrt_cv_finishq, &smrt->smrt_mutex);
1365*b8aa3defSJoshua M. Clulow }
1366*b8aa3defSJoshua M. Clulow return (0);
1367*b8aa3defSJoshua M. Clulow }
1368*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_STATUS_RESETTING;
1369*b8aa3defSJoshua M. Clulow smrt->smrt_last_reset_start = gethrtime();
1370*b8aa3defSJoshua M. Clulow smrt->smrt_stats.smrts_ctlr_resets++;
1371*b8aa3defSJoshua M. Clulow
1372*b8aa3defSJoshua M. Clulow skip_check:
1373*b8aa3defSJoshua M. Clulow /*
1374*b8aa3defSJoshua M. Clulow * Allocate two commands: one for the soft reset message, which we
1375*b8aa3defSJoshua M. Clulow * cannot free until the controller has reset; and one for the ping we
1376*b8aa3defSJoshua M. Clulow * will use to determine when it is once again functional.
1377*b8aa3defSJoshua M. Clulow */
1378*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1379*b8aa3defSJoshua M. Clulow if ((smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
1380*b8aa3defSJoshua M. Clulow KM_NOSLEEP)) == NULL) {
1381*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1382*b8aa3defSJoshua M. Clulow return (ENOMEM);
1383*b8aa3defSJoshua M. Clulow }
1384*b8aa3defSJoshua M. Clulow if ((smcm_nop = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
1385*b8aa3defSJoshua M. Clulow KM_NOSLEEP)) == NULL) {
1386*b8aa3defSJoshua M. Clulow smrt_command_free(smcm);
1387*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1388*b8aa3defSJoshua M. Clulow return (ENOMEM);
1389*b8aa3defSJoshua M. Clulow }
1390*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1391*b8aa3defSJoshua M. Clulow
1392*b8aa3defSJoshua M. Clulow /*
1393*b8aa3defSJoshua M. Clulow * Send a soft reset command to the controller. If this command
1394*b8aa3defSJoshua M. Clulow * succeeds, there will likely be no completion notification. Instead,
1395*b8aa3defSJoshua M. Clulow * the device should become unavailable for some period of time and
1396*b8aa3defSJoshua M. Clulow * then become available again. Once available again, we know the soft
1397*b8aa3defSJoshua M. Clulow * reset has completed and should abort all in-flight commands.
1398*b8aa3defSJoshua M. Clulow */
1399*b8aa3defSJoshua M. Clulow smrt_write_message_reset_ctlr(smcm);
1400*b8aa3defSJoshua M. Clulow
1401*b8aa3defSJoshua M. Clulow /*
1402*b8aa3defSJoshua M. Clulow * Disable interrupts now.
1403*b8aa3defSJoshua M. Clulow */
1404*b8aa3defSJoshua M. Clulow smrt_intr_set(smrt, B_FALSE);
1405*b8aa3defSJoshua M. Clulow
1406*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "attempting controller soft reset");
1407*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
1408*b8aa3defSJoshua M. Clulow if ((r = smrt_submit(smrt, smcm)) != 0) {
1409*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
1410*b8aa3defSJoshua M. Clulow "submit failed (%d)", r);
1411*b8aa3defSJoshua M. Clulow }
1412*b8aa3defSJoshua M. Clulow
1413*b8aa3defSJoshua M. Clulow /*
1414*b8aa3defSJoshua M. Clulow * Mark every currently inflight command as being reset, including the
1415*b8aa3defSJoshua M. Clulow * soft reset command we just sent. Once we confirm the reset works,
1416*b8aa3defSJoshua M. Clulow * we can safely report that these commands have failed.
1417*b8aa3defSJoshua M. Clulow */
1418*b8aa3defSJoshua M. Clulow for (smrt_command_t *t = avl_first(&smrt->smrt_inflight);
1419*b8aa3defSJoshua M. Clulow t != NULL; t = AVL_NEXT(&smrt->smrt_inflight, t)) {
1420*b8aa3defSJoshua M. Clulow t->smcm_status |= SMRT_CMD_STATUS_RESET_SENT;
1421*b8aa3defSJoshua M. Clulow }
1422*b8aa3defSJoshua M. Clulow
1423*b8aa3defSJoshua M. Clulow /*
1424*b8aa3defSJoshua M. Clulow * Now that we have submitted our soft reset command, prevent
1425*b8aa3defSJoshua M. Clulow * the rest of the driver from interacting with the controller.
1426*b8aa3defSJoshua M. Clulow */
1427*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_STATUS_RUNNING;
1428*b8aa3defSJoshua M. Clulow
1429*b8aa3defSJoshua M. Clulow /*
1430*b8aa3defSJoshua M. Clulow * We do not expect a completion from the controller for our soft
1431*b8aa3defSJoshua M. Clulow * reset command, but we also cannot remove it from the inflight
1432*b8aa3defSJoshua M. Clulow * list until we know the controller has actually reset. To do
1433*b8aa3defSJoshua M. Clulow * otherwise would potentially allow the controller to scribble
1434*b8aa3defSJoshua M. Clulow * on the memory we were using.
1435*b8aa3defSJoshua M. Clulow */
1436*b8aa3defSJoshua M. Clulow smcm->smcm_status |= SMRT_CMD_STATUS_ABANDONED;
1437*b8aa3defSJoshua M. Clulow
1438*b8aa3defSJoshua M. Clulow if (smrt_ctlr_wait_for_state(smrt, SMRT_WAIT_STATE_UNREADY) !=
1439*b8aa3defSJoshua M. Clulow DDI_SUCCESS) {
1440*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
1441*b8aa3defSJoshua M. Clulow "controller did not become unready");
1442*b8aa3defSJoshua M. Clulow }
1443*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: controller unready");
1444*b8aa3defSJoshua M. Clulow
1445*b8aa3defSJoshua M. Clulow if (smrt_ctlr_wait_for_state(smrt, SMRT_WAIT_STATE_READY) !=
1446*b8aa3defSJoshua M. Clulow DDI_SUCCESS) {
1447*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
1448*b8aa3defSJoshua M. Clulow "controller did not come become ready");
1449*b8aa3defSJoshua M. Clulow }
1450*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: controller ready");
1451*b8aa3defSJoshua M. Clulow
1452*b8aa3defSJoshua M. Clulow /*
1453*b8aa3defSJoshua M. Clulow * In at least the Smart Array P420i, the controller can take 30-45
1454*b8aa3defSJoshua M. Clulow * seconds after the scratchpad register shows it as being available
1455*b8aa3defSJoshua M. Clulow * before it is ready to receive commands. In order to avoid hitting
1456*b8aa3defSJoshua M. Clulow * it too early with our post-reset ping, we will sleep for 10 seconds
1457*b8aa3defSJoshua M. Clulow * here.
1458*b8aa3defSJoshua M. Clulow */
1459*b8aa3defSJoshua M. Clulow if (ddi_in_panic()) {
1460*b8aa3defSJoshua M. Clulow drv_usecwait(10 * MICROSEC);
1461*b8aa3defSJoshua M. Clulow } else {
1462*b8aa3defSJoshua M. Clulow delay(drv_usectohz(10 * MICROSEC));
1463*b8aa3defSJoshua M. Clulow }
1464*b8aa3defSJoshua M. Clulow
1465*b8aa3defSJoshua M. Clulow smrt_ctlr_teardown(smrt);
1466*b8aa3defSJoshua M. Clulow if (smrt_ctlr_init(smrt) != DDI_SUCCESS) {
1467*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
1468*b8aa3defSJoshua M. Clulow "controller transport could not be configured");
1469*b8aa3defSJoshua M. Clulow }
1470*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: controller configured");
1471*b8aa3defSJoshua M. Clulow
1472*b8aa3defSJoshua M. Clulow smrt_write_message_nop(smcm_nop, 0);
1473*b8aa3defSJoshua M. Clulow smcm_nop->smcm_status |= SMRT_CMD_STATUS_POLLED |
1474*b8aa3defSJoshua M. Clulow SMRT_CMD_IGNORE_RUNNING;
1475*b8aa3defSJoshua M. Clulow if ((r = smrt_submit(smrt, smcm_nop)) != 0) {
1476*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
1477*b8aa3defSJoshua M. Clulow "ping could not be submitted (%d)", r);
1478*b8aa3defSJoshua M. Clulow }
1479*b8aa3defSJoshua M. Clulow
1480*b8aa3defSJoshua M. Clulow /*
1481*b8aa3defSJoshua M. Clulow * Interrupts are still masked at this stage. Poll manually in
1482*b8aa3defSJoshua M. Clulow * a way that will not trigger regular finish queue processing:
1483*b8aa3defSJoshua M. Clulow */
1484*b8aa3defSJoshua M. Clulow VERIFY(smcm_nop->smcm_status & SMRT_CMD_STATUS_INFLIGHT);
1485*b8aa3defSJoshua M. Clulow for (unsigned i = 0; i < 600; i++) {
1486*b8aa3defSJoshua M. Clulow smrt_retrieve_simple(smrt);
1487*b8aa3defSJoshua M. Clulow
1488*b8aa3defSJoshua M. Clulow if (!(smcm_nop->smcm_status & SMRT_CMD_STATUS_INFLIGHT)) {
1489*b8aa3defSJoshua M. Clulow /*
1490*b8aa3defSJoshua M. Clulow * Remove the ping command from the finish queue and
1491*b8aa3defSJoshua M. Clulow * process it manually. This processing must mirror
1492*b8aa3defSJoshua M. Clulow * what would have been done in smrt_process_finishq().
1493*b8aa3defSJoshua M. Clulow */
1494*b8aa3defSJoshua M. Clulow VERIFY(list_link_active(&smcm_nop->smcm_link_finish));
1495*b8aa3defSJoshua M. Clulow list_remove(&smrt->smrt_finishq, smcm_nop);
1496*b8aa3defSJoshua M. Clulow smrt_process_finishq_sync(smcm_nop);
1497*b8aa3defSJoshua M. Clulow smcm_nop->smcm_status |= SMRT_CMD_STATUS_POLL_COMPLETE;
1498*b8aa3defSJoshua M. Clulow smrt_process_finishq_one(smcm_nop);
1499*b8aa3defSJoshua M. Clulow break;
1500*b8aa3defSJoshua M. Clulow }
1501*b8aa3defSJoshua M. Clulow
1502*b8aa3defSJoshua M. Clulow if (ddi_in_panic()) {
1503*b8aa3defSJoshua M. Clulow drv_usecwait(100 * 1000);
1504*b8aa3defSJoshua M. Clulow } else {
1505*b8aa3defSJoshua M. Clulow delay(drv_usectohz(100 * 1000));
1506*b8aa3defSJoshua M. Clulow }
1507*b8aa3defSJoshua M. Clulow }
1508*b8aa3defSJoshua M. Clulow
1509*b8aa3defSJoshua M. Clulow if (!(smcm_nop->smcm_status & SMRT_CMD_STATUS_COMPLETE)) {
1510*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_PANIC, "soft reset failed: "
1511*b8aa3defSJoshua M. Clulow "ping did not complete");
1512*b8aa3defSJoshua M. Clulow } else if (smcm_nop->smcm_status & SMRT_CMD_STATUS_ERROR) {
1513*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "soft reset: ping completed "
1514*b8aa3defSJoshua M. Clulow "in error (status %u)",
1515*b8aa3defSJoshua M. Clulow (unsigned)smcm_nop->smcm_va_err->CommandStatus);
1516*b8aa3defSJoshua M. Clulow } else {
1517*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_NOTE, "soft reset: ping completed");
1518*b8aa3defSJoshua M. Clulow }
1519*b8aa3defSJoshua M. Clulow
1520*b8aa3defSJoshua M. Clulow /*
1521*b8aa3defSJoshua M. Clulow * Now that the controller is working again, we can abort any
1522*b8aa3defSJoshua M. Clulow * commands that were inflight during the reset.
1523*b8aa3defSJoshua M. Clulow */
1524*b8aa3defSJoshua M. Clulow smrt_command_t *nt;
1525*b8aa3defSJoshua M. Clulow for (smrt_command_t *t = avl_first(&smrt->smrt_inflight);
1526*b8aa3defSJoshua M. Clulow t != NULL; t = nt) {
1527*b8aa3defSJoshua M. Clulow nt = AVL_NEXT(&smrt->smrt_inflight, t);
1528*b8aa3defSJoshua M. Clulow
1529*b8aa3defSJoshua M. Clulow if (t->smcm_status & SMRT_CMD_STATUS_RESET_SENT) {
1530*b8aa3defSJoshua M. Clulow avl_remove(&smrt->smrt_inflight, t);
1531*b8aa3defSJoshua M. Clulow t->smcm_status &= ~SMRT_CMD_STATUS_INFLIGHT;
1532*b8aa3defSJoshua M. Clulow
1533*b8aa3defSJoshua M. Clulow list_insert_tail(&smrt->smrt_finishq, t);
1534*b8aa3defSJoshua M. Clulow }
1535*b8aa3defSJoshua M. Clulow }
1536*b8aa3defSJoshua M. Clulow
1537*b8aa3defSJoshua M. Clulow /*
1538*b8aa3defSJoshua M. Clulow * Quiesce our discovery thread. Note, because
1539*b8aa3defSJoshua M. Clulow * SMRT_CTLR_STATUS_RESTARTING is set, nothing can cause it to be
1540*b8aa3defSJoshua M. Clulow * enabled again.
1541*b8aa3defSJoshua M. Clulow */
1542*b8aa3defSJoshua M. Clulow if (!ddi_in_panic()) {
1543*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1544*b8aa3defSJoshua M. Clulow ddi_taskq_wait(smrt->smrt_discover_taskq);
1545*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1546*b8aa3defSJoshua M. Clulow }
1547*b8aa3defSJoshua M. Clulow
1548*b8aa3defSJoshua M. Clulow /*
1549*b8aa3defSJoshua M. Clulow * Re-enable interrupts. Now, we must kick off a discovery to make sure
1550*b8aa3defSJoshua M. Clulow * that the system is in a sane state and that we can perform I/O.
1551*b8aa3defSJoshua M. Clulow */
1552*b8aa3defSJoshua M. Clulow smrt_intr_set(smrt, B_TRUE);
1553*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_STATUS_RESETTING;
1554*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_DISCOVERY_REQUIRED;
1555*b8aa3defSJoshua M. Clulow
1556*b8aa3defSJoshua M. Clulow /*
1557*b8aa3defSJoshua M. Clulow * Attempt a discovery to make sure that the drivers sees a realistic
1558*b8aa3defSJoshua M. Clulow * view of the world. If we're not in panic context, spin for the
1559*b8aa3defSJoshua M. Clulow * asynchronous process to complete, otherwise we're in panic context
1560*b8aa3defSJoshua M. Clulow * and this is going to happen regardless if we want it to or not.
1561*b8aa3defSJoshua M. Clulow * Before we kick off the request to run discovery, we reset the
1562*b8aa3defSJoshua M. Clulow * discovery request flags as we know that nothing else can consider
1563*b8aa3defSJoshua M. Clulow * running discovery and we don't want to delay until the next smrt
1564*b8aa3defSJoshua M. Clulow * periodic tick if we can avoid it. In panic context, if this failed,
1565*b8aa3defSJoshua M. Clulow * then we won't make it back.
1566*b8aa3defSJoshua M. Clulow */
1567*b8aa3defSJoshua M. Clulow VERIFY0(smrt->smrt_status & SMRT_CTLR_DISCOVERY_RUNNING);
1568*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~(SMRT_CTLR_DISCOVERY_MASK);
1569*b8aa3defSJoshua M. Clulow smrt_discover(smrt);
1570*b8aa3defSJoshua M. Clulow if (!ddi_in_panic()) {
1571*b8aa3defSJoshua M. Clulow while (smrt->smrt_status & SMRT_CTLR_DISCOVERY_REQUIRED) {
1572*b8aa3defSJoshua M. Clulow cv_wait(&smrt->smrt_cv_finishq, &smrt->smrt_mutex);
1573*b8aa3defSJoshua M. Clulow }
1574*b8aa3defSJoshua M. Clulow }
1575*b8aa3defSJoshua M. Clulow
1576*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_STATUS_RUNNING;
1577*b8aa3defSJoshua M. Clulow smrt->smrt_last_reset_finish = gethrtime();
1578*b8aa3defSJoshua M. Clulow
1579*b8aa3defSJoshua M. Clulow /*
1580*b8aa3defSJoshua M. Clulow * Wake anybody that was waiting for the reset to complete.
1581*b8aa3defSJoshua M. Clulow */
1582*b8aa3defSJoshua M. Clulow cv_broadcast(&smrt->smrt_cv_finishq);
1583*b8aa3defSJoshua M. Clulow
1584*b8aa3defSJoshua M. Clulow /*
1585*b8aa3defSJoshua M. Clulow * Process the completion queue one last time before we let go
1586*b8aa3defSJoshua M. Clulow * of the mutex.
1587*b8aa3defSJoshua M. Clulow */
1588*b8aa3defSJoshua M. Clulow smrt_process_finishq(smrt);
1589*b8aa3defSJoshua M. Clulow
1590*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1591*b8aa3defSJoshua M. Clulow smrt_command_free(smcm_nop);
1592*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1593*b8aa3defSJoshua M. Clulow return (0);
1594*b8aa3defSJoshua M. Clulow }
1595*b8aa3defSJoshua M. Clulow
1596*b8aa3defSJoshua M. Clulow int
smrt_event_init(smrt_t * smrt)1597*b8aa3defSJoshua M. Clulow smrt_event_init(smrt_t *smrt)
1598*b8aa3defSJoshua M. Clulow {
1599*b8aa3defSJoshua M. Clulow int ret;
1600*b8aa3defSJoshua M. Clulow smrt_command_t *event, *cancel;
1601*b8aa3defSJoshua M. Clulow
1602*b8aa3defSJoshua M. Clulow event = smrt_command_alloc(smrt, SMRT_CMDTYPE_EVENT, KM_NOSLEEP);
1603*b8aa3defSJoshua M. Clulow if (event == NULL)
1604*b8aa3defSJoshua M. Clulow return (ENOMEM);
1605*b8aa3defSJoshua M. Clulow if (smrt_command_attach_internal(smrt, event, SMRT_EVENT_NOTIFY_BUFLEN,
1606*b8aa3defSJoshua M. Clulow KM_NOSLEEP) != 0) {
1607*b8aa3defSJoshua M. Clulow smrt_command_free(event);
1608*b8aa3defSJoshua M. Clulow return (ENOMEM);
1609*b8aa3defSJoshua M. Clulow }
1610*b8aa3defSJoshua M. Clulow smrt_write_message_event_notify(event);
1611*b8aa3defSJoshua M. Clulow
1612*b8aa3defSJoshua M. Clulow cancel = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL, KM_NOSLEEP);
1613*b8aa3defSJoshua M. Clulow if (cancel == NULL) {
1614*b8aa3defSJoshua M. Clulow smrt_command_free(event);
1615*b8aa3defSJoshua M. Clulow return (ENOMEM);
1616*b8aa3defSJoshua M. Clulow }
1617*b8aa3defSJoshua M. Clulow if (smrt_command_attach_internal(smrt, cancel, SMRT_EVENT_NOTIFY_BUFLEN,
1618*b8aa3defSJoshua M. Clulow KM_NOSLEEP) != 0) {
1619*b8aa3defSJoshua M. Clulow smrt_command_free(event);
1620*b8aa3defSJoshua M. Clulow smrt_command_free(cancel);
1621*b8aa3defSJoshua M. Clulow return (ENOMEM);
1622*b8aa3defSJoshua M. Clulow }
1623*b8aa3defSJoshua M. Clulow smrt_write_message_cancel_event_notify(cancel);
1624*b8aa3defSJoshua M. Clulow
1625*b8aa3defSJoshua M. Clulow cv_init(&smrt->smrt_event_queue, NULL, CV_DRIVER, NULL);
1626*b8aa3defSJoshua M. Clulow
1627*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1628*b8aa3defSJoshua M. Clulow if ((ret = smrt_submit(smrt, event)) != 0) {
1629*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1630*b8aa3defSJoshua M. Clulow smrt_command_free(event);
1631*b8aa3defSJoshua M. Clulow smrt_command_free(cancel);
1632*b8aa3defSJoshua M. Clulow return (ret);
1633*b8aa3defSJoshua M. Clulow }
1634*b8aa3defSJoshua M. Clulow
1635*b8aa3defSJoshua M. Clulow smrt->smrt_event_cmd = event;
1636*b8aa3defSJoshua M. Clulow smrt->smrt_event_cancel_cmd = cancel;
1637*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1638*b8aa3defSJoshua M. Clulow
1639*b8aa3defSJoshua M. Clulow return (0);
1640*b8aa3defSJoshua M. Clulow }
1641*b8aa3defSJoshua M. Clulow
1642*b8aa3defSJoshua M. Clulow void
smrt_event_complete(smrt_command_t * smcm)1643*b8aa3defSJoshua M. Clulow smrt_event_complete(smrt_command_t *smcm)
1644*b8aa3defSJoshua M. Clulow {
1645*b8aa3defSJoshua M. Clulow smrt_event_notify_t *sen;
1646*b8aa3defSJoshua M. Clulow boolean_t log, rescan;
1647*b8aa3defSJoshua M. Clulow
1648*b8aa3defSJoshua M. Clulow boolean_t intervene = B_FALSE;
1649*b8aa3defSJoshua M. Clulow smrt_t *smrt = smcm->smcm_ctlr;
1650*b8aa3defSJoshua M. Clulow
1651*b8aa3defSJoshua M. Clulow VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
1652*b8aa3defSJoshua M. Clulow VERIFY3P(smcm, ==, smrt->smrt_event_cmd);
1653*b8aa3defSJoshua M. Clulow VERIFY0(smrt->smrt_status & SMRT_CTLR_ASYNC_INTERVENTION);
1654*b8aa3defSJoshua M. Clulow
1655*b8aa3defSJoshua M. Clulow smrt->smrt_stats.smrts_events_received++;
1656*b8aa3defSJoshua M. Clulow
1657*b8aa3defSJoshua M. Clulow if (smrt->smrt_status & SMRT_CTLR_STATUS_DETACHING) {
1658*b8aa3defSJoshua M. Clulow cv_signal(&smrt->smrt_event_queue);
1659*b8aa3defSJoshua M. Clulow return;
1660*b8aa3defSJoshua M. Clulow }
1661*b8aa3defSJoshua M. Clulow
1662*b8aa3defSJoshua M. Clulow if (smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) {
1663*b8aa3defSJoshua M. Clulow intervene = B_TRUE;
1664*b8aa3defSJoshua M. Clulow goto clean;
1665*b8aa3defSJoshua M. Clulow }
1666*b8aa3defSJoshua M. Clulow
1667*b8aa3defSJoshua M. Clulow /*
1668*b8aa3defSJoshua M. Clulow * The event notification command failed for some reason. Attempt to
1669*b8aa3defSJoshua M. Clulow * drive on and try again at the next intervention period. Because this
1670*b8aa3defSJoshua M. Clulow * may represent a programmer error (though it's hard to know), we wait
1671*b8aa3defSJoshua M. Clulow * until the next intervention period and don't panic.
1672*b8aa3defSJoshua M. Clulow */
1673*b8aa3defSJoshua M. Clulow if (smcm->smcm_status & SMRT_CMD_STATUS_ERROR) {
1674*b8aa3defSJoshua M. Clulow ErrorInfo_t *ei = smcm->smcm_va_err;
1675*b8aa3defSJoshua M. Clulow intervene = B_TRUE;
1676*b8aa3defSJoshua M. Clulow
1677*b8aa3defSJoshua M. Clulow smrt->smrt_stats.smrts_events_errors++;
1678*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "!event notification request "
1679*b8aa3defSJoshua M. Clulow "error: status 0x%x", ei->CommandStatus);
1680*b8aa3defSJoshua M. Clulow goto clean;
1681*b8aa3defSJoshua M. Clulow }
1682*b8aa3defSJoshua M. Clulow
1683*b8aa3defSJoshua M. Clulow sen = smcm->smcm_internal->smcmi_va;
1684*b8aa3defSJoshua M. Clulow log = rescan = B_FALSE;
1685*b8aa3defSJoshua M. Clulow switch (sen->sen_class) {
1686*b8aa3defSJoshua M. Clulow case SMRT_EVENT_CLASS_PROTOCOL:
1687*b8aa3defSJoshua M. Clulow /*
1688*b8aa3defSJoshua M. Clulow * Most of the event protocol class events aren't really
1689*b8aa3defSJoshua M. Clulow * actionable. However, subclass 1 indicates errors. Today,
1690*b8aa3defSJoshua M. Clulow * the only error is an event overflow. If there's an event
1691*b8aa3defSJoshua M. Clulow * overflow, then we must assume that we need to rescan.
1692*b8aa3defSJoshua M. Clulow */
1693*b8aa3defSJoshua M. Clulow if (sen->sen_subclass == SMRT_EVENT_PROTOCOL_SUBCLASS_ERROR) {
1694*b8aa3defSJoshua M. Clulow rescan = B_TRUE;
1695*b8aa3defSJoshua M. Clulow }
1696*b8aa3defSJoshua M. Clulow break;
1697*b8aa3defSJoshua M. Clulow case SMRT_EVENT_CLASS_HOTPLUG:
1698*b8aa3defSJoshua M. Clulow /*
1699*b8aa3defSJoshua M. Clulow * We want to log all hotplug events. However we only need to
1700*b8aa3defSJoshua M. Clulow * scan these if the subclass indicates the event is for a disk.
1701*b8aa3defSJoshua M. Clulow */
1702*b8aa3defSJoshua M. Clulow log = B_TRUE;
1703*b8aa3defSJoshua M. Clulow if (sen->sen_subclass == SMRT_EVENT_HOTPLUG_SUBCLASS_DRIVE) {
1704*b8aa3defSJoshua M. Clulow rescan = B_TRUE;
1705*b8aa3defSJoshua M. Clulow }
1706*b8aa3defSJoshua M. Clulow break;
1707*b8aa3defSJoshua M. Clulow case SMRT_EVENT_CLASS_HWERROR:
1708*b8aa3defSJoshua M. Clulow case SMRT_EVENT_CLASS_ENVIRONMENT:
1709*b8aa3defSJoshua M. Clulow log = B_TRUE;
1710*b8aa3defSJoshua M. Clulow break;
1711*b8aa3defSJoshua M. Clulow case SMRT_EVENT_CLASS_PHYS:
1712*b8aa3defSJoshua M. Clulow log = B_TRUE;
1713*b8aa3defSJoshua M. Clulow /*
1714*b8aa3defSJoshua M. Clulow * This subclass indicates some change for physical drives. As
1715*b8aa3defSJoshua M. Clulow * such, this should trigger a rescan.
1716*b8aa3defSJoshua M. Clulow */
1717*b8aa3defSJoshua M. Clulow if (sen->sen_subclass == SMRT_EVENT_PHYS_SUBCLASS_STATE) {
1718*b8aa3defSJoshua M. Clulow rescan = B_TRUE;
1719*b8aa3defSJoshua M. Clulow }
1720*b8aa3defSJoshua M. Clulow break;
1721*b8aa3defSJoshua M. Clulow case SMRT_EVENT_CLASS_LOGVOL:
1722*b8aa3defSJoshua M. Clulow rescan = B_TRUE;
1723*b8aa3defSJoshua M. Clulow log = B_TRUE;
1724*b8aa3defSJoshua M. Clulow break;
1725*b8aa3defSJoshua M. Clulow default:
1726*b8aa3defSJoshua M. Clulow /*
1727*b8aa3defSJoshua M. Clulow * While there are other classes of events, it's hard to say how
1728*b8aa3defSJoshua M. Clulow * actionable they are for the moment. If we revamp this such
1729*b8aa3defSJoshua M. Clulow * that it becomes an ireport based system, then we should just
1730*b8aa3defSJoshua M. Clulow * always log these. We opt not to at the moment to try and be
1731*b8aa3defSJoshua M. Clulow * kind to the system log.
1732*b8aa3defSJoshua M. Clulow */
1733*b8aa3defSJoshua M. Clulow break;
1734*b8aa3defSJoshua M. Clulow }
1735*b8aa3defSJoshua M. Clulow
1736*b8aa3defSJoshua M. Clulow /*
1737*b8aa3defSJoshua M. Clulow * Ideally, this would be an ireport that we could pass onto
1738*b8aa3defSJoshua M. Clulow * administrators; however, since we don't have any way to generate
1739*b8aa3defSJoshua M. Clulow * that, we provide a subset of the event information.
1740*b8aa3defSJoshua M. Clulow */
1741*b8aa3defSJoshua M. Clulow if (log) {
1742*b8aa3defSJoshua M. Clulow const char *rmsg;
1743*b8aa3defSJoshua M. Clulow if (rescan == B_TRUE) {
1744*b8aa3defSJoshua M. Clulow rmsg = "rescanning";
1745*b8aa3defSJoshua M. Clulow } else {
1746*b8aa3defSJoshua M. Clulow rmsg = "not rescanning";
1747*b8aa3defSJoshua M. Clulow }
1748*b8aa3defSJoshua M. Clulow if (sen->sen_message[0] != '\0') {
1749*b8aa3defSJoshua M. Clulow sen->sen_message[sizeof (sen->sen_message) - 1] = '\0';
1750*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_NOTE, "!controller event "
1751*b8aa3defSJoshua M. Clulow "class/sub-class/detail %x, %x, %x: %s; %s devices",
1752*b8aa3defSJoshua M. Clulow sen->sen_class, sen->sen_subclass, sen->sen_detail,
1753*b8aa3defSJoshua M. Clulow sen->sen_message, rmsg);
1754*b8aa3defSJoshua M. Clulow } else {
1755*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_NOTE, "!controller event "
1756*b8aa3defSJoshua M. Clulow "class/sub-class/detail %x, %x, %x; %s devices",
1757*b8aa3defSJoshua M. Clulow sen->sen_class, sen->sen_subclass, sen->sen_detail,
1758*b8aa3defSJoshua M. Clulow rmsg);
1759*b8aa3defSJoshua M. Clulow }
1760*b8aa3defSJoshua M. Clulow }
1761*b8aa3defSJoshua M. Clulow
1762*b8aa3defSJoshua M. Clulow if (rescan)
1763*b8aa3defSJoshua M. Clulow smrt_discover_request(smrt);
1764*b8aa3defSJoshua M. Clulow
1765*b8aa3defSJoshua M. Clulow clean:
1766*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1767*b8aa3defSJoshua M. Clulow smrt_command_reuse(smcm);
1768*b8aa3defSJoshua M. Clulow bzero(smcm->smcm_internal->smcmi_va, SMRT_EVENT_NOTIFY_BUFLEN);
1769*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1770*b8aa3defSJoshua M. Clulow
1771*b8aa3defSJoshua M. Clulow /*
1772*b8aa3defSJoshua M. Clulow * Make sure we're not _now_ detaching or resetting.
1773*b8aa3defSJoshua M. Clulow */
1774*b8aa3defSJoshua M. Clulow if (smrt->smrt_status & SMRT_CTLR_STATUS_DETACHING) {
1775*b8aa3defSJoshua M. Clulow cv_signal(&smrt->smrt_event_queue);
1776*b8aa3defSJoshua M. Clulow return;
1777*b8aa3defSJoshua M. Clulow }
1778*b8aa3defSJoshua M. Clulow
1779*b8aa3defSJoshua M. Clulow if ((smrt->smrt_status & SMRT_CTLR_STATUS_RESETTING) != 0 ||
1780*b8aa3defSJoshua M. Clulow intervene == B_TRUE) {
1781*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_ASYNC_INTERVENTION;
1782*b8aa3defSJoshua M. Clulow return;
1783*b8aa3defSJoshua M. Clulow }
1784*b8aa3defSJoshua M. Clulow
1785*b8aa3defSJoshua M. Clulow /*
1786*b8aa3defSJoshua M. Clulow * Check out command count per tick. If it's too high, leave it for
1787*b8aa3defSJoshua M. Clulow * intervention to solve. Likely there is some serious driver or
1788*b8aa3defSJoshua M. Clulow * firmware error going on.
1789*b8aa3defSJoshua M. Clulow */
1790*b8aa3defSJoshua M. Clulow smrt->smrt_event_count++;
1791*b8aa3defSJoshua M. Clulow if (smrt->smrt_event_count > smrt_event_intervention_threshold) {
1792*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_ASYNC_INTERVENTION;
1793*b8aa3defSJoshua M. Clulow return;
1794*b8aa3defSJoshua M. Clulow }
1795*b8aa3defSJoshua M. Clulow
1796*b8aa3defSJoshua M. Clulow if (smrt_submit(smrt, smcm) != 0) {
1797*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_ASYNC_INTERVENTION;
1798*b8aa3defSJoshua M. Clulow }
1799*b8aa3defSJoshua M. Clulow }
1800*b8aa3defSJoshua M. Clulow
1801*b8aa3defSJoshua M. Clulow void
smrt_event_fini(smrt_t * smrt)1802*b8aa3defSJoshua M. Clulow smrt_event_fini(smrt_t *smrt)
1803*b8aa3defSJoshua M. Clulow {
1804*b8aa3defSJoshua M. Clulow int ret;
1805*b8aa3defSJoshua M. Clulow smrt_command_t *event, *cancel;
1806*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1807*b8aa3defSJoshua M. Clulow
1808*b8aa3defSJoshua M. Clulow /*
1809*b8aa3defSJoshua M. Clulow * If intervention has been requested, there is nothing for us to do. We
1810*b8aa3defSJoshua M. Clulow * clear the flag so nothing else accidentally sees this and takes
1811*b8aa3defSJoshua M. Clulow * action. We also don't need to bother sending a cancellation request,
1812*b8aa3defSJoshua M. Clulow * as there is no outstanding event.
1813*b8aa3defSJoshua M. Clulow */
1814*b8aa3defSJoshua M. Clulow if (smrt->smrt_status & SMRT_CTLR_ASYNC_INTERVENTION) {
1815*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_ASYNC_INTERVENTION;
1816*b8aa3defSJoshua M. Clulow goto free;
1817*b8aa3defSJoshua M. Clulow }
1818*b8aa3defSJoshua M. Clulow
1819*b8aa3defSJoshua M. Clulow /*
1820*b8aa3defSJoshua M. Clulow * Submit a cancel request for the event notification queue. Because we
1821*b8aa3defSJoshua M. Clulow * submit both the cancel event and the regular notification event as an
1822*b8aa3defSJoshua M. Clulow * ordered command, we know that by the time this completes, that the
1823*b8aa3defSJoshua M. Clulow * existing one will have completed.
1824*b8aa3defSJoshua M. Clulow */
1825*b8aa3defSJoshua M. Clulow smrt->smrt_event_cancel_cmd->smcm_status |= SMRT_CMD_STATUS_POLLED;
1826*b8aa3defSJoshua M. Clulow if ((ret = smrt_submit(smrt, smrt->smrt_event_cancel_cmd)) != 0) {
1827*b8aa3defSJoshua M. Clulow /*
1828*b8aa3defSJoshua M. Clulow * This is unfortunate. We've failed to submit the command. At
1829*b8aa3defSJoshua M. Clulow * this point all we can do is reset the device. If the reset
1830*b8aa3defSJoshua M. Clulow * succeeds, we're done and we can clear all the memory. If it
1831*b8aa3defSJoshua M. Clulow * fails, then all we can do is just leak the command and scream
1832*b8aa3defSJoshua M. Clulow * to the system, sorry.
1833*b8aa3defSJoshua M. Clulow */
1834*b8aa3defSJoshua M. Clulow if (smrt_ctlr_reset(smrt) != 0) {
1835*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "failed to reset "
1836*b8aa3defSJoshua M. Clulow "device after failure to submit cancellation "
1837*b8aa3defSJoshua M. Clulow "(%d), abandoning smrt_command_t at address %p",
1838*b8aa3defSJoshua M. Clulow ret, smrt->smrt_event_cmd);
1839*b8aa3defSJoshua M. Clulow smrt->smrt_event_cmd = NULL;
1840*b8aa3defSJoshua M. Clulow goto free;
1841*b8aa3defSJoshua M. Clulow }
1842*b8aa3defSJoshua M. Clulow }
1843*b8aa3defSJoshua M. Clulow
1844*b8aa3defSJoshua M. Clulow smrt->smrt_event_cancel_cmd->smcm_expiry = gethrtime() +
1845*b8aa3defSJoshua M. Clulow SMRT_ASYNC_CANCEL_TIMEOUT * NANOSEC;
1846*b8aa3defSJoshua M. Clulow if ((ret = smrt_poll_for(smrt, smrt->smrt_event_cancel_cmd)) != 0) {
1847*b8aa3defSJoshua M. Clulow VERIFY3S(ret, ==, ETIMEDOUT);
1848*b8aa3defSJoshua M. Clulow VERIFY0(smrt->smrt_event_cancel_cmd->smcm_status &
1849*b8aa3defSJoshua M. Clulow SMRT_CMD_STATUS_POLL_COMPLETE);
1850*b8aa3defSJoshua M. Clulow
1851*b8aa3defSJoshua M. Clulow /*
1852*b8aa3defSJoshua M. Clulow * The command timed out. All we can do is hope a reset will
1853*b8aa3defSJoshua M. Clulow * work.
1854*b8aa3defSJoshua M. Clulow */
1855*b8aa3defSJoshua M. Clulow if (smrt_ctlr_reset(smrt) != 0) {
1856*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "failed to reset "
1857*b8aa3defSJoshua M. Clulow "device after failure to poll for async "
1858*b8aa3defSJoshua M. Clulow "cancellation command abandoning smrt_command_t "
1859*b8aa3defSJoshua M. Clulow "event command at address %p and cancellation "
1860*b8aa3defSJoshua M. Clulow "command at %p", smrt->smrt_event_cmd,
1861*b8aa3defSJoshua M. Clulow smrt->smrt_event_cancel_cmd);
1862*b8aa3defSJoshua M. Clulow smrt->smrt_event_cmd = NULL;
1863*b8aa3defSJoshua M. Clulow smrt->smrt_event_cancel_cmd = NULL;
1864*b8aa3defSJoshua M. Clulow goto free;
1865*b8aa3defSJoshua M. Clulow }
1866*b8aa3defSJoshua M. Clulow
1867*b8aa3defSJoshua M. Clulow }
1868*b8aa3defSJoshua M. Clulow
1869*b8aa3defSJoshua M. Clulow /*
1870*b8aa3defSJoshua M. Clulow * Well, in the end, it's results that count.
1871*b8aa3defSJoshua M. Clulow */
1872*b8aa3defSJoshua M. Clulow if (smrt->smrt_event_cancel_cmd->smcm_status &
1873*b8aa3defSJoshua M. Clulow SMRT_CMD_STATUS_RESET_SENT) {
1874*b8aa3defSJoshua M. Clulow goto free;
1875*b8aa3defSJoshua M. Clulow }
1876*b8aa3defSJoshua M. Clulow
1877*b8aa3defSJoshua M. Clulow if (smrt->smrt_event_cancel_cmd->smcm_status & SMRT_CMD_STATUS_ERROR) {
1878*b8aa3defSJoshua M. Clulow ErrorInfo_t *ei = smrt->smrt_event_cancel_cmd->smcm_va_err;
1879*b8aa3defSJoshua M. Clulow
1880*b8aa3defSJoshua M. Clulow /*
1881*b8aa3defSJoshua M. Clulow * This can return a CISS_CMD_TARGET_STATUS entry when the
1882*b8aa3defSJoshua M. Clulow * controller doesn't think a command is outstanding. It is
1883*b8aa3defSJoshua M. Clulow * possible we raced, so don't think too much about that case.
1884*b8aa3defSJoshua M. Clulow * Anything else leaves us between a rock and a hard place, the
1885*b8aa3defSJoshua M. Clulow * only way out is a reset.
1886*b8aa3defSJoshua M. Clulow */
1887*b8aa3defSJoshua M. Clulow if (ei->CommandStatus != CISS_CMD_TARGET_STATUS &&
1888*b8aa3defSJoshua M. Clulow smrt_ctlr_reset(smrt) != 0) {
1889*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "failed to reset "
1890*b8aa3defSJoshua M. Clulow "device after receiving an error on the async "
1891*b8aa3defSJoshua M. Clulow "cancellation command (%d); abandoning "
1892*b8aa3defSJoshua M. Clulow "smrt_command_t event command at address %p and "
1893*b8aa3defSJoshua M. Clulow "cancellation command at %p", ei->CommandStatus,
1894*b8aa3defSJoshua M. Clulow smrt->smrt_event_cmd, smrt->smrt_event_cancel_cmd);
1895*b8aa3defSJoshua M. Clulow smrt->smrt_event_cmd = NULL;
1896*b8aa3defSJoshua M. Clulow smrt->smrt_event_cancel_cmd = NULL;
1897*b8aa3defSJoshua M. Clulow goto free;
1898*b8aa3defSJoshua M. Clulow }
1899*b8aa3defSJoshua M. Clulow }
1900*b8aa3defSJoshua M. Clulow
1901*b8aa3defSJoshua M. Clulow free:
1902*b8aa3defSJoshua M. Clulow event = smrt->smrt_event_cmd;
1903*b8aa3defSJoshua M. Clulow smrt->smrt_event_cmd = NULL;
1904*b8aa3defSJoshua M. Clulow cancel = smrt->smrt_event_cancel_cmd;
1905*b8aa3defSJoshua M. Clulow smrt->smrt_event_cancel_cmd = NULL;
1906*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1907*b8aa3defSJoshua M. Clulow if (event != NULL)
1908*b8aa3defSJoshua M. Clulow smrt_command_free(event);
1909*b8aa3defSJoshua M. Clulow if (cancel != NULL)
1910*b8aa3defSJoshua M. Clulow smrt_command_free(cancel);
1911*b8aa3defSJoshua M. Clulow cv_destroy(&smrt->smrt_event_queue);
1912*b8aa3defSJoshua M. Clulow }
1913*b8aa3defSJoshua M. Clulow
1914*b8aa3defSJoshua M. Clulow /*
1915*b8aa3defSJoshua M. Clulow * We've been asked to do a discovery in panic context. This would have
1916*b8aa3defSJoshua M. Clulow * occurred because there was a device reset. Because we can't rely on the
1917*b8aa3defSJoshua M. Clulow * target maps, all we can do at the moment is go over all the active targets
1918*b8aa3defSJoshua M. Clulow * and note which ones no longer exist. If this target was required to dump,
1919*b8aa3defSJoshua M. Clulow * then the dump code will encounter a fatal error. If not, then we should
1920*b8aa3defSJoshua M. Clulow * count ourselves surprisingly lucky.
1921*b8aa3defSJoshua M. Clulow */
1922*b8aa3defSJoshua M. Clulow static void
smrt_discover_panic_check(smrt_t * smrt)1923*b8aa3defSJoshua M. Clulow smrt_discover_panic_check(smrt_t *smrt)
1924*b8aa3defSJoshua M. Clulow {
1925*b8aa3defSJoshua M. Clulow smrt_target_t *smtg;
1926*b8aa3defSJoshua M. Clulow
1927*b8aa3defSJoshua M. Clulow ASSERT(MUTEX_HELD(&smrt->smrt_mutex));
1928*b8aa3defSJoshua M. Clulow for (smtg = list_head(&smrt->smrt_targets); smtg != NULL;
1929*b8aa3defSJoshua M. Clulow smtg = list_next(&smrt->smrt_targets, smtg)) {
1930*b8aa3defSJoshua M. Clulow uint64_t gen;
1931*b8aa3defSJoshua M. Clulow
1932*b8aa3defSJoshua M. Clulow if (smtg->smtg_physical) {
1933*b8aa3defSJoshua M. Clulow smrt_physical_t *smpt = smtg->smtg_lun.smtg_phys;
1934*b8aa3defSJoshua M. Clulow /*
1935*b8aa3defSJoshua M. Clulow * Don't worry about drives that aren't visible.
1936*b8aa3defSJoshua M. Clulow */
1937*b8aa3defSJoshua M. Clulow if (!smpt->smpt_visible)
1938*b8aa3defSJoshua M. Clulow continue;
1939*b8aa3defSJoshua M. Clulow gen = smpt->smpt_gen;
1940*b8aa3defSJoshua M. Clulow } else {
1941*b8aa3defSJoshua M. Clulow smrt_volume_t *smlv = smtg->smtg_lun.smtg_vol;
1942*b8aa3defSJoshua M. Clulow gen = smlv->smlv_gen;
1943*b8aa3defSJoshua M. Clulow }
1944*b8aa3defSJoshua M. Clulow
1945*b8aa3defSJoshua M. Clulow if (gen != smrt->smrt_discover_gen) {
1946*b8aa3defSJoshua M. Clulow dev_err(smrt->smrt_dip, CE_WARN, "target %s "
1947*b8aa3defSJoshua M. Clulow "disappeared during post-panic discovery",
1948*b8aa3defSJoshua M. Clulow scsi_device_unit_address(smtg->smtg_scsi_dev));
1949*b8aa3defSJoshua M. Clulow smtg->smtg_gone = B_TRUE;
1950*b8aa3defSJoshua M. Clulow }
1951*b8aa3defSJoshua M. Clulow }
1952*b8aa3defSJoshua M. Clulow }
1953*b8aa3defSJoshua M. Clulow
1954*b8aa3defSJoshua M. Clulow static void
smrt_discover(void * arg)1955*b8aa3defSJoshua M. Clulow smrt_discover(void *arg)
1956*b8aa3defSJoshua M. Clulow {
1957*b8aa3defSJoshua M. Clulow int log = 0, phys = 0;
1958*b8aa3defSJoshua M. Clulow smrt_t *smrt = arg;
1959*b8aa3defSJoshua M. Clulow uint64_t gen;
1960*b8aa3defSJoshua M. Clulow boolean_t runphys, runvirt;
1961*b8aa3defSJoshua M. Clulow
1962*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1963*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_DISCOVERY_RUNNING;
1964*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_DISCOVERY_REQUESTED;
1965*b8aa3defSJoshua M. Clulow
1966*b8aa3defSJoshua M. Clulow smrt->smrt_discover_gen++;
1967*b8aa3defSJoshua M. Clulow gen = smrt->smrt_discover_gen;
1968*b8aa3defSJoshua M. Clulow runphys = smrt->smrt_phys_tgtmap != NULL;
1969*b8aa3defSJoshua M. Clulow runvirt = smrt->smrt_virt_tgtmap != NULL;
1970*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
1971*b8aa3defSJoshua M. Clulow if (runphys)
1972*b8aa3defSJoshua M. Clulow phys = smrt_phys_discover(smrt, SMRT_DISCOVER_TIMEOUT, gen);
1973*b8aa3defSJoshua M. Clulow if (runvirt)
1974*b8aa3defSJoshua M. Clulow log = smrt_logvol_discover(smrt, SMRT_DISCOVER_TIMEOUT, gen);
1975*b8aa3defSJoshua M. Clulow mutex_enter(&smrt->smrt_mutex);
1976*b8aa3defSJoshua M. Clulow
1977*b8aa3defSJoshua M. Clulow if (phys != 0 || log != 0) {
1978*b8aa3defSJoshua M. Clulow if (!ddi_in_panic()) {
1979*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_DISCOVERY_PERIODIC;
1980*b8aa3defSJoshua M. Clulow } else {
1981*b8aa3defSJoshua M. Clulow panic("smrt_t %p failed to perform discovery after "
1982*b8aa3defSJoshua M. Clulow "a reset in panic context, unable to continue. "
1983*b8aa3defSJoshua M. Clulow "logvol: %d, phys: %d", smrt, log, phys);
1984*b8aa3defSJoshua M. Clulow }
1985*b8aa3defSJoshua M. Clulow } else {
1986*b8aa3defSJoshua M. Clulow if (!ddi_in_panic() &&
1987*b8aa3defSJoshua M. Clulow smrt->smrt_status & SMRT_CTLR_DISCOVERY_REQUIRED) {
1988*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_DISCOVERY_REQUIRED;
1989*b8aa3defSJoshua M. Clulow cv_broadcast(&smrt->smrt_cv_finishq);
1990*b8aa3defSJoshua M. Clulow }
1991*b8aa3defSJoshua M. Clulow
1992*b8aa3defSJoshua M. Clulow if (ddi_in_panic()) {
1993*b8aa3defSJoshua M. Clulow smrt_discover_panic_check(smrt);
1994*b8aa3defSJoshua M. Clulow }
1995*b8aa3defSJoshua M. Clulow }
1996*b8aa3defSJoshua M. Clulow smrt->smrt_status &= ~SMRT_CTLR_DISCOVERY_RUNNING;
1997*b8aa3defSJoshua M. Clulow if (smrt->smrt_status & SMRT_CTLR_DISCOVERY_REQUESTED)
1998*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_DISCOVERY_PERIODIC;
1999*b8aa3defSJoshua M. Clulow mutex_exit(&smrt->smrt_mutex);
2000*b8aa3defSJoshua M. Clulow }
2001*b8aa3defSJoshua M. Clulow
2002*b8aa3defSJoshua M. Clulow /*
2003*b8aa3defSJoshua M. Clulow * Request discovery, which is always run via a taskq.
2004*b8aa3defSJoshua M. Clulow */
2005*b8aa3defSJoshua M. Clulow void
smrt_discover_request(smrt_t * smrt)2006*b8aa3defSJoshua M. Clulow smrt_discover_request(smrt_t *smrt)
2007*b8aa3defSJoshua M. Clulow {
2008*b8aa3defSJoshua M. Clulow boolean_t run;
2009*b8aa3defSJoshua M. Clulow ASSERT(MUTEX_HELD(&smrt->smrt_mutex));
2010*b8aa3defSJoshua M. Clulow
2011*b8aa3defSJoshua M. Clulow if (ddi_in_panic()) {
2012*b8aa3defSJoshua M. Clulow smrt_discover(smrt);
2013*b8aa3defSJoshua M. Clulow return;
2014*b8aa3defSJoshua M. Clulow }
2015*b8aa3defSJoshua M. Clulow
2016*b8aa3defSJoshua M. Clulow run = (smrt->smrt_status & SMRT_CTLR_DISCOVERY_MASK) == 0;
2017*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_DISCOVERY_REQUESTED;
2018*b8aa3defSJoshua M. Clulow if (run && ddi_taskq_dispatch(smrt->smrt_discover_taskq,
2019*b8aa3defSJoshua M. Clulow smrt_discover, smrt, DDI_NOSLEEP) != DDI_SUCCESS) {
2020*b8aa3defSJoshua M. Clulow smrt->smrt_status |= SMRT_CTLR_DISCOVERY_PERIODIC;
2021*b8aa3defSJoshua M. Clulow smrt->smrt_stats.smrts_discovery_tq_errors++;
2022*b8aa3defSJoshua M. Clulow }
2023*b8aa3defSJoshua M. Clulow }
2024