xref: /onnv-gate/usr/src/uts/common/os/callout.c (revision 9039:94951b21f634)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56422Sqiao  * Common Development and Distribution License (the "License").
66422Sqiao  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
228566SMadhavan.Venkataraman@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <sys/callo.h>
270Sstevel@tonic-gate #include <sys/param.h>
280Sstevel@tonic-gate #include <sys/types.h>
290Sstevel@tonic-gate #include <sys/cpuvar.h>
300Sstevel@tonic-gate #include <sys/thread.h>
310Sstevel@tonic-gate #include <sys/kmem.h>
328048SMadhavan.Venkataraman@Sun.COM #include <sys/kmem_impl.h>
330Sstevel@tonic-gate #include <sys/cmn_err.h>
340Sstevel@tonic-gate #include <sys/callb.h>
350Sstevel@tonic-gate #include <sys/debug.h>
360Sstevel@tonic-gate #include <sys/vtrace.h>
370Sstevel@tonic-gate #include <sys/sysmacros.h>
380Sstevel@tonic-gate #include <sys/sdt.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate  * Callout tables.  See timeout(9F) for details.
420Sstevel@tonic-gate  */
438048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_debug_hrtime;		/* debugger entry time */
448048SMadhavan.Venkataraman@Sun.COM static int callout_min_resolution;		/* Minimum resolution */
458048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_boot_ct;	/* Boot CPU's callout tables */
468566SMadhavan.Venkataraman@Sun.COM static clock_t callout_max_ticks;		/* max interval */
478048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_longterm;		/* longterm nanoseconds */
488048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_counter_low;		/* callout ID increment */
498048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_bits;		/* number of table bits in ID */
508048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_mask;		/* mask for the table bits */
518048SMadhavan.Venkataraman@Sun.COM static callout_cache_t *callout_caches;		/* linked list of caches */
528048SMadhavan.Venkataraman@Sun.COM #pragma align 64(callout_table)
538048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_table;		/* global callout table array */
540Sstevel@tonic-gate 
55*9039SMadhavan.Venkataraman@Sun.COM /*
56*9039SMadhavan.Venkataraman@Sun.COM  * We run normal callouts from PIL 10. This means that no other handler that
57*9039SMadhavan.Venkataraman@Sun.COM  * runs at PIL 10 is allowed to wait for normal callouts directly or indirectly
58*9039SMadhavan.Venkataraman@Sun.COM  * as it will cause a deadlock. This has always been an unwritten rule.
59*9039SMadhavan.Venkataraman@Sun.COM  * We are making it explicit here.
60*9039SMadhavan.Venkataraman@Sun.COM  */
61*9039SMadhavan.Venkataraman@Sun.COM static int callout_realtime_level = CY_LOW_LEVEL;
62*9039SMadhavan.Venkataraman@Sun.COM static int callout_normal_level = CY_LOCK_LEVEL;
63*9039SMadhavan.Venkataraman@Sun.COM 
648048SMadhavan.Venkataraman@Sun.COM static char *callout_kstat_names[] = {
658048SMadhavan.Venkataraman@Sun.COM 	"callout_timeouts",
668048SMadhavan.Venkataraman@Sun.COM 	"callout_timeouts_pending",
678048SMadhavan.Venkataraman@Sun.COM 	"callout_untimeouts_unexpired",
688048SMadhavan.Venkataraman@Sun.COM 	"callout_untimeouts_executing",
698048SMadhavan.Venkataraman@Sun.COM 	"callout_untimeouts_expired",
708048SMadhavan.Venkataraman@Sun.COM 	"callout_expirations",
718048SMadhavan.Venkataraman@Sun.COM 	"callout_allocations",
728048SMadhavan.Venkataraman@Sun.COM };
738048SMadhavan.Venkataraman@Sun.COM 
748048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_HASH_INSERT(hash, cp, cnext, cprev)	\
750Sstevel@tonic-gate {							\
768048SMadhavan.Venkataraman@Sun.COM 	callout_hash_t *hashp = &(hash);		\
778048SMadhavan.Venkataraman@Sun.COM 							\
780Sstevel@tonic-gate 	cp->cprev = NULL;				\
798048SMadhavan.Venkataraman@Sun.COM 	cp->cnext = hashp->ch_head;			\
808048SMadhavan.Venkataraman@Sun.COM 	if (hashp->ch_head == NULL)			\
818048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_tail = cp;			\
828048SMadhavan.Venkataraman@Sun.COM 	else						\
838048SMadhavan.Venkataraman@Sun.COM 		cp->cnext->cprev = cp;			\
848048SMadhavan.Venkataraman@Sun.COM 	hashp->ch_head = cp;				\
858048SMadhavan.Venkataraman@Sun.COM }
868048SMadhavan.Venkataraman@Sun.COM 
878048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_HASH_APPEND(hash, cp, cnext, cprev)	\
888048SMadhavan.Venkataraman@Sun.COM {							\
898048SMadhavan.Venkataraman@Sun.COM 	callout_hash_t *hashp = &(hash);		\
908048SMadhavan.Venkataraman@Sun.COM 							\
918048SMadhavan.Venkataraman@Sun.COM 	cp->cnext = NULL;				\
928048SMadhavan.Venkataraman@Sun.COM 	cp->cprev = hashp->ch_tail;			\
938048SMadhavan.Venkataraman@Sun.COM 	if (hashp->ch_tail == NULL)			\
948048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_head = cp;			\
958048SMadhavan.Venkataraman@Sun.COM 	else						\
968048SMadhavan.Venkataraman@Sun.COM 		cp->cprev->cnext = cp;			\
978048SMadhavan.Venkataraman@Sun.COM 	hashp->ch_tail = cp;				\
980Sstevel@tonic-gate }
990Sstevel@tonic-gate 
1008048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_HASH_DELETE(hash, cp, cnext, cprev)	\
1010Sstevel@tonic-gate {							\
1028048SMadhavan.Venkataraman@Sun.COM 	callout_hash_t *hashp = &(hash);		\
1038048SMadhavan.Venkataraman@Sun.COM 							\
1048048SMadhavan.Venkataraman@Sun.COM 	if (cp->cnext == NULL)				\
1058048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_tail = cp->cprev;		\
1060Sstevel@tonic-gate 	else						\
1078048SMadhavan.Venkataraman@Sun.COM 		cp->cnext->cprev = cp->cprev;		\
1088048SMadhavan.Venkataraman@Sun.COM 	if (cp->cprev == NULL)				\
1098048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_head = cp->cnext;		\
1108048SMadhavan.Venkataraman@Sun.COM 	else						\
1118048SMadhavan.Venkataraman@Sun.COM 		cp->cprev->cnext = cp->cnext;		\
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate 
1148048SMadhavan.Venkataraman@Sun.COM /*
1158048SMadhavan.Venkataraman@Sun.COM  * These definitions help us queue callouts and callout lists. Here is
1168048SMadhavan.Venkataraman@Sun.COM  * the queueing rationale:
1178048SMadhavan.Venkataraman@Sun.COM  *
1188048SMadhavan.Venkataraman@Sun.COM  *	- callouts are queued in a FIFO manner in the ID hash table.
1198048SMadhavan.Venkataraman@Sun.COM  *	  TCP timers are typically cancelled in the same order that they
1208048SMadhavan.Venkataraman@Sun.COM  *	  were issued. The FIFO queueing shortens the search for a callout
1218048SMadhavan.Venkataraman@Sun.COM  *	  during untimeout().
1228048SMadhavan.Venkataraman@Sun.COM  *
1238048SMadhavan.Venkataraman@Sun.COM  *	- callouts are queued in a FIFO manner in their callout lists.
1248048SMadhavan.Venkataraman@Sun.COM  *	  This ensures that the callouts are executed in the same order that
1258048SMadhavan.Venkataraman@Sun.COM  *	  they were queued. This is fair. Plus, it helps to make each
1268048SMadhavan.Venkataraman@Sun.COM  *	  callout expiration timely. It also favors cancellations.
1278048SMadhavan.Venkataraman@Sun.COM  *
1288048SMadhavan.Venkataraman@Sun.COM  *	- callout lists are queued in a LIFO manner in the callout list hash
1298048SMadhavan.Venkataraman@Sun.COM  *	  table. This ensures that long term timers stay at the rear of the
1308048SMadhavan.Venkataraman@Sun.COM  *	  hash lists.
1318048SMadhavan.Venkataraman@Sun.COM  *
1328048SMadhavan.Venkataraman@Sun.COM  *	- callout lists are queued in a FIFO manner in the expired callouts
1338048SMadhavan.Venkataraman@Sun.COM  *	  list. This ensures that callout lists are executed in the order
1348048SMadhavan.Venkataraman@Sun.COM  *	  of expiration.
1358048SMadhavan.Venkataraman@Sun.COM  */
1368048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_APPEND(ct, cp)						\
1378048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_APPEND(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)],	\
1388048SMadhavan.Venkataraman@Sun.COM 		cp, c_idnext, c_idprev);				\
1398048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_APPEND(cp->c_list->cl_callouts, cp, c_clnext, c_clprev)
1408048SMadhavan.Venkataraman@Sun.COM 
1418048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_DELETE(ct, cp)						\
1428048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_DELETE(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)],	\
1438048SMadhavan.Venkataraman@Sun.COM 		cp, c_idnext, c_idprev);				\
1448048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_DELETE(cp->c_list->cl_callouts, cp, c_clnext, c_clprev)
1458048SMadhavan.Venkataraman@Sun.COM 
1468048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_INSERT(hash, cl)				\
1478048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_INSERT(hash, cl, cl_next, cl_prev)
1488048SMadhavan.Venkataraman@Sun.COM 
1498048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_APPEND(hash, cl)				\
1508048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_APPEND(hash, cl, cl_next, cl_prev)
1518048SMadhavan.Venkataraman@Sun.COM 
1528048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_DELETE(hash, cl)				\
1538048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_DELETE(hash, cl, cl_next, cl_prev)
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate /*
156*9039SMadhavan.Venkataraman@Sun.COM  * For normal callouts, there is a deadlock scenario if two callouts that
157*9039SMadhavan.Venkataraman@Sun.COM  * have an inter-dependency end up on the same callout list. To break the
158*9039SMadhavan.Venkataraman@Sun.COM  * deadlock, you need two taskq threads running in parallel. We compute
159*9039SMadhavan.Venkataraman@Sun.COM  * the number of taskq threads here using a bunch of conditions to make
160*9039SMadhavan.Venkataraman@Sun.COM  * it optimal for the common case. This is an ugly hack, but one that is
161*9039SMadhavan.Venkataraman@Sun.COM  * necessary (sigh).
162*9039SMadhavan.Venkataraman@Sun.COM  */
163*9039SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_THRESHOLD	100000000
164*9039SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_EXEC_COMPUTE(ct, exec)					\
165*9039SMadhavan.Venkataraman@Sun.COM {									\
166*9039SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;						\
167*9039SMadhavan.Venkataraman@Sun.COM 									\
168*9039SMadhavan.Venkataraman@Sun.COM 	cl = ct->ct_expired.ch_head;					\
169*9039SMadhavan.Venkataraman@Sun.COM 	if (cl == NULL) {						\
170*9039SMadhavan.Venkataraman@Sun.COM 		/*							\
171*9039SMadhavan.Venkataraman@Sun.COM 		 * If the expired list is NULL, there is nothing to	\
172*9039SMadhavan.Venkataraman@Sun.COM 		 * process.						\
173*9039SMadhavan.Venkataraman@Sun.COM 		 */							\
174*9039SMadhavan.Venkataraman@Sun.COM 		exec = 0;						\
175*9039SMadhavan.Venkataraman@Sun.COM 	} else if ((cl->cl_next == NULL) &&				\
176*9039SMadhavan.Venkataraman@Sun.COM 	    (cl->cl_callouts.ch_head == cl->cl_callouts.ch_tail)) {	\
177*9039SMadhavan.Venkataraman@Sun.COM 		/*							\
178*9039SMadhavan.Venkataraman@Sun.COM 		 * If there is only one callout list and it contains	\
179*9039SMadhavan.Venkataraman@Sun.COM 		 * only one callout, there is no need for two threads.	\
180*9039SMadhavan.Venkataraman@Sun.COM 		 */							\
181*9039SMadhavan.Venkataraman@Sun.COM 		exec = 1;						\
182*9039SMadhavan.Venkataraman@Sun.COM 	} else if ((ct->ct_heap_num == 0) ||				\
183*9039SMadhavan.Venkataraman@Sun.COM 	    (ct->ct_heap[0] > gethrtime() + CALLOUT_THRESHOLD)) {	\
184*9039SMadhavan.Venkataraman@Sun.COM 		/*							\
185*9039SMadhavan.Venkataraman@Sun.COM 		 * If the heap has become empty, we need two threads as	\
186*9039SMadhavan.Venkataraman@Sun.COM 		 * there is no one to kick off the second thread in the	\
187*9039SMadhavan.Venkataraman@Sun.COM 		 * future. If the heap is not empty and the top of the	\
188*9039SMadhavan.Venkataraman@Sun.COM 		 * heap does not expire in the near future, we need two	\
189*9039SMadhavan.Venkataraman@Sun.COM 		 * threads.						\
190*9039SMadhavan.Venkataraman@Sun.COM 		 */							\
191*9039SMadhavan.Venkataraman@Sun.COM 		exec = 2;						\
192*9039SMadhavan.Venkataraman@Sun.COM 	} else {							\
193*9039SMadhavan.Venkataraman@Sun.COM 		/*							\
194*9039SMadhavan.Venkataraman@Sun.COM 		 * We have multiple callouts to process. But the cyclic	\
195*9039SMadhavan.Venkataraman@Sun.COM 		 * will fire in the near future. So, we only need one	\
196*9039SMadhavan.Venkataraman@Sun.COM 		 * thread for now.					\
197*9039SMadhavan.Venkataraman@Sun.COM 		 */							\
198*9039SMadhavan.Venkataraman@Sun.COM 		exec = 1;						\
199*9039SMadhavan.Venkataraman@Sun.COM 	}								\
200*9039SMadhavan.Venkataraman@Sun.COM }
201*9039SMadhavan.Venkataraman@Sun.COM 
202*9039SMadhavan.Venkataraman@Sun.COM /*
2030Sstevel@tonic-gate  * Allocate a callout structure.  We try quite hard because we
2040Sstevel@tonic-gate  * can't sleep, and if we can't do the allocation, we're toast.
2058048SMadhavan.Venkataraman@Sun.COM  * Failing all, we try a KM_PANIC allocation. Note that we never
2068048SMadhavan.Venkataraman@Sun.COM  * deallocate a callout. See untimeout() for the reasoning.
2070Sstevel@tonic-gate  */
2080Sstevel@tonic-gate static callout_t *
2090Sstevel@tonic-gate callout_alloc(callout_table_t *ct)
2100Sstevel@tonic-gate {
2118048SMadhavan.Venkataraman@Sun.COM 	size_t size;
2128048SMadhavan.Venkataraman@Sun.COM 	callout_t *cp;
2138048SMadhavan.Venkataraman@Sun.COM 
2148048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
2158048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
2160Sstevel@tonic-gate 
2178048SMadhavan.Venkataraman@Sun.COM 	cp = kmem_cache_alloc(ct->ct_cache, KM_NOSLEEP);
2188048SMadhavan.Venkataraman@Sun.COM 	if (cp == NULL) {
2198048SMadhavan.Venkataraman@Sun.COM 		size = sizeof (callout_t);
2208048SMadhavan.Venkataraman@Sun.COM 		cp = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC);
2218048SMadhavan.Venkataraman@Sun.COM 	}
2228048SMadhavan.Venkataraman@Sun.COM 	cp->c_xid = 0;
223*9039SMadhavan.Venkataraman@Sun.COM 	cp->c_executor = NULL;
224*9039SMadhavan.Venkataraman@Sun.COM 	cv_init(&cp->c_done, NULL, CV_DEFAULT, NULL);
225*9039SMadhavan.Venkataraman@Sun.COM 	cp->c_waiting = 0;
2268048SMadhavan.Venkataraman@Sun.COM 
2278048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
2288048SMadhavan.Venkataraman@Sun.COM 	ct->ct_allocations++;
2290Sstevel@tonic-gate 	return (cp);
2300Sstevel@tonic-gate }
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate /*
2338048SMadhavan.Venkataraman@Sun.COM  * Allocate a callout list structure.  We try quite hard because we
2348048SMadhavan.Venkataraman@Sun.COM  * can't sleep, and if we can't do the allocation, we're toast.
2358048SMadhavan.Venkataraman@Sun.COM  * Failing all, we try a KM_PANIC allocation. Note that we never
2368048SMadhavan.Venkataraman@Sun.COM  * deallocate a callout list.
2378048SMadhavan.Venkataraman@Sun.COM  */
2388048SMadhavan.Venkataraman@Sun.COM static void
2398048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(callout_table_t *ct)
2408048SMadhavan.Venkataraman@Sun.COM {
2418048SMadhavan.Venkataraman@Sun.COM 	size_t size;
2428048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
2438048SMadhavan.Venkataraman@Sun.COM 
2448048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
2458048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
2468048SMadhavan.Venkataraman@Sun.COM 
2478048SMadhavan.Venkataraman@Sun.COM 	cl = kmem_cache_alloc(ct->ct_lcache, KM_NOSLEEP);
2488048SMadhavan.Venkataraman@Sun.COM 	if (cl == NULL) {
2498048SMadhavan.Venkataraman@Sun.COM 		size = sizeof (callout_list_t);
2508048SMadhavan.Venkataraman@Sun.COM 		cl = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC);
2518048SMadhavan.Venkataraman@Sun.COM 	}
2528048SMadhavan.Venkataraman@Sun.COM 	bzero(cl, sizeof (callout_list_t));
2538048SMadhavan.Venkataraman@Sun.COM 
2548048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
2558048SMadhavan.Venkataraman@Sun.COM 	cl->cl_next = ct->ct_lfree;
2568048SMadhavan.Venkataraman@Sun.COM 	ct->ct_lfree = cl;
2578048SMadhavan.Venkataraman@Sun.COM }
2588048SMadhavan.Venkataraman@Sun.COM 
2598048SMadhavan.Venkataraman@Sun.COM /*
260*9039SMadhavan.Venkataraman@Sun.COM  * Find a callout list that corresponds to an expiration.
2618048SMadhavan.Venkataraman@Sun.COM  */
2628048SMadhavan.Venkataraman@Sun.COM static callout_list_t *
263*9039SMadhavan.Venkataraman@Sun.COM callout_list_get(callout_table_t *ct, hrtime_t expiration, int flags, int hash)
2648048SMadhavan.Venkataraman@Sun.COM {
2658048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
2668048SMadhavan.Venkataraman@Sun.COM 
2678048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
2688048SMadhavan.Venkataraman@Sun.COM 
2698048SMadhavan.Venkataraman@Sun.COM 	for (cl = ct->ct_clhash[hash].ch_head; (cl != NULL); cl = cl->cl_next) {
270*9039SMadhavan.Venkataraman@Sun.COM 		if ((cl->cl_expiration == expiration) &&
271*9039SMadhavan.Venkataraman@Sun.COM 		    (cl->cl_flags == flags))
2728048SMadhavan.Venkataraman@Sun.COM 			return (cl);
2738048SMadhavan.Venkataraman@Sun.COM 	}
2748048SMadhavan.Venkataraman@Sun.COM 
2758048SMadhavan.Venkataraman@Sun.COM 	return (NULL);
2768048SMadhavan.Venkataraman@Sun.COM }
2778048SMadhavan.Venkataraman@Sun.COM 
2788048SMadhavan.Venkataraman@Sun.COM /*
279*9039SMadhavan.Venkataraman@Sun.COM  * Find the callout list that corresponds to an expiration.
280*9039SMadhavan.Venkataraman@Sun.COM  * If the callout list is null, free it. Else, return it.
2818048SMadhavan.Venkataraman@Sun.COM  */
2828048SMadhavan.Venkataraman@Sun.COM static callout_list_t *
2838048SMadhavan.Venkataraman@Sun.COM callout_list_check(callout_table_t *ct, hrtime_t expiration, int hash)
2848048SMadhavan.Venkataraman@Sun.COM {
2858048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
2868048SMadhavan.Venkataraman@Sun.COM 
2878048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
2888048SMadhavan.Venkataraman@Sun.COM 
289*9039SMadhavan.Venkataraman@Sun.COM 	for (cl = ct->ct_clhash[hash].ch_head; (cl != NULL); cl = cl->cl_next) {
290*9039SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_expiration == expiration) {
291*9039SMadhavan.Venkataraman@Sun.COM 			if (cl->cl_callouts.ch_head != NULL) {
292*9039SMadhavan.Venkataraman@Sun.COM 				/*
293*9039SMadhavan.Venkataraman@Sun.COM 				 * Found a match.
294*9039SMadhavan.Venkataraman@Sun.COM 				 */
295*9039SMadhavan.Venkataraman@Sun.COM 				return (cl);
296*9039SMadhavan.Venkataraman@Sun.COM 			}
297*9039SMadhavan.Venkataraman@Sun.COM 
298*9039SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
299*9039SMadhavan.Venkataraman@Sun.COM 			cl->cl_next = ct->ct_lfree;
300*9039SMadhavan.Venkataraman@Sun.COM 			ct->ct_lfree = cl;
301*9039SMadhavan.Venkataraman@Sun.COM 
302*9039SMadhavan.Venkataraman@Sun.COM 			return (NULL);
3038048SMadhavan.Venkataraman@Sun.COM 		}
3048048SMadhavan.Venkataraman@Sun.COM 	}
3058048SMadhavan.Venkataraman@Sun.COM 
3068048SMadhavan.Venkataraman@Sun.COM 	return (NULL);
3078048SMadhavan.Venkataraman@Sun.COM }
3088048SMadhavan.Venkataraman@Sun.COM /*
3098048SMadhavan.Venkataraman@Sun.COM  * Initialize a callout table's heap, if necessary. Preallocate some free
3108048SMadhavan.Venkataraman@Sun.COM  * entries so we don't have to check for NULL elsewhere.
3118048SMadhavan.Venkataraman@Sun.COM  */
3128048SMadhavan.Venkataraman@Sun.COM static void
3138048SMadhavan.Venkataraman@Sun.COM callout_heap_init(callout_table_t *ct)
3148048SMadhavan.Venkataraman@Sun.COM {
3158048SMadhavan.Venkataraman@Sun.COM 	size_t size;
3168048SMadhavan.Venkataraman@Sun.COM 
3178048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
3188048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap == NULL);
3198048SMadhavan.Venkataraman@Sun.COM 
3208048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_num = 0;
3218048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_max = CALLOUT_CHUNK;
3228048SMadhavan.Venkataraman@Sun.COM 	size = sizeof (hrtime_t) * CALLOUT_CHUNK;
3238048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap = kmem_alloc(size, KM_SLEEP);
3248048SMadhavan.Venkataraman@Sun.COM }
3258048SMadhavan.Venkataraman@Sun.COM 
3268048SMadhavan.Venkataraman@Sun.COM /*
3278048SMadhavan.Venkataraman@Sun.COM  * Reallocate the heap. We try quite hard because we can't sleep, and if
3288048SMadhavan.Venkataraman@Sun.COM  * we can't do the allocation, we're toast. Failing all, we try a KM_PANIC
3298048SMadhavan.Venkataraman@Sun.COM  * allocation. Note that the heap only expands, it never contracts.
3308048SMadhavan.Venkataraman@Sun.COM  */
3318048SMadhavan.Venkataraman@Sun.COM static void
3328048SMadhavan.Venkataraman@Sun.COM callout_heap_expand(callout_table_t *ct)
3338048SMadhavan.Venkataraman@Sun.COM {
3348048SMadhavan.Venkataraman@Sun.COM 	size_t max, size, osize;
3358048SMadhavan.Venkataraman@Sun.COM 	hrtime_t *heap;
3368048SMadhavan.Venkataraman@Sun.COM 
3378048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
3388048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num <= ct->ct_heap_max);
3398048SMadhavan.Venkataraman@Sun.COM 
3408048SMadhavan.Venkataraman@Sun.COM 	while (ct->ct_heap_num == ct->ct_heap_max) {
3418048SMadhavan.Venkataraman@Sun.COM 		max = ct->ct_heap_max;
3428048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
3438048SMadhavan.Venkataraman@Sun.COM 
3448048SMadhavan.Venkataraman@Sun.COM 		osize = sizeof (hrtime_t) * max;
3458048SMadhavan.Venkataraman@Sun.COM 		size = sizeof (hrtime_t) * (max + CALLOUT_CHUNK);
3468048SMadhavan.Venkataraman@Sun.COM 		heap = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC);
3478048SMadhavan.Venkataraman@Sun.COM 
3488048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
3498048SMadhavan.Venkataraman@Sun.COM 		if (max < ct->ct_heap_max) {
3508048SMadhavan.Venkataraman@Sun.COM 			/*
3518048SMadhavan.Venkataraman@Sun.COM 			 * Someone beat us to the allocation. Free what we
3528048SMadhavan.Venkataraman@Sun.COM 			 * just allocated and proceed.
3538048SMadhavan.Venkataraman@Sun.COM 			 */
3548048SMadhavan.Venkataraman@Sun.COM 			kmem_free(heap, size);
3558048SMadhavan.Venkataraman@Sun.COM 			continue;
3568048SMadhavan.Venkataraman@Sun.COM 		}
3578048SMadhavan.Venkataraman@Sun.COM 
3588048SMadhavan.Venkataraman@Sun.COM 		bcopy(ct->ct_heap, heap, osize);
3598048SMadhavan.Venkataraman@Sun.COM 		kmem_free(ct->ct_heap, osize);
3608048SMadhavan.Venkataraman@Sun.COM 		ct->ct_heap = heap;
3618048SMadhavan.Venkataraman@Sun.COM 		ct->ct_heap_max = size / sizeof (hrtime_t);
3628048SMadhavan.Venkataraman@Sun.COM 	}
3638048SMadhavan.Venkataraman@Sun.COM }
3648048SMadhavan.Venkataraman@Sun.COM 
3658048SMadhavan.Venkataraman@Sun.COM /*
3668048SMadhavan.Venkataraman@Sun.COM  * Move an expiration from the bottom of the heap to its correct place
3678048SMadhavan.Venkataraman@Sun.COM  * in the heap. If we reached the root doing this, return 1. Else,
3688048SMadhavan.Venkataraman@Sun.COM  * return 0.
3690Sstevel@tonic-gate  */
3708048SMadhavan.Venkataraman@Sun.COM static int
3718048SMadhavan.Venkataraman@Sun.COM callout_upheap(callout_table_t *ct)
3728048SMadhavan.Venkataraman@Sun.COM {
3738048SMadhavan.Venkataraman@Sun.COM 	int current, parent;
3748048SMadhavan.Venkataraman@Sun.COM 	hrtime_t *heap, current_expiration, parent_expiration;
3758048SMadhavan.Venkataraman@Sun.COM 
3768048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
3778048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num >= 1);
3788048SMadhavan.Venkataraman@Sun.COM 
3798048SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_heap_num == 1) {
3808048SMadhavan.Venkataraman@Sun.COM 		return (1);
3818048SMadhavan.Venkataraman@Sun.COM 	}
3828048SMadhavan.Venkataraman@Sun.COM 
3838048SMadhavan.Venkataraman@Sun.COM 	heap = ct->ct_heap;
3848048SMadhavan.Venkataraman@Sun.COM 	current = ct->ct_heap_num - 1;
3858048SMadhavan.Venkataraman@Sun.COM 
3868048SMadhavan.Venkataraman@Sun.COM 	for (;;) {
3878048SMadhavan.Venkataraman@Sun.COM 		parent = CALLOUT_HEAP_PARENT(current);
3888048SMadhavan.Venkataraman@Sun.COM 		current_expiration = heap[current];
3898048SMadhavan.Venkataraman@Sun.COM 		parent_expiration = heap[parent];
3908048SMadhavan.Venkataraman@Sun.COM 
3918048SMadhavan.Venkataraman@Sun.COM 		/*
3928048SMadhavan.Venkataraman@Sun.COM 		 * We have an expiration later than our parent; we're done.
3938048SMadhavan.Venkataraman@Sun.COM 		 */
3948048SMadhavan.Venkataraman@Sun.COM 		if (current_expiration >= parent_expiration) {
3958048SMadhavan.Venkataraman@Sun.COM 			return (0);
3968048SMadhavan.Venkataraman@Sun.COM 		}
3978048SMadhavan.Venkataraman@Sun.COM 
3988048SMadhavan.Venkataraman@Sun.COM 		/*
3998048SMadhavan.Venkataraman@Sun.COM 		 * We need to swap with our parent, and continue up the heap.
4008048SMadhavan.Venkataraman@Sun.COM 		 */
4018048SMadhavan.Venkataraman@Sun.COM 		heap[parent] = current_expiration;
4028048SMadhavan.Venkataraman@Sun.COM 		heap[current] = parent_expiration;
4038048SMadhavan.Venkataraman@Sun.COM 
4048048SMadhavan.Venkataraman@Sun.COM 		/*
4058048SMadhavan.Venkataraman@Sun.COM 		 * If we just reached the root, we're done.
4068048SMadhavan.Venkataraman@Sun.COM 		 */
4078048SMadhavan.Venkataraman@Sun.COM 		if (parent == 0) {
4088048SMadhavan.Venkataraman@Sun.COM 			return (1);
4098048SMadhavan.Venkataraman@Sun.COM 		}
4108048SMadhavan.Venkataraman@Sun.COM 
4118048SMadhavan.Venkataraman@Sun.COM 		current = parent;
4128048SMadhavan.Venkataraman@Sun.COM 	}
4138048SMadhavan.Venkataraman@Sun.COM 	/*NOTREACHED*/
4148048SMadhavan.Venkataraman@Sun.COM }
4158048SMadhavan.Venkataraman@Sun.COM 
4168048SMadhavan.Venkataraman@Sun.COM /*
417*9039SMadhavan.Venkataraman@Sun.COM  * Insert a new expiration into a callout table's heap.
4188048SMadhavan.Venkataraman@Sun.COM  */
4198048SMadhavan.Venkataraman@Sun.COM static void
4208048SMadhavan.Venkataraman@Sun.COM callout_heap_insert(callout_table_t *ct, hrtime_t expiration)
4218048SMadhavan.Venkataraman@Sun.COM {
4228048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
4238048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num < ct->ct_heap_max);
4248048SMadhavan.Venkataraman@Sun.COM 
4258048SMadhavan.Venkataraman@Sun.COM 	/*
4268048SMadhavan.Venkataraman@Sun.COM 	 * First, copy the expiration to the bottom of the heap.
4278048SMadhavan.Venkataraman@Sun.COM 	 */
4288048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap[ct->ct_heap_num] = expiration;
4298048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_num++;
4308048SMadhavan.Venkataraman@Sun.COM 
4318048SMadhavan.Venkataraman@Sun.COM 	/*
4328048SMadhavan.Venkataraman@Sun.COM 	 * Now, perform an upheap operation. If we reached the root, then
4338048SMadhavan.Venkataraman@Sun.COM 	 * the cyclic needs to be reprogrammed as we have an earlier
4348048SMadhavan.Venkataraman@Sun.COM 	 * expiration.
4358048SMadhavan.Venkataraman@Sun.COM 	 *
4368048SMadhavan.Venkataraman@Sun.COM 	 * Also, during the CPR suspend phase, do not reprogram the cyclic.
4378048SMadhavan.Venkataraman@Sun.COM 	 * We don't want any callout activity. When the CPR resume phase is
4388048SMadhavan.Venkataraman@Sun.COM 	 * entered, the cyclic will be programmed for the earliest expiration
4398048SMadhavan.Venkataraman@Sun.COM 	 * in the heap.
4408048SMadhavan.Venkataraman@Sun.COM 	 */
4418566SMadhavan.Venkataraman@Sun.COM 	if (callout_upheap(ct) && (ct->ct_suspend == 0))
4428048SMadhavan.Venkataraman@Sun.COM 		(void) cyclic_reprogram(ct->ct_cyclic, expiration);
4438048SMadhavan.Venkataraman@Sun.COM }
4448048SMadhavan.Venkataraman@Sun.COM 
4458048SMadhavan.Venkataraman@Sun.COM /*
4468048SMadhavan.Venkataraman@Sun.COM  * Move an expiration from the top of the heap to its correct place
4478048SMadhavan.Venkataraman@Sun.COM  * in the heap.
4488048SMadhavan.Venkataraman@Sun.COM  */
4498048SMadhavan.Venkataraman@Sun.COM static void
4508048SMadhavan.Venkataraman@Sun.COM callout_downheap(callout_table_t *ct)
4510Sstevel@tonic-gate {
4528048SMadhavan.Venkataraman@Sun.COM 	int left, right, current, nelems;
4538048SMadhavan.Venkataraman@Sun.COM 	hrtime_t *heap, left_expiration, right_expiration, current_expiration;
4548048SMadhavan.Venkataraman@Sun.COM 
4558048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
4568048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num >= 1);
4578048SMadhavan.Venkataraman@Sun.COM 
4588048SMadhavan.Venkataraman@Sun.COM 	heap = ct->ct_heap;
4598048SMadhavan.Venkataraman@Sun.COM 	current = 0;
4608048SMadhavan.Venkataraman@Sun.COM 	nelems = ct->ct_heap_num;
4618048SMadhavan.Venkataraman@Sun.COM 
4628048SMadhavan.Venkataraman@Sun.COM 	for (;;) {
4638048SMadhavan.Venkataraman@Sun.COM 		/*
4648048SMadhavan.Venkataraman@Sun.COM 		 * If we don't have a left child (i.e., we're a leaf), we're
4658048SMadhavan.Venkataraman@Sun.COM 		 * done.
4668048SMadhavan.Venkataraman@Sun.COM 		 */
4678048SMadhavan.Venkataraman@Sun.COM 		if ((left = CALLOUT_HEAP_LEFT(current)) >= nelems)
4688048SMadhavan.Venkataraman@Sun.COM 			return;
4698048SMadhavan.Venkataraman@Sun.COM 
4708048SMadhavan.Venkataraman@Sun.COM 		left_expiration = heap[left];
4718048SMadhavan.Venkataraman@Sun.COM 		current_expiration = heap[current];
4728048SMadhavan.Venkataraman@Sun.COM 
4738048SMadhavan.Venkataraman@Sun.COM 		right = CALLOUT_HEAP_RIGHT(current);
4748048SMadhavan.Venkataraman@Sun.COM 
4758048SMadhavan.Venkataraman@Sun.COM 		/*
4768048SMadhavan.Venkataraman@Sun.COM 		 * Even if we don't have a right child, we still need to compare
4778048SMadhavan.Venkataraman@Sun.COM 		 * our expiration against that of our left child.
4788048SMadhavan.Venkataraman@Sun.COM 		 */
4798048SMadhavan.Venkataraman@Sun.COM 		if (right >= nelems)
4808048SMadhavan.Venkataraman@Sun.COM 			goto comp_left;
4818048SMadhavan.Venkataraman@Sun.COM 
4828048SMadhavan.Venkataraman@Sun.COM 		right_expiration = heap[right];
4838048SMadhavan.Venkataraman@Sun.COM 
4848048SMadhavan.Venkataraman@Sun.COM 		/*
4858048SMadhavan.Venkataraman@Sun.COM 		 * We have both a left and a right child.  We need to compare
4868048SMadhavan.Venkataraman@Sun.COM 		 * the expiration of the children to determine which
4878048SMadhavan.Venkataraman@Sun.COM 		 * expires earlier.
4888048SMadhavan.Venkataraman@Sun.COM 		 */
4898048SMadhavan.Venkataraman@Sun.COM 		if (right_expiration < left_expiration) {
4908048SMadhavan.Venkataraman@Sun.COM 			/*
4918048SMadhavan.Venkataraman@Sun.COM 			 * Our right child is the earlier of our children.
4928048SMadhavan.Venkataraman@Sun.COM 			 * We'll now compare our expiration to its expiration.
4938048SMadhavan.Venkataraman@Sun.COM 			 * If ours is the earlier one, we're done.
4948048SMadhavan.Venkataraman@Sun.COM 			 */
4958048SMadhavan.Venkataraman@Sun.COM 			if (current_expiration <= right_expiration)
4968048SMadhavan.Venkataraman@Sun.COM 				return;
4978048SMadhavan.Venkataraman@Sun.COM 
4988048SMadhavan.Venkataraman@Sun.COM 			/*
4998048SMadhavan.Venkataraman@Sun.COM 			 * Our right child expires earlier than we do; swap
5008048SMadhavan.Venkataraman@Sun.COM 			 * with our right child, and descend right.
5018048SMadhavan.Venkataraman@Sun.COM 			 */
5028048SMadhavan.Venkataraman@Sun.COM 			heap[right] = current_expiration;
5038048SMadhavan.Venkataraman@Sun.COM 			heap[current] = right_expiration;
5048048SMadhavan.Venkataraman@Sun.COM 			current = right;
5058048SMadhavan.Venkataraman@Sun.COM 			continue;
5068048SMadhavan.Venkataraman@Sun.COM 		}
5078048SMadhavan.Venkataraman@Sun.COM 
5088048SMadhavan.Venkataraman@Sun.COM comp_left:
5098048SMadhavan.Venkataraman@Sun.COM 		/*
5108048SMadhavan.Venkataraman@Sun.COM 		 * Our left child is the earlier of our children (or we have
5118048SMadhavan.Venkataraman@Sun.COM 		 * no right child).  We'll now compare our expiration
5128048SMadhavan.Venkataraman@Sun.COM 		 * to its expiration. If ours is the earlier one, we're done.
5138048SMadhavan.Venkataraman@Sun.COM 		 */
5148048SMadhavan.Venkataraman@Sun.COM 		if (current_expiration <= left_expiration)
5158048SMadhavan.Venkataraman@Sun.COM 			return;
5168048SMadhavan.Venkataraman@Sun.COM 
5178048SMadhavan.Venkataraman@Sun.COM 		/*
5188048SMadhavan.Venkataraman@Sun.COM 		 * Our left child expires earlier than we do; swap with our
5198048SMadhavan.Venkataraman@Sun.COM 		 * left child, and descend left.
5208048SMadhavan.Venkataraman@Sun.COM 		 */
5218048SMadhavan.Venkataraman@Sun.COM 		heap[left] = current_expiration;
5228048SMadhavan.Venkataraman@Sun.COM 		heap[current] = left_expiration;
5238048SMadhavan.Venkataraman@Sun.COM 		current = left;
5248048SMadhavan.Venkataraman@Sun.COM 	}
5258048SMadhavan.Venkataraman@Sun.COM }
5268048SMadhavan.Venkataraman@Sun.COM 
5278048SMadhavan.Venkataraman@Sun.COM /*
5288048SMadhavan.Venkataraman@Sun.COM  * Delete and handle all past expirations in a callout table's heap.
5298048SMadhavan.Venkataraman@Sun.COM  */
5308048SMadhavan.Venkataraman@Sun.COM static void
5318048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(callout_table_t *ct)
5328048SMadhavan.Venkataraman@Sun.COM {
5338048SMadhavan.Venkataraman@Sun.COM 	hrtime_t now, expiration;
5348048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
5358048SMadhavan.Venkataraman@Sun.COM 	int hash;
5368048SMadhavan.Venkataraman@Sun.COM 
5378048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
5388048SMadhavan.Venkataraman@Sun.COM 
5398048SMadhavan.Venkataraman@Sun.COM 	now = gethrtime();
5408048SMadhavan.Venkataraman@Sun.COM 
5418048SMadhavan.Venkataraman@Sun.COM 	while (ct->ct_heap_num > 0) {
5428048SMadhavan.Venkataraman@Sun.COM 		expiration = ct->ct_heap[0];
5438048SMadhavan.Venkataraman@Sun.COM 		/*
5448048SMadhavan.Venkataraman@Sun.COM 		 * Find the callout list that corresponds to the expiration.
5458048SMadhavan.Venkataraman@Sun.COM 		 * If the callout list is empty, callout_list_check()
5468048SMadhavan.Venkataraman@Sun.COM 		 * will free the callout list and return NULL.
5478048SMadhavan.Venkataraman@Sun.COM 		 */
5488048SMadhavan.Venkataraman@Sun.COM 		hash = CALLOUT_CLHASH(expiration);
5498048SMadhavan.Venkataraman@Sun.COM 		cl = callout_list_check(ct, expiration, hash);
5508048SMadhavan.Venkataraman@Sun.COM 		if (cl != NULL) {
5518048SMadhavan.Venkataraman@Sun.COM 			/*
5528048SMadhavan.Venkataraman@Sun.COM 			 * If the root of the heap expires in the future, we are
5538048SMadhavan.Venkataraman@Sun.COM 			 * done. We are doing this check here instead of at the
5548048SMadhavan.Venkataraman@Sun.COM 			 * beginning because we want to first free all the
5558048SMadhavan.Venkataraman@Sun.COM 			 * empty callout lists at the top of the heap.
5568048SMadhavan.Venkataraman@Sun.COM 			 */
5578048SMadhavan.Venkataraman@Sun.COM 			if (expiration > now)
5588048SMadhavan.Venkataraman@Sun.COM 				break;
5598048SMadhavan.Venkataraman@Sun.COM 
5608048SMadhavan.Venkataraman@Sun.COM 			/*
5618048SMadhavan.Venkataraman@Sun.COM 			 * Move the callout list for this expiration to the
5628048SMadhavan.Venkataraman@Sun.COM 			 * list of expired callout lists. It will be processed
5638048SMadhavan.Venkataraman@Sun.COM 			 * by the callout executor.
5648048SMadhavan.Venkataraman@Sun.COM 			 */
5658048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
5668048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_APPEND(ct->ct_expired, cl);
5678048SMadhavan.Venkataraman@Sun.COM 		}
5688048SMadhavan.Venkataraman@Sun.COM 
5698048SMadhavan.Venkataraman@Sun.COM 		/*
5708048SMadhavan.Venkataraman@Sun.COM 		 * Now delete the root. This is done by swapping the root with
5718048SMadhavan.Venkataraman@Sun.COM 		 * the last item in the heap and downheaping the item.
5728048SMadhavan.Venkataraman@Sun.COM 		 */
5738048SMadhavan.Venkataraman@Sun.COM 		ct->ct_heap_num--;
5748048SMadhavan.Venkataraman@Sun.COM 		if (ct->ct_heap_num > 0) {
5758048SMadhavan.Venkataraman@Sun.COM 			ct->ct_heap[0] = ct->ct_heap[ct->ct_heap_num];
5768048SMadhavan.Venkataraman@Sun.COM 			callout_downheap(ct);
5778048SMadhavan.Venkataraman@Sun.COM 		}
5788048SMadhavan.Venkataraman@Sun.COM 	}
5798048SMadhavan.Venkataraman@Sun.COM 
5808048SMadhavan.Venkataraman@Sun.COM 	/*
5818048SMadhavan.Venkataraman@Sun.COM 	 * If this callout table is empty or callouts have been suspended
5828048SMadhavan.Venkataraman@Sun.COM 	 * by CPR, just return. The cyclic has already been programmed to
5838048SMadhavan.Venkataraman@Sun.COM 	 * infinity by the cyclic subsystem.
5848048SMadhavan.Venkataraman@Sun.COM 	 */
5858566SMadhavan.Venkataraman@Sun.COM 	if ((ct->ct_heap_num == 0) || (ct->ct_suspend > 0))
5868048SMadhavan.Venkataraman@Sun.COM 		return;
5878048SMadhavan.Venkataraman@Sun.COM 
5888048SMadhavan.Venkataraman@Sun.COM 	(void) cyclic_reprogram(ct->ct_cyclic, expiration);
5898048SMadhavan.Venkataraman@Sun.COM }
5908048SMadhavan.Venkataraman@Sun.COM 
5918566SMadhavan.Venkataraman@Sun.COM /*
5928566SMadhavan.Venkataraman@Sun.COM  * Common function used to create normal and realtime callouts.
5938566SMadhavan.Venkataraman@Sun.COM  *
5948566SMadhavan.Venkataraman@Sun.COM  * Realtime callouts are handled at CY_LOW_PIL by a cyclic handler. So,
5958566SMadhavan.Venkataraman@Sun.COM  * there is one restriction on a realtime callout handler - it should not
5968566SMadhavan.Venkataraman@Sun.COM  * directly or indirectly acquire cpu_lock. CPU offline waits for pending
5978566SMadhavan.Venkataraman@Sun.COM  * cyclic handlers to complete while holding cpu_lock. So, if a realtime
5988566SMadhavan.Venkataraman@Sun.COM  * callout handler were to try to get cpu_lock, there would be a deadlock
5998566SMadhavan.Venkataraman@Sun.COM  * during CPU offline.
6008566SMadhavan.Venkataraman@Sun.COM  */
6018048SMadhavan.Venkataraman@Sun.COM callout_id_t
6028048SMadhavan.Venkataraman@Sun.COM timeout_generic(int type, void (*func)(void *), void *arg,
6038048SMadhavan.Venkataraman@Sun.COM 	hrtime_t expiration, hrtime_t resolution, int flags)
6048048SMadhavan.Venkataraman@Sun.COM {
6058048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
6064123Sdm120769 	callout_t *cp;
6074123Sdm120769 	callout_id_t id;
6088048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
6098048SMadhavan.Venkataraman@Sun.COM 	hrtime_t now, interval;
6108048SMadhavan.Venkataraman@Sun.COM 	int hash;
6118048SMadhavan.Venkataraman@Sun.COM 
6128048SMadhavan.Venkataraman@Sun.COM 	ASSERT(resolution > 0);
6138048SMadhavan.Venkataraman@Sun.COM 	ASSERT(func != NULL);
6148048SMadhavan.Venkataraman@Sun.COM 
6158048SMadhavan.Venkataraman@Sun.COM 	/*
6168048SMadhavan.Venkataraman@Sun.COM 	 * Please see comment about minimum resolution in callout_init().
6178048SMadhavan.Venkataraman@Sun.COM 	 */
6188048SMadhavan.Venkataraman@Sun.COM 	if (resolution < callout_min_resolution)
6198048SMadhavan.Venkataraman@Sun.COM 		resolution = callout_min_resolution;
6206422Sqiao 
6218048SMadhavan.Venkataraman@Sun.COM 	/*
6228048SMadhavan.Venkataraman@Sun.COM 	 * We disable kernel preemption so that we remain on the same CPU
6238048SMadhavan.Venkataraman@Sun.COM 	 * throughout. If we needed to reprogram the callout table's cyclic,
6248048SMadhavan.Venkataraman@Sun.COM 	 * we can avoid X-calls if we are on the same CPU.
6258048SMadhavan.Venkataraman@Sun.COM 	 *
6268048SMadhavan.Venkataraman@Sun.COM 	 * Note that callout_alloc() releases and reacquires the callout
6278048SMadhavan.Venkataraman@Sun.COM 	 * table mutex. While reacquiring the mutex, it is possible for us
6288048SMadhavan.Venkataraman@Sun.COM 	 * to go to sleep and later migrate to another CPU. This should be
6298048SMadhavan.Venkataraman@Sun.COM 	 * pretty rare, though.
6308048SMadhavan.Venkataraman@Sun.COM 	 */
6318048SMadhavan.Venkataraman@Sun.COM 	kpreempt_disable();
6320Sstevel@tonic-gate 
6338048SMadhavan.Venkataraman@Sun.COM 	ct = &callout_table[CALLOUT_TABLE(type, CPU->cpu_seqid)];
6348048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
6350Sstevel@tonic-gate 
6368048SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_cyclic == CYCLIC_NONE) {
6378048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
6388048SMadhavan.Venkataraman@Sun.COM 		/*
6398048SMadhavan.Venkataraman@Sun.COM 		 * The callout table has not yet been initialized fully.
6408048SMadhavan.Venkataraman@Sun.COM 		 * So, put this one on the boot callout table which is
6418048SMadhavan.Venkataraman@Sun.COM 		 * always initialized.
6428048SMadhavan.Venkataraman@Sun.COM 		 */
6438048SMadhavan.Venkataraman@Sun.COM 		ct = &callout_boot_ct[type];
6448048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
6458048SMadhavan.Venkataraman@Sun.COM 	}
6468048SMadhavan.Venkataraman@Sun.COM 
6478048SMadhavan.Venkataraman@Sun.COM 	if ((cp = ct->ct_free) == NULL)
6480Sstevel@tonic-gate 		cp = callout_alloc(ct);
6490Sstevel@tonic-gate 	else
6508048SMadhavan.Venkataraman@Sun.COM 		ct->ct_free = cp->c_idnext;
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 	cp->c_func = func;
6530Sstevel@tonic-gate 	cp->c_arg = arg;
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	/*
6568048SMadhavan.Venkataraman@Sun.COM 	 * Compute the expiration hrtime.
6578048SMadhavan.Venkataraman@Sun.COM 	 */
6588048SMadhavan.Venkataraman@Sun.COM 	now = gethrtime();
6598048SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_FLAG_ABSOLUTE) {
6608048SMadhavan.Venkataraman@Sun.COM 		interval = expiration - now;
6618048SMadhavan.Venkataraman@Sun.COM 	} else {
6628048SMadhavan.Venkataraman@Sun.COM 		interval = expiration;
6638048SMadhavan.Venkataraman@Sun.COM 		expiration += now;
6648048SMadhavan.Venkataraman@Sun.COM 	}
6658048SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_FLAG_ROUNDUP)
6668048SMadhavan.Venkataraman@Sun.COM 		expiration += resolution - 1;
6678048SMadhavan.Venkataraman@Sun.COM 	expiration = (expiration / resolution) * resolution;
6688566SMadhavan.Venkataraman@Sun.COM 	if (expiration <= 0) {
6698566SMadhavan.Venkataraman@Sun.COM 		/*
6708566SMadhavan.Venkataraman@Sun.COM 		 * expiration hrtime overflow has occurred. Just set the
6718566SMadhavan.Venkataraman@Sun.COM 		 * expiration to infinity.
6728566SMadhavan.Venkataraman@Sun.COM 		 */
6738566SMadhavan.Venkataraman@Sun.COM 		expiration = CY_INFINITY;
6748566SMadhavan.Venkataraman@Sun.COM 	}
6758048SMadhavan.Venkataraman@Sun.COM 
6768048SMadhavan.Venkataraman@Sun.COM 	/*
6778048SMadhavan.Venkataraman@Sun.COM 	 * Assign an ID to this callout
6788048SMadhavan.Venkataraman@Sun.COM 	 */
6798048SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_FLAG_32BIT) {
6808048SMadhavan.Venkataraman@Sun.COM 		if (interval > callout_longterm) {
6818048SMadhavan.Venkataraman@Sun.COM 			id = (ct->ct_long_id - callout_counter_low);
6828048SMadhavan.Venkataraman@Sun.COM 			id |= CALLOUT_COUNTER_HIGH;
6838048SMadhavan.Venkataraman@Sun.COM 			ct->ct_long_id = id;
6848048SMadhavan.Venkataraman@Sun.COM 		} else {
6858048SMadhavan.Venkataraman@Sun.COM 			id = (ct->ct_short_id - callout_counter_low);
6868048SMadhavan.Venkataraman@Sun.COM 			id |= CALLOUT_COUNTER_HIGH;
6878048SMadhavan.Venkataraman@Sun.COM 			ct->ct_short_id = id;
6888048SMadhavan.Venkataraman@Sun.COM 		}
6898048SMadhavan.Venkataraman@Sun.COM 	} else {
6908048SMadhavan.Venkataraman@Sun.COM 		id = (ct->ct_gen_id - callout_counter_low);
6918048SMadhavan.Venkataraman@Sun.COM 		if ((id & CALLOUT_COUNTER_HIGH) == 0) {
6928048SMadhavan.Venkataraman@Sun.COM 			id |= CALLOUT_COUNTER_HIGH;
6938048SMadhavan.Venkataraman@Sun.COM 			id += CALLOUT_GENERATION_LOW;
6948048SMadhavan.Venkataraman@Sun.COM 		}
6958048SMadhavan.Venkataraman@Sun.COM 		ct->ct_gen_id = id;
6968048SMadhavan.Venkataraman@Sun.COM 	}
6978048SMadhavan.Venkataraman@Sun.COM 
6988048SMadhavan.Venkataraman@Sun.COM 	cp->c_xid = id;
6998048SMadhavan.Venkataraman@Sun.COM 
700*9039SMadhavan.Venkataraman@Sun.COM 	flags &= CALLOUT_LIST_FLAGS;
7018048SMadhavan.Venkataraman@Sun.COM 	hash = CALLOUT_CLHASH(expiration);
7028048SMadhavan.Venkataraman@Sun.COM 
7038048SMadhavan.Venkataraman@Sun.COM again:
7048048SMadhavan.Venkataraman@Sun.COM 	/*
7058048SMadhavan.Venkataraman@Sun.COM 	 * Try to see if a callout list already exists for this expiration.
7068048SMadhavan.Venkataraman@Sun.COM 	 * Most of the time, this will be the case.
7078048SMadhavan.Venkataraman@Sun.COM 	 */
708*9039SMadhavan.Venkataraman@Sun.COM 	cl = callout_list_get(ct, expiration, flags, hash);
7098048SMadhavan.Venkataraman@Sun.COM 	if (cl == NULL) {
7108048SMadhavan.Venkataraman@Sun.COM 		/*
7118048SMadhavan.Venkataraman@Sun.COM 		 * Check if we have enough space in the heap to insert one
7128048SMadhavan.Venkataraman@Sun.COM 		 * expiration. If not, expand the heap.
7138048SMadhavan.Venkataraman@Sun.COM 		 */
7148048SMadhavan.Venkataraman@Sun.COM 		if (ct->ct_heap_num == ct->ct_heap_max) {
7158048SMadhavan.Venkataraman@Sun.COM 			callout_heap_expand(ct);
7168048SMadhavan.Venkataraman@Sun.COM 			/*
7178048SMadhavan.Venkataraman@Sun.COM 			 * In the above call, we drop the lock, allocate and
7188048SMadhavan.Venkataraman@Sun.COM 			 * reacquire the lock. So, we could have been away
7198048SMadhavan.Venkataraman@Sun.COM 			 * for a while. In the meantime, someone could have
7208048SMadhavan.Venkataraman@Sun.COM 			 * inserted a callout list with the same expiration.
7218048SMadhavan.Venkataraman@Sun.COM 			 * So, the best course is to repeat the steps. This
7228048SMadhavan.Venkataraman@Sun.COM 			 * should be an infrequent event.
7238048SMadhavan.Venkataraman@Sun.COM 			 */
7248048SMadhavan.Venkataraman@Sun.COM 			goto again;
7258048SMadhavan.Venkataraman@Sun.COM 		}
7268048SMadhavan.Venkataraman@Sun.COM 
7278048SMadhavan.Venkataraman@Sun.COM 		/*
7288048SMadhavan.Venkataraman@Sun.COM 		 * Check the free list. If we don't find one, we have to
7298048SMadhavan.Venkataraman@Sun.COM 		 * take the slow path and allocate from kmem.
7308048SMadhavan.Venkataraman@Sun.COM 		 */
7318048SMadhavan.Venkataraman@Sun.COM 		if ((cl = ct->ct_lfree) == NULL) {
7328048SMadhavan.Venkataraman@Sun.COM 			callout_list_alloc(ct);
7338048SMadhavan.Venkataraman@Sun.COM 			/*
7348048SMadhavan.Venkataraman@Sun.COM 			 * In the above call, we drop the lock, allocate and
7358048SMadhavan.Venkataraman@Sun.COM 			 * reacquire the lock. So, we could have been away
7368048SMadhavan.Venkataraman@Sun.COM 			 * for a while. In the meantime, someone could have
7378048SMadhavan.Venkataraman@Sun.COM 			 * inserted a callout list with the same expiration.
7388048SMadhavan.Venkataraman@Sun.COM 			 * Plus, the heap could have become full. So, the best
7398048SMadhavan.Venkataraman@Sun.COM 			 * course is to repeat the steps. This should be an
7408048SMadhavan.Venkataraman@Sun.COM 			 * infrequent event.
7418048SMadhavan.Venkataraman@Sun.COM 			 */
7428048SMadhavan.Venkataraman@Sun.COM 			goto again;
7438048SMadhavan.Venkataraman@Sun.COM 		}
7448048SMadhavan.Venkataraman@Sun.COM 		ct->ct_lfree = cl->cl_next;
7458048SMadhavan.Venkataraman@Sun.COM 		cl->cl_expiration = expiration;
746*9039SMadhavan.Venkataraman@Sun.COM 		cl->cl_flags = flags;
7478048SMadhavan.Venkataraman@Sun.COM 
7488048SMadhavan.Venkataraman@Sun.COM 		CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl);
7498048SMadhavan.Venkataraman@Sun.COM 
7508048SMadhavan.Venkataraman@Sun.COM 		/*
7518048SMadhavan.Venkataraman@Sun.COM 		 * This is a new expiration. So, insert it into the heap.
7528048SMadhavan.Venkataraman@Sun.COM 		 * This will also reprogram the cyclic, if the expiration
7538048SMadhavan.Venkataraman@Sun.COM 		 * propagated to the root of the heap.
7548048SMadhavan.Venkataraman@Sun.COM 		 */
7558048SMadhavan.Venkataraman@Sun.COM 		callout_heap_insert(ct, expiration);
7568048SMadhavan.Venkataraman@Sun.COM 	}
7578048SMadhavan.Venkataraman@Sun.COM 	cp->c_list = cl;
7588048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_APPEND(ct, cp);
7598048SMadhavan.Venkataraman@Sun.COM 
7608048SMadhavan.Venkataraman@Sun.COM 	ct->ct_timeouts++;
7618048SMadhavan.Venkataraman@Sun.COM 	ct->ct_timeouts_pending++;
7628048SMadhavan.Venkataraman@Sun.COM 
7638048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
7648048SMadhavan.Venkataraman@Sun.COM 
7658048SMadhavan.Venkataraman@Sun.COM 	kpreempt_enable();
7668048SMadhavan.Venkataraman@Sun.COM 
7678048SMadhavan.Venkataraman@Sun.COM 	TRACE_4(TR_FAC_CALLOUT, TR_TIMEOUT,
7688048SMadhavan.Venkataraman@Sun.COM 	    "timeout:%K(%p) in %llx expiration, cp %p", func, arg, expiration,
7698048SMadhavan.Venkataraman@Sun.COM 	    cp);
7708048SMadhavan.Venkataraman@Sun.COM 
7718048SMadhavan.Venkataraman@Sun.COM 	return (id);
7728048SMadhavan.Venkataraman@Sun.COM }
7738048SMadhavan.Venkataraman@Sun.COM 
7748048SMadhavan.Venkataraman@Sun.COM timeout_id_t
7758048SMadhavan.Venkataraman@Sun.COM timeout(void (*func)(void *), void *arg, clock_t delta)
7768048SMadhavan.Venkataraman@Sun.COM {
7778048SMadhavan.Venkataraman@Sun.COM 	ulong_t id;
7788048SMadhavan.Venkataraman@Sun.COM 
7798048SMadhavan.Venkataraman@Sun.COM 	/*
7800Sstevel@tonic-gate 	 * Make sure the callout runs at least 1 tick in the future.
7810Sstevel@tonic-gate 	 */
7820Sstevel@tonic-gate 	if (delta <= 0)
7830Sstevel@tonic-gate 		delta = 1;
7848566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
7858566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
7860Sstevel@tonic-gate 
7878048SMadhavan.Venkataraman@Sun.COM 	id =  (ulong_t)timeout_generic(CALLOUT_NORMAL, func, arg,
7888048SMadhavan.Venkataraman@Sun.COM 	    TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 	return ((timeout_id_t)id);
7910Sstevel@tonic-gate }
7920Sstevel@tonic-gate 
7938048SMadhavan.Venkataraman@Sun.COM /*
7948048SMadhavan.Venkataraman@Sun.COM  * Convenience function that creates a normal callout with default parameters
7958048SMadhavan.Venkataraman@Sun.COM  * and returns a full ID.
7968048SMadhavan.Venkataraman@Sun.COM  */
7978048SMadhavan.Venkataraman@Sun.COM callout_id_t
7988048SMadhavan.Venkataraman@Sun.COM timeout_default(void (*func)(void *), void *arg, clock_t delta)
7990Sstevel@tonic-gate {
8008048SMadhavan.Venkataraman@Sun.COM 	callout_id_t id;
8010Sstevel@tonic-gate 
8028048SMadhavan.Venkataraman@Sun.COM 	/*
8038048SMadhavan.Venkataraman@Sun.COM 	 * Make sure the callout runs at least 1 tick in the future.
8048048SMadhavan.Venkataraman@Sun.COM 	 */
8058048SMadhavan.Venkataraman@Sun.COM 	if (delta <= 0)
8068048SMadhavan.Venkataraman@Sun.COM 		delta = 1;
8078566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
8088566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
8098048SMadhavan.Venkataraman@Sun.COM 
8108048SMadhavan.Venkataraman@Sun.COM 	id = timeout_generic(CALLOUT_NORMAL, func, arg, TICK_TO_NSEC(delta),
8118048SMadhavan.Venkataraman@Sun.COM 	    nsec_per_tick, 0);
8128048SMadhavan.Venkataraman@Sun.COM 
8138048SMadhavan.Venkataraman@Sun.COM 	return (id);
8140Sstevel@tonic-gate }
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate timeout_id_t
8170Sstevel@tonic-gate realtime_timeout(void (*func)(void *), void *arg, clock_t delta)
8180Sstevel@tonic-gate {
8198048SMadhavan.Venkataraman@Sun.COM 	ulong_t id;
8208048SMadhavan.Venkataraman@Sun.COM 
8218048SMadhavan.Venkataraman@Sun.COM 	/*
8228048SMadhavan.Venkataraman@Sun.COM 	 * Make sure the callout runs at least 1 tick in the future.
8238048SMadhavan.Venkataraman@Sun.COM 	 */
8248048SMadhavan.Venkataraman@Sun.COM 	if (delta <= 0)
8258048SMadhavan.Venkataraman@Sun.COM 		delta = 1;
8268566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
8278566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
8288048SMadhavan.Venkataraman@Sun.COM 
8298048SMadhavan.Venkataraman@Sun.COM 	id =  (ulong_t)timeout_generic(CALLOUT_REALTIME, func, arg,
8308048SMadhavan.Venkataraman@Sun.COM 	    TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
8318048SMadhavan.Venkataraman@Sun.COM 
8328048SMadhavan.Venkataraman@Sun.COM 	return ((timeout_id_t)id);
8330Sstevel@tonic-gate }
8340Sstevel@tonic-gate 
8358048SMadhavan.Venkataraman@Sun.COM /*
8368048SMadhavan.Venkataraman@Sun.COM  * Convenience function that creates a realtime callout with default parameters
8378048SMadhavan.Venkataraman@Sun.COM  * and returns a full ID.
8388048SMadhavan.Venkataraman@Sun.COM  */
8398048SMadhavan.Venkataraman@Sun.COM callout_id_t
8408048SMadhavan.Venkataraman@Sun.COM realtime_timeout_default(void (*func)(void *), void *arg, clock_t delta)
8410Sstevel@tonic-gate {
8428048SMadhavan.Venkataraman@Sun.COM 	callout_id_t id;
8438048SMadhavan.Venkataraman@Sun.COM 
8448048SMadhavan.Venkataraman@Sun.COM 	/*
8458048SMadhavan.Venkataraman@Sun.COM 	 * Make sure the callout runs at least 1 tick in the future.
8468048SMadhavan.Venkataraman@Sun.COM 	 */
8478048SMadhavan.Venkataraman@Sun.COM 	if (delta <= 0)
8488048SMadhavan.Venkataraman@Sun.COM 		delta = 1;
8498566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
8508566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
8518048SMadhavan.Venkataraman@Sun.COM 
8528048SMadhavan.Venkataraman@Sun.COM 	id = timeout_generic(CALLOUT_REALTIME, func, arg, TICK_TO_NSEC(delta),
8538048SMadhavan.Venkataraman@Sun.COM 	    nsec_per_tick, 0);
8548048SMadhavan.Venkataraman@Sun.COM 
8558048SMadhavan.Venkataraman@Sun.COM 	return (id);
8568048SMadhavan.Venkataraman@Sun.COM }
8578048SMadhavan.Venkataraman@Sun.COM 
8588048SMadhavan.Venkataraman@Sun.COM hrtime_t
8598048SMadhavan.Venkataraman@Sun.COM untimeout_generic(callout_id_t id, int nowait)
8608048SMadhavan.Venkataraman@Sun.COM {
8610Sstevel@tonic-gate 	callout_table_t *ct;
8620Sstevel@tonic-gate 	callout_t *cp;
8630Sstevel@tonic-gate 	callout_id_t xid;
8648048SMadhavan.Venkataraman@Sun.COM 	int hash;
8658048SMadhavan.Venkataraman@Sun.COM 	callout_id_t bogus;
8660Sstevel@tonic-gate 
8678048SMadhavan.Venkataraman@Sun.COM 	ct = &callout_table[CALLOUT_ID_TO_TABLE(id)];
8688048SMadhavan.Venkataraman@Sun.COM 	hash = CALLOUT_IDHASH(id);
8698048SMadhavan.Venkataraman@Sun.COM 
8708048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
8710Sstevel@tonic-gate 
8728048SMadhavan.Venkataraman@Sun.COM 	/*
8738048SMadhavan.Venkataraman@Sun.COM 	 * Search the ID hash table for the callout.
8748048SMadhavan.Venkataraman@Sun.COM 	 */
8758048SMadhavan.Venkataraman@Sun.COM 	for (cp = ct->ct_idhash[hash].ch_head; cp; cp = cp->c_idnext) {
8760Sstevel@tonic-gate 
8778048SMadhavan.Venkataraman@Sun.COM 		xid = cp->c_xid;
8788048SMadhavan.Venkataraman@Sun.COM 
8798048SMadhavan.Venkataraman@Sun.COM 		/*
8808048SMadhavan.Venkataraman@Sun.COM 		 * Match the ID and generation number.
8818048SMadhavan.Venkataraman@Sun.COM 		 */
8828048SMadhavan.Venkataraman@Sun.COM 		if ((xid & CALLOUT_ID_MASK) != id)
8838048SMadhavan.Venkataraman@Sun.COM 			continue;
8840Sstevel@tonic-gate 
8858048SMadhavan.Venkataraman@Sun.COM 		if ((xid & CALLOUT_EXECUTING) == 0) {
8868048SMadhavan.Venkataraman@Sun.COM 			hrtime_t expiration;
8870Sstevel@tonic-gate 
8888048SMadhavan.Venkataraman@Sun.COM 			/*
8898048SMadhavan.Venkataraman@Sun.COM 			 * Delete the callout. If the callout list becomes
8908048SMadhavan.Venkataraman@Sun.COM 			 * NULL, we don't remove it from the table. This is
8918048SMadhavan.Venkataraman@Sun.COM 			 * so it can be reused. If the empty callout list
8928048SMadhavan.Venkataraman@Sun.COM 			 * corresponds to the top of the the callout heap, we
8938048SMadhavan.Venkataraman@Sun.COM 			 * don't reprogram the table cyclic here. This is in
8948048SMadhavan.Venkataraman@Sun.COM 			 * order to avoid lots of X-calls to the CPU associated
8958048SMadhavan.Venkataraman@Sun.COM 			 * with the callout table.
8968048SMadhavan.Venkataraman@Sun.COM 			 */
897*9039SMadhavan.Venkataraman@Sun.COM 			expiration = cp->c_list->cl_expiration;
8988048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_DELETE(ct, cp);
8998048SMadhavan.Venkataraman@Sun.COM 			cp->c_idnext = ct->ct_free;
9008048SMadhavan.Venkataraman@Sun.COM 			ct->ct_free = cp;
9018048SMadhavan.Venkataraman@Sun.COM 			ct->ct_untimeouts_unexpired++;
9028048SMadhavan.Venkataraman@Sun.COM 			ct->ct_timeouts_pending--;
9038048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
9046422Sqiao 
9058048SMadhavan.Venkataraman@Sun.COM 			expiration -= gethrtime();
9060Sstevel@tonic-gate 			TRACE_2(TR_FAC_CALLOUT, TR_UNTIMEOUT,
9078048SMadhavan.Venkataraman@Sun.COM 			    "untimeout:ID %lx hrtime left %llx", id,
9088048SMadhavan.Venkataraman@Sun.COM 			    expiration);
9098048SMadhavan.Venkataraman@Sun.COM 			return (expiration < 0 ? 0 : expiration);
9100Sstevel@tonic-gate 		}
9110Sstevel@tonic-gate 
9128048SMadhavan.Venkataraman@Sun.COM 		ct->ct_untimeouts_executing++;
9130Sstevel@tonic-gate 		/*
9140Sstevel@tonic-gate 		 * The callout we want to delete is currently executing.
9150Sstevel@tonic-gate 		 * The DDI states that we must wait until the callout
916*9039SMadhavan.Venkataraman@Sun.COM 		 * completes before returning, so we block on c_done until the
9178048SMadhavan.Venkataraman@Sun.COM 		 * callout ID changes (to the old ID if it's on the freelist,
9180Sstevel@tonic-gate 		 * or to a new callout ID if it's in use).  This implicitly
9190Sstevel@tonic-gate 		 * assumes that callout structures are persistent (they are).
9200Sstevel@tonic-gate 		 */
921*9039SMadhavan.Venkataraman@Sun.COM 		if (cp->c_executor == curthread) {
9220Sstevel@tonic-gate 			/*
9230Sstevel@tonic-gate 			 * The timeout handler called untimeout() on itself.
9240Sstevel@tonic-gate 			 * Stupid, but legal.  We can't wait for the timeout
9250Sstevel@tonic-gate 			 * to complete without deadlocking, so we just return.
9260Sstevel@tonic-gate 			 */
9278048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
9280Sstevel@tonic-gate 			TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_SELF,
9290Sstevel@tonic-gate 			    "untimeout_self:ID %x", id);
9300Sstevel@tonic-gate 			return (-1);
9310Sstevel@tonic-gate 		}
9328048SMadhavan.Venkataraman@Sun.COM 		if (nowait == 0) {
9338048SMadhavan.Venkataraman@Sun.COM 			/*
9348048SMadhavan.Venkataraman@Sun.COM 			 * We need to wait. Indicate that we are waiting by
935*9039SMadhavan.Venkataraman@Sun.COM 			 * incrementing c_waiting. This prevents the executor
936*9039SMadhavan.Venkataraman@Sun.COM 			 * from doing a wakeup on c_done if there are no
9378048SMadhavan.Venkataraman@Sun.COM 			 * waiters.
9388048SMadhavan.Venkataraman@Sun.COM 			 */
9398048SMadhavan.Venkataraman@Sun.COM 			while (cp->c_xid == xid) {
940*9039SMadhavan.Venkataraman@Sun.COM 				cp->c_waiting = 1;
941*9039SMadhavan.Venkataraman@Sun.COM 				cv_wait(&cp->c_done, &ct->ct_mutex);
9428048SMadhavan.Venkataraman@Sun.COM 			}
9438048SMadhavan.Venkataraman@Sun.COM 		}
9448048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
9450Sstevel@tonic-gate 		TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_EXECUTING,
9460Sstevel@tonic-gate 		    "untimeout_executing:ID %lx", id);
9470Sstevel@tonic-gate 		return (-1);
9480Sstevel@tonic-gate 	}
9498048SMadhavan.Venkataraman@Sun.COM 	ct->ct_untimeouts_expired++;
9500Sstevel@tonic-gate 
9518048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
9520Sstevel@tonic-gate 	TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_BOGUS_ID,
9530Sstevel@tonic-gate 	    "untimeout_bogus_id:ID %lx", id);
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 	/*
9560Sstevel@tonic-gate 	 * We didn't find the specified callout ID.  This means either
9570Sstevel@tonic-gate 	 * (1) the callout already fired, or (2) the caller passed us
9580Sstevel@tonic-gate 	 * a bogus value.  Perform a sanity check to detect case (2).
9590Sstevel@tonic-gate 	 */
960*9039SMadhavan.Venkataraman@Sun.COM 	bogus = (CALLOUT_EXECUTING | CALLOUT_COUNTER_HIGH);
9618048SMadhavan.Venkataraman@Sun.COM 	if (((id & bogus) != CALLOUT_COUNTER_HIGH) && (id != 0))
9628048SMadhavan.Venkataraman@Sun.COM 		panic("untimeout: impossible timeout id %llx",
9638048SMadhavan.Venkataraman@Sun.COM 		    (unsigned long long)id);
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	return (-1);
9660Sstevel@tonic-gate }
9670Sstevel@tonic-gate 
9688048SMadhavan.Venkataraman@Sun.COM clock_t
9698048SMadhavan.Venkataraman@Sun.COM untimeout(timeout_id_t id_arg)
9708048SMadhavan.Venkataraman@Sun.COM {
9718048SMadhavan.Venkataraman@Sun.COM 	hrtime_t hleft;
9728048SMadhavan.Venkataraman@Sun.COM 	clock_t tleft;
9738048SMadhavan.Venkataraman@Sun.COM 	callout_id_t id;
9748048SMadhavan.Venkataraman@Sun.COM 
9758048SMadhavan.Venkataraman@Sun.COM 	id = (ulong_t)id_arg;
9768048SMadhavan.Venkataraman@Sun.COM 	hleft = untimeout_generic(id, 0);
9778048SMadhavan.Venkataraman@Sun.COM 	if (hleft < 0)
9788048SMadhavan.Venkataraman@Sun.COM 		tleft = -1;
9798048SMadhavan.Venkataraman@Sun.COM 	else if (hleft == 0)
9808048SMadhavan.Venkataraman@Sun.COM 		tleft = 0;
9818048SMadhavan.Venkataraman@Sun.COM 	else
9828048SMadhavan.Venkataraman@Sun.COM 		tleft = NSEC_TO_TICK(hleft);
9838048SMadhavan.Venkataraman@Sun.COM 
9848048SMadhavan.Venkataraman@Sun.COM 	return (tleft);
9858048SMadhavan.Venkataraman@Sun.COM }
9868048SMadhavan.Venkataraman@Sun.COM 
9870Sstevel@tonic-gate /*
9888048SMadhavan.Venkataraman@Sun.COM  * Convenience function to untimeout a timeout with a full ID with default
9898048SMadhavan.Venkataraman@Sun.COM  * parameters.
9908048SMadhavan.Venkataraman@Sun.COM  */
9918048SMadhavan.Venkataraman@Sun.COM clock_t
9928048SMadhavan.Venkataraman@Sun.COM untimeout_default(callout_id_t id, int nowait)
9938048SMadhavan.Venkataraman@Sun.COM {
9948048SMadhavan.Venkataraman@Sun.COM 	hrtime_t hleft;
9958048SMadhavan.Venkataraman@Sun.COM 	clock_t tleft;
9968048SMadhavan.Venkataraman@Sun.COM 
9978048SMadhavan.Venkataraman@Sun.COM 	hleft = untimeout_generic(id, nowait);
9988048SMadhavan.Venkataraman@Sun.COM 	if (hleft < 0)
9998048SMadhavan.Venkataraman@Sun.COM 		tleft = -1;
10008048SMadhavan.Venkataraman@Sun.COM 	else if (hleft == 0)
10018048SMadhavan.Venkataraman@Sun.COM 		tleft = 0;
10028048SMadhavan.Venkataraman@Sun.COM 	else
10038048SMadhavan.Venkataraman@Sun.COM 		tleft = NSEC_TO_TICK(hleft);
10048048SMadhavan.Venkataraman@Sun.COM 
10058048SMadhavan.Venkataraman@Sun.COM 	return (tleft);
10068048SMadhavan.Venkataraman@Sun.COM }
10078048SMadhavan.Venkataraman@Sun.COM 
10088048SMadhavan.Venkataraman@Sun.COM /*
10098048SMadhavan.Venkataraman@Sun.COM  * Expire all the callouts queued in the specified callout list.
10100Sstevel@tonic-gate  */
10110Sstevel@tonic-gate static void
10128048SMadhavan.Venkataraman@Sun.COM callout_list_expire(callout_table_t *ct, callout_list_t *cl)
10130Sstevel@tonic-gate {
1014*9039SMadhavan.Venkataraman@Sun.COM 	callout_t *cp, *cnext;
10158048SMadhavan.Venkataraman@Sun.COM 
10168048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
10178048SMadhavan.Venkataraman@Sun.COM 	ASSERT(cl != NULL);
10188048SMadhavan.Venkataraman@Sun.COM 
1019*9039SMadhavan.Venkataraman@Sun.COM 	for (cp = cl->cl_callouts.ch_head; cp != NULL; cp = cnext) {
1020*9039SMadhavan.Venkataraman@Sun.COM 		/*
1021*9039SMadhavan.Venkataraman@Sun.COM 		 * Multiple executor threads could be running at the same
1022*9039SMadhavan.Venkataraman@Sun.COM 		 * time. If this callout is already being executed,
1023*9039SMadhavan.Venkataraman@Sun.COM 		 * go on to the next one.
1024*9039SMadhavan.Venkataraman@Sun.COM 		 */
1025*9039SMadhavan.Venkataraman@Sun.COM 		if (cp->c_xid & CALLOUT_EXECUTING) {
1026*9039SMadhavan.Venkataraman@Sun.COM 			cnext = cp->c_clnext;
1027*9039SMadhavan.Venkataraman@Sun.COM 			continue;
1028*9039SMadhavan.Venkataraman@Sun.COM 		}
10298048SMadhavan.Venkataraman@Sun.COM 
10308048SMadhavan.Venkataraman@Sun.COM 		/*
10318048SMadhavan.Venkataraman@Sun.COM 		 * Indicate to untimeout() that a callout is
10328048SMadhavan.Venkataraman@Sun.COM 		 * being expired by the executor.
10338048SMadhavan.Venkataraman@Sun.COM 		 */
10348048SMadhavan.Venkataraman@Sun.COM 		cp->c_xid |= CALLOUT_EXECUTING;
1035*9039SMadhavan.Venkataraman@Sun.COM 		cp->c_executor = curthread;
10368048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
10378048SMadhavan.Venkataraman@Sun.COM 
10388048SMadhavan.Venkataraman@Sun.COM 		DTRACE_PROBE1(callout__start, callout_t *, cp);
10398048SMadhavan.Venkataraman@Sun.COM 		(*cp->c_func)(cp->c_arg);
10408048SMadhavan.Venkataraman@Sun.COM 		DTRACE_PROBE1(callout__end, callout_t *, cp);
10410Sstevel@tonic-gate 
10428048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
10438048SMadhavan.Venkataraman@Sun.COM 
10448048SMadhavan.Venkataraman@Sun.COM 		ct->ct_expirations++;
10458048SMadhavan.Venkataraman@Sun.COM 		ct->ct_timeouts_pending--;
10468048SMadhavan.Venkataraman@Sun.COM 		/*
1047*9039SMadhavan.Venkataraman@Sun.COM 		 * Indicate completion for c_done.
10488048SMadhavan.Venkataraman@Sun.COM 		 */
10498048SMadhavan.Venkataraman@Sun.COM 		cp->c_xid &= ~CALLOUT_EXECUTING;
1050*9039SMadhavan.Venkataraman@Sun.COM 		cp->c_executor = NULL;
1051*9039SMadhavan.Venkataraman@Sun.COM 		cnext = cp->c_clnext;
10528048SMadhavan.Venkataraman@Sun.COM 
10538048SMadhavan.Venkataraman@Sun.COM 		/*
10548048SMadhavan.Venkataraman@Sun.COM 		 * Delete callout from ID hash table and the callout
10558048SMadhavan.Venkataraman@Sun.COM 		 * list, return to freelist, and tell any untimeout() that
10568048SMadhavan.Venkataraman@Sun.COM 		 * cares that we're done.
10578048SMadhavan.Venkataraman@Sun.COM 		 */
10588048SMadhavan.Venkataraman@Sun.COM 		CALLOUT_DELETE(ct, cp);
10598048SMadhavan.Venkataraman@Sun.COM 		cp->c_idnext = ct->ct_free;
10608048SMadhavan.Venkataraman@Sun.COM 		ct->ct_free = cp;
10610Sstevel@tonic-gate 
1062*9039SMadhavan.Venkataraman@Sun.COM 		if (cp->c_waiting) {
1063*9039SMadhavan.Venkataraman@Sun.COM 			cp->c_waiting = 0;
1064*9039SMadhavan.Venkataraman@Sun.COM 			cv_broadcast(&cp->c_done);
10658048SMadhavan.Venkataraman@Sun.COM 		}
10668048SMadhavan.Venkataraman@Sun.COM 	}
10678048SMadhavan.Venkataraman@Sun.COM }
10688048SMadhavan.Venkataraman@Sun.COM 
10698048SMadhavan.Venkataraman@Sun.COM /*
10708048SMadhavan.Venkataraman@Sun.COM  * Execute all expired callout lists for a callout table.
10718048SMadhavan.Venkataraman@Sun.COM  */
10728048SMadhavan.Venkataraman@Sun.COM static void
10738048SMadhavan.Venkataraman@Sun.COM callout_expire(callout_table_t *ct)
10748048SMadhavan.Venkataraman@Sun.COM {
10758048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl, *clnext;
10768048SMadhavan.Venkataraman@Sun.COM 
10778048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
10780Sstevel@tonic-gate 
10798048SMadhavan.Venkataraman@Sun.COM 	for (cl = ct->ct_expired.ch_head; (cl != NULL); cl = clnext) {
10808048SMadhavan.Venkataraman@Sun.COM 		/*
10818048SMadhavan.Venkataraman@Sun.COM 		 * Expire all the callouts in this callout list.
10828048SMadhavan.Venkataraman@Sun.COM 		 */
10838048SMadhavan.Venkataraman@Sun.COM 		callout_list_expire(ct, cl);
10848048SMadhavan.Venkataraman@Sun.COM 
10858048SMadhavan.Venkataraman@Sun.COM 		clnext = cl->cl_next;
1086*9039SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_callouts.ch_head == NULL) {
1087*9039SMadhavan.Venkataraman@Sun.COM 			/*
1088*9039SMadhavan.Venkataraman@Sun.COM 			 * Free the callout list.
1089*9039SMadhavan.Venkataraman@Sun.COM 			 */
1090*9039SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_expired, cl);
1091*9039SMadhavan.Venkataraman@Sun.COM 			cl->cl_next = ct->ct_lfree;
1092*9039SMadhavan.Venkataraman@Sun.COM 			ct->ct_lfree = cl;
1093*9039SMadhavan.Venkataraman@Sun.COM 		}
10940Sstevel@tonic-gate 	}
10950Sstevel@tonic-gate }
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate /*
10988048SMadhavan.Venkataraman@Sun.COM  * The cyclic handlers below process callouts in two steps:
10998048SMadhavan.Venkataraman@Sun.COM  *
11008048SMadhavan.Venkataraman@Sun.COM  *	1. Find all expired callout lists and queue them in a separate
11018048SMadhavan.Venkataraman@Sun.COM  *	   list of expired callouts.
11028048SMadhavan.Venkataraman@Sun.COM  *	2. Execute the expired callout lists.
11038048SMadhavan.Venkataraman@Sun.COM  *
11048048SMadhavan.Venkataraman@Sun.COM  * This is done for two reasons:
11058048SMadhavan.Venkataraman@Sun.COM  *
11068048SMadhavan.Venkataraman@Sun.COM  *	1. We want to quickly find the next earliest expiration to program
11078048SMadhavan.Venkataraman@Sun.COM  *	   the cyclic to and reprogram it. We can do this right at the end
11088048SMadhavan.Venkataraman@Sun.COM  *	   of step 1.
11098048SMadhavan.Venkataraman@Sun.COM  *	2. The realtime cyclic handler expires callouts in place. However,
11108048SMadhavan.Venkataraman@Sun.COM  *	   for normal callouts, callouts are expired by a taskq thread.
11118048SMadhavan.Venkataraman@Sun.COM  *	   So, it is simpler and more robust to have the taskq thread just
11128048SMadhavan.Venkataraman@Sun.COM  *	   do step 2.
11130Sstevel@tonic-gate  */
11146422Sqiao 
11158048SMadhavan.Venkataraman@Sun.COM /*
11168048SMadhavan.Venkataraman@Sun.COM  * Realtime callout cyclic handler.
11178048SMadhavan.Venkataraman@Sun.COM  */
11188048SMadhavan.Venkataraman@Sun.COM void
11198048SMadhavan.Venkataraman@Sun.COM callout_realtime(callout_table_t *ct)
11208048SMadhavan.Venkataraman@Sun.COM {
11218048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
11228048SMadhavan.Venkataraman@Sun.COM 	callout_heap_delete(ct);
11238048SMadhavan.Venkataraman@Sun.COM 	callout_expire(ct);
11248048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
11258048SMadhavan.Venkataraman@Sun.COM }
11268048SMadhavan.Venkataraman@Sun.COM 
11278048SMadhavan.Venkataraman@Sun.COM void
11288048SMadhavan.Venkataraman@Sun.COM callout_execute(callout_table_t *ct)
11298048SMadhavan.Venkataraman@Sun.COM {
11308048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
11318048SMadhavan.Venkataraman@Sun.COM 	callout_expire(ct);
11328048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
11338048SMadhavan.Venkataraman@Sun.COM }
11348048SMadhavan.Venkataraman@Sun.COM 
11358048SMadhavan.Venkataraman@Sun.COM /*
11368048SMadhavan.Venkataraman@Sun.COM  * Normal callout cyclic handler.
11378048SMadhavan.Venkataraman@Sun.COM  */
11388048SMadhavan.Venkataraman@Sun.COM void
11398048SMadhavan.Venkataraman@Sun.COM callout_normal(callout_table_t *ct)
11408048SMadhavan.Venkataraman@Sun.COM {
1141*9039SMadhavan.Venkataraman@Sun.COM 	int i, exec;
11428048SMadhavan.Venkataraman@Sun.COM 
11438048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
11448048SMadhavan.Venkataraman@Sun.COM 	callout_heap_delete(ct);
1145*9039SMadhavan.Venkataraman@Sun.COM 	CALLOUT_EXEC_COMPUTE(ct, exec);
11468048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
11478048SMadhavan.Venkataraman@Sun.COM 
1148*9039SMadhavan.Venkataraman@Sun.COM 	for (i = 0; i < exec; i++) {
11498048SMadhavan.Venkataraman@Sun.COM 		ASSERT(ct->ct_taskq != NULL);
11508048SMadhavan.Venkataraman@Sun.COM 		(void) taskq_dispatch(ct->ct_taskq,
11518048SMadhavan.Venkataraman@Sun.COM 		    (task_func_t *)callout_execute, ct, TQ_NOSLEEP);
11520Sstevel@tonic-gate 	}
11530Sstevel@tonic-gate }
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate /*
11568048SMadhavan.Venkataraman@Sun.COM  * Suspend callout processing.
11570Sstevel@tonic-gate  */
11588048SMadhavan.Venkataraman@Sun.COM static void
11598048SMadhavan.Venkataraman@Sun.COM callout_suspend(void)
11600Sstevel@tonic-gate {
11618048SMadhavan.Venkataraman@Sun.COM 	int t, f;
11628048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
11638048SMadhavan.Venkataraman@Sun.COM 
11648048SMadhavan.Venkataraman@Sun.COM 	/*
11658048SMadhavan.Venkataraman@Sun.COM 	 * Traverse every callout table in the system and suspend callout
11668048SMadhavan.Venkataraman@Sun.COM 	 * processing.
11678048SMadhavan.Venkataraman@Sun.COM 	 *
11688048SMadhavan.Venkataraman@Sun.COM 	 * We need to suspend all the tables (including the inactive ones)
11698048SMadhavan.Venkataraman@Sun.COM 	 * so that if a table is made active while the suspend is still on,
11708048SMadhavan.Venkataraman@Sun.COM 	 * the table remains suspended.
11718048SMadhavan.Venkataraman@Sun.COM 	 */
11728048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
11738048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
11748048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[CALLOUT_TABLE(t, f)];
11758048SMadhavan.Venkataraman@Sun.COM 
11768048SMadhavan.Venkataraman@Sun.COM 			mutex_enter(&ct->ct_mutex);
11778566SMadhavan.Venkataraman@Sun.COM 			ct->ct_suspend++;
11788048SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_cyclic == CYCLIC_NONE) {
11798048SMadhavan.Venkataraman@Sun.COM 				mutex_exit(&ct->ct_mutex);
11808048SMadhavan.Venkataraman@Sun.COM 				continue;
11818048SMadhavan.Venkataraman@Sun.COM 			}
11828566SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_suspend == 1)
11838566SMadhavan.Venkataraman@Sun.COM 				(void) cyclic_reprogram(ct->ct_cyclic,
11848566SMadhavan.Venkataraman@Sun.COM 				    CY_INFINITY);
11858048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
11868048SMadhavan.Venkataraman@Sun.COM 		}
11878048SMadhavan.Venkataraman@Sun.COM 	}
11888048SMadhavan.Venkataraman@Sun.COM }
11898048SMadhavan.Venkataraman@Sun.COM 
11908048SMadhavan.Venkataraman@Sun.COM static void
11918048SMadhavan.Venkataraman@Sun.COM callout_adjust(callout_table_t *ct, hrtime_t delta)
11928048SMadhavan.Venkataraman@Sun.COM {
11938048SMadhavan.Venkataraman@Sun.COM 	int hash, newhash;
11948048SMadhavan.Venkataraman@Sun.COM 	hrtime_t expiration;
11958048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
11968048SMadhavan.Venkataraman@Sun.COM 	callout_hash_t list;
11978048SMadhavan.Venkataraman@Sun.COM 
11988048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
11998048SMadhavan.Venkataraman@Sun.COM 
12008048SMadhavan.Venkataraman@Sun.COM 	/*
12018048SMadhavan.Venkataraman@Sun.COM 	 * In order to adjust the expirations, we null out the heap. Then,
12028048SMadhavan.Venkataraman@Sun.COM 	 * we reinsert adjusted expirations in the heap. Keeps it simple.
12038048SMadhavan.Venkataraman@Sun.COM 	 * Note that since the CALLOUT_TABLE_SUSPENDED flag is set by the
12048048SMadhavan.Venkataraman@Sun.COM 	 * caller, the heap insert does not result in cyclic reprogramming.
12058048SMadhavan.Venkataraman@Sun.COM 	 */
12068048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_num = 0;
12078048SMadhavan.Venkataraman@Sun.COM 
12088048SMadhavan.Venkataraman@Sun.COM 	/*
12098048SMadhavan.Venkataraman@Sun.COM 	 * First, remove all the callout lists from the table and string them
12108048SMadhavan.Venkataraman@Sun.COM 	 * in a list.
12118048SMadhavan.Venkataraman@Sun.COM 	 */
12128048SMadhavan.Venkataraman@Sun.COM 	list.ch_head = list.ch_tail = NULL;
12138048SMadhavan.Venkataraman@Sun.COM 	for (hash = 0; hash < CALLOUT_BUCKETS; hash++) {
12148048SMadhavan.Venkataraman@Sun.COM 		while ((cl = ct->ct_clhash[hash].ch_head) != NULL) {
12158048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
12168048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_APPEND(list, cl);
12178048SMadhavan.Venkataraman@Sun.COM 		}
12188048SMadhavan.Venkataraman@Sun.COM 	}
12190Sstevel@tonic-gate 
12208048SMadhavan.Venkataraman@Sun.COM 	/*
12218048SMadhavan.Venkataraman@Sun.COM 	 * Now, traverse the callout lists and adjust their expirations.
12228048SMadhavan.Venkataraman@Sun.COM 	 */
12238048SMadhavan.Venkataraman@Sun.COM 	while ((cl = list.ch_head) != NULL) {
12248048SMadhavan.Venkataraman@Sun.COM 		CALLOUT_LIST_DELETE(list, cl);
12258048SMadhavan.Venkataraman@Sun.COM 		/*
12268048SMadhavan.Venkataraman@Sun.COM 		 * Set the new expiration and reinsert in the right
12278048SMadhavan.Venkataraman@Sun.COM 		 * hash bucket.
12288048SMadhavan.Venkataraman@Sun.COM 		 */
12298048SMadhavan.Venkataraman@Sun.COM 		expiration = cl->cl_expiration;
12308048SMadhavan.Venkataraman@Sun.COM 		expiration += delta;
12318048SMadhavan.Venkataraman@Sun.COM 		cl->cl_expiration = expiration;
12328048SMadhavan.Venkataraman@Sun.COM 		newhash = CALLOUT_CLHASH(expiration);
12338048SMadhavan.Venkataraman@Sun.COM 		CALLOUT_LIST_INSERT(ct->ct_clhash[newhash], cl);
12348048SMadhavan.Venkataraman@Sun.COM 		callout_heap_insert(ct, expiration);
12358048SMadhavan.Venkataraman@Sun.COM 	}
12368048SMadhavan.Venkataraman@Sun.COM }
12378048SMadhavan.Venkataraman@Sun.COM 
12388048SMadhavan.Venkataraman@Sun.COM /*
12398048SMadhavan.Venkataraman@Sun.COM  * Resume callout processing.
12408048SMadhavan.Venkataraman@Sun.COM  */
12418048SMadhavan.Venkataraman@Sun.COM static void
12428048SMadhavan.Venkataraman@Sun.COM callout_resume(hrtime_t delta)
12438048SMadhavan.Venkataraman@Sun.COM {
12448048SMadhavan.Venkataraman@Sun.COM 	hrtime_t exp;
12458048SMadhavan.Venkataraman@Sun.COM 	int t, f;
12468048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
12470Sstevel@tonic-gate 
12488048SMadhavan.Venkataraman@Sun.COM 	/*
12498048SMadhavan.Venkataraman@Sun.COM 	 * Traverse every callout table in the system and resume callout
12508048SMadhavan.Venkataraman@Sun.COM 	 * processing. For active tables, perform any hrtime adjustments
12518048SMadhavan.Venkataraman@Sun.COM 	 * necessary.
12528048SMadhavan.Venkataraman@Sun.COM 	 */
12538048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
12548048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
12558048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[CALLOUT_TABLE(t, f)];
12568048SMadhavan.Venkataraman@Sun.COM 
12578048SMadhavan.Venkataraman@Sun.COM 			mutex_enter(&ct->ct_mutex);
12588048SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_cyclic == CYCLIC_NONE) {
12598566SMadhavan.Venkataraman@Sun.COM 				ct->ct_suspend--;
12608048SMadhavan.Venkataraman@Sun.COM 				mutex_exit(&ct->ct_mutex);
12618048SMadhavan.Venkataraman@Sun.COM 				continue;
12628048SMadhavan.Venkataraman@Sun.COM 			}
12638048SMadhavan.Venkataraman@Sun.COM 
12648048SMadhavan.Venkataraman@Sun.COM 			if (delta)
12658048SMadhavan.Venkataraman@Sun.COM 				callout_adjust(ct, delta);
12668048SMadhavan.Venkataraman@Sun.COM 
12678566SMadhavan.Venkataraman@Sun.COM 			ct->ct_suspend--;
12688566SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_suspend == 0) {
12698566SMadhavan.Venkataraman@Sun.COM 				/*
12708566SMadhavan.Venkataraman@Sun.COM 				 * If the expired list is non-empty, then have
12718566SMadhavan.Venkataraman@Sun.COM 				 * the cyclic expire immediately. Else, program
12728566SMadhavan.Venkataraman@Sun.COM 				 * the cyclic based on the heap.
12738566SMadhavan.Venkataraman@Sun.COM 				 */
12748566SMadhavan.Venkataraman@Sun.COM 				if (ct->ct_expired.ch_head != NULL)
12758566SMadhavan.Venkataraman@Sun.COM 					exp = gethrtime();
12768566SMadhavan.Venkataraman@Sun.COM 				else if (ct->ct_heap_num > 0)
12778566SMadhavan.Venkataraman@Sun.COM 					exp = ct->ct_heap[0];
12788566SMadhavan.Venkataraman@Sun.COM 				else
12798566SMadhavan.Venkataraman@Sun.COM 					exp = 0;
12808566SMadhavan.Venkataraman@Sun.COM 				if (exp != 0)
12818566SMadhavan.Venkataraman@Sun.COM 					(void) cyclic_reprogram(ct->ct_cyclic,
12828566SMadhavan.Venkataraman@Sun.COM 					    exp);
12838566SMadhavan.Venkataraman@Sun.COM 			}
12848048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
12858048SMadhavan.Venkataraman@Sun.COM 		}
12868048SMadhavan.Venkataraman@Sun.COM 	}
12870Sstevel@tonic-gate }
12880Sstevel@tonic-gate 
12890Sstevel@tonic-gate /*
12900Sstevel@tonic-gate  * Callback handler used by CPR to stop and resume callouts.
12910Sstevel@tonic-gate  */
12920Sstevel@tonic-gate /*ARGSUSED*/
12930Sstevel@tonic-gate static boolean_t
12940Sstevel@tonic-gate callout_cpr_callb(void *arg, int code)
12950Sstevel@tonic-gate {
12968048SMadhavan.Venkataraman@Sun.COM 	if (code == CB_CODE_CPR_CHKPT)
12978048SMadhavan.Venkataraman@Sun.COM 		callout_suspend();
12988048SMadhavan.Venkataraman@Sun.COM 	else
12998048SMadhavan.Venkataraman@Sun.COM 		callout_resume(0);
13008048SMadhavan.Venkataraman@Sun.COM 
13018048SMadhavan.Venkataraman@Sun.COM 	return (B_TRUE);
13028048SMadhavan.Venkataraman@Sun.COM }
13038048SMadhavan.Venkataraman@Sun.COM 
13048048SMadhavan.Venkataraman@Sun.COM /*
13058048SMadhavan.Venkataraman@Sun.COM  * Callback handler invoked when the debugger is entered or exited.
13068048SMadhavan.Venkataraman@Sun.COM  */
13078048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/
13088048SMadhavan.Venkataraman@Sun.COM static boolean_t
13098048SMadhavan.Venkataraman@Sun.COM callout_debug_callb(void *arg, int code)
13108048SMadhavan.Venkataraman@Sun.COM {
13118048SMadhavan.Venkataraman@Sun.COM 	hrtime_t delta;
13128048SMadhavan.Venkataraman@Sun.COM 
13138048SMadhavan.Venkataraman@Sun.COM 	/*
13148048SMadhavan.Venkataraman@Sun.COM 	 * When the system enters the debugger. make a note of the hrtime.
13158048SMadhavan.Venkataraman@Sun.COM 	 * When it is resumed, compute how long the system was in the
13168048SMadhavan.Venkataraman@Sun.COM 	 * debugger. This interval should not be counted for callouts.
13178048SMadhavan.Venkataraman@Sun.COM 	 */
13188048SMadhavan.Venkataraman@Sun.COM 	if (code == 0) {
13198048SMadhavan.Venkataraman@Sun.COM 		callout_suspend();
13208048SMadhavan.Venkataraman@Sun.COM 		callout_debug_hrtime = gethrtime();
13218048SMadhavan.Venkataraman@Sun.COM 	} else {
13228048SMadhavan.Venkataraman@Sun.COM 		delta = gethrtime() - callout_debug_hrtime;
13238048SMadhavan.Venkataraman@Sun.COM 		callout_resume(delta);
13248048SMadhavan.Venkataraman@Sun.COM 	}
13258048SMadhavan.Venkataraman@Sun.COM 
13260Sstevel@tonic-gate 	return (B_TRUE);
13270Sstevel@tonic-gate }
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate /*
1330*9039SMadhavan.Venkataraman@Sun.COM  * Move the absolute hrestime callouts to the expired list. Then program the
1331*9039SMadhavan.Venkataraman@Sun.COM  * table's cyclic to expire immediately so that the callouts can be executed
13328048SMadhavan.Venkataraman@Sun.COM  * immediately.
13338048SMadhavan.Venkataraman@Sun.COM  */
13348048SMadhavan.Venkataraman@Sun.COM static void
13358048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(callout_table_t *ct)
13368048SMadhavan.Venkataraman@Sun.COM {
1337*9039SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl, *clnext;
1338*9039SMadhavan.Venkataraman@Sun.COM 	int hash, flags;
13398048SMadhavan.Venkataraman@Sun.COM 
13408048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
13418048SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_heap_num == 0) {
13428048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
13438048SMadhavan.Venkataraman@Sun.COM 		return;
13448048SMadhavan.Venkataraman@Sun.COM 	}
13458048SMadhavan.Venkataraman@Sun.COM 
1346*9039SMadhavan.Venkataraman@Sun.COM 	flags = CALLOUT_LIST_FLAGS;
13478048SMadhavan.Venkataraman@Sun.COM 	for (hash = 0; hash < CALLOUT_BUCKETS; hash++) {
1348*9039SMadhavan.Venkataraman@Sun.COM 		for (cl = ct->ct_clhash[hash].ch_head; cl; cl = clnext) {
1349*9039SMadhavan.Venkataraman@Sun.COM 			clnext = cl->cl_next;
1350*9039SMadhavan.Venkataraman@Sun.COM 			if (cl->cl_flags == flags) {
1351*9039SMadhavan.Venkataraman@Sun.COM 				CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
1352*9039SMadhavan.Venkataraman@Sun.COM 				CALLOUT_LIST_APPEND(ct->ct_expired, cl);
13538048SMadhavan.Venkataraman@Sun.COM 			}
13548048SMadhavan.Venkataraman@Sun.COM 		}
13558048SMadhavan.Venkataraman@Sun.COM 	}
13568048SMadhavan.Venkataraman@Sun.COM 
1357*9039SMadhavan.Venkataraman@Sun.COM 	if ((ct->ct_expired.ch_head != NULL) && (ct->ct_suspend == 0))
1358*9039SMadhavan.Venkataraman@Sun.COM 		(void) cyclic_reprogram(ct->ct_cyclic, gethrtime());
1359*9039SMadhavan.Venkataraman@Sun.COM 
13608048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
13618048SMadhavan.Venkataraman@Sun.COM }
13628048SMadhavan.Venkataraman@Sun.COM 
13638048SMadhavan.Venkataraman@Sun.COM /*
13648048SMadhavan.Venkataraman@Sun.COM  * This function is called whenever system time (hrestime) is changed
13658048SMadhavan.Venkataraman@Sun.COM  * explicitly. All the HRESTIME callouts must be expired at once.
13668048SMadhavan.Venkataraman@Sun.COM  */
13678048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/
13688048SMadhavan.Venkataraman@Sun.COM void
13698048SMadhavan.Venkataraman@Sun.COM callout_hrestime(void)
13708048SMadhavan.Venkataraman@Sun.COM {
13718048SMadhavan.Venkataraman@Sun.COM 	int t, f;
13728048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
13738048SMadhavan.Venkataraman@Sun.COM 
13748048SMadhavan.Venkataraman@Sun.COM 	/*
13758048SMadhavan.Venkataraman@Sun.COM 	 * Traverse every callout table in the system and process the hrestime
13768048SMadhavan.Venkataraman@Sun.COM 	 * callouts therein.
13778048SMadhavan.Venkataraman@Sun.COM 	 *
13788048SMadhavan.Venkataraman@Sun.COM 	 * We look at all the tables because we don't know which ones were
13798048SMadhavan.Venkataraman@Sun.COM 	 * onlined and offlined in the past. The offlined tables may still
13808048SMadhavan.Venkataraman@Sun.COM 	 * have active cyclics processing timers somewhere.
13818048SMadhavan.Venkataraman@Sun.COM 	 */
13828048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
13838048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
13848048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[CALLOUT_TABLE(t, f)];
13858048SMadhavan.Venkataraman@Sun.COM 			callout_hrestime_one(ct);
13868048SMadhavan.Venkataraman@Sun.COM 		}
13878048SMadhavan.Venkataraman@Sun.COM 	}
13888048SMadhavan.Venkataraman@Sun.COM }
13898048SMadhavan.Venkataraman@Sun.COM 
13908048SMadhavan.Venkataraman@Sun.COM /*
13918048SMadhavan.Venkataraman@Sun.COM  * Create the hash tables for this callout table.
13928048SMadhavan.Venkataraman@Sun.COM  */
13938048SMadhavan.Venkataraman@Sun.COM static void
13948048SMadhavan.Venkataraman@Sun.COM callout_hash_init(callout_table_t *ct)
13958048SMadhavan.Venkataraman@Sun.COM {
13968048SMadhavan.Venkataraman@Sun.COM 	size_t size;
13978048SMadhavan.Venkataraman@Sun.COM 
13988048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
13998048SMadhavan.Venkataraman@Sun.COM 	ASSERT((ct->ct_idhash == NULL) && (ct->ct_clhash == NULL));
14008048SMadhavan.Venkataraman@Sun.COM 
14018048SMadhavan.Venkataraman@Sun.COM 	size = sizeof (callout_hash_t) * CALLOUT_BUCKETS;
14028048SMadhavan.Venkataraman@Sun.COM 	ct->ct_idhash = kmem_zalloc(size, KM_SLEEP);
14038048SMadhavan.Venkataraman@Sun.COM 	ct->ct_clhash = kmem_zalloc(size, KM_SLEEP);
14048048SMadhavan.Venkataraman@Sun.COM }
14058048SMadhavan.Venkataraman@Sun.COM 
14068048SMadhavan.Venkataraman@Sun.COM /*
14078048SMadhavan.Venkataraman@Sun.COM  * Create per-callout table kstats.
14088048SMadhavan.Venkataraman@Sun.COM  */
14098048SMadhavan.Venkataraman@Sun.COM static void
14108048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(callout_table_t *ct)
14118048SMadhavan.Venkataraman@Sun.COM {
14128048SMadhavan.Venkataraman@Sun.COM 	callout_stat_type_t stat;
14138048SMadhavan.Venkataraman@Sun.COM 	kstat_t *ct_kstats;
14148048SMadhavan.Venkataraman@Sun.COM 	int ndx;
14158048SMadhavan.Venkataraman@Sun.COM 
14168048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
14178048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_kstats == NULL);
14188048SMadhavan.Venkataraman@Sun.COM 
14198048SMadhavan.Venkataraman@Sun.COM 	ndx = ct - callout_table;
14208048SMadhavan.Venkataraman@Sun.COM 	ct_kstats = kstat_create("unix", ndx, "callout",
14218048SMadhavan.Venkataraman@Sun.COM 	    "misc", KSTAT_TYPE_NAMED, CALLOUT_NUM_STATS, KSTAT_FLAG_VIRTUAL);
14228048SMadhavan.Venkataraman@Sun.COM 
14238048SMadhavan.Venkataraman@Sun.COM 	if (ct_kstats == NULL) {
14248048SMadhavan.Venkataraman@Sun.COM 		cmn_err(CE_WARN, "kstat_create for callout table %p failed",
14258048SMadhavan.Venkataraman@Sun.COM 		    (void *)ct);
14268048SMadhavan.Venkataraman@Sun.COM 	} else {
14278048SMadhavan.Venkataraman@Sun.COM 		ct_kstats->ks_data = ct->ct_kstat_data;
14288048SMadhavan.Venkataraman@Sun.COM 		for (stat = 0; stat < CALLOUT_NUM_STATS; stat++)
14298048SMadhavan.Venkataraman@Sun.COM 			kstat_named_init(&ct->ct_kstat_data[stat],
14308048SMadhavan.Venkataraman@Sun.COM 			    callout_kstat_names[stat], KSTAT_DATA_INT64);
14318048SMadhavan.Venkataraman@Sun.COM 		ct->ct_kstats = ct_kstats;
14328048SMadhavan.Venkataraman@Sun.COM 		kstat_install(ct_kstats);
14338048SMadhavan.Venkataraman@Sun.COM 	}
14348048SMadhavan.Venkataraman@Sun.COM }
14358048SMadhavan.Venkataraman@Sun.COM 
14368048SMadhavan.Venkataraman@Sun.COM static void
14378048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(callout_table_t *ct)
14388048SMadhavan.Venkataraman@Sun.COM {
14398048SMadhavan.Venkataraman@Sun.COM 	cyc_handler_t hdlr;
14408048SMadhavan.Venkataraman@Sun.COM 	cyc_time_t when;
14418048SMadhavan.Venkataraman@Sun.COM 	processorid_t seqid;
14428048SMadhavan.Venkataraman@Sun.COM 	int t;
14438048SMadhavan.Venkataraman@Sun.COM 
14448048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
14458048SMadhavan.Venkataraman@Sun.COM 
14468048SMadhavan.Venkataraman@Sun.COM 	t = CALLOUT_TABLE_TYPE(ct);
14478048SMadhavan.Venkataraman@Sun.COM 	seqid = CALLOUT_TABLE_SEQID(ct);
14488048SMadhavan.Venkataraman@Sun.COM 
14498048SMadhavan.Venkataraman@Sun.COM 	/*
14508048SMadhavan.Venkataraman@Sun.COM 	 * Create the taskq thread if the table type is normal.
14518048SMadhavan.Venkataraman@Sun.COM 	 * Realtime tables are handled at PIL1 by a softint
14528048SMadhavan.Venkataraman@Sun.COM 	 * handler.
14538048SMadhavan.Venkataraman@Sun.COM 	 */
14548048SMadhavan.Venkataraman@Sun.COM 	if (t == CALLOUT_NORMAL) {
14558048SMadhavan.Venkataraman@Sun.COM 		ASSERT(ct->ct_taskq == NULL);
14568048SMadhavan.Venkataraman@Sun.COM 		/*
14578048SMadhavan.Venkataraman@Sun.COM 		 * Each callout thread consumes exactly one
14588048SMadhavan.Venkataraman@Sun.COM 		 * task structure while active.  Therefore,
14598048SMadhavan.Venkataraman@Sun.COM 		 * prepopulating with 2 * CALLOUT_THREADS tasks
14608048SMadhavan.Venkataraman@Sun.COM 		 * ensures that there's at least one task per
14618048SMadhavan.Venkataraman@Sun.COM 		 * thread that's either scheduled or on the
14628048SMadhavan.Venkataraman@Sun.COM 		 * freelist.  In turn, this guarantees that
14638048SMadhavan.Venkataraman@Sun.COM 		 * taskq_dispatch() will always either succeed
14648048SMadhavan.Venkataraman@Sun.COM 		 * (because there's a free task structure) or
14658048SMadhavan.Venkataraman@Sun.COM 		 * be unnecessary (because "callout_excute(ct)"
14668048SMadhavan.Venkataraman@Sun.COM 		 * has already scheduled).
14678048SMadhavan.Venkataraman@Sun.COM 		 */
14688048SMadhavan.Venkataraman@Sun.COM 		ct->ct_taskq =
14698048SMadhavan.Venkataraman@Sun.COM 		    taskq_create_instance("callout_taskq", seqid,
14708048SMadhavan.Venkataraman@Sun.COM 		    CALLOUT_THREADS, maxclsyspri,
14718048SMadhavan.Venkataraman@Sun.COM 		    2 * CALLOUT_THREADS, 2 * CALLOUT_THREADS,
14728048SMadhavan.Venkataraman@Sun.COM 		    TASKQ_PREPOPULATE | TASKQ_CPR_SAFE);
14738048SMadhavan.Venkataraman@Sun.COM 	}
14748048SMadhavan.Venkataraman@Sun.COM 
14758048SMadhavan.Venkataraman@Sun.COM 	/*
14768048SMadhavan.Venkataraman@Sun.COM 	 * callouts can only be created in a table whose
14778048SMadhavan.Venkataraman@Sun.COM 	 * cyclic has been initialized.
14788048SMadhavan.Venkataraman@Sun.COM 	 */
14798048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num == 0);
14808048SMadhavan.Venkataraman@Sun.COM 
14818048SMadhavan.Venkataraman@Sun.COM 	/*
14828048SMadhavan.Venkataraman@Sun.COM 	 * Create the callout table cyclics.
1483*9039SMadhavan.Venkataraman@Sun.COM 	 *
1484*9039SMadhavan.Venkataraman@Sun.COM 	 * The realtime cyclic handler executes at low PIL. The normal cyclic
1485*9039SMadhavan.Venkataraman@Sun.COM 	 * handler executes at lock PIL. This is because there are cases
1486*9039SMadhavan.Venkataraman@Sun.COM 	 * where code can block at PIL > 1 waiting for a normal callout handler
1487*9039SMadhavan.Venkataraman@Sun.COM 	 * to unblock it directly or indirectly. If the normal cyclic were to
1488*9039SMadhavan.Venkataraman@Sun.COM 	 * be executed at low PIL, it could get blocked out by the waiter
1489*9039SMadhavan.Venkataraman@Sun.COM 	 * and cause a deadlock.
14908048SMadhavan.Venkataraman@Sun.COM 	 */
14918048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_cyclic == CYCLIC_NONE);
14928048SMadhavan.Venkataraman@Sun.COM 
14938048SMadhavan.Venkataraman@Sun.COM 	hdlr.cyh_func = (cyc_func_t)CALLOUT_CYCLIC_HANDLER(t);
1494*9039SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_type == CALLOUT_REALTIME)
1495*9039SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_level = callout_realtime_level;
1496*9039SMadhavan.Venkataraman@Sun.COM 	else
1497*9039SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_level = callout_normal_level;
14988048SMadhavan.Venkataraman@Sun.COM 	hdlr.cyh_arg = ct;
14998048SMadhavan.Venkataraman@Sun.COM 	when.cyt_when = CY_INFINITY;
15008048SMadhavan.Venkataraman@Sun.COM 	when.cyt_interval = CY_INFINITY;
15018048SMadhavan.Venkataraman@Sun.COM 
15028048SMadhavan.Venkataraman@Sun.COM 	ct->ct_cyclic = cyclic_add(&hdlr, &when);
15038048SMadhavan.Venkataraman@Sun.COM }
15048048SMadhavan.Venkataraman@Sun.COM 
15058048SMadhavan.Venkataraman@Sun.COM void
15068048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cpu_t *cp)
15078048SMadhavan.Venkataraman@Sun.COM {
15088048SMadhavan.Venkataraman@Sun.COM 	lgrp_handle_t hand;
15098048SMadhavan.Venkataraman@Sun.COM 	callout_cache_t *cache;
15108048SMadhavan.Venkataraman@Sun.COM 	char s[KMEM_CACHE_NAMELEN];
15118048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
15128048SMadhavan.Venkataraman@Sun.COM 	processorid_t seqid;
15138048SMadhavan.Venkataraman@Sun.COM 	int t;
15148048SMadhavan.Venkataraman@Sun.COM 
15158048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
15168048SMadhavan.Venkataraman@Sun.COM 
15178048SMadhavan.Venkataraman@Sun.COM 	/*
15188048SMadhavan.Venkataraman@Sun.COM 	 * Locate the cache corresponding to the onlined CPU's lgroup.
15198048SMadhavan.Venkataraman@Sun.COM 	 * Note that access to callout_caches is protected by cpu_lock.
15208048SMadhavan.Venkataraman@Sun.COM 	 */
15218048SMadhavan.Venkataraman@Sun.COM 	hand = lgrp_plat_cpu_to_hand(cp->cpu_id);
15228048SMadhavan.Venkataraman@Sun.COM 	for (cache = callout_caches; cache != NULL; cache = cache->cc_next) {
15238048SMadhavan.Venkataraman@Sun.COM 		if (cache->cc_hand == hand)
15248048SMadhavan.Venkataraman@Sun.COM 			break;
15258048SMadhavan.Venkataraman@Sun.COM 	}
15268048SMadhavan.Venkataraman@Sun.COM 
15278048SMadhavan.Venkataraman@Sun.COM 	/*
15288048SMadhavan.Venkataraman@Sun.COM 	 * If not found, create one. The caches are never destroyed.
15298048SMadhavan.Venkataraman@Sun.COM 	 */
15308048SMadhavan.Venkataraman@Sun.COM 	if (cache == NULL) {
15318048SMadhavan.Venkataraman@Sun.COM 		cache = kmem_alloc(sizeof (callout_cache_t), KM_SLEEP);
15328048SMadhavan.Venkataraman@Sun.COM 		cache->cc_hand = hand;
15338048SMadhavan.Venkataraman@Sun.COM 		(void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_cache%lx",
15348048SMadhavan.Venkataraman@Sun.COM 		    (long)hand);
15358048SMadhavan.Venkataraman@Sun.COM 		cache->cc_cache = kmem_cache_create(s, sizeof (callout_t),
15368048SMadhavan.Venkataraman@Sun.COM 		    CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0);
15378048SMadhavan.Venkataraman@Sun.COM 		(void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_lcache%lx",
15388048SMadhavan.Venkataraman@Sun.COM 		    (long)hand);
15398048SMadhavan.Venkataraman@Sun.COM 		cache->cc_lcache = kmem_cache_create(s, sizeof (callout_list_t),
15408048SMadhavan.Venkataraman@Sun.COM 		    CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0);
15418048SMadhavan.Venkataraman@Sun.COM 		cache->cc_next = callout_caches;
15428048SMadhavan.Venkataraman@Sun.COM 		callout_caches = cache;
15438048SMadhavan.Venkataraman@Sun.COM 	}
15448048SMadhavan.Venkataraman@Sun.COM 
15458048SMadhavan.Venkataraman@Sun.COM 	seqid = cp->cpu_seqid;
15468048SMadhavan.Venkataraman@Sun.COM 
15478048SMadhavan.Venkataraman@Sun.COM 	for (t = 0; t < CALLOUT_NTYPES; t++) {
15488048SMadhavan.Venkataraman@Sun.COM 		ct = &callout_table[CALLOUT_TABLE(t, seqid)];
15498048SMadhavan.Venkataraman@Sun.COM 
15508048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
15518048SMadhavan.Venkataraman@Sun.COM 		/*
15528048SMadhavan.Venkataraman@Sun.COM 		 * Store convinience pointers to the kmem caches
15538048SMadhavan.Venkataraman@Sun.COM 		 * in the callout table. These assignments should always be
15548048SMadhavan.Venkataraman@Sun.COM 		 * done as callout tables can map to different physical
15558048SMadhavan.Venkataraman@Sun.COM 		 * CPUs each time.
15568048SMadhavan.Venkataraman@Sun.COM 		 */
15578048SMadhavan.Venkataraman@Sun.COM 		ct->ct_cache = cache->cc_cache;
15588048SMadhavan.Venkataraman@Sun.COM 		ct->ct_lcache = cache->cc_lcache;
15598048SMadhavan.Venkataraman@Sun.COM 
15608048SMadhavan.Venkataraman@Sun.COM 		/*
15618048SMadhavan.Venkataraman@Sun.COM 		 * We use the heap pointer to check if stuff has been
15628048SMadhavan.Venkataraman@Sun.COM 		 * initialized for this callout table.
15638048SMadhavan.Venkataraman@Sun.COM 		 */
15648048SMadhavan.Venkataraman@Sun.COM 		if (ct->ct_heap == NULL) {
15658048SMadhavan.Venkataraman@Sun.COM 			callout_heap_init(ct);
15668048SMadhavan.Venkataraman@Sun.COM 			callout_hash_init(ct);
15678048SMadhavan.Venkataraman@Sun.COM 			callout_kstat_init(ct);
15688048SMadhavan.Venkataraman@Sun.COM 			callout_cyclic_init(ct);
15698048SMadhavan.Venkataraman@Sun.COM 		}
15708048SMadhavan.Venkataraman@Sun.COM 
15718048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
15728048SMadhavan.Venkataraman@Sun.COM 
15738048SMadhavan.Venkataraman@Sun.COM 		/*
15748566SMadhavan.Venkataraman@Sun.COM 		 * Move the cyclic to this CPU by doing a bind.
15758048SMadhavan.Venkataraman@Sun.COM 		 */
15768048SMadhavan.Venkataraman@Sun.COM 		cyclic_bind(ct->ct_cyclic, cp, NULL);
15778566SMadhavan.Venkataraman@Sun.COM 	}
15788566SMadhavan.Venkataraman@Sun.COM }
15798566SMadhavan.Venkataraman@Sun.COM 
15808566SMadhavan.Venkataraman@Sun.COM void
15818566SMadhavan.Venkataraman@Sun.COM callout_cpu_offline(cpu_t *cp)
15828566SMadhavan.Venkataraman@Sun.COM {
15838566SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
15848566SMadhavan.Venkataraman@Sun.COM 	processorid_t seqid;
15858566SMadhavan.Venkataraman@Sun.COM 	int t;
15868566SMadhavan.Venkataraman@Sun.COM 
15878566SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
15888566SMadhavan.Venkataraman@Sun.COM 
15898566SMadhavan.Venkataraman@Sun.COM 	seqid = cp->cpu_seqid;
15908566SMadhavan.Venkataraman@Sun.COM 
15918566SMadhavan.Venkataraman@Sun.COM 	for (t = 0; t < CALLOUT_NTYPES; t++) {
15928566SMadhavan.Venkataraman@Sun.COM 		ct = &callout_table[CALLOUT_TABLE(t, seqid)];
15938566SMadhavan.Venkataraman@Sun.COM 
15948566SMadhavan.Venkataraman@Sun.COM 		/*
15958566SMadhavan.Venkataraman@Sun.COM 		 * Unbind the cyclic. This will allow the cyclic subsystem
15968566SMadhavan.Venkataraman@Sun.COM 		 * to juggle the cyclic during CPU offline.
15978566SMadhavan.Venkataraman@Sun.COM 		 */
15988048SMadhavan.Venkataraman@Sun.COM 		cyclic_bind(ct->ct_cyclic, NULL, NULL);
15998048SMadhavan.Venkataraman@Sun.COM 	}
16008048SMadhavan.Venkataraman@Sun.COM }
16018048SMadhavan.Venkataraman@Sun.COM 
16028048SMadhavan.Venkataraman@Sun.COM /*
16038048SMadhavan.Venkataraman@Sun.COM  * This is called to perform per-CPU initialization for slave CPUs at
16048048SMadhavan.Venkataraman@Sun.COM  * boot time.
16058048SMadhavan.Venkataraman@Sun.COM  */
16068048SMadhavan.Venkataraman@Sun.COM void
16078048SMadhavan.Venkataraman@Sun.COM callout_mp_init(void)
16088048SMadhavan.Venkataraman@Sun.COM {
16098048SMadhavan.Venkataraman@Sun.COM 	cpu_t *cp;
16108048SMadhavan.Venkataraman@Sun.COM 
16118048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&cpu_lock);
16128048SMadhavan.Venkataraman@Sun.COM 
16138048SMadhavan.Venkataraman@Sun.COM 	cp = cpu_active;
16148048SMadhavan.Venkataraman@Sun.COM 	do {
16158048SMadhavan.Venkataraman@Sun.COM 		callout_cpu_online(cp);
16168048SMadhavan.Venkataraman@Sun.COM 	} while ((cp = cp->cpu_next_onln) != cpu_active);
16178048SMadhavan.Venkataraman@Sun.COM 
16188048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&cpu_lock);
16198048SMadhavan.Venkataraman@Sun.COM }
16208048SMadhavan.Venkataraman@Sun.COM 
16218048SMadhavan.Venkataraman@Sun.COM /*
16220Sstevel@tonic-gate  * Initialize all callout tables.  Called at boot time just before clkstart().
16230Sstevel@tonic-gate  */
16240Sstevel@tonic-gate void
16250Sstevel@tonic-gate callout_init(void)
16260Sstevel@tonic-gate {
16270Sstevel@tonic-gate 	int f, t;
16288048SMadhavan.Venkataraman@Sun.COM 	size_t size;
16290Sstevel@tonic-gate 	int table_id;
16300Sstevel@tonic-gate 	callout_table_t *ct;
16318048SMadhavan.Venkataraman@Sun.COM 	long bits, fanout;
16328048SMadhavan.Venkataraman@Sun.COM 	uintptr_t buf;
16330Sstevel@tonic-gate 
16348048SMadhavan.Venkataraman@Sun.COM 	/*
16358048SMadhavan.Venkataraman@Sun.COM 	 * Initialize callout globals.
16368048SMadhavan.Venkataraman@Sun.COM 	 */
16378048SMadhavan.Venkataraman@Sun.COM 	bits = 0;
16388048SMadhavan.Venkataraman@Sun.COM 	for (fanout = 1; (fanout < max_ncpus); fanout <<= 1)
16398048SMadhavan.Venkataraman@Sun.COM 		bits++;
16408048SMadhavan.Venkataraman@Sun.COM 	callout_table_bits = CALLOUT_TYPE_BITS + bits;
16418048SMadhavan.Venkataraman@Sun.COM 	callout_table_mask = (1 << callout_table_bits) - 1;
16428048SMadhavan.Venkataraman@Sun.COM 	callout_counter_low = 1 << CALLOUT_COUNTER_SHIFT;
16438048SMadhavan.Venkataraman@Sun.COM 	callout_longterm = TICK_TO_NSEC(CALLOUT_LONGTERM_TICKS);
16448566SMadhavan.Venkataraman@Sun.COM 	callout_max_ticks = CALLOUT_MAX_TICKS;
16450Sstevel@tonic-gate 
16468048SMadhavan.Venkataraman@Sun.COM 	/*
16478048SMadhavan.Venkataraman@Sun.COM 	 * Because of the variability in timing behavior across systems with
16488048SMadhavan.Venkataraman@Sun.COM 	 * different architectures, we cannot allow arbitrarily low
16498048SMadhavan.Venkataraman@Sun.COM 	 * resolutions. The minimum resolution has to be determined in a
16508048SMadhavan.Venkataraman@Sun.COM 	 * platform-specific way. Until then, we define a blanket minimum
16518048SMadhavan.Venkataraman@Sun.COM 	 * resolution for callouts of CALLOUT_MIN_RESOLUTION.
16528048SMadhavan.Venkataraman@Sun.COM 	 *
16538048SMadhavan.Venkataraman@Sun.COM 	 * If, in the future, someone requires lower resolution timers, they
16548048SMadhavan.Venkataraman@Sun.COM 	 * can do one of two things:
16558048SMadhavan.Venkataraman@Sun.COM 	 *
16568048SMadhavan.Venkataraman@Sun.COM 	 *	- Define a lower value for callout_min_resolution. This would
16578048SMadhavan.Venkataraman@Sun.COM 	 *	  affect all clients of the callout subsystem. If this done
16588048SMadhavan.Venkataraman@Sun.COM 	 *	  via /etc/system, then no code changes are required and it
16598048SMadhavan.Venkataraman@Sun.COM 	 *	  would affect only that customer.
16608048SMadhavan.Venkataraman@Sun.COM 	 *
16618048SMadhavan.Venkataraman@Sun.COM 	 *	- Define a flag to be passed to timeout creation that allows
16628048SMadhavan.Venkataraman@Sun.COM 	 *	  the lower resolution. This involves code changes. But it
16638048SMadhavan.Venkataraman@Sun.COM 	 *	  would affect only the calling module. It is the developer's
16648048SMadhavan.Venkataraman@Sun.COM 	 *	  responsibility to test on all systems and make sure that
16658048SMadhavan.Venkataraman@Sun.COM 	 *	  everything works.
16668048SMadhavan.Venkataraman@Sun.COM 	 */
16678048SMadhavan.Venkataraman@Sun.COM 	if (callout_min_resolution <= 0)
16688048SMadhavan.Venkataraman@Sun.COM 		callout_min_resolution = CALLOUT_MIN_RESOLUTION;
16698048SMadhavan.Venkataraman@Sun.COM 
16708048SMadhavan.Venkataraman@Sun.COM 	/*
16718048SMadhavan.Venkataraman@Sun.COM 	 * Allocate all the callout tables based on max_ncpus. We have chosen
16728048SMadhavan.Venkataraman@Sun.COM 	 * to do boot-time allocation instead of dynamic allocation because:
16738048SMadhavan.Venkataraman@Sun.COM 	 *
16748048SMadhavan.Venkataraman@Sun.COM 	 *	- the size of the callout tables is not too large.
16758048SMadhavan.Venkataraman@Sun.COM 	 *	- there are race conditions involved in making this dynamic.
16768048SMadhavan.Venkataraman@Sun.COM 	 *	- the hash tables that go with the callout tables consume
16778048SMadhavan.Venkataraman@Sun.COM 	 *	  most of the memory and they are only allocated in
16788048SMadhavan.Venkataraman@Sun.COM 	 *	  callout_cpu_online().
16798048SMadhavan.Venkataraman@Sun.COM 	 *
16808048SMadhavan.Venkataraman@Sun.COM 	 * Each CPU has two tables that are consecutive in the array. The first
16818048SMadhavan.Venkataraman@Sun.COM 	 * one is for realtime callouts and the second one is for normal ones.
16828048SMadhavan.Venkataraman@Sun.COM 	 *
16838048SMadhavan.Venkataraman@Sun.COM 	 * We do this alignment dance to make sure that callout table
16848048SMadhavan.Venkataraman@Sun.COM 	 * structures will always be on a cache line boundary.
16858048SMadhavan.Venkataraman@Sun.COM 	 */
16868048SMadhavan.Venkataraman@Sun.COM 	size = sizeof (callout_table_t) * CALLOUT_NTYPES * max_ncpus;
16878048SMadhavan.Venkataraman@Sun.COM 	size += CALLOUT_ALIGN;
16888048SMadhavan.Venkataraman@Sun.COM 	buf = (uintptr_t)kmem_zalloc(size, KM_SLEEP);
16898048SMadhavan.Venkataraman@Sun.COM 	callout_table = (callout_table_t *)P2ROUNDUP(buf, CALLOUT_ALIGN);
16908048SMadhavan.Venkataraman@Sun.COM 
16918048SMadhavan.Venkataraman@Sun.COM 	size = sizeof (kstat_named_t) * CALLOUT_NUM_STATS;
16928048SMadhavan.Venkataraman@Sun.COM 	/*
16938048SMadhavan.Venkataraman@Sun.COM 	 * Now, initialize the tables for all the CPUs.
16948048SMadhavan.Venkataraman@Sun.COM 	 */
16958048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
16968048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
16970Sstevel@tonic-gate 			table_id = CALLOUT_TABLE(t, f);
16988048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[table_id];
16998566SMadhavan.Venkataraman@Sun.COM 			ct->ct_type = t;
17008048SMadhavan.Venkataraman@Sun.COM 			mutex_init(&ct->ct_mutex, NULL, MUTEX_DEFAULT, NULL);
17016422Sqiao 			/*
17028048SMadhavan.Venkataraman@Sun.COM 			 * Precompute the base IDs for long and short-term
17038048SMadhavan.Venkataraman@Sun.COM 			 * legacy IDs. This makes ID generation during
17048048SMadhavan.Venkataraman@Sun.COM 			 * timeout() fast.
17056422Sqiao 			 */
17068048SMadhavan.Venkataraman@Sun.COM 			ct->ct_short_id = CALLOUT_SHORT_ID(table_id);
17078048SMadhavan.Venkataraman@Sun.COM 			ct->ct_long_id = CALLOUT_LONG_ID(table_id);
17088048SMadhavan.Venkataraman@Sun.COM 			/*
17098048SMadhavan.Venkataraman@Sun.COM 			 * Precompute the base ID for generation-based IDs.
17108048SMadhavan.Venkataraman@Sun.COM 			 * Note that when the first ID gets allocated, the
17118048SMadhavan.Venkataraman@Sun.COM 			 * ID will wrap. This will cause the generation
17128048SMadhavan.Venkataraman@Sun.COM 			 * number to be incremented to 1.
17138048SMadhavan.Venkataraman@Sun.COM 			 */
17148048SMadhavan.Venkataraman@Sun.COM 			ct->ct_gen_id = CALLOUT_SHORT_ID(table_id);
17158048SMadhavan.Venkataraman@Sun.COM 			/*
17168048SMadhavan.Venkataraman@Sun.COM 			 * Initialize the cyclic as NONE. This will get set
17178048SMadhavan.Venkataraman@Sun.COM 			 * during CPU online. This is so that partially
17188048SMadhavan.Venkataraman@Sun.COM 			 * populated systems will only have the required
17198048SMadhavan.Venkataraman@Sun.COM 			 * number of cyclics, not more.
17208048SMadhavan.Venkataraman@Sun.COM 			 */
17218048SMadhavan.Venkataraman@Sun.COM 			ct->ct_cyclic = CYCLIC_NONE;
17228048SMadhavan.Venkataraman@Sun.COM 			ct->ct_kstat_data = kmem_zalloc(size, KM_SLEEP);
17230Sstevel@tonic-gate 		}
17240Sstevel@tonic-gate 	}
17258048SMadhavan.Venkataraman@Sun.COM 
17268048SMadhavan.Venkataraman@Sun.COM 	/*
17278048SMadhavan.Venkataraman@Sun.COM 	 * Add the callback for CPR. This is called during checkpoint
17288048SMadhavan.Venkataraman@Sun.COM 	 * resume to suspend and resume callouts.
17298048SMadhavan.Venkataraman@Sun.COM 	 */
17308048SMadhavan.Venkataraman@Sun.COM 	(void) callb_add(callout_cpr_callb, 0, CB_CL_CPR_CALLOUT,
17318048SMadhavan.Venkataraman@Sun.COM 	    "callout_cpr");
17328048SMadhavan.Venkataraman@Sun.COM 	(void) callb_add(callout_debug_callb, 0, CB_CL_ENTER_DEBUGGER,
17338048SMadhavan.Venkataraman@Sun.COM 	    "callout_debug");
17348048SMadhavan.Venkataraman@Sun.COM 
17358048SMadhavan.Venkataraman@Sun.COM 	/*
17368048SMadhavan.Venkataraman@Sun.COM 	 * Call the per-CPU initialization function for the boot CPU. This
17378048SMadhavan.Venkataraman@Sun.COM 	 * is done here because the function is not called automatically for
17388048SMadhavan.Venkataraman@Sun.COM 	 * the boot CPU from the CPU online/offline hooks. Note that the
17398048SMadhavan.Venkataraman@Sun.COM 	 * CPU lock is taken here because of convention.
17408048SMadhavan.Venkataraman@Sun.COM 	 */
17418048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&cpu_lock);
17428048SMadhavan.Venkataraman@Sun.COM 	callout_boot_ct = &callout_table[CALLOUT_TABLE(0, CPU->cpu_seqid)];
17438048SMadhavan.Venkataraman@Sun.COM 	callout_cpu_online(CPU);
17448048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&cpu_lock);
17450Sstevel@tonic-gate }
1746