1507c3241Smlf /*
2507c3241Smlf  * CDDL HEADER START
3507c3241Smlf  *
4507c3241Smlf  * The contents of this file are subject to the terms of the
5507c3241Smlf  * Common Development and Distribution License (the "License").
6507c3241Smlf  * You may not use this file except in compliance with the License.
7507c3241Smlf  *
8507c3241Smlf  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9507c3241Smlf  * or http://www.opensolaris.org/os/licensing.
10507c3241Smlf  * See the License for the specific language governing permissions
11507c3241Smlf  * and limitations under the License.
12507c3241Smlf  *
13507c3241Smlf  * When distributing Covered Code, include this CDDL HEADER in each
14507c3241Smlf  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15507c3241Smlf  * If applicable, add the following below this CDDL HEADER, with the
16507c3241Smlf  * fields enclosed by brackets "[]" replaced with your own identifying
17507c3241Smlf  * information: Portions Copyright [yyyy] [name of copyright owner]
18507c3241Smlf  *
19507c3241Smlf  * CDDL HEADER END
20507c3241Smlf  */
21507c3241Smlf 
22507c3241Smlf /*
23903a11ebSrh  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24507c3241Smlf  * Use is subject to license terms.
25507c3241Smlf  */
26507c3241Smlf 
27507c3241Smlf 
28507c3241Smlf #include <sys/types.h>
29507c3241Smlf #include <sys/conf.h>
30507c3241Smlf #include <sys/ddi.h>
31507c3241Smlf #include <sys/sunddi.h>
32507c3241Smlf #include <sys/ksynch.h>
33507c3241Smlf #include <sys/scsi/conf/autoconf.h>
34507c3241Smlf #include <sys/reboot.h>
35507c3241Smlf 
36507c3241Smlf #include "ghd.h"
37507c3241Smlf 
38507c3241Smlf /*
39507c3241Smlf  * Local functions
40507c3241Smlf  */
41507c3241Smlf 
42507c3241Smlf static	gcmd_t	*ghd_timeout_get(ccc_t *cccp);
43507c3241Smlf static	int	 ghd_timeout_loop(ccc_t *cccp);
44507c3241Smlf static	uint_t	 ghd_timeout_softintr(caddr_t arg);
45507c3241Smlf static	void	 ghd_timeout(void *arg);
46507c3241Smlf static	void	 ghd_timeout_disable(tmr_t *tmrp);
47507c3241Smlf static	void	 ghd_timeout_enable(tmr_t *tmrp);
48507c3241Smlf 
49507c3241Smlf /*
50507c3241Smlf  * Local data
51507c3241Smlf  */
52507c3241Smlf long	ghd_HZ;
53507c3241Smlf static	kmutex_t tglobal_mutex;
54507c3241Smlf 
55507c3241Smlf /* table of timeouts for abort processing steps */
56507c3241Smlf cmdstate_t ghd_timeout_table[GCMD_NSTATES];
57507c3241Smlf 
58507c3241Smlf /* This table indirectly initializes the ghd_timeout_table */
59507c3241Smlf struct {
60507c3241Smlf 	int		valid;
61507c3241Smlf 	cmdstate_t	state;
62507c3241Smlf 	long		value;
63507c3241Smlf } ghd_time_inits[] = {
64507c3241Smlf 	{ TRUE, GCMD_STATE_ABORTING_CMD, 3 },
65507c3241Smlf 	{ TRUE, GCMD_STATE_ABORTING_DEV, 3 },
66507c3241Smlf 	{ TRUE, GCMD_STATE_RESETTING_DEV, 5 },
67507c3241Smlf 	{ TRUE, GCMD_STATE_RESETTING_BUS, 10 },
68507c3241Smlf 	{ TRUE, GCMD_STATE_HUNG, 60},
69507c3241Smlf 	{ FALSE, 0, 0 },	/* spare entry */
70507c3241Smlf 	{ FALSE, 0, 0 },	/* spare entry */
71507c3241Smlf 	{ FALSE, 0, 0 },	/* spare entry */
72507c3241Smlf 	{ FALSE, 0, 0 },	/* spare entry */
73507c3241Smlf 	{ FALSE, 0, 0 }		/* spare entry */
74507c3241Smlf };
75507c3241Smlf int	ghd_ntime_inits = sizeof (ghd_time_inits)
76507c3241Smlf 				/ sizeof (ghd_time_inits[0]);
77507c3241Smlf 
78507c3241Smlf /*
79507c3241Smlf  * Locally-used macros
80507c3241Smlf  */
81507c3241Smlf 
82507c3241Smlf /*
83507c3241Smlf  * Compare two gcmd_t's to see if they're for the same device (same gdev_t)
84507c3241Smlf  */
85507c3241Smlf #define	GCMD_SAME_DEV(gcmdp1, gcmdp2)		\
86507c3241Smlf 	(GCMDP2GDEVP(gcmdp1) == GCMDP2GDEVP(gcmdp2))
87507c3241Smlf 
88507c3241Smlf /*
89507c3241Smlf  * Compare two gcmd_t's to see if they're for the same bus (same HBA inst)
90507c3241Smlf  */
91507c3241Smlf #define	GCMD_SAME_BUS(gcmdp1, gcmdp2)		\
92507c3241Smlf 	(GCMDP2CCCP(gcmdp1) == GCMDP2CCCP(gcmdp2))
93507c3241Smlf 
94507c3241Smlf 
95507c3241Smlf /*
96507c3241Smlf  * Update state of gcmdp (in one direction, increasing state number, only)
97507c3241Smlf  */
98*1bff1300SToomas Soome #define	GCMD_UPDATE_STATE(gcmdp, newstate)		\
99507c3241Smlf {							\
100507c3241Smlf 	if ((gcmdp)->cmd_state < (newstate)) {		\
101507c3241Smlf 		((gcmdp)->cmd_state = (newstate));	\
102507c3241Smlf 	}						\
103507c3241Smlf }
104507c3241Smlf 
105507c3241Smlf #ifdef ___notyet___
106507c3241Smlf 
107507c3241Smlf #include <sys/modctl.h>
108507c3241Smlf extern struct mod_ops mod_miscops;
109507c3241Smlf static struct modlmisc modlmisc = {
110507c3241Smlf 	&mod_miscops,	/* Type of module */
111507c3241Smlf 	"CCB Timeout Utility Routines"
112507c3241Smlf };
113507c3241Smlf static struct modlinkage modlinkage = {
114507c3241Smlf 	MODREV_1, (void *)&modlmisc, NULL
115507c3241Smlf };
116507c3241Smlf 
117507c3241Smlf /*
118507c3241Smlf  * If this is a loadable module then there's a single CCB timer configure
119507c3241Smlf  * structure for all HBA drivers (rather than one per HBA driver).
120507c3241Smlf  */
121507c3241Smlf static	tmr_t	tmr_conf;
122507c3241Smlf 
123507c3241Smlf int
_init()124507c3241Smlf _init()
125507c3241Smlf {
126507c3241Smlf 	int	err;
127507c3241Smlf 
128507c3241Smlf 	ghd_timer_init(&tmr_conf, 0);
129507c3241Smlf 	return ((err = mod_install(&modlinkage)) != 0)
130903a11ebSrh 	    ghd_timer_fini(&tmr_conf);
131507c3241Smlf 	return (err);
132507c3241Smlf }
133507c3241Smlf 
134507c3241Smlf int
_fini()135507c3241Smlf _fini()
136507c3241Smlf {
137507c3241Smlf 	int	err;
138507c3241Smlf 
139507c3241Smlf 	if ((err = mod_remove(&modlinkage)) == 0)
140507c3241Smlf 		ghd_timer_fini(&tmr_conf);
141507c3241Smlf 	return (err);
142507c3241Smlf }
143507c3241Smlf 
144507c3241Smlf int
_info(struct modinfo * modinfop)145507c3241Smlf _info(struct modinfo *modinfop)
146507c3241Smlf {
147507c3241Smlf 	return (mod_info(&modlinkage, modinfop));
148507c3241Smlf }
149507c3241Smlf 
150507c3241Smlf #endif /* ___notyet___ */
151507c3241Smlf 
152507c3241Smlf 
153507c3241Smlf 
154507c3241Smlf /*
155507c3241Smlf  *
156507c3241Smlf  * ghd_timeout_loop()
157507c3241Smlf  *
158507c3241Smlf  *	Check the CCB timer value for every active CCB for this
159507c3241Smlf  * HBA driver instance.
160507c3241Smlf  *
161507c3241Smlf  *	This function is called both by the ghd_timeout() interrupt
162507c3241Smlf  * handler when called via the timer callout, and by ghd_timer_poll()
163507c3241Smlf  * while procesing "polled" (FLAG_NOINTR) requests.
164507c3241Smlf  *
165*1bff1300SToomas Soome  *	The ccc_activel_mutex is held while a CCB list is being scanned.
166507c3241Smlf  * This prevents the HBA driver's transport or interrupt functions
167507c3241Smlf  * from changing the active CCB list. But we wake up very infrequently
168507c3241Smlf  * and do as little as possible so it shouldn't affect performance.
169507c3241Smlf  *
170507c3241Smlf  */
171507c3241Smlf 
172507c3241Smlf static int
ghd_timeout_loop(ccc_t * cccp)173507c3241Smlf ghd_timeout_loop(ccc_t *cccp)
174507c3241Smlf {
175507c3241Smlf 	int	 got_any = FALSE;
176507c3241Smlf 	gcmd_t	*gcmdp;
177507c3241Smlf 	ulong_t	 lbolt;
178507c3241Smlf 
179507c3241Smlf 	mutex_enter(&cccp->ccc_activel_mutex);
180507c3241Smlf 	lbolt = ddi_get_lbolt();
181507c3241Smlf 	gcmdp = (gcmd_t *)L2_next(&cccp->ccc_activel);
182507c3241Smlf 	while (gcmdp) {
183507c3241Smlf 		/*
184507c3241Smlf 		 * check to see if this one has timed out
185507c3241Smlf 		 */
186507c3241Smlf 		if ((gcmdp->cmd_timeout > 0) &&
187507c3241Smlf 		    (lbolt - gcmdp->cmd_start_time >= gcmdp->cmd_timeout)) {
188507c3241Smlf 			got_any = TRUE;
189507c3241Smlf 		}
190507c3241Smlf 		gcmdp = (gcmd_t *)L2_next(&gcmdp->cmd_timer_link);
191507c3241Smlf 	}
192507c3241Smlf 	mutex_exit(&cccp->ccc_activel_mutex);
193507c3241Smlf 	return (got_any);
194507c3241Smlf }
195507c3241Smlf 
196507c3241Smlf /*
197507c3241Smlf  *
198507c3241Smlf  * ghd_timeout()
199507c3241Smlf  *
200507c3241Smlf  *	Called every t_ticks ticks to scan the CCB timer lists
201507c3241Smlf  *
202507c3241Smlf  *	The t_mutex mutex is held the entire time this routine is active.
203507c3241Smlf  *	It protects the list of ccc_t's.
204507c3241Smlf  *
205507c3241Smlf  *	The list of cmd_t's is protected by the ccc_activel_mutex mutex
206507c3241Smlf  *	in the ghd_timeout_loop() routine.
207507c3241Smlf  *
208*1bff1300SToomas Soome  *	We also check to see if the waitq is frozen, and if so,
209*1bff1300SToomas Soome  *	adjust our timeout to call back sooner if necessary (to
210*1bff1300SToomas Soome  *	unfreeze the waitq as soon as possible).
211507c3241Smlf  *
212507c3241Smlf  *
213507c3241Smlf  *	+------------+
214507c3241Smlf  *	|   tmr_t    |----+
215507c3241Smlf  *	+------------+    |
216507c3241Smlf  *			  |
217507c3241Smlf  *			  V
218507c3241Smlf  *			  +---------+
219507c3241Smlf  *			  |  ccc_t  |----+
220507c3241Smlf  *			  +---------+    |
221507c3241Smlf  *			  |		 V
222507c3241Smlf  *			  |		 +--------+   +--------+
223507c3241Smlf  *			  |		 | gcmd_t |-->| gcmd_t |--> ...
224507c3241Smlf  *			  |		 +--------+   +--------+
225507c3241Smlf  *			  V
226507c3241Smlf  *			  +---------+
227507c3241Smlf  *			  |  ccc_t  |----+
228507c3241Smlf  *			  +---------+    |
229507c3241Smlf  *			  |		 V
230507c3241Smlf  *			  |		 +--------+
231507c3241Smlf  *			  |		 | gcmd_t |
232507c3241Smlf  *			  V		 +--------+
233507c3241Smlf  *			  ...
234507c3241Smlf  *
235507c3241Smlf  *
236507c3241Smlf  *
237507c3241Smlf  */
238507c3241Smlf 
239507c3241Smlf static void
ghd_timeout(void * arg)240507c3241Smlf ghd_timeout(void *arg)
241507c3241Smlf {
242507c3241Smlf 	tmr_t	*tmrp = (tmr_t *)arg;
243507c3241Smlf 	ccc_t	*cccp;
244507c3241Smlf 	clock_t	ufdelay_curr;
245507c3241Smlf 	clock_t	lbolt, delay_in_hz;
246507c3241Smlf 	clock_t	resched = (clock_t)0x7FFFFFFF;
247507c3241Smlf 
248507c3241Smlf 	/*
249507c3241Smlf 	 * Each HBA driver instance has a separate CCB timer list.  Skip
250507c3241Smlf 	 * timeout processing if there are no more active timeout lists
251507c3241Smlf 	 * to process.  (There are no lists only if there are no attached
252507c3241Smlf 	 * HBA instances; the list still exists if there are no outstanding
253507c3241Smlf 	 * active commands.)
254507c3241Smlf 	 */
255507c3241Smlf 	mutex_enter(&tmrp->t_mutex);
256507c3241Smlf 	if ((cccp = tmrp->t_ccc_listp) == NULL) {
257507c3241Smlf 		mutex_exit(&tmrp->t_mutex);
258507c3241Smlf 		return;
259507c3241Smlf 	}
260507c3241Smlf 
261507c3241Smlf 	lbolt = ddi_get_lbolt();
262507c3241Smlf 
263507c3241Smlf 	do {
264507c3241Smlf 		/*
265507c3241Smlf 		 * If any active CCBs on this HBA have timed out
266507c3241Smlf 		 * then kick off the HBA driver's softintr
267507c3241Smlf 		 * handler to do the timeout processing
268507c3241Smlf 		 */
269507c3241Smlf 		if (ghd_timeout_loop(cccp)) {
270507c3241Smlf 			cccp->ccc_timeout_pending = 1;
271507c3241Smlf 			ddi_trigger_softintr(cccp->ccc_soft_id);
272507c3241Smlf 		}
273507c3241Smlf 
274507c3241Smlf 		/* Record closest unfreeze time for use in next timeout */
275507c3241Smlf 
276507c3241Smlf 		mutex_enter(&cccp->ccc_waitq_mutex);
277507c3241Smlf 		if (cccp->ccc_waitq_frozen) {
278507c3241Smlf 
279507c3241Smlf 			delay_in_hz =
280507c3241Smlf 			    drv_usectohz(cccp->ccc_waitq_freezedelay * 1000);
281507c3241Smlf 			ufdelay_curr = delay_in_hz -
282507c3241Smlf 			    (lbolt - cccp->ccc_waitq_freezetime);
283507c3241Smlf 
284507c3241Smlf 			if (ufdelay_curr < resched)
285507c3241Smlf 				resched = ufdelay_curr;
286507c3241Smlf 
287507c3241Smlf 			/* frozen; trigger softintr to maybe unfreeze */
288507c3241Smlf 			ddi_trigger_softintr(cccp->ccc_soft_id);
289507c3241Smlf 		}
290507c3241Smlf 		mutex_exit(&cccp->ccc_waitq_mutex);
291507c3241Smlf 
292507c3241Smlf 	} while ((cccp = cccp->ccc_nextp) != NULL);
293507c3241Smlf 
294507c3241Smlf 	/* don't allow any unfreeze delays to increase the timeout delay */
295507c3241Smlf 	if (resched > tmrp->t_ticks)
296507c3241Smlf 		resched = tmrp->t_ticks;
297507c3241Smlf 
298507c3241Smlf 	/* re-establish the timeout callback */
299507c3241Smlf 	tmrp->t_timeout_id = timeout(ghd_timeout, (void *)tmrp, resched);
300507c3241Smlf 
301507c3241Smlf 	mutex_exit(&tmrp->t_mutex);
302507c3241Smlf }
303507c3241Smlf 
304507c3241Smlf 
305507c3241Smlf /*
306507c3241Smlf  *
307507c3241Smlf  * ghd_timer_newstate()
308507c3241Smlf  *
309507c3241Smlf  *	The HBA mutex is held by my caller.
310507c3241Smlf  *
311507c3241Smlf  */
312507c3241Smlf 
313507c3241Smlf void
ghd_timer_newstate(ccc_t * cccp,gcmd_t * gcmdp,gtgt_t * gtgtp,gact_t action,int calltype)314507c3241Smlf ghd_timer_newstate(ccc_t *cccp, gcmd_t *gcmdp, gtgt_t *gtgtp,
315507c3241Smlf     gact_t action, int calltype)
316507c3241Smlf {
317507c3241Smlf 	gact_t	next_action;
318507c3241Smlf 	cmdstate_t next_state;
319507c3241Smlf 	char	*msgp;
320*1bff1300SToomas Soome 	long	new_timeout = 0;
321507c3241Smlf 	int	(*func)(void *, gcmd_t *, gtgt_t *, gact_t, int);
322507c3241Smlf 	void	*hba_handle;
323507c3241Smlf 	gcmd_t	gsav;
324507c3241Smlf 	int	gsav_used = 0;
325507c3241Smlf 	gcmd_t	*gcmdp_scan;
326507c3241Smlf 
327507c3241Smlf 	ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
328507c3241Smlf 
329507c3241Smlf #ifdef	DEBUG
330507c3241Smlf 	/* it shouldn't be on the timer active list */
331507c3241Smlf 	if (gcmdp != NULL) {
332507c3241Smlf 		L2el_t	*lp = &gcmdp->cmd_timer_link;
333507c3241Smlf 		ASSERT(lp->l2_nextp == lp);
334507c3241Smlf 		ASSERT(lp->l2_prevp == lp);
335507c3241Smlf 	}
336507c3241Smlf #endif
337507c3241Smlf 
338*1bff1300SToomas Soome 	bzero(&gsav, sizeof (gsav));
339507c3241Smlf 	func = cccp->ccc_timeout_func;
340507c3241Smlf 	hba_handle = cccp->ccc_hba_handle;
341507c3241Smlf 
342507c3241Smlf 	for (;;) {
343507c3241Smlf 		switch (action) {
344507c3241Smlf 		case GACTION_EARLY_ABORT:
345507c3241Smlf 			/* done before it started */
346507c3241Smlf 			ASSERT(gcmdp != NULL);
347507c3241Smlf 			msgp = "early abort";
348507c3241Smlf 			next_state = GCMD_STATE_DONEQ;
349507c3241Smlf 			next_action = GACTION_ABORT_CMD;
350507c3241Smlf 			break;
351507c3241Smlf 
352507c3241Smlf 		case GACTION_EARLY_TIMEOUT:
353507c3241Smlf 			/* done before it started */
354507c3241Smlf 			ASSERT(gcmdp != NULL);
355507c3241Smlf 			msgp = "early timeout";
356507c3241Smlf 			next_state = GCMD_STATE_DONEQ;
357507c3241Smlf 			next_action = GACTION_ABORT_CMD;
358507c3241Smlf 			break;
359507c3241Smlf 
360507c3241Smlf 		case GACTION_ABORT_CMD:
361507c3241Smlf 			msgp = "abort request";
362507c3241Smlf 			ASSERT(gcmdp != NULL);
363507c3241Smlf 			next_state = GCMD_STATE_ABORTING_CMD;
364507c3241Smlf 			next_action = GACTION_ABORT_DEV;
365507c3241Smlf 			break;
366507c3241Smlf 
367507c3241Smlf 		case GACTION_ABORT_DEV:
368507c3241Smlf 			msgp = "abort device";
369507c3241Smlf 			next_state = GCMD_STATE_ABORTING_DEV;
370507c3241Smlf 			next_action = GACTION_RESET_TARGET;
371507c3241Smlf 			break;
372507c3241Smlf 
373507c3241Smlf 		case GACTION_RESET_TARGET:
374507c3241Smlf 			msgp = "reset target";
375507c3241Smlf 			next_state = GCMD_STATE_RESETTING_DEV;
376507c3241Smlf 			next_action = GACTION_RESET_BUS;
377507c3241Smlf 			break;
378507c3241Smlf 
379507c3241Smlf 		case GACTION_RESET_BUS:
380507c3241Smlf 			msgp = "reset bus";
381507c3241Smlf 			next_state = GCMD_STATE_RESETTING_BUS;
382507c3241Smlf 			next_action = GACTION_INCOMPLETE;
383507c3241Smlf 			break;
384507c3241Smlf 
385507c3241Smlf 		case GACTION_INCOMPLETE:
386507c3241Smlf 		default:
387507c3241Smlf 			/* be verbose about HBA resets */
388507c3241Smlf 			GDBG_ERROR(("?ghd_timer_newstate: HBA reset failed "
389507c3241Smlf 			    "hba 0x%p gcmdp 0x%p gtgtp 0x%p\n",
390507c3241Smlf 			    (void *)hba_handle, (void *)gcmdp, (void *)gtgtp));
391507c3241Smlf 			/*
392507c3241Smlf 			 * When all else fails, punt.
393507c3241Smlf 			 *
394507c3241Smlf 			 * We're in big trouble if we get to this point.
395507c3241Smlf 			 * Maybe we should try to re-initialize the HBA.
396507c3241Smlf 			 */
397507c3241Smlf 			msgp = "HBA reset";
398507c3241Smlf 			next_state = GCMD_STATE_HUNG;
399507c3241Smlf 			next_action = GACTION_INCOMPLETE;
400507c3241Smlf 			break;
401507c3241Smlf 		}
402507c3241Smlf 
403507c3241Smlf 		/*
404507c3241Smlf 		 * I want to see target requests only if verbose, but
405507c3241Smlf 		 * scsi_log() only prints the device pathname if level
406507c3241Smlf 		 * is CE_WARN or CE_PANIC...so I guess we can't use
407507c3241Smlf 		 * scsi_log for TGTREQ messages, or they must come to
408507c3241Smlf 		 * the console.  How silly.  Looking for "verbose boot"
409507c3241Smlf 		 * is non-DDI-compliant, but let's do it anyway.
410507c3241Smlf 		 */
411507c3241Smlf 
412507c3241Smlf 		if (calltype == GHD_TGTREQ) {
413507c3241Smlf 			if ((boothowto & RB_VERBOSE)) {
414507c3241Smlf 				scsi_log(cccp->ccc_hba_dip, cccp->ccc_label,
415507c3241Smlf 				    CE_WARN,
416507c3241Smlf 				    "target request: %s, target=%d lun=%d",
417507c3241Smlf 				    msgp, gtgtp->gt_target, gtgtp->gt_lun);
418507c3241Smlf 			}
419507c3241Smlf 		} else {
420507c3241Smlf 			scsi_log(cccp->ccc_hba_dip, cccp->ccc_label, CE_WARN,
421507c3241Smlf 			    "timeout: %s, target=%d lun=%d", msgp,
422903a11ebSrh 			    gtgtp->gt_target, gtgtp->gt_lun);
423507c3241Smlf 		}
424507c3241Smlf 
425507c3241Smlf 		/*
426507c3241Smlf 		 * Before firing off the HBA action, restart the timer
427507c3241Smlf 		 * using the timeout value from ghd_timeout_table[].
428507c3241Smlf 		 *
429507c3241Smlf 		 * The table entries should never restart the timer
430507c3241Smlf 		 * for the GHD_STATE_IDLE and GHD_STATE_DONEQ states.
431507c3241Smlf 		 *
432507c3241Smlf 		 */
433507c3241Smlf 		if (gcmdp) {
434507c3241Smlf 			gcmdp->cmd_state = next_state;
435507c3241Smlf 			new_timeout = ghd_timeout_table[gcmdp->cmd_state];
436507c3241Smlf 			if (new_timeout != 0)
437507c3241Smlf 				ghd_timer_start(cccp, gcmdp, new_timeout);
438507c3241Smlf 
439507c3241Smlf 			/* save a copy in case action function frees it */
440507c3241Smlf 			gsav = *gcmdp;
441507c3241Smlf 			gsav_used = 1;
442507c3241Smlf 		}
443507c3241Smlf 
444507c3241Smlf 		if (action == GACTION_RESET_BUS && cccp->ccc_waitq_frozen) {
445507c3241Smlf 			GDBG_WARN(("avoiding bus reset while waitq frozen\n"));
446507c3241Smlf 			break;
447507c3241Smlf 		}
448507c3241Smlf 
449507c3241Smlf 		/* invoke the HBA's action function */
450507c3241Smlf 		if ((*func)(hba_handle, gcmdp, gtgtp, action, calltype)) {
451507c3241Smlf 			/* if it took wait for an interrupt or timeout */
452507c3241Smlf 			break;
453507c3241Smlf 		}
454507c3241Smlf 		/*
455507c3241Smlf 		 * if the HBA reset fails leave the retry
456507c3241Smlf 		 * timer running and just exit.
457507c3241Smlf 		 */
458507c3241Smlf 		if (action == GACTION_INCOMPLETE)
459507c3241Smlf 			return;
460507c3241Smlf 
461507c3241Smlf 		/* all other failures cause transition to next action */
462507c3241Smlf 		if (gcmdp != NULL && new_timeout != 0) {
463507c3241Smlf 			/*
464507c3241Smlf 			 * But stop the old timer prior to
465507c3241Smlf 			 * restarting a new timer because each step may
466507c3241Smlf 			 * have a different timeout value.
467507c3241Smlf 			 */
468507c3241Smlf 			GHD_TIMER_STOP(cccp, gcmdp);
469507c3241Smlf 		}
470507c3241Smlf 		action = next_action;
471507c3241Smlf 	}
472507c3241Smlf 
473507c3241Smlf 	/*
474507c3241Smlf 	 * HBA action function is done with gsav (if used)
475507c3241Smlf 	 * or gtgtp/cccp (if gsav not used).  We need to mark other
476507c3241Smlf 	 * outstanding requests if they were affected by this action
477507c3241Smlf 	 * (say, a device reset which also cancels all outstanding
478507c3241Smlf 	 * requests on this device) to prevent multiple timeouts/HBA
479507c3241Smlf 	 * actions for the same device or bus condition.  Scan the timer
480507c3241Smlf 	 * list (all active requests) and update states as necessary.
481507c3241Smlf 	 * Hold the activel_mutex while scanning the active list.  Check
482507c3241Smlf 	 * for either same dev/bus as gsav (if used) or for same
483507c3241Smlf 	 * dev/bus as gtgtp or cccp (if gsav is not used).
484507c3241Smlf 	 */
485507c3241Smlf 
486507c3241Smlf 	mutex_enter(&cccp->ccc_activel_mutex);
487507c3241Smlf 
488507c3241Smlf 	for (gcmdp_scan = (gcmd_t *)L2_next(&cccp->ccc_activel);
489507c3241Smlf 	    gcmdp_scan != NULL;
490507c3241Smlf 	    gcmdp_scan = (gcmd_t *)L2_next(&gcmdp_scan->cmd_timer_link)) {
491507c3241Smlf 
492507c3241Smlf 		/* skip idle or waitq commands */
493507c3241Smlf 		if (gcmdp_scan->cmd_state <= GCMD_STATE_WAITQ)
494507c3241Smlf 			continue;
495507c3241Smlf 
496507c3241Smlf 		switch (action) {
497507c3241Smlf 
498507c3241Smlf 		case GACTION_ABORT_DEV:
499507c3241Smlf 			if ((gsav_used && GCMD_SAME_DEV(&gsav, gcmdp_scan)) ||
500507c3241Smlf 			    (GCMDP2GDEVP(gcmdp_scan) == GTGTP2GDEVP(gtgtp))) {
501507c3241Smlf 				GCMD_UPDATE_STATE(gcmdp_scan,
502507c3241Smlf 				    GCMD_STATE_ABORTING_DEV);
503507c3241Smlf 			}
504507c3241Smlf 			break;
505507c3241Smlf 
506507c3241Smlf 		case GACTION_RESET_TARGET:
507507c3241Smlf 			if ((gsav_used && GCMD_SAME_DEV(&gsav, gcmdp_scan)) ||
508507c3241Smlf 			    (GCMDP2GDEVP(gcmdp_scan) == GTGTP2GDEVP(gtgtp))) {
509507c3241Smlf 				GCMD_UPDATE_STATE(gcmdp_scan,
510507c3241Smlf 				    GCMD_STATE_RESETTING_DEV);
511507c3241Smlf 			}
512507c3241Smlf 			break;
513507c3241Smlf 
514507c3241Smlf 		case GACTION_RESET_BUS:
515507c3241Smlf 			if ((gsav_used && GCMD_SAME_BUS(&gsav, gcmdp_scan)) ||
516507c3241Smlf 			    (GCMDP2CCCP(gcmdp_scan) == cccp)) {
517507c3241Smlf 				GCMD_UPDATE_STATE(gcmdp_scan,
518507c3241Smlf 				    GCMD_STATE_RESETTING_BUS);
519507c3241Smlf 			}
520507c3241Smlf 			break;
521507c3241Smlf 		default:
522507c3241Smlf 			break;
523507c3241Smlf 		}
524507c3241Smlf 	}
525507c3241Smlf 
526507c3241Smlf 	mutex_exit(&cccp->ccc_activel_mutex);
527507c3241Smlf }
528507c3241Smlf 
529507c3241Smlf 
530507c3241Smlf /*
531507c3241Smlf  *
532507c3241Smlf  * ghd_timeout_softintr()
533507c3241Smlf  *
534507c3241Smlf  *	This interrupt is scheduled if a particular HBA instance's
535507c3241Smlf  *	CCB timer list has a timed out CCB, or if the waitq is in a
536*1bff1300SToomas Soome  *	frozen state.
537507c3241Smlf  *
538507c3241Smlf  *	Find the timed out CCB and then call the HBA driver's timeout
539507c3241Smlf  *	function.
540507c3241Smlf  *
541507c3241Smlf  *	In order to avoid race conditions all processing must be done
542507c3241Smlf  *	while holding the HBA instance's mutex. If the mutex wasn't
543507c3241Smlf  *	held the HBA driver's hardware interrupt routine could be
544507c3241Smlf  *	triggered and it might try to remove a CCB from the list at
545507c3241Smlf  *	same time as were trying to abort it.
546507c3241Smlf  *
547507c3241Smlf  *	For frozen-waitq processing, just call ghd_waitq_process...
548507c3241Smlf  *	it takes care of the time calculations.
549507c3241Smlf  *
550507c3241Smlf  */
551507c3241Smlf 
552507c3241Smlf static uint_t
ghd_timeout_softintr(caddr_t arg)553507c3241Smlf ghd_timeout_softintr(caddr_t arg)
554507c3241Smlf {
555507c3241Smlf 	ccc_t	*cccp = (ccc_t *)arg;
556507c3241Smlf 
557507c3241Smlf 	if (cccp->ccc_timeout_pending) {
558507c3241Smlf 
559507c3241Smlf 		/* grab this HBA instance's mutex */
560507c3241Smlf 		mutex_enter(&cccp->ccc_hba_mutex);
561507c3241Smlf 
562507c3241Smlf 		/*
563507c3241Smlf 		 * The claim is we could reset "pending" outside the mutex, but
564507c3241Smlf 		 * since we have to acquire the mutex anyway, it doesn't hurt
565507c3241Smlf 		 */
566507c3241Smlf 		cccp->ccc_timeout_pending = 0;
567507c3241Smlf 
568507c3241Smlf 		/* timeout each expired CCB */
569507c3241Smlf 		ghd_timer_poll(cccp, GHD_TIMER_POLL_ALL);
570507c3241Smlf 
571507c3241Smlf 		mutex_enter(&cccp->ccc_waitq_mutex);
572507c3241Smlf 		ghd_waitq_process_and_mutex_exit(cccp);
573507c3241Smlf 
574507c3241Smlf 	} else if (cccp->ccc_waitq_frozen) {
575507c3241Smlf 		mutex_enter(&cccp->ccc_hba_mutex);
576507c3241Smlf 		mutex_enter(&cccp->ccc_waitq_mutex);
577507c3241Smlf 		ghd_waitq_process_and_mutex_exit(cccp);
578507c3241Smlf 	}
579507c3241Smlf 
580507c3241Smlf 	return (DDI_INTR_UNCLAIMED);
581507c3241Smlf }
582507c3241Smlf 
583507c3241Smlf 
584507c3241Smlf /*
585507c3241Smlf  * ghd_timer_poll()
586507c3241Smlf  *
587507c3241Smlf  * This function steps a packet to the next action in the recovery
588507c3241Smlf  * procedure.
589507c3241Smlf  *
590507c3241Smlf  * The caller must be  already holding the HBA mutex and take care of
591507c3241Smlf  * running the pkt completion functions.
592507c3241Smlf  *
593507c3241Smlf  */
594507c3241Smlf 
595507c3241Smlf void
ghd_timer_poll(ccc_t * cccp,gtimer_poll_t calltype)596507c3241Smlf ghd_timer_poll(ccc_t *cccp, gtimer_poll_t calltype)
597507c3241Smlf {
598507c3241Smlf 	gcmd_t	*gcmdp;
599507c3241Smlf 	gact_t	 action;
600507c3241Smlf 
601507c3241Smlf 	ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
602507c3241Smlf 
603507c3241Smlf 	/* abort each expired CCB */
604507c3241Smlf 	while (gcmdp = ghd_timeout_get(cccp)) {
605507c3241Smlf 
606507c3241Smlf 		GDBG_INTR(("?ghd_timer_poll: cccp=0x%p gcmdp=0x%p\n",
607903a11ebSrh 		    (void *)cccp, (void *)gcmdp));
608507c3241Smlf 
609507c3241Smlf 		switch (gcmdp->cmd_state) {
610507c3241Smlf 		case GCMD_STATE_IDLE:
611507c3241Smlf 		case GCMD_STATE_DONEQ:
612507c3241Smlf 		default:
613507c3241Smlf 			/* not supposed to happen */
614507c3241Smlf 			GDBG_ERROR(("ghd_timer_poll: invalid state %d\n",
615507c3241Smlf 			    gcmdp->cmd_state));
616507c3241Smlf 			return;
617507c3241Smlf 
618507c3241Smlf 		case GCMD_STATE_WAITQ:
619507c3241Smlf 			action = GACTION_EARLY_TIMEOUT;
620507c3241Smlf 			break;
621507c3241Smlf 
622507c3241Smlf 		case GCMD_STATE_ACTIVE:
623507c3241Smlf 			action = GACTION_ABORT_CMD;
624507c3241Smlf 			break;
625507c3241Smlf 
626507c3241Smlf 		case GCMD_STATE_ABORTING_CMD:
627507c3241Smlf 			action = GACTION_ABORT_DEV;
628507c3241Smlf 			break;
629507c3241Smlf 
630507c3241Smlf 		case GCMD_STATE_ABORTING_DEV:
631507c3241Smlf 			action = GACTION_RESET_TARGET;
632507c3241Smlf 			break;
633507c3241Smlf 
634507c3241Smlf 		case GCMD_STATE_RESETTING_DEV:
635507c3241Smlf 			action = GACTION_RESET_BUS;
636507c3241Smlf 			break;
637507c3241Smlf 
638507c3241Smlf 		case GCMD_STATE_RESETTING_BUS:
639507c3241Smlf 			action = GACTION_INCOMPLETE;
640507c3241Smlf 			break;
641507c3241Smlf 
642507c3241Smlf 		case GCMD_STATE_HUNG:
643507c3241Smlf 			action = GACTION_INCOMPLETE;
644507c3241Smlf 			break;
645507c3241Smlf 		}
646507c3241Smlf 
647507c3241Smlf 		ghd_timer_newstate(cccp, gcmdp, gcmdp->cmd_gtgtp, action,
648507c3241Smlf 		    GHD_TIMEOUT);
649507c3241Smlf 
650507c3241Smlf 		/* return after processing first cmd if requested */
651507c3241Smlf 
652507c3241Smlf 		if (calltype == GHD_TIMER_POLL_ONE)
653507c3241Smlf 			return;
654507c3241Smlf 	}
655507c3241Smlf }
656507c3241Smlf 
657507c3241Smlf 
658507c3241Smlf 
659507c3241Smlf 
660507c3241Smlf /*
661507c3241Smlf  *
662507c3241Smlf  * ghd_timeout_get()
663507c3241Smlf  *
664507c3241Smlf  *	Remove the first expired CCB from a particular timer list.
665507c3241Smlf  *
666507c3241Smlf  */
667507c3241Smlf 
668507c3241Smlf static gcmd_t *
ghd_timeout_get(ccc_t * cccp)669507c3241Smlf ghd_timeout_get(ccc_t *cccp)
670507c3241Smlf {
671507c3241Smlf 	gcmd_t	*gcmdp;
672507c3241Smlf 	ulong_t	lbolt;
673507c3241Smlf 
674507c3241Smlf 	ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
675507c3241Smlf 
676507c3241Smlf 	mutex_enter(&cccp->ccc_activel_mutex);
677507c3241Smlf 	lbolt = ddi_get_lbolt();
678507c3241Smlf 	gcmdp = (gcmd_t *)L2_next(&cccp->ccc_activel);
679507c3241Smlf 	while (gcmdp != NULL) {
680507c3241Smlf 		if ((gcmdp->cmd_timeout > 0) &&
681507c3241Smlf 		    (lbolt - gcmdp->cmd_start_time >= gcmdp->cmd_timeout))
682507c3241Smlf 			goto expired;
683507c3241Smlf 		gcmdp = (gcmd_t *)L2_next(&gcmdp->cmd_timer_link);
684507c3241Smlf 	}
685507c3241Smlf 	mutex_exit(&cccp->ccc_activel_mutex);
686507c3241Smlf 	return (NULL);
687507c3241Smlf 
688507c3241Smlf expired:
689507c3241Smlf 	/* unlink if from the CCB timer list */
690507c3241Smlf 	L2_delete(&gcmdp->cmd_timer_link);
691507c3241Smlf 	mutex_exit(&cccp->ccc_activel_mutex);
692507c3241Smlf 	return (gcmdp);
693507c3241Smlf }
694507c3241Smlf 
695507c3241Smlf 
696507c3241Smlf /*
697507c3241Smlf  *
698507c3241Smlf  * ghd_timeout_enable()
699507c3241Smlf  *
700507c3241Smlf  *	Only start a single timeout callback for each HBA driver
701507c3241Smlf  *	regardless of the number of boards it supports.
702507c3241Smlf  *
703507c3241Smlf  */
704507c3241Smlf 
705507c3241Smlf static void
ghd_timeout_enable(tmr_t * tmrp)706507c3241Smlf ghd_timeout_enable(tmr_t *tmrp)
707507c3241Smlf {
708507c3241Smlf 	mutex_enter(&tglobal_mutex);
709507c3241Smlf 	if (tmrp->t_refs++ == 0)  {
710507c3241Smlf 		/* establish the timeout callback */
711507c3241Smlf 		tmrp->t_timeout_id = timeout(ghd_timeout, (void *)tmrp,
712903a11ebSrh 		    tmrp->t_ticks);
713507c3241Smlf 	}
714507c3241Smlf 	mutex_exit(&tglobal_mutex);
715507c3241Smlf }
716507c3241Smlf 
717507c3241Smlf static void
ghd_timeout_disable(tmr_t * tmrp)718507c3241Smlf ghd_timeout_disable(tmr_t *tmrp)
719507c3241Smlf {
720507c3241Smlf 	ASSERT(tmrp != NULL);
721507c3241Smlf 
722507c3241Smlf 	mutex_enter(&tglobal_mutex);
723b3c0e203Smlf 	if (tmrp->t_refs-- <= 1) {
724507c3241Smlf 		(void) untimeout(tmrp->t_timeout_id);
725b3c0e203Smlf 	}
726507c3241Smlf 	mutex_exit(&tglobal_mutex);
727507c3241Smlf }
728507c3241Smlf 
729507c3241Smlf /* ************************************************************************ */
730507c3241Smlf 
731507c3241Smlf 	/* these are the externally callable routines */
732507c3241Smlf 
733507c3241Smlf 
734507c3241Smlf void
ghd_timer_init(tmr_t * tmrp,long ticks)735507c3241Smlf ghd_timer_init(tmr_t *tmrp, long ticks)
736507c3241Smlf {
737507c3241Smlf 	int	indx;
738507c3241Smlf 
739507c3241Smlf 	mutex_init(&tglobal_mutex, NULL, MUTEX_DRIVER, NULL);
740507c3241Smlf 	mutex_init(&tmrp->t_mutex, NULL, MUTEX_DRIVER, NULL);
741507c3241Smlf 
742507c3241Smlf 	/*
743507c3241Smlf 	 * determine default timeout value
744507c3241Smlf 	 */
745507c3241Smlf 	ghd_HZ = drv_usectohz(1000000);
746507c3241Smlf 	if (ticks == 0)
747507c3241Smlf 		ticks = scsi_watchdog_tick * ghd_HZ;
748507c3241Smlf 	tmrp->t_ticks = ticks;
749507c3241Smlf 
750507c3241Smlf 
751507c3241Smlf 	/*
752507c3241Smlf 	 * Initialize the table of abort timer values using an
753507c3241Smlf 	 * indirect lookup table so that this code isn't dependant
754507c3241Smlf 	 * on the cmdstate_t enum values or order.
755507c3241Smlf 	 */
756507c3241Smlf 	for (indx = 0; indx < ghd_ntime_inits; indx++) {
757507c3241Smlf 		int	state;
758507c3241Smlf 		ulong_t	value;
759507c3241Smlf 
760507c3241Smlf 		if (!ghd_time_inits[indx].valid)
761507c3241Smlf 			continue;
762507c3241Smlf 		state = ghd_time_inits[indx].state;
763507c3241Smlf 		value = ghd_time_inits[indx].value;
764f304523cSzhongyan gu - Sun Microsystems - Beijing China 		ghd_timeout_table[state] = (cmdstate_t)value;
765507c3241Smlf 	}
766507c3241Smlf }
767507c3241Smlf 
768507c3241Smlf void
ghd_timer_fini(tmr_t * tmrp)769507c3241Smlf ghd_timer_fini(tmr_t *tmrp)
770507c3241Smlf {
771507c3241Smlf 	mutex_destroy(&tmrp->t_mutex);
772507c3241Smlf 	mutex_destroy(&tglobal_mutex);
773507c3241Smlf }
774507c3241Smlf 
775507c3241Smlf int
ghd_timer_attach(ccc_t * cccp,tmr_t * tmrp,int (* timeout_func)(void *,gcmd_t *,gtgt_t *,gact_t,int))776507c3241Smlf ghd_timer_attach(ccc_t *cccp, tmr_t *tmrp,
777507c3241Smlf     int (*timeout_func)(void *, gcmd_t *, gtgt_t *, gact_t, int))
778507c3241Smlf {
779507c3241Smlf 	ddi_iblock_cookie_t iblock;
780507c3241Smlf 
781507c3241Smlf 	if (ddi_add_softintr(cccp->ccc_hba_dip, DDI_SOFTINT_LOW,
782507c3241Smlf 	    &cccp->ccc_soft_id, &iblock, NULL,
783507c3241Smlf 	    ghd_timeout_softintr, (caddr_t)cccp) != DDI_SUCCESS) {
784507c3241Smlf 		GDBG_ERROR((
785507c3241Smlf 		    "ghd_timer_attach: add softintr failed cccp 0x%p\n",
786507c3241Smlf 		    (void *)cccp));
787507c3241Smlf 		return (FALSE);
788507c3241Smlf 	}
789507c3241Smlf 
790507c3241Smlf 	/* init the per HBA-instance control fields */
791507c3241Smlf 	mutex_init(&cccp->ccc_activel_mutex, NULL, MUTEX_DRIVER, iblock);
792507c3241Smlf 	L2_INIT(&cccp->ccc_activel);
793507c3241Smlf 	cccp->ccc_timeout_func = timeout_func;
794507c3241Smlf 
795507c3241Smlf 	/* stick this HBA's control structure on the master list */
796507c3241Smlf 	mutex_enter(&tmrp->t_mutex);
797507c3241Smlf 
798507c3241Smlf 	cccp->ccc_nextp = tmrp->t_ccc_listp;
799507c3241Smlf 	tmrp->t_ccc_listp = cccp;
800507c3241Smlf 	cccp->ccc_tmrp = tmrp;
801507c3241Smlf 	mutex_exit(&tmrp->t_mutex);
802507c3241Smlf 
803507c3241Smlf 	/*
804507c3241Smlf 	 * The enable and disable routines use a separate mutex than
805507c3241Smlf 	 * t_mutex which is used by the timeout callback function.
806507c3241Smlf 	 * This is to avoid a deadlock when calling untimeout() from
807507c3241Smlf 	 * the disable routine.
808507c3241Smlf 	 */
809507c3241Smlf 	ghd_timeout_enable(tmrp);
810507c3241Smlf 
811507c3241Smlf 	return (TRUE);
812507c3241Smlf }
813507c3241Smlf 
814507c3241Smlf 
815507c3241Smlf /*
816507c3241Smlf  *
817507c3241Smlf  * ghd_timer_detach()
818507c3241Smlf  *
819507c3241Smlf  *	clean up for a detaching HBA instance
820507c3241Smlf  *
821507c3241Smlf  */
822507c3241Smlf 
823507c3241Smlf void
ghd_timer_detach(ccc_t * cccp)824507c3241Smlf ghd_timer_detach(ccc_t *cccp)
825507c3241Smlf {
826507c3241Smlf 	tmr_t	*tmrp = cccp->ccc_tmrp;
827507c3241Smlf 	ccc_t	**prevpp;
828507c3241Smlf 
829507c3241Smlf 	/* make certain the CCB list is empty */
830507c3241Smlf 	ASSERT(cccp->ccc_activel.l2_nextp == &cccp->ccc_activel);
831507c3241Smlf 	ASSERT(cccp->ccc_activel.l2_nextp == cccp->ccc_activel.l2_prevp);
832507c3241Smlf 
833507c3241Smlf 	mutex_enter(&tmrp->t_mutex);
834507c3241Smlf 
835507c3241Smlf 	prevpp = &tmrp->t_ccc_listp;
836507c3241Smlf 	ASSERT(*prevpp != NULL);
837507c3241Smlf 
838507c3241Smlf 	/* run down the linked list to find the entry that preceeds this one */
839507c3241Smlf 	do {
840507c3241Smlf 		if (*prevpp == cccp)
841507c3241Smlf 			goto remove_it;
842507c3241Smlf 		prevpp = &(*prevpp)->ccc_nextp;
843507c3241Smlf 	} while (*prevpp != NULL);
844507c3241Smlf 
845507c3241Smlf 	/* fell off the end of the list */
846507c3241Smlf 	GDBG_ERROR(("ghd_timer_detach: corrupt list, cccp=0x%p\n",
847507c3241Smlf 	    (void *)cccp));
848507c3241Smlf 
849507c3241Smlf remove_it:
850507c3241Smlf 	*prevpp = cccp->ccc_nextp;
851507c3241Smlf 	mutex_exit(&tmrp->t_mutex);
852507c3241Smlf 	mutex_destroy(&cccp->ccc_activel_mutex);
853507c3241Smlf 
854507c3241Smlf 	ddi_remove_softintr(cccp->ccc_soft_id);
855507c3241Smlf 
856507c3241Smlf 	ghd_timeout_disable(tmrp);
857507c3241Smlf }
858507c3241Smlf 
859507c3241Smlf /*
860507c3241Smlf  *
861507c3241Smlf  * ghd_timer_start()
862507c3241Smlf  *
863507c3241Smlf  *	Add a CCB to the CCB timer list.
864507c3241Smlf  */
865507c3241Smlf 
866507c3241Smlf void
ghd_timer_start(ccc_t * cccp,gcmd_t * gcmdp,long cmd_timeout)867507c3241Smlf ghd_timer_start(ccc_t *cccp, gcmd_t *gcmdp, long cmd_timeout)
868507c3241Smlf {
869507c3241Smlf 	ulong_t	lbolt;
870507c3241Smlf 
871507c3241Smlf 	mutex_enter(&cccp->ccc_activel_mutex);
872507c3241Smlf 	lbolt = ddi_get_lbolt();
873507c3241Smlf 
874507c3241Smlf 	/* initialize this CCB's timer */
875507c3241Smlf 	gcmdp->cmd_start_time = lbolt;
876507c3241Smlf 	gcmdp->cmd_timeout = (cmd_timeout * ghd_HZ);
877507c3241Smlf 
878507c3241Smlf 	/* add it to the list */
879507c3241Smlf 	L2_add(&cccp->ccc_activel, &gcmdp->cmd_timer_link, gcmdp);
880507c3241Smlf 	mutex_exit(&cccp->ccc_activel_mutex);
881507c3241Smlf }
882507c3241Smlf 
883507c3241Smlf 
884507c3241Smlf /*
885507c3241Smlf  *
886507c3241Smlf  * ghd_timer_stop()
887507c3241Smlf  *
888507c3241Smlf  *	Remove a completed CCB from the CCB timer list.
889507c3241Smlf  *
890507c3241Smlf  *	See the GHD_TIMER_STOP_INLINE() macro in ghd.h for
891507c3241Smlf  *	the actual code.
892507c3241Smlf  */
893507c3241Smlf 
894507c3241Smlf void
ghd_timer_stop(ccc_t * cccp,gcmd_t * gcmdp)895507c3241Smlf ghd_timer_stop(ccc_t *cccp, gcmd_t *gcmdp)
896507c3241Smlf {
897507c3241Smlf 	GHD_TIMER_STOP_INLINE(cccp, gcmdp);
898507c3241Smlf }
899