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