xref: /onnv-gate/usr/src/uts/common/os/callout.c (revision 11745:5892479af17d)
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 /*
2211655SMadhavan.Venkataraman@Sun.COM  * Copyright 2010 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  */
439334SMadhavan.Venkataraman@Sun.COM static int callout_threads;			/* callout normal threads */
448048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_debug_hrtime;		/* debugger entry time */
45*11745SMadhavan.Venkataraman@Sun.COM static int callout_chunk;			/* callout heap chunk size */
469334SMadhavan.Venkataraman@Sun.COM static int callout_min_reap;			/* callout minimum reap count */
479334SMadhavan.Venkataraman@Sun.COM static int callout_tolerance;			/* callout hires tolerance */
488048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_boot_ct;	/* Boot CPU's callout tables */
498566SMadhavan.Venkataraman@Sun.COM static clock_t callout_max_ticks;		/* max interval */
508048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_longterm;		/* longterm nanoseconds */
518048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_counter_low;		/* callout ID increment */
528048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_bits;		/* number of table bits in ID */
538048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_mask;		/* mask for the table bits */
548048SMadhavan.Venkataraman@Sun.COM static callout_cache_t *callout_caches;		/* linked list of caches */
558048SMadhavan.Venkataraman@Sun.COM #pragma align 64(callout_table)
568048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_table;		/* global callout table array */
570Sstevel@tonic-gate 
589039SMadhavan.Venkataraman@Sun.COM /*
5910696SDavid.Hollister@Sun.COM  * We run 'realtime' callouts at PIL 1 (CY_LOW_LEVEL). For 'normal'
6010696SDavid.Hollister@Sun.COM  * callouts, from PIL 10 (CY_LOCK_LEVEL) we dispatch the callout,
6110696SDavid.Hollister@Sun.COM  * via taskq, to a thread that executes at PIL 0 - so we end up running
6210696SDavid.Hollister@Sun.COM  * 'normal' callouts at PIL 0.
639039SMadhavan.Venkataraman@Sun.COM  */
649334SMadhavan.Venkataraman@Sun.COM static volatile int callout_realtime_level = CY_LOW_LEVEL;
659334SMadhavan.Venkataraman@Sun.COM static volatile int callout_normal_level = CY_LOCK_LEVEL;
669039SMadhavan.Venkataraman@Sun.COM 
678048SMadhavan.Venkataraman@Sun.COM static char *callout_kstat_names[] = {
688048SMadhavan.Venkataraman@Sun.COM 	"callout_timeouts",
698048SMadhavan.Venkataraman@Sun.COM 	"callout_timeouts_pending",
708048SMadhavan.Venkataraman@Sun.COM 	"callout_untimeouts_unexpired",
718048SMadhavan.Venkataraman@Sun.COM 	"callout_untimeouts_executing",
728048SMadhavan.Venkataraman@Sun.COM 	"callout_untimeouts_expired",
738048SMadhavan.Venkataraman@Sun.COM 	"callout_expirations",
748048SMadhavan.Venkataraman@Sun.COM 	"callout_allocations",
759334SMadhavan.Venkataraman@Sun.COM 	"callout_cleanups",
768048SMadhavan.Venkataraman@Sun.COM };
778048SMadhavan.Venkataraman@Sun.COM 
789334SMadhavan.Venkataraman@Sun.COM static hrtime_t	callout_heap_process(callout_table_t *, hrtime_t, int);
799334SMadhavan.Venkataraman@Sun.COM 
808048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_HASH_INSERT(hash, cp, cnext, cprev)	\
810Sstevel@tonic-gate {							\
828048SMadhavan.Venkataraman@Sun.COM 	callout_hash_t *hashp = &(hash);		\
838048SMadhavan.Venkataraman@Sun.COM 							\
840Sstevel@tonic-gate 	cp->cprev = NULL;				\
858048SMadhavan.Venkataraman@Sun.COM 	cp->cnext = hashp->ch_head;			\
868048SMadhavan.Venkataraman@Sun.COM 	if (hashp->ch_head == NULL)			\
878048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_tail = cp;			\
888048SMadhavan.Venkataraman@Sun.COM 	else						\
898048SMadhavan.Venkataraman@Sun.COM 		cp->cnext->cprev = cp;			\
908048SMadhavan.Venkataraman@Sun.COM 	hashp->ch_head = cp;				\
918048SMadhavan.Venkataraman@Sun.COM }
928048SMadhavan.Venkataraman@Sun.COM 
938048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_HASH_APPEND(hash, cp, cnext, cprev)	\
948048SMadhavan.Venkataraman@Sun.COM {							\
958048SMadhavan.Venkataraman@Sun.COM 	callout_hash_t *hashp = &(hash);		\
968048SMadhavan.Venkataraman@Sun.COM 							\
978048SMadhavan.Venkataraman@Sun.COM 	cp->cnext = NULL;				\
988048SMadhavan.Venkataraman@Sun.COM 	cp->cprev = hashp->ch_tail;			\
998048SMadhavan.Venkataraman@Sun.COM 	if (hashp->ch_tail == NULL)			\
1008048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_head = cp;			\
1018048SMadhavan.Venkataraman@Sun.COM 	else						\
1028048SMadhavan.Venkataraman@Sun.COM 		cp->cprev->cnext = cp;			\
1038048SMadhavan.Venkataraman@Sun.COM 	hashp->ch_tail = cp;				\
1040Sstevel@tonic-gate }
1050Sstevel@tonic-gate 
1068048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_HASH_DELETE(hash, cp, cnext, cprev)	\
1070Sstevel@tonic-gate {							\
1088048SMadhavan.Venkataraman@Sun.COM 	callout_hash_t *hashp = &(hash);		\
1098048SMadhavan.Venkataraman@Sun.COM 							\
1108048SMadhavan.Venkataraman@Sun.COM 	if (cp->cnext == NULL)				\
1118048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_tail = cp->cprev;		\
1120Sstevel@tonic-gate 	else						\
1138048SMadhavan.Venkataraman@Sun.COM 		cp->cnext->cprev = cp->cprev;		\
1148048SMadhavan.Venkataraman@Sun.COM 	if (cp->cprev == NULL)				\
1158048SMadhavan.Venkataraman@Sun.COM 		hashp->ch_head = cp->cnext;		\
1168048SMadhavan.Venkataraman@Sun.COM 	else						\
1178048SMadhavan.Venkataraman@Sun.COM 		cp->cprev->cnext = cp->cnext;		\
1180Sstevel@tonic-gate }
1190Sstevel@tonic-gate 
1208048SMadhavan.Venkataraman@Sun.COM /*
1218048SMadhavan.Venkataraman@Sun.COM  * These definitions help us queue callouts and callout lists. Here is
1228048SMadhavan.Venkataraman@Sun.COM  * the queueing rationale:
1238048SMadhavan.Venkataraman@Sun.COM  *
1248048SMadhavan.Venkataraman@Sun.COM  *	- callouts are queued in a FIFO manner in the ID hash table.
1258048SMadhavan.Venkataraman@Sun.COM  *	  TCP timers are typically cancelled in the same order that they
1268048SMadhavan.Venkataraman@Sun.COM  *	  were issued. The FIFO queueing shortens the search for a callout
1278048SMadhavan.Venkataraman@Sun.COM  *	  during untimeout().
1288048SMadhavan.Venkataraman@Sun.COM  *
1298048SMadhavan.Venkataraman@Sun.COM  *	- callouts are queued in a FIFO manner in their callout lists.
1308048SMadhavan.Venkataraman@Sun.COM  *	  This ensures that the callouts are executed in the same order that
1318048SMadhavan.Venkataraman@Sun.COM  *	  they were queued. This is fair. Plus, it helps to make each
1328048SMadhavan.Venkataraman@Sun.COM  *	  callout expiration timely. It also favors cancellations.
1338048SMadhavan.Venkataraman@Sun.COM  *
1349334SMadhavan.Venkataraman@Sun.COM  *	- callout lists are queued in the following manner in the callout
1359334SMadhavan.Venkataraman@Sun.COM  *	  hash table buckets:
1369334SMadhavan.Venkataraman@Sun.COM  *
1379334SMadhavan.Venkataraman@Sun.COM  *		- appended, if the callout list is a 1-nanosecond resolution
1389334SMadhavan.Venkataraman@Sun.COM  *		  callout list. When a callout is created, we first look for
1399334SMadhavan.Venkataraman@Sun.COM  *		  a callout list that has the same expiration so we can avoid
1409334SMadhavan.Venkataraman@Sun.COM  *		  allocating a callout list and inserting the expiration into
1419334SMadhavan.Venkataraman@Sun.COM  *		  the heap. However, we do not want to look at 1-nanosecond
1429334SMadhavan.Venkataraman@Sun.COM  *		  resolution callout lists as we will seldom find a match in
1439334SMadhavan.Venkataraman@Sun.COM  *		  them. Keeping these callout lists in the rear of the hash
1449334SMadhavan.Venkataraman@Sun.COM  *		  buckets allows us to skip these during the lookup.
1459334SMadhavan.Venkataraman@Sun.COM  *
1469334SMadhavan.Venkataraman@Sun.COM  *		- inserted at the beginning, if the callout list is not a
1479334SMadhavan.Venkataraman@Sun.COM  *		  1-nanosecond resolution callout list. This also has the
1489334SMadhavan.Venkataraman@Sun.COM  *		  side-effect of keeping the long term timers away from the
1499334SMadhavan.Venkataraman@Sun.COM  *		  front of the buckets.
1508048SMadhavan.Venkataraman@Sun.COM  *
1518048SMadhavan.Venkataraman@Sun.COM  *	- callout lists are queued in a FIFO manner in the expired callouts
1528048SMadhavan.Venkataraman@Sun.COM  *	  list. This ensures that callout lists are executed in the order
1538048SMadhavan.Venkataraman@Sun.COM  *	  of expiration.
1548048SMadhavan.Venkataraman@Sun.COM  */
1558048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_APPEND(ct, cp)						\
1568048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_APPEND(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)],	\
1578048SMadhavan.Venkataraman@Sun.COM 		cp, c_idnext, c_idprev);				\
1588048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_APPEND(cp->c_list->cl_callouts, cp, c_clnext, c_clprev)
1598048SMadhavan.Venkataraman@Sun.COM 
1608048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_DELETE(ct, cp)						\
1618048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_DELETE(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)],	\
1628048SMadhavan.Venkataraman@Sun.COM 		cp, c_idnext, c_idprev);				\
1638048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_DELETE(cp->c_list->cl_callouts, cp, c_clnext, c_clprev)
1648048SMadhavan.Venkataraman@Sun.COM 
1658048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_INSERT(hash, cl)				\
1668048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_INSERT(hash, cl, cl_next, cl_prev)
1678048SMadhavan.Venkataraman@Sun.COM 
1688048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_APPEND(hash, cl)				\
1698048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_APPEND(hash, cl, cl_next, cl_prev)
1708048SMadhavan.Venkataraman@Sun.COM 
1718048SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_DELETE(hash, cl)				\
1728048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_HASH_DELETE(hash, cl, cl_next, cl_prev)
1730Sstevel@tonic-gate 
174*11745SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_BEFORE(cl, nextcl)			\
175*11745SMadhavan.Venkataraman@Sun.COM {							\
176*11745SMadhavan.Venkataraman@Sun.COM 	(cl)->cl_prev = (nextcl)->cl_prev;		\
177*11745SMadhavan.Venkataraman@Sun.COM 	(cl)->cl_next = (nextcl);			\
178*11745SMadhavan.Venkataraman@Sun.COM 	(nextcl)->cl_prev = (cl);			\
179*11745SMadhavan.Venkataraman@Sun.COM 	if (cl->cl_prev != NULL)			\
180*11745SMadhavan.Venkataraman@Sun.COM 		cl->cl_prev->cl_next = cl;		\
181*11745SMadhavan.Venkataraman@Sun.COM }
182*11745SMadhavan.Venkataraman@Sun.COM 
1830Sstevel@tonic-gate /*
1849039SMadhavan.Venkataraman@Sun.COM  * For normal callouts, there is a deadlock scenario if two callouts that
1859039SMadhavan.Venkataraman@Sun.COM  * have an inter-dependency end up on the same callout list. To break the
1869039SMadhavan.Venkataraman@Sun.COM  * deadlock, you need two taskq threads running in parallel. We compute
1879039SMadhavan.Venkataraman@Sun.COM  * the number of taskq threads here using a bunch of conditions to make
1889039SMadhavan.Venkataraman@Sun.COM  * it optimal for the common case. This is an ugly hack, but one that is
1899039SMadhavan.Venkataraman@Sun.COM  * necessary (sigh).
1909039SMadhavan.Venkataraman@Sun.COM  */
1919039SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_THRESHOLD	100000000
192*11745SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_EXEC_COMPUTE(ct, nextexp, exec)				\
1939039SMadhavan.Venkataraman@Sun.COM {									\
1949039SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;						\
1959039SMadhavan.Venkataraman@Sun.COM 									\
1969039SMadhavan.Venkataraman@Sun.COM 	cl = ct->ct_expired.ch_head;					\
1979039SMadhavan.Venkataraman@Sun.COM 	if (cl == NULL) {						\
1989039SMadhavan.Venkataraman@Sun.COM 		/*							\
1999039SMadhavan.Venkataraman@Sun.COM 		 * If the expired list is NULL, there is nothing to	\
2009039SMadhavan.Venkataraman@Sun.COM 		 * process.						\
2019039SMadhavan.Venkataraman@Sun.COM 		 */							\
2029039SMadhavan.Venkataraman@Sun.COM 		exec = 0;						\
2039039SMadhavan.Venkataraman@Sun.COM 	} else if ((cl->cl_next == NULL) &&				\
2049039SMadhavan.Venkataraman@Sun.COM 	    (cl->cl_callouts.ch_head == cl->cl_callouts.ch_tail)) {	\
2059039SMadhavan.Venkataraman@Sun.COM 		/*							\
2069039SMadhavan.Venkataraman@Sun.COM 		 * If there is only one callout list and it contains	\
2079039SMadhavan.Venkataraman@Sun.COM 		 * only one callout, there is no need for two threads.	\
2089039SMadhavan.Venkataraman@Sun.COM 		 */							\
2099039SMadhavan.Venkataraman@Sun.COM 		exec = 1;						\
210*11745SMadhavan.Venkataraman@Sun.COM 	} else if ((nextexp) > (gethrtime() + CALLOUT_THRESHOLD)) {	\
2119039SMadhavan.Venkataraman@Sun.COM 		/*							\
212*11745SMadhavan.Venkataraman@Sun.COM 		 * If the next expiration of the cyclic is way out into	\
213*11745SMadhavan.Venkataraman@Sun.COM 		 * the future, we need two threads.			\
2149039SMadhavan.Venkataraman@Sun.COM 		 */							\
2159039SMadhavan.Venkataraman@Sun.COM 		exec = 2;						\
2169039SMadhavan.Venkataraman@Sun.COM 	} else {							\
2179039SMadhavan.Venkataraman@Sun.COM 		/*							\
2189039SMadhavan.Venkataraman@Sun.COM 		 * We have multiple callouts to process. But the cyclic	\
2199039SMadhavan.Venkataraman@Sun.COM 		 * will fire in the near future. So, we only need one	\
2209039SMadhavan.Venkataraman@Sun.COM 		 * thread for now.					\
2219039SMadhavan.Venkataraman@Sun.COM 		 */							\
2229039SMadhavan.Venkataraman@Sun.COM 		exec = 1;						\
2239039SMadhavan.Venkataraman@Sun.COM 	}								\
2249039SMadhavan.Venkataraman@Sun.COM }
2259039SMadhavan.Venkataraman@Sun.COM 
2269039SMadhavan.Venkataraman@Sun.COM /*
2279334SMadhavan.Venkataraman@Sun.COM  * Macro to swap two heap items.
2289334SMadhavan.Venkataraman@Sun.COM  */
2299334SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_SWAP(h1, h2)		\
2309334SMadhavan.Venkataraman@Sun.COM {					\
2319334SMadhavan.Venkataraman@Sun.COM 	callout_heap_t tmp;		\
2329334SMadhavan.Venkataraman@Sun.COM 					\
2339334SMadhavan.Venkataraman@Sun.COM 	tmp = *h1;			\
2349334SMadhavan.Venkataraman@Sun.COM 	*h1 = *h2;			\
2359334SMadhavan.Venkataraman@Sun.COM 	*h2 = tmp;			\
2369334SMadhavan.Venkataraman@Sun.COM }
2379334SMadhavan.Venkataraman@Sun.COM 
2389334SMadhavan.Venkataraman@Sun.COM /*
2399334SMadhavan.Venkataraman@Sun.COM  * Macro to free a callout list.
2409334SMadhavan.Venkataraman@Sun.COM  */
2419334SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_LIST_FREE(ct, cl)			\
2429334SMadhavan.Venkataraman@Sun.COM {							\
2439334SMadhavan.Venkataraman@Sun.COM 	cl->cl_next = ct->ct_lfree;			\
2449334SMadhavan.Venkataraman@Sun.COM 	ct->ct_lfree = cl;				\
2459334SMadhavan.Venkataraman@Sun.COM 	cl->cl_flags |= CALLOUT_LIST_FLAG_FREE;		\
2469334SMadhavan.Venkataraman@Sun.COM }
2479334SMadhavan.Venkataraman@Sun.COM 
2489334SMadhavan.Venkataraman@Sun.COM /*
249*11745SMadhavan.Venkataraman@Sun.COM  * Macro to free a callout.
250*11745SMadhavan.Venkataraman@Sun.COM  */
251*11745SMadhavan.Venkataraman@Sun.COM #define	CALLOUT_FREE(ct, cl)			\
252*11745SMadhavan.Venkataraman@Sun.COM {						\
253*11745SMadhavan.Venkataraman@Sun.COM 	cp->c_idnext = ct->ct_free;		\
254*11745SMadhavan.Venkataraman@Sun.COM 	ct->ct_free = cp;			\
255*11745SMadhavan.Venkataraman@Sun.COM 	cp->c_xid |= CALLOUT_ID_FREE;		\
256*11745SMadhavan.Venkataraman@Sun.COM }
257*11745SMadhavan.Venkataraman@Sun.COM 
258*11745SMadhavan.Venkataraman@Sun.COM /*
2590Sstevel@tonic-gate  * Allocate a callout structure.  We try quite hard because we
2600Sstevel@tonic-gate  * can't sleep, and if we can't do the allocation, we're toast.
2618048SMadhavan.Venkataraman@Sun.COM  * Failing all, we try a KM_PANIC allocation. Note that we never
2628048SMadhavan.Venkataraman@Sun.COM  * deallocate a callout. See untimeout() for the reasoning.
2630Sstevel@tonic-gate  */
2640Sstevel@tonic-gate static callout_t *
2650Sstevel@tonic-gate callout_alloc(callout_table_t *ct)
2660Sstevel@tonic-gate {
2678048SMadhavan.Venkataraman@Sun.COM 	size_t size;
2688048SMadhavan.Venkataraman@Sun.COM 	callout_t *cp;
2698048SMadhavan.Venkataraman@Sun.COM 
2708048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
2718048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
2720Sstevel@tonic-gate 
2738048SMadhavan.Venkataraman@Sun.COM 	cp = kmem_cache_alloc(ct->ct_cache, KM_NOSLEEP);
2748048SMadhavan.Venkataraman@Sun.COM 	if (cp == NULL) {
2758048SMadhavan.Venkataraman@Sun.COM 		size = sizeof (callout_t);
2768048SMadhavan.Venkataraman@Sun.COM 		cp = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC);
2778048SMadhavan.Venkataraman@Sun.COM 	}
2788048SMadhavan.Venkataraman@Sun.COM 	cp->c_xid = 0;
2799039SMadhavan.Venkataraman@Sun.COM 	cp->c_executor = NULL;
2809039SMadhavan.Venkataraman@Sun.COM 	cv_init(&cp->c_done, NULL, CV_DEFAULT, NULL);
2819039SMadhavan.Venkataraman@Sun.COM 	cp->c_waiting = 0;
2828048SMadhavan.Venkataraman@Sun.COM 
2838048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
2848048SMadhavan.Venkataraman@Sun.COM 	ct->ct_allocations++;
2850Sstevel@tonic-gate 	return (cp);
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate /*
2898048SMadhavan.Venkataraman@Sun.COM  * Allocate a callout list structure.  We try quite hard because we
2908048SMadhavan.Venkataraman@Sun.COM  * can't sleep, and if we can't do the allocation, we're toast.
2918048SMadhavan.Venkataraman@Sun.COM  * Failing all, we try a KM_PANIC allocation. Note that we never
2928048SMadhavan.Venkataraman@Sun.COM  * deallocate a callout list.
2938048SMadhavan.Venkataraman@Sun.COM  */
2948048SMadhavan.Venkataraman@Sun.COM static void
2958048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(callout_table_t *ct)
2968048SMadhavan.Venkataraman@Sun.COM {
2978048SMadhavan.Venkataraman@Sun.COM 	size_t size;
2988048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
2998048SMadhavan.Venkataraman@Sun.COM 
3008048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
3018048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
3028048SMadhavan.Venkataraman@Sun.COM 
3038048SMadhavan.Venkataraman@Sun.COM 	cl = kmem_cache_alloc(ct->ct_lcache, KM_NOSLEEP);
3048048SMadhavan.Venkataraman@Sun.COM 	if (cl == NULL) {
3058048SMadhavan.Venkataraman@Sun.COM 		size = sizeof (callout_list_t);
3068048SMadhavan.Venkataraman@Sun.COM 		cl = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC);
3078048SMadhavan.Venkataraman@Sun.COM 	}
3088048SMadhavan.Venkataraman@Sun.COM 	bzero(cl, sizeof (callout_list_t));
3098048SMadhavan.Venkataraman@Sun.COM 
3108048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
3119334SMadhavan.Venkataraman@Sun.COM 	CALLOUT_LIST_FREE(ct, cl);
3128048SMadhavan.Venkataraman@Sun.COM }
3138048SMadhavan.Venkataraman@Sun.COM 
3148048SMadhavan.Venkataraman@Sun.COM /*
3159334SMadhavan.Venkataraman@Sun.COM  * Find a callout list that corresponds to an expiration and matching flags.
3168048SMadhavan.Venkataraman@Sun.COM  */
3178048SMadhavan.Venkataraman@Sun.COM static callout_list_t *
3189039SMadhavan.Venkataraman@Sun.COM callout_list_get(callout_table_t *ct, hrtime_t expiration, int flags, int hash)
3198048SMadhavan.Venkataraman@Sun.COM {
3208048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
3219334SMadhavan.Venkataraman@Sun.COM 	int clflags;
3228048SMadhavan.Venkataraman@Sun.COM 
3238048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
3248048SMadhavan.Venkataraman@Sun.COM 
3259334SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_LIST_FLAG_NANO) {
3269334SMadhavan.Venkataraman@Sun.COM 		/*
3279334SMadhavan.Venkataraman@Sun.COM 		 * This is a 1-nanosecond resolution callout. We will rarely
3289334SMadhavan.Venkataraman@Sun.COM 		 * find a match for this. So, bail out.
3299334SMadhavan.Venkataraman@Sun.COM 		 */
3309334SMadhavan.Venkataraman@Sun.COM 		return (NULL);
3319334SMadhavan.Venkataraman@Sun.COM 	}
3329334SMadhavan.Venkataraman@Sun.COM 
3339334SMadhavan.Venkataraman@Sun.COM 	clflags = (CALLOUT_LIST_FLAG_ABSOLUTE | CALLOUT_LIST_FLAG_HRESTIME);
3348048SMadhavan.Venkataraman@Sun.COM 	for (cl = ct->ct_clhash[hash].ch_head; (cl != NULL); cl = cl->cl_next) {
3359334SMadhavan.Venkataraman@Sun.COM 		/*
3369334SMadhavan.Venkataraman@Sun.COM 		 * If we have reached a 1-nanosecond resolution callout list,
3379334SMadhavan.Venkataraman@Sun.COM 		 * we don't have much hope of finding a match in this hash
3389334SMadhavan.Venkataraman@Sun.COM 		 * bucket. So, just bail out.
3399334SMadhavan.Venkataraman@Sun.COM 		 */
3409334SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_flags & CALLOUT_LIST_FLAG_NANO)
3419334SMadhavan.Venkataraman@Sun.COM 			return (NULL);
3429334SMadhavan.Venkataraman@Sun.COM 
3439039SMadhavan.Venkataraman@Sun.COM 		if ((cl->cl_expiration == expiration) &&
3449334SMadhavan.Venkataraman@Sun.COM 		    ((cl->cl_flags & clflags) == (flags & clflags)))
3458048SMadhavan.Venkataraman@Sun.COM 			return (cl);
3468048SMadhavan.Venkataraman@Sun.COM 	}
3478048SMadhavan.Venkataraman@Sun.COM 
3488048SMadhavan.Venkataraman@Sun.COM 	return (NULL);
3498048SMadhavan.Venkataraman@Sun.COM }
3508048SMadhavan.Venkataraman@Sun.COM 
3518048SMadhavan.Venkataraman@Sun.COM /*
352*11745SMadhavan.Venkataraman@Sun.COM  * Add a new callout list into a callout table's queue in sorted order by
353*11745SMadhavan.Venkataraman@Sun.COM  * expiration.
354*11745SMadhavan.Venkataraman@Sun.COM  */
355*11745SMadhavan.Venkataraman@Sun.COM static int
356*11745SMadhavan.Venkataraman@Sun.COM callout_queue_add(callout_table_t *ct, callout_list_t *cl)
357*11745SMadhavan.Venkataraman@Sun.COM {
358*11745SMadhavan.Venkataraman@Sun.COM 	callout_list_t *nextcl;
359*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t expiration;
360*11745SMadhavan.Venkataraman@Sun.COM 
361*11745SMadhavan.Venkataraman@Sun.COM 	expiration = cl->cl_expiration;
362*11745SMadhavan.Venkataraman@Sun.COM 	nextcl = ct->ct_queue.ch_head;
363*11745SMadhavan.Venkataraman@Sun.COM 	if ((nextcl == NULL) || (expiration < nextcl->cl_expiration)) {
364*11745SMadhavan.Venkataraman@Sun.COM 		CALLOUT_LIST_INSERT(ct->ct_queue, cl);
365*11745SMadhavan.Venkataraman@Sun.COM 		return (1);
366*11745SMadhavan.Venkataraman@Sun.COM 	}
367*11745SMadhavan.Venkataraman@Sun.COM 
368*11745SMadhavan.Venkataraman@Sun.COM 	while (nextcl != NULL) {
369*11745SMadhavan.Venkataraman@Sun.COM 		if (expiration < nextcl->cl_expiration) {
370*11745SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_BEFORE(cl, nextcl);
371*11745SMadhavan.Venkataraman@Sun.COM 			return (0);
372*11745SMadhavan.Venkataraman@Sun.COM 		}
373*11745SMadhavan.Venkataraman@Sun.COM 		nextcl = nextcl->cl_next;
374*11745SMadhavan.Venkataraman@Sun.COM 	}
375*11745SMadhavan.Venkataraman@Sun.COM 	CALLOUT_LIST_APPEND(ct->ct_queue, cl);
376*11745SMadhavan.Venkataraman@Sun.COM 
377*11745SMadhavan.Venkataraman@Sun.COM 	return (0);
378*11745SMadhavan.Venkataraman@Sun.COM }
379*11745SMadhavan.Venkataraman@Sun.COM 
380*11745SMadhavan.Venkataraman@Sun.COM /*
381*11745SMadhavan.Venkataraman@Sun.COM  * Insert a callout list into a callout table's queue and reprogram the queue
382*11745SMadhavan.Venkataraman@Sun.COM  * cyclic if needed.
383*11745SMadhavan.Venkataraman@Sun.COM  */
384*11745SMadhavan.Venkataraman@Sun.COM static void
385*11745SMadhavan.Venkataraman@Sun.COM callout_queue_insert(callout_table_t *ct, callout_list_t *cl)
386*11745SMadhavan.Venkataraman@Sun.COM {
387*11745SMadhavan.Venkataraman@Sun.COM 	cl->cl_flags |= CALLOUT_LIST_FLAG_QUEUED;
388*11745SMadhavan.Venkataraman@Sun.COM 
389*11745SMadhavan.Venkataraman@Sun.COM 	/*
390*11745SMadhavan.Venkataraman@Sun.COM 	 * Add the callout to the callout queue. If it ends up at the head,
391*11745SMadhavan.Venkataraman@Sun.COM 	 * the cyclic needs to be reprogrammed as we have an earlier
392*11745SMadhavan.Venkataraman@Sun.COM 	 * expiration.
393*11745SMadhavan.Venkataraman@Sun.COM 	 *
394*11745SMadhavan.Venkataraman@Sun.COM 	 * Also, during the CPR suspend phase, do not reprogram the cyclic.
395*11745SMadhavan.Venkataraman@Sun.COM 	 * We don't want any callout activity. When the CPR resume phase is
396*11745SMadhavan.Venkataraman@Sun.COM 	 * entered, the cyclic will be programmed for the earliest expiration
397*11745SMadhavan.Venkataraman@Sun.COM 	 * in the queue.
398*11745SMadhavan.Venkataraman@Sun.COM 	 */
399*11745SMadhavan.Venkataraman@Sun.COM 	if (callout_queue_add(ct, cl) && (ct->ct_suspend == 0))
400*11745SMadhavan.Venkataraman@Sun.COM 		(void) cyclic_reprogram(ct->ct_qcyclic, cl->cl_expiration);
401*11745SMadhavan.Venkataraman@Sun.COM }
402*11745SMadhavan.Venkataraman@Sun.COM 
403*11745SMadhavan.Venkataraman@Sun.COM /*
404*11745SMadhavan.Venkataraman@Sun.COM  * Delete and handle all past expirations in a callout table's queue.
405*11745SMadhavan.Venkataraman@Sun.COM  */
406*11745SMadhavan.Venkataraman@Sun.COM static hrtime_t
407*11745SMadhavan.Venkataraman@Sun.COM callout_queue_delete(callout_table_t *ct)
408*11745SMadhavan.Venkataraman@Sun.COM {
409*11745SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
410*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t now;
411*11745SMadhavan.Venkataraman@Sun.COM 
412*11745SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
413*11745SMadhavan.Venkataraman@Sun.COM 
414*11745SMadhavan.Venkataraman@Sun.COM 	now = gethrtime();
415*11745SMadhavan.Venkataraman@Sun.COM 	while ((cl = ct->ct_queue.ch_head) != NULL) {
416*11745SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_expiration > now)
417*11745SMadhavan.Venkataraman@Sun.COM 			break;
418*11745SMadhavan.Venkataraman@Sun.COM 		cl->cl_flags &= ~CALLOUT_LIST_FLAG_QUEUED;
419*11745SMadhavan.Venkataraman@Sun.COM 		CALLOUT_LIST_DELETE(ct->ct_queue, cl);
420*11745SMadhavan.Venkataraman@Sun.COM 		CALLOUT_LIST_APPEND(ct->ct_expired, cl);
421*11745SMadhavan.Venkataraman@Sun.COM 	}
422*11745SMadhavan.Venkataraman@Sun.COM 
423*11745SMadhavan.Venkataraman@Sun.COM 	/*
424*11745SMadhavan.Venkataraman@Sun.COM 	 * If this callout queue is empty or callouts have been suspended,
425*11745SMadhavan.Venkataraman@Sun.COM 	 * just return.
426*11745SMadhavan.Venkataraman@Sun.COM 	 */
427*11745SMadhavan.Venkataraman@Sun.COM 	if ((cl == NULL) || (ct->ct_suspend > 0))
428*11745SMadhavan.Venkataraman@Sun.COM 		return (CY_INFINITY);
429*11745SMadhavan.Venkataraman@Sun.COM 
430*11745SMadhavan.Venkataraman@Sun.COM 	(void) cyclic_reprogram(ct->ct_qcyclic, cl->cl_expiration);
431*11745SMadhavan.Venkataraman@Sun.COM 
432*11745SMadhavan.Venkataraman@Sun.COM 	return (cl->cl_expiration);
433*11745SMadhavan.Venkataraman@Sun.COM }
434*11745SMadhavan.Venkataraman@Sun.COM 
435*11745SMadhavan.Venkataraman@Sun.COM static hrtime_t
436*11745SMadhavan.Venkataraman@Sun.COM callout_queue_process(callout_table_t *ct, hrtime_t delta, int timechange)
437*11745SMadhavan.Venkataraman@Sun.COM {
438*11745SMadhavan.Venkataraman@Sun.COM 	callout_list_t *firstcl, *cl;
439*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t expiration, now;
440*11745SMadhavan.Venkataraman@Sun.COM 	int clflags;
441*11745SMadhavan.Venkataraman@Sun.COM 	callout_hash_t temp;
442*11745SMadhavan.Venkataraman@Sun.COM 
443*11745SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
444*11745SMadhavan.Venkataraman@Sun.COM 
445*11745SMadhavan.Venkataraman@Sun.COM 	firstcl = ct->ct_queue.ch_head;
446*11745SMadhavan.Venkataraman@Sun.COM 	if (firstcl == NULL)
447*11745SMadhavan.Venkataraman@Sun.COM 		return (CY_INFINITY);
448*11745SMadhavan.Venkataraman@Sun.COM 
449*11745SMadhavan.Venkataraman@Sun.COM 	/*
450*11745SMadhavan.Venkataraman@Sun.COM 	 * We walk the callout queue. If we encounter a hrestime entry that
451*11745SMadhavan.Venkataraman@Sun.COM 	 * must be removed, we clean it out. Otherwise, we apply any
452*11745SMadhavan.Venkataraman@Sun.COM 	 * adjustments needed to it. Because of the latter, we need to
453*11745SMadhavan.Venkataraman@Sun.COM 	 * recreate the list as we go along.
454*11745SMadhavan.Venkataraman@Sun.COM 	 */
455*11745SMadhavan.Venkataraman@Sun.COM 	temp = ct->ct_queue;
456*11745SMadhavan.Venkataraman@Sun.COM 	ct->ct_queue.ch_head = NULL;
457*11745SMadhavan.Venkataraman@Sun.COM 	ct->ct_queue.ch_tail = NULL;
458*11745SMadhavan.Venkataraman@Sun.COM 
459*11745SMadhavan.Venkataraman@Sun.COM 	clflags = (CALLOUT_LIST_FLAG_HRESTIME | CALLOUT_LIST_FLAG_ABSOLUTE);
460*11745SMadhavan.Venkataraman@Sun.COM 	now = gethrtime();
461*11745SMadhavan.Venkataraman@Sun.COM 	while ((cl = temp.ch_head) != NULL) {
462*11745SMadhavan.Venkataraman@Sun.COM 		CALLOUT_LIST_DELETE(temp, cl);
463*11745SMadhavan.Venkataraman@Sun.COM 
464*11745SMadhavan.Venkataraman@Sun.COM 		/*
465*11745SMadhavan.Venkataraman@Sun.COM 		 * Delete the callout and expire it, if one of the following
466*11745SMadhavan.Venkataraman@Sun.COM 		 * is true:
467*11745SMadhavan.Venkataraman@Sun.COM 		 *	- the callout has expired
468*11745SMadhavan.Venkataraman@Sun.COM 		 *	- the callout is an absolute hrestime one and
469*11745SMadhavan.Venkataraman@Sun.COM 		 *	  there has been a system time change
470*11745SMadhavan.Venkataraman@Sun.COM 		 */
471*11745SMadhavan.Venkataraman@Sun.COM 		if ((cl->cl_expiration <= now) ||
472*11745SMadhavan.Venkataraman@Sun.COM 		    (timechange && ((cl->cl_flags & clflags) == clflags))) {
473*11745SMadhavan.Venkataraman@Sun.COM 			cl->cl_flags &= ~CALLOUT_LIST_FLAG_QUEUED;
474*11745SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_APPEND(ct->ct_expired, cl);
475*11745SMadhavan.Venkataraman@Sun.COM 			continue;
476*11745SMadhavan.Venkataraman@Sun.COM 		}
477*11745SMadhavan.Venkataraman@Sun.COM 
478*11745SMadhavan.Venkataraman@Sun.COM 		/*
479*11745SMadhavan.Venkataraman@Sun.COM 		 * Apply adjustments, if any. Adjustments are applied after
480*11745SMadhavan.Venkataraman@Sun.COM 		 * the system returns from KMDB or OBP. They are only applied
481*11745SMadhavan.Venkataraman@Sun.COM 		 * to relative callout lists.
482*11745SMadhavan.Venkataraman@Sun.COM 		 */
483*11745SMadhavan.Venkataraman@Sun.COM 		if (delta && !(cl->cl_flags & CALLOUT_LIST_FLAG_ABSOLUTE)) {
484*11745SMadhavan.Venkataraman@Sun.COM 			expiration = cl->cl_expiration + delta;
485*11745SMadhavan.Venkataraman@Sun.COM 			if (expiration <= 0)
486*11745SMadhavan.Venkataraman@Sun.COM 				expiration = CY_INFINITY;
487*11745SMadhavan.Venkataraman@Sun.COM 			cl->cl_expiration = expiration;
488*11745SMadhavan.Venkataraman@Sun.COM 		}
489*11745SMadhavan.Venkataraman@Sun.COM 
490*11745SMadhavan.Venkataraman@Sun.COM 		(void) callout_queue_add(ct, cl);
491*11745SMadhavan.Venkataraman@Sun.COM 	}
492*11745SMadhavan.Venkataraman@Sun.COM 
493*11745SMadhavan.Venkataraman@Sun.COM 	/*
494*11745SMadhavan.Venkataraman@Sun.COM 	 * We need to return the expiration to help program the cyclic.
495*11745SMadhavan.Venkataraman@Sun.COM 	 * If there are expired callouts, the cyclic needs to go off
496*11745SMadhavan.Venkataraman@Sun.COM 	 * immediately. If the queue has become empty, then we return infinity.
497*11745SMadhavan.Venkataraman@Sun.COM 	 * Else, we return the expiration of the earliest callout in the queue.
498*11745SMadhavan.Venkataraman@Sun.COM 	 */
499*11745SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_expired.ch_head != NULL)
500*11745SMadhavan.Venkataraman@Sun.COM 		return (gethrtime());
501*11745SMadhavan.Venkataraman@Sun.COM 
502*11745SMadhavan.Venkataraman@Sun.COM 	cl = ct->ct_queue.ch_head;
503*11745SMadhavan.Venkataraman@Sun.COM 	if (cl == NULL)
504*11745SMadhavan.Venkataraman@Sun.COM 		return (CY_INFINITY);
505*11745SMadhavan.Venkataraman@Sun.COM 
506*11745SMadhavan.Venkataraman@Sun.COM 	return (cl->cl_expiration);
507*11745SMadhavan.Venkataraman@Sun.COM }
508*11745SMadhavan.Venkataraman@Sun.COM 
509*11745SMadhavan.Venkataraman@Sun.COM /*
5108048SMadhavan.Venkataraman@Sun.COM  * Initialize a callout table's heap, if necessary. Preallocate some free
5118048SMadhavan.Venkataraman@Sun.COM  * entries so we don't have to check for NULL elsewhere.
5128048SMadhavan.Venkataraman@Sun.COM  */
5138048SMadhavan.Venkataraman@Sun.COM static void
5148048SMadhavan.Venkataraman@Sun.COM callout_heap_init(callout_table_t *ct)
5158048SMadhavan.Venkataraman@Sun.COM {
5168048SMadhavan.Venkataraman@Sun.COM 	size_t size;
5178048SMadhavan.Venkataraman@Sun.COM 
5188048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
5198048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap == NULL);
5208048SMadhavan.Venkataraman@Sun.COM 
5218048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_num = 0;
522*11745SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_max = callout_chunk;
523*11745SMadhavan.Venkataraman@Sun.COM 	size = sizeof (callout_heap_t) * callout_chunk;
5248048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap = kmem_alloc(size, KM_SLEEP);
5258048SMadhavan.Venkataraman@Sun.COM }
5268048SMadhavan.Venkataraman@Sun.COM 
5278048SMadhavan.Venkataraman@Sun.COM /*
528*11745SMadhavan.Venkataraman@Sun.COM  * Reallocate the heap. Return 0 if the heap is still full at the end of it.
529*11745SMadhavan.Venkataraman@Sun.COM  * Return 1 otherwise. Note that the heap only expands, it never contracts.
5308048SMadhavan.Venkataraman@Sun.COM  */
531*11745SMadhavan.Venkataraman@Sun.COM static int
5328048SMadhavan.Venkataraman@Sun.COM callout_heap_expand(callout_table_t *ct)
5338048SMadhavan.Venkataraman@Sun.COM {
5348048SMadhavan.Venkataraman@Sun.COM 	size_t max, size, osize;
5359334SMadhavan.Venkataraman@Sun.COM 	callout_heap_t *heap;
5368048SMadhavan.Venkataraman@Sun.COM 
5378048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
5388048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num <= ct->ct_heap_max);
5398048SMadhavan.Venkataraman@Sun.COM 
5408048SMadhavan.Venkataraman@Sun.COM 	while (ct->ct_heap_num == ct->ct_heap_max) {
5418048SMadhavan.Venkataraman@Sun.COM 		max = ct->ct_heap_max;
5428048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
5438048SMadhavan.Venkataraman@Sun.COM 
5449334SMadhavan.Venkataraman@Sun.COM 		osize = sizeof (callout_heap_t) * max;
545*11745SMadhavan.Venkataraman@Sun.COM 		size = sizeof (callout_heap_t) * (max + callout_chunk);
546*11745SMadhavan.Venkataraman@Sun.COM 		heap = kmem_alloc(size, KM_NOSLEEP);
5478048SMadhavan.Venkataraman@Sun.COM 
5488048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
549*11745SMadhavan.Venkataraman@Sun.COM 		if (heap == NULL) {
550*11745SMadhavan.Venkataraman@Sun.COM 			/*
551*11745SMadhavan.Venkataraman@Sun.COM 			 * We could not allocate memory. If we can free up
552*11745SMadhavan.Venkataraman@Sun.COM 			 * some entries, that would be great.
553*11745SMadhavan.Venkataraman@Sun.COM 			 */
554*11745SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_nreap > 0)
555*11745SMadhavan.Venkataraman@Sun.COM 				(void) callout_heap_process(ct, 0, 0);
556*11745SMadhavan.Venkataraman@Sun.COM 			/*
557*11745SMadhavan.Venkataraman@Sun.COM 			 * If we still have no space in the heap, inform the
558*11745SMadhavan.Venkataraman@Sun.COM 			 * caller.
559*11745SMadhavan.Venkataraman@Sun.COM 			 */
560*11745SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_heap_num == ct->ct_heap_max)
561*11745SMadhavan.Venkataraman@Sun.COM 				return (0);
562*11745SMadhavan.Venkataraman@Sun.COM 			return (1);
563*11745SMadhavan.Venkataraman@Sun.COM 		}
5648048SMadhavan.Venkataraman@Sun.COM 		if (max < ct->ct_heap_max) {
5658048SMadhavan.Venkataraman@Sun.COM 			/*
5668048SMadhavan.Venkataraman@Sun.COM 			 * Someone beat us to the allocation. Free what we
5678048SMadhavan.Venkataraman@Sun.COM 			 * just allocated and proceed.
5688048SMadhavan.Venkataraman@Sun.COM 			 */
5698048SMadhavan.Venkataraman@Sun.COM 			kmem_free(heap, size);
5708048SMadhavan.Venkataraman@Sun.COM 			continue;
5718048SMadhavan.Venkataraman@Sun.COM 		}
5728048SMadhavan.Venkataraman@Sun.COM 
5738048SMadhavan.Venkataraman@Sun.COM 		bcopy(ct->ct_heap, heap, osize);
5748048SMadhavan.Venkataraman@Sun.COM 		kmem_free(ct->ct_heap, osize);
5758048SMadhavan.Venkataraman@Sun.COM 		ct->ct_heap = heap;
5769334SMadhavan.Venkataraman@Sun.COM 		ct->ct_heap_max = size / sizeof (callout_heap_t);
5778048SMadhavan.Venkataraman@Sun.COM 	}
578*11745SMadhavan.Venkataraman@Sun.COM 
579*11745SMadhavan.Venkataraman@Sun.COM 	return (1);
5808048SMadhavan.Venkataraman@Sun.COM }
5818048SMadhavan.Venkataraman@Sun.COM 
5828048SMadhavan.Venkataraman@Sun.COM /*
5838048SMadhavan.Venkataraman@Sun.COM  * Move an expiration from the bottom of the heap to its correct place
5848048SMadhavan.Venkataraman@Sun.COM  * in the heap. If we reached the root doing this, return 1. Else,
5858048SMadhavan.Venkataraman@Sun.COM  * return 0.
5860Sstevel@tonic-gate  */
5878048SMadhavan.Venkataraman@Sun.COM static int
5888048SMadhavan.Venkataraman@Sun.COM callout_upheap(callout_table_t *ct)
5898048SMadhavan.Venkataraman@Sun.COM {
5908048SMadhavan.Venkataraman@Sun.COM 	int current, parent;
5919334SMadhavan.Venkataraman@Sun.COM 	callout_heap_t *heap, *hcurrent, *hparent;
5928048SMadhavan.Venkataraman@Sun.COM 
5938048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
5948048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num >= 1);
5958048SMadhavan.Venkataraman@Sun.COM 
5968048SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_heap_num == 1) {
5978048SMadhavan.Venkataraman@Sun.COM 		return (1);
5988048SMadhavan.Venkataraman@Sun.COM 	}
5998048SMadhavan.Venkataraman@Sun.COM 
6008048SMadhavan.Venkataraman@Sun.COM 	heap = ct->ct_heap;
6018048SMadhavan.Venkataraman@Sun.COM 	current = ct->ct_heap_num - 1;
6028048SMadhavan.Venkataraman@Sun.COM 
6038048SMadhavan.Venkataraman@Sun.COM 	for (;;) {
6048048SMadhavan.Venkataraman@Sun.COM 		parent = CALLOUT_HEAP_PARENT(current);
6059334SMadhavan.Venkataraman@Sun.COM 		hparent = &heap[parent];
6069334SMadhavan.Venkataraman@Sun.COM 		hcurrent = &heap[current];
6078048SMadhavan.Venkataraman@Sun.COM 
6088048SMadhavan.Venkataraman@Sun.COM 		/*
6098048SMadhavan.Venkataraman@Sun.COM 		 * We have an expiration later than our parent; we're done.
6108048SMadhavan.Venkataraman@Sun.COM 		 */
6119334SMadhavan.Venkataraman@Sun.COM 		if (hcurrent->ch_expiration >= hparent->ch_expiration) {
6128048SMadhavan.Venkataraman@Sun.COM 			return (0);
6138048SMadhavan.Venkataraman@Sun.COM 		}
6148048SMadhavan.Venkataraman@Sun.COM 
6158048SMadhavan.Venkataraman@Sun.COM 		/*
6168048SMadhavan.Venkataraman@Sun.COM 		 * We need to swap with our parent, and continue up the heap.
6178048SMadhavan.Venkataraman@Sun.COM 		 */
6189334SMadhavan.Venkataraman@Sun.COM 		CALLOUT_SWAP(hparent, hcurrent);
6198048SMadhavan.Venkataraman@Sun.COM 
6208048SMadhavan.Venkataraman@Sun.COM 		/*
6218048SMadhavan.Venkataraman@Sun.COM 		 * If we just reached the root, we're done.
6228048SMadhavan.Venkataraman@Sun.COM 		 */
6238048SMadhavan.Venkataraman@Sun.COM 		if (parent == 0) {
6248048SMadhavan.Venkataraman@Sun.COM 			return (1);
6258048SMadhavan.Venkataraman@Sun.COM 		}
6268048SMadhavan.Venkataraman@Sun.COM 
6278048SMadhavan.Venkataraman@Sun.COM 		current = parent;
6288048SMadhavan.Venkataraman@Sun.COM 	}
6298048SMadhavan.Venkataraman@Sun.COM 	/*NOTREACHED*/
6308048SMadhavan.Venkataraman@Sun.COM }
6318048SMadhavan.Venkataraman@Sun.COM 
6328048SMadhavan.Venkataraman@Sun.COM /*
6339334SMadhavan.Venkataraman@Sun.COM  * Insert a new heap item into a callout table's heap.
6348048SMadhavan.Venkataraman@Sun.COM  */
6358048SMadhavan.Venkataraman@Sun.COM static void
6369334SMadhavan.Venkataraman@Sun.COM callout_heap_insert(callout_table_t *ct, callout_list_t *cl)
6378048SMadhavan.Venkataraman@Sun.COM {
6388048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
6398048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num < ct->ct_heap_max);
6408048SMadhavan.Venkataraman@Sun.COM 
641*11745SMadhavan.Venkataraman@Sun.COM 	cl->cl_flags |= CALLOUT_LIST_FLAG_HEAPED;
6428048SMadhavan.Venkataraman@Sun.COM 	/*
6439334SMadhavan.Venkataraman@Sun.COM 	 * First, copy the expiration and callout list pointer to the bottom
6449334SMadhavan.Venkataraman@Sun.COM 	 * of the heap.
6458048SMadhavan.Venkataraman@Sun.COM 	 */
6469334SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap[ct->ct_heap_num].ch_expiration = cl->cl_expiration;
6479334SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap[ct->ct_heap_num].ch_list = cl;
6488048SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_num++;
6498048SMadhavan.Venkataraman@Sun.COM 
6508048SMadhavan.Venkataraman@Sun.COM 	/*
6518048SMadhavan.Venkataraman@Sun.COM 	 * Now, perform an upheap operation. If we reached the root, then
6528048SMadhavan.Venkataraman@Sun.COM 	 * the cyclic needs to be reprogrammed as we have an earlier
6538048SMadhavan.Venkataraman@Sun.COM 	 * expiration.
6548048SMadhavan.Venkataraman@Sun.COM 	 *
6558048SMadhavan.Venkataraman@Sun.COM 	 * Also, during the CPR suspend phase, do not reprogram the cyclic.
6568048SMadhavan.Venkataraman@Sun.COM 	 * We don't want any callout activity. When the CPR resume phase is
6578048SMadhavan.Venkataraman@Sun.COM 	 * entered, the cyclic will be programmed for the earliest expiration
6588048SMadhavan.Venkataraman@Sun.COM 	 * in the heap.
6598048SMadhavan.Venkataraman@Sun.COM 	 */
6608566SMadhavan.Venkataraman@Sun.COM 	if (callout_upheap(ct) && (ct->ct_suspend == 0))
6619334SMadhavan.Venkataraman@Sun.COM 		(void) cyclic_reprogram(ct->ct_cyclic, cl->cl_expiration);
6628048SMadhavan.Venkataraman@Sun.COM }
6638048SMadhavan.Venkataraman@Sun.COM 
6648048SMadhavan.Venkataraman@Sun.COM /*
6658048SMadhavan.Venkataraman@Sun.COM  * Move an expiration from the top of the heap to its correct place
6668048SMadhavan.Venkataraman@Sun.COM  * in the heap.
6678048SMadhavan.Venkataraman@Sun.COM  */
6688048SMadhavan.Venkataraman@Sun.COM static void
6698048SMadhavan.Venkataraman@Sun.COM callout_downheap(callout_table_t *ct)
6700Sstevel@tonic-gate {
6719334SMadhavan.Venkataraman@Sun.COM 	int current, left, right, nelems;
6729334SMadhavan.Venkataraman@Sun.COM 	callout_heap_t *heap, *hleft, *hright, *hcurrent;
6738048SMadhavan.Venkataraman@Sun.COM 
6748048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
6758048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num >= 1);
6768048SMadhavan.Venkataraman@Sun.COM 
6778048SMadhavan.Venkataraman@Sun.COM 	heap = ct->ct_heap;
6788048SMadhavan.Venkataraman@Sun.COM 	current = 0;
6798048SMadhavan.Venkataraman@Sun.COM 	nelems = ct->ct_heap_num;
6808048SMadhavan.Venkataraman@Sun.COM 
6818048SMadhavan.Venkataraman@Sun.COM 	for (;;) {
6828048SMadhavan.Venkataraman@Sun.COM 		/*
6838048SMadhavan.Venkataraman@Sun.COM 		 * If we don't have a left child (i.e., we're a leaf), we're
6848048SMadhavan.Venkataraman@Sun.COM 		 * done.
6858048SMadhavan.Venkataraman@Sun.COM 		 */
6868048SMadhavan.Venkataraman@Sun.COM 		if ((left = CALLOUT_HEAP_LEFT(current)) >= nelems)
6878048SMadhavan.Venkataraman@Sun.COM 			return;
6888048SMadhavan.Venkataraman@Sun.COM 
6899334SMadhavan.Venkataraman@Sun.COM 		hleft = &heap[left];
6909334SMadhavan.Venkataraman@Sun.COM 		hcurrent = &heap[current];
6918048SMadhavan.Venkataraman@Sun.COM 
6928048SMadhavan.Venkataraman@Sun.COM 		right = CALLOUT_HEAP_RIGHT(current);
6938048SMadhavan.Venkataraman@Sun.COM 
6948048SMadhavan.Venkataraman@Sun.COM 		/*
6958048SMadhavan.Venkataraman@Sun.COM 		 * Even if we don't have a right child, we still need to compare
6968048SMadhavan.Venkataraman@Sun.COM 		 * our expiration against that of our left child.
6978048SMadhavan.Venkataraman@Sun.COM 		 */
6988048SMadhavan.Venkataraman@Sun.COM 		if (right >= nelems)
6998048SMadhavan.Venkataraman@Sun.COM 			goto comp_left;
7008048SMadhavan.Venkataraman@Sun.COM 
7019334SMadhavan.Venkataraman@Sun.COM 		hright = &heap[right];
7028048SMadhavan.Venkataraman@Sun.COM 
7038048SMadhavan.Venkataraman@Sun.COM 		/*
7048048SMadhavan.Venkataraman@Sun.COM 		 * We have both a left and a right child.  We need to compare
7058048SMadhavan.Venkataraman@Sun.COM 		 * the expiration of the children to determine which
7068048SMadhavan.Venkataraman@Sun.COM 		 * expires earlier.
7078048SMadhavan.Venkataraman@Sun.COM 		 */
7089334SMadhavan.Venkataraman@Sun.COM 		if (hright->ch_expiration < hleft->ch_expiration) {
7098048SMadhavan.Venkataraman@Sun.COM 			/*
7108048SMadhavan.Venkataraman@Sun.COM 			 * Our right child is the earlier of our children.
7118048SMadhavan.Venkataraman@Sun.COM 			 * We'll now compare our expiration to its expiration.
7128048SMadhavan.Venkataraman@Sun.COM 			 * If ours is the earlier one, we're done.
7138048SMadhavan.Venkataraman@Sun.COM 			 */
7149334SMadhavan.Venkataraman@Sun.COM 			if (hcurrent->ch_expiration <= hright->ch_expiration)
7158048SMadhavan.Venkataraman@Sun.COM 				return;
7168048SMadhavan.Venkataraman@Sun.COM 
7178048SMadhavan.Venkataraman@Sun.COM 			/*
7188048SMadhavan.Venkataraman@Sun.COM 			 * Our right child expires earlier than we do; swap
7198048SMadhavan.Venkataraman@Sun.COM 			 * with our right child, and descend right.
7208048SMadhavan.Venkataraman@Sun.COM 			 */
7219334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_SWAP(hright, hcurrent);
7228048SMadhavan.Venkataraman@Sun.COM 			current = right;
7238048SMadhavan.Venkataraman@Sun.COM 			continue;
7248048SMadhavan.Venkataraman@Sun.COM 		}
7258048SMadhavan.Venkataraman@Sun.COM 
7268048SMadhavan.Venkataraman@Sun.COM comp_left:
7278048SMadhavan.Venkataraman@Sun.COM 		/*
7288048SMadhavan.Venkataraman@Sun.COM 		 * Our left child is the earlier of our children (or we have
7298048SMadhavan.Venkataraman@Sun.COM 		 * no right child).  We'll now compare our expiration
7308048SMadhavan.Venkataraman@Sun.COM 		 * to its expiration. If ours is the earlier one, we're done.
7318048SMadhavan.Venkataraman@Sun.COM 		 */
7329334SMadhavan.Venkataraman@Sun.COM 		if (hcurrent->ch_expiration <= hleft->ch_expiration)
7338048SMadhavan.Venkataraman@Sun.COM 			return;
7348048SMadhavan.Venkataraman@Sun.COM 
7358048SMadhavan.Venkataraman@Sun.COM 		/*
7368048SMadhavan.Venkataraman@Sun.COM 		 * Our left child expires earlier than we do; swap with our
7378048SMadhavan.Venkataraman@Sun.COM 		 * left child, and descend left.
7388048SMadhavan.Venkataraman@Sun.COM 		 */
7399334SMadhavan.Venkataraman@Sun.COM 		CALLOUT_SWAP(hleft, hcurrent);
7408048SMadhavan.Venkataraman@Sun.COM 		current = left;
7418048SMadhavan.Venkataraman@Sun.COM 	}
7428048SMadhavan.Venkataraman@Sun.COM }
7438048SMadhavan.Venkataraman@Sun.COM 
7448048SMadhavan.Venkataraman@Sun.COM /*
7458048SMadhavan.Venkataraman@Sun.COM  * Delete and handle all past expirations in a callout table's heap.
7468048SMadhavan.Venkataraman@Sun.COM  */
747*11745SMadhavan.Venkataraman@Sun.COM static hrtime_t
7488048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(callout_table_t *ct)
7498048SMadhavan.Venkataraman@Sun.COM {
7509334SMadhavan.Venkataraman@Sun.COM 	hrtime_t now, expiration, next;
7518048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
7529334SMadhavan.Venkataraman@Sun.COM 	callout_heap_t *heap;
7538048SMadhavan.Venkataraman@Sun.COM 	int hash;
7548048SMadhavan.Venkataraman@Sun.COM 
7558048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
7568048SMadhavan.Venkataraman@Sun.COM 
7579334SMadhavan.Venkataraman@Sun.COM 	if (CALLOUT_CLEANUP(ct)) {
7589334SMadhavan.Venkataraman@Sun.COM 		/*
7599334SMadhavan.Venkataraman@Sun.COM 		 * There are too many heap elements pointing to empty callout
7609334SMadhavan.Venkataraman@Sun.COM 		 * lists. Clean them out.
7619334SMadhavan.Venkataraman@Sun.COM 		 */
7629334SMadhavan.Venkataraman@Sun.COM 		(void) callout_heap_process(ct, 0, 0);
7639334SMadhavan.Venkataraman@Sun.COM 	}
7649334SMadhavan.Venkataraman@Sun.COM 
7658048SMadhavan.Venkataraman@Sun.COM 	now = gethrtime();
7669334SMadhavan.Venkataraman@Sun.COM 	heap = ct->ct_heap;
7678048SMadhavan.Venkataraman@Sun.COM 
7688048SMadhavan.Venkataraman@Sun.COM 	while (ct->ct_heap_num > 0) {
7699334SMadhavan.Venkataraman@Sun.COM 		expiration = heap->ch_expiration;
7708048SMadhavan.Venkataraman@Sun.COM 		hash = CALLOUT_CLHASH(expiration);
7719334SMadhavan.Venkataraman@Sun.COM 		cl = heap->ch_list;
7729334SMadhavan.Venkataraman@Sun.COM 		ASSERT(expiration == cl->cl_expiration);
7739334SMadhavan.Venkataraman@Sun.COM 
7749334SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_callouts.ch_head == NULL) {
7758048SMadhavan.Venkataraman@Sun.COM 			/*
7769334SMadhavan.Venkataraman@Sun.COM 			 * If the callout list is empty, reap it.
7779334SMadhavan.Venkataraman@Sun.COM 			 * Decrement the reap count.
7789334SMadhavan.Venkataraman@Sun.COM 			 */
7799334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
7809334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_FREE(ct, cl);
7819334SMadhavan.Venkataraman@Sun.COM 			ct->ct_nreap--;
7829334SMadhavan.Venkataraman@Sun.COM 		} else {
7839334SMadhavan.Venkataraman@Sun.COM 			/*
7849334SMadhavan.Venkataraman@Sun.COM 			 * If the root of the heap expires in the future,
7859334SMadhavan.Venkataraman@Sun.COM 			 * bail out.
7868048SMadhavan.Venkataraman@Sun.COM 			 */
7878048SMadhavan.Venkataraman@Sun.COM 			if (expiration > now)
7888048SMadhavan.Venkataraman@Sun.COM 				break;
7898048SMadhavan.Venkataraman@Sun.COM 
7908048SMadhavan.Venkataraman@Sun.COM 			/*
7918048SMadhavan.Venkataraman@Sun.COM 			 * Move the callout list for this expiration to the
7928048SMadhavan.Venkataraman@Sun.COM 			 * list of expired callout lists. It will be processed
7938048SMadhavan.Venkataraman@Sun.COM 			 * by the callout executor.
7948048SMadhavan.Venkataraman@Sun.COM 			 */
795*11745SMadhavan.Venkataraman@Sun.COM 			cl->cl_flags &= ~CALLOUT_LIST_FLAG_HEAPED;
7968048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
7978048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_APPEND(ct->ct_expired, cl);
7988048SMadhavan.Venkataraman@Sun.COM 		}
7998048SMadhavan.Venkataraman@Sun.COM 
8008048SMadhavan.Venkataraman@Sun.COM 		/*
8018048SMadhavan.Venkataraman@Sun.COM 		 * Now delete the root. This is done by swapping the root with
8028048SMadhavan.Venkataraman@Sun.COM 		 * the last item in the heap and downheaping the item.
8038048SMadhavan.Venkataraman@Sun.COM 		 */
8048048SMadhavan.Venkataraman@Sun.COM 		ct->ct_heap_num--;
8058048SMadhavan.Venkataraman@Sun.COM 		if (ct->ct_heap_num > 0) {
8069334SMadhavan.Venkataraman@Sun.COM 			heap[0] = heap[ct->ct_heap_num];
8078048SMadhavan.Venkataraman@Sun.COM 			callout_downheap(ct);
8088048SMadhavan.Venkataraman@Sun.COM 		}
8098048SMadhavan.Venkataraman@Sun.COM 	}
8108048SMadhavan.Venkataraman@Sun.COM 
8118048SMadhavan.Venkataraman@Sun.COM 	/*
8129334SMadhavan.Venkataraman@Sun.COM 	 * If this callout table is empty or callouts have been suspended,
8139334SMadhavan.Venkataraman@Sun.COM 	 * just return. The cyclic has already been programmed to
8148048SMadhavan.Venkataraman@Sun.COM 	 * infinity by the cyclic subsystem.
8158048SMadhavan.Venkataraman@Sun.COM 	 */
8168566SMadhavan.Venkataraman@Sun.COM 	if ((ct->ct_heap_num == 0) || (ct->ct_suspend > 0))
817*11745SMadhavan.Venkataraman@Sun.COM 		return (CY_INFINITY);
8188048SMadhavan.Venkataraman@Sun.COM 
8199334SMadhavan.Venkataraman@Sun.COM 	/*
8209334SMadhavan.Venkataraman@Sun.COM 	 * If the top expirations are within callout_tolerance of each other,
8219334SMadhavan.Venkataraman@Sun.COM 	 * delay the cyclic expire so that they can be processed together.
8229334SMadhavan.Venkataraman@Sun.COM 	 * This is to prevent high resolution timers from swamping the system
8239334SMadhavan.Venkataraman@Sun.COM 	 * with cyclic activity.
8249334SMadhavan.Venkataraman@Sun.COM 	 */
8259334SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_heap_num > 2) {
8269334SMadhavan.Venkataraman@Sun.COM 		next = expiration + callout_tolerance;
8279334SMadhavan.Venkataraman@Sun.COM 		if ((heap[1].ch_expiration < next) ||
8289334SMadhavan.Venkataraman@Sun.COM 		    (heap[2].ch_expiration < next))
8299334SMadhavan.Venkataraman@Sun.COM 			expiration = next;
8309334SMadhavan.Venkataraman@Sun.COM 	}
8319334SMadhavan.Venkataraman@Sun.COM 
8328048SMadhavan.Venkataraman@Sun.COM 	(void) cyclic_reprogram(ct->ct_cyclic, expiration);
833*11745SMadhavan.Venkataraman@Sun.COM 
834*11745SMadhavan.Venkataraman@Sun.COM 	return (expiration);
8358048SMadhavan.Venkataraman@Sun.COM }
8368048SMadhavan.Venkataraman@Sun.COM 
8378566SMadhavan.Venkataraman@Sun.COM /*
8389334SMadhavan.Venkataraman@Sun.COM  * There are some situations when the entire heap is walked and processed.
8399334SMadhavan.Venkataraman@Sun.COM  * This function is called to do the processing. These are the situations:
8409334SMadhavan.Venkataraman@Sun.COM  *
8419334SMadhavan.Venkataraman@Sun.COM  * 1. When the reap count reaches its threshold, the heap has to be cleared
8429334SMadhavan.Venkataraman@Sun.COM  *    of all empty callout lists.
8439334SMadhavan.Venkataraman@Sun.COM  *
8449334SMadhavan.Venkataraman@Sun.COM  * 2. When the system enters and exits KMDB/OBP, all entries in the heap
8459334SMadhavan.Venkataraman@Sun.COM  *    need to be adjusted by the interval spent in KMDB/OBP.
8469334SMadhavan.Venkataraman@Sun.COM  *
8479334SMadhavan.Venkataraman@Sun.COM  * 3. When system time is changed, the heap has to be scanned for
8489334SMadhavan.Venkataraman@Sun.COM  *    absolute hrestime timers. These need to be removed from the heap
8499334SMadhavan.Venkataraman@Sun.COM  *    and expired immediately.
8509334SMadhavan.Venkataraman@Sun.COM  *
8519334SMadhavan.Venkataraman@Sun.COM  * In cases 2 and 3, it is a good idea to do 1 as well since we are
8529334SMadhavan.Venkataraman@Sun.COM  * scanning the heap anyway.
8539334SMadhavan.Venkataraman@Sun.COM  *
8549334SMadhavan.Venkataraman@Sun.COM  * If the root gets changed and/or callout lists are expired, return the
8559334SMadhavan.Venkataraman@Sun.COM  * new expiration to the caller so he can reprogram the cyclic accordingly.
8569334SMadhavan.Venkataraman@Sun.COM  */
8579334SMadhavan.Venkataraman@Sun.COM static hrtime_t
8589334SMadhavan.Venkataraman@Sun.COM callout_heap_process(callout_table_t *ct, hrtime_t delta, int timechange)
8599334SMadhavan.Venkataraman@Sun.COM {
8609334SMadhavan.Venkataraman@Sun.COM 	callout_heap_t *heap;
861*11745SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
8629334SMadhavan.Venkataraman@Sun.COM 	hrtime_t expiration, now;
863*11745SMadhavan.Venkataraman@Sun.COM 	int i, hash, clflags;
8649334SMadhavan.Venkataraman@Sun.COM 	ulong_t num;
8659334SMadhavan.Venkataraman@Sun.COM 
8669334SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
8679334SMadhavan.Venkataraman@Sun.COM 
8689334SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_heap_num == 0)
869*11745SMadhavan.Venkataraman@Sun.COM 		return (CY_INFINITY);
8709334SMadhavan.Venkataraman@Sun.COM 
8719334SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_nreap > 0)
8729334SMadhavan.Venkataraman@Sun.COM 		ct->ct_cleanups++;
8739334SMadhavan.Venkataraman@Sun.COM 
8749334SMadhavan.Venkataraman@Sun.COM 	heap = ct->ct_heap;
8759334SMadhavan.Venkataraman@Sun.COM 
8769334SMadhavan.Venkataraman@Sun.COM 	/*
8779334SMadhavan.Venkataraman@Sun.COM 	 * We walk the heap from the top to the bottom. If we encounter
8789334SMadhavan.Venkataraman@Sun.COM 	 * a heap item that points to an empty callout list, we clean
8799334SMadhavan.Venkataraman@Sun.COM 	 * it out. If we encounter a hrestime entry that must be removed,
8809334SMadhavan.Venkataraman@Sun.COM 	 * again we clean it out. Otherwise, we apply any adjustments needed
8819334SMadhavan.Venkataraman@Sun.COM 	 * to an element.
8829334SMadhavan.Venkataraman@Sun.COM 	 *
8839334SMadhavan.Venkataraman@Sun.COM 	 * During the walk, we also compact the heap from the bottom and
8849334SMadhavan.Venkataraman@Sun.COM 	 * reconstruct the heap using upheap operations. This is very
8859334SMadhavan.Venkataraman@Sun.COM 	 * efficient if the number of elements to be cleaned is greater than
8869334SMadhavan.Venkataraman@Sun.COM 	 * or equal to half the heap. This is the common case.
8879334SMadhavan.Venkataraman@Sun.COM 	 *
8889334SMadhavan.Venkataraman@Sun.COM 	 * Even in the non-common case, the upheap operations should be short
8899334SMadhavan.Venkataraman@Sun.COM 	 * as the entries below generally tend to be bigger than the entries
8909334SMadhavan.Venkataraman@Sun.COM 	 * above.
8919334SMadhavan.Venkataraman@Sun.COM 	 */
8929334SMadhavan.Venkataraman@Sun.COM 	num = ct->ct_heap_num;
8939334SMadhavan.Venkataraman@Sun.COM 	ct->ct_heap_num = 0;
8949334SMadhavan.Venkataraman@Sun.COM 	clflags = (CALLOUT_LIST_FLAG_HRESTIME | CALLOUT_LIST_FLAG_ABSOLUTE);
8959334SMadhavan.Venkataraman@Sun.COM 	now = gethrtime();
8969334SMadhavan.Venkataraman@Sun.COM 	for (i = 0; i < num; i++) {
8979334SMadhavan.Venkataraman@Sun.COM 		cl = heap[i].ch_list;
8989334SMadhavan.Venkataraman@Sun.COM 		/*
8999334SMadhavan.Venkataraman@Sun.COM 		 * If the callout list is empty, delete the heap element and
9009334SMadhavan.Venkataraman@Sun.COM 		 * free the callout list.
9019334SMadhavan.Venkataraman@Sun.COM 		 */
9029334SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_callouts.ch_head == NULL) {
9039334SMadhavan.Venkataraman@Sun.COM 			hash = CALLOUT_CLHASH(cl->cl_expiration);
9049334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
9059334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_FREE(ct, cl);
9069334SMadhavan.Venkataraman@Sun.COM 			continue;
9079334SMadhavan.Venkataraman@Sun.COM 		}
9089334SMadhavan.Venkataraman@Sun.COM 
9099334SMadhavan.Venkataraman@Sun.COM 		/*
9109334SMadhavan.Venkataraman@Sun.COM 		 * Delete the heap element and expire the callout list, if
9119334SMadhavan.Venkataraman@Sun.COM 		 * one of the following is true:
9129334SMadhavan.Venkataraman@Sun.COM 		 *	- the callout list has expired
9139334SMadhavan.Venkataraman@Sun.COM 		 *	- the callout list is an absolute hrestime one and
9149334SMadhavan.Venkataraman@Sun.COM 		 *	  there has been a system time change
9159334SMadhavan.Venkataraman@Sun.COM 		 */
9169334SMadhavan.Venkataraman@Sun.COM 		if ((cl->cl_expiration <= now) ||
9179334SMadhavan.Venkataraman@Sun.COM 		    (timechange && ((cl->cl_flags & clflags) == clflags))) {
9189334SMadhavan.Venkataraman@Sun.COM 			hash = CALLOUT_CLHASH(cl->cl_expiration);
919*11745SMadhavan.Venkataraman@Sun.COM 			cl->cl_flags &= ~CALLOUT_LIST_FLAG_HEAPED;
9209334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
9219334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_APPEND(ct->ct_expired, cl);
9229334SMadhavan.Venkataraman@Sun.COM 			continue;
9239334SMadhavan.Venkataraman@Sun.COM 		}
9249334SMadhavan.Venkataraman@Sun.COM 
9259334SMadhavan.Venkataraman@Sun.COM 		/*
9269334SMadhavan.Venkataraman@Sun.COM 		 * Apply adjustments, if any. Adjustments are applied after
9279334SMadhavan.Venkataraman@Sun.COM 		 * the system returns from KMDB or OBP. They are only applied
9289334SMadhavan.Venkataraman@Sun.COM 		 * to relative callout lists.
9299334SMadhavan.Venkataraman@Sun.COM 		 */
9309334SMadhavan.Venkataraman@Sun.COM 		if (delta && !(cl->cl_flags & CALLOUT_LIST_FLAG_ABSOLUTE)) {
9319334SMadhavan.Venkataraman@Sun.COM 			hash = CALLOUT_CLHASH(cl->cl_expiration);
9329334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
9339334SMadhavan.Venkataraman@Sun.COM 			expiration = cl->cl_expiration + delta;
9349334SMadhavan.Venkataraman@Sun.COM 			if (expiration <= 0)
9359334SMadhavan.Venkataraman@Sun.COM 				expiration = CY_INFINITY;
9369334SMadhavan.Venkataraman@Sun.COM 			heap[i].ch_expiration = expiration;
9379334SMadhavan.Venkataraman@Sun.COM 			cl->cl_expiration = expiration;
9389334SMadhavan.Venkataraman@Sun.COM 			hash = CALLOUT_CLHASH(cl->cl_expiration);
9399334SMadhavan.Venkataraman@Sun.COM 			if (cl->cl_flags & CALLOUT_LIST_FLAG_NANO) {
9409334SMadhavan.Venkataraman@Sun.COM 				CALLOUT_LIST_APPEND(ct->ct_clhash[hash], cl);
9419334SMadhavan.Venkataraman@Sun.COM 			} else {
9429334SMadhavan.Venkataraman@Sun.COM 				CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl);
9439334SMadhavan.Venkataraman@Sun.COM 			}
9449334SMadhavan.Venkataraman@Sun.COM 		}
9459334SMadhavan.Venkataraman@Sun.COM 
9469334SMadhavan.Venkataraman@Sun.COM 		heap[ct->ct_heap_num] = heap[i];
9479334SMadhavan.Venkataraman@Sun.COM 		ct->ct_heap_num++;
9489334SMadhavan.Venkataraman@Sun.COM 		(void) callout_upheap(ct);
9499334SMadhavan.Venkataraman@Sun.COM 	}
9509334SMadhavan.Venkataraman@Sun.COM 
9519334SMadhavan.Venkataraman@Sun.COM 	ct->ct_nreap = 0;
9529334SMadhavan.Venkataraman@Sun.COM 
953*11745SMadhavan.Venkataraman@Sun.COM 	/*
954*11745SMadhavan.Venkataraman@Sun.COM 	 * We need to return the expiration to help program the cyclic.
955*11745SMadhavan.Venkataraman@Sun.COM 	 * If there are expired callouts, the cyclic needs to go off
956*11745SMadhavan.Venkataraman@Sun.COM 	 * immediately. If the heap has become empty, then we return infinity.
957*11745SMadhavan.Venkataraman@Sun.COM 	 * Else, return the expiration of the earliest callout in the heap.
958*11745SMadhavan.Venkataraman@Sun.COM 	 */
959*11745SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_expired.ch_head != NULL)
960*11745SMadhavan.Venkataraman@Sun.COM 		return (gethrtime());
9619334SMadhavan.Venkataraman@Sun.COM 
962*11745SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_heap_num == 0)
963*11745SMadhavan.Venkataraman@Sun.COM 		return (CY_INFINITY);
964*11745SMadhavan.Venkataraman@Sun.COM 
965*11745SMadhavan.Venkataraman@Sun.COM 	return (heap->ch_expiration);
9669334SMadhavan.Venkataraman@Sun.COM }
9679334SMadhavan.Venkataraman@Sun.COM 
9689334SMadhavan.Venkataraman@Sun.COM /*
9698566SMadhavan.Venkataraman@Sun.COM  * Common function used to create normal and realtime callouts.
9708566SMadhavan.Venkataraman@Sun.COM  *
9718566SMadhavan.Venkataraman@Sun.COM  * Realtime callouts are handled at CY_LOW_PIL by a cyclic handler. So,
9728566SMadhavan.Venkataraman@Sun.COM  * there is one restriction on a realtime callout handler - it should not
9738566SMadhavan.Venkataraman@Sun.COM  * directly or indirectly acquire cpu_lock. CPU offline waits for pending
9748566SMadhavan.Venkataraman@Sun.COM  * cyclic handlers to complete while holding cpu_lock. So, if a realtime
9758566SMadhavan.Venkataraman@Sun.COM  * callout handler were to try to get cpu_lock, there would be a deadlock
9768566SMadhavan.Venkataraman@Sun.COM  * during CPU offline.
9778566SMadhavan.Venkataraman@Sun.COM  */
9788048SMadhavan.Venkataraman@Sun.COM callout_id_t
9798048SMadhavan.Venkataraman@Sun.COM timeout_generic(int type, void (*func)(void *), void *arg,
9808048SMadhavan.Venkataraman@Sun.COM 	hrtime_t expiration, hrtime_t resolution, int flags)
9818048SMadhavan.Venkataraman@Sun.COM {
9828048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
9834123Sdm120769 	callout_t *cp;
9844123Sdm120769 	callout_id_t id;
9858048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
986*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t now, interval;
9879334SMadhavan.Venkataraman@Sun.COM 	int hash, clflags;
9888048SMadhavan.Venkataraman@Sun.COM 
9898048SMadhavan.Venkataraman@Sun.COM 	ASSERT(resolution > 0);
9908048SMadhavan.Venkataraman@Sun.COM 	ASSERT(func != NULL);
9918048SMadhavan.Venkataraman@Sun.COM 
9928048SMadhavan.Venkataraman@Sun.COM 	/*
9939334SMadhavan.Venkataraman@Sun.COM 	 * We get the current hrtime right upfront so that latencies in
9949334SMadhavan.Venkataraman@Sun.COM 	 * this function do not affect the accuracy of the callout.
9958048SMadhavan.Venkataraman@Sun.COM 	 */
9969334SMadhavan.Venkataraman@Sun.COM 	now = gethrtime();
9976422Sqiao 
9988048SMadhavan.Venkataraman@Sun.COM 	/*
9998048SMadhavan.Venkataraman@Sun.COM 	 * We disable kernel preemption so that we remain on the same CPU
10008048SMadhavan.Venkataraman@Sun.COM 	 * throughout. If we needed to reprogram the callout table's cyclic,
10018048SMadhavan.Venkataraman@Sun.COM 	 * we can avoid X-calls if we are on the same CPU.
10028048SMadhavan.Venkataraman@Sun.COM 	 *
10038048SMadhavan.Venkataraman@Sun.COM 	 * Note that callout_alloc() releases and reacquires the callout
10048048SMadhavan.Venkataraman@Sun.COM 	 * table mutex. While reacquiring the mutex, it is possible for us
10058048SMadhavan.Venkataraman@Sun.COM 	 * to go to sleep and later migrate to another CPU. This should be
10068048SMadhavan.Venkataraman@Sun.COM 	 * pretty rare, though.
10078048SMadhavan.Venkataraman@Sun.COM 	 */
10088048SMadhavan.Venkataraman@Sun.COM 	kpreempt_disable();
10090Sstevel@tonic-gate 
10108048SMadhavan.Venkataraman@Sun.COM 	ct = &callout_table[CALLOUT_TABLE(type, CPU->cpu_seqid)];
10118048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
10120Sstevel@tonic-gate 
10138048SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_cyclic == CYCLIC_NONE) {
10148048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
10158048SMadhavan.Venkataraman@Sun.COM 		/*
10168048SMadhavan.Venkataraman@Sun.COM 		 * The callout table has not yet been initialized fully.
10178048SMadhavan.Venkataraman@Sun.COM 		 * So, put this one on the boot callout table which is
10188048SMadhavan.Venkataraman@Sun.COM 		 * always initialized.
10198048SMadhavan.Venkataraman@Sun.COM 		 */
10208048SMadhavan.Venkataraman@Sun.COM 		ct = &callout_boot_ct[type];
10218048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
10228048SMadhavan.Venkataraman@Sun.COM 	}
10238048SMadhavan.Venkataraman@Sun.COM 
10249334SMadhavan.Venkataraman@Sun.COM 	if (CALLOUT_CLEANUP(ct)) {
10259334SMadhavan.Venkataraman@Sun.COM 		/*
10269334SMadhavan.Venkataraman@Sun.COM 		 * There are too many heap elements pointing to empty callout
1027*11745SMadhavan.Venkataraman@Sun.COM 		 * lists. Clean them out. Since cleanup is only done once
1028*11745SMadhavan.Venkataraman@Sun.COM 		 * in a while, no need to reprogram the cyclic if the root
1029*11745SMadhavan.Venkataraman@Sun.COM 		 * of the heap gets cleaned out.
10309334SMadhavan.Venkataraman@Sun.COM 		 */
1031*11745SMadhavan.Venkataraman@Sun.COM 		(void) callout_heap_process(ct, 0, 0);
10329334SMadhavan.Venkataraman@Sun.COM 	}
10339334SMadhavan.Venkataraman@Sun.COM 
10348048SMadhavan.Venkataraman@Sun.COM 	if ((cp = ct->ct_free) == NULL)
10350Sstevel@tonic-gate 		cp = callout_alloc(ct);
10360Sstevel@tonic-gate 	else
10378048SMadhavan.Venkataraman@Sun.COM 		ct->ct_free = cp->c_idnext;
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	cp->c_func = func;
10400Sstevel@tonic-gate 	cp->c_arg = arg;
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 	/*
10438048SMadhavan.Venkataraman@Sun.COM 	 * Compute the expiration hrtime.
10448048SMadhavan.Venkataraman@Sun.COM 	 */
10458048SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_FLAG_ABSOLUTE) {
10468048SMadhavan.Venkataraman@Sun.COM 		interval = expiration - now;
10478048SMadhavan.Venkataraman@Sun.COM 	} else {
10488048SMadhavan.Venkataraman@Sun.COM 		interval = expiration;
10498048SMadhavan.Venkataraman@Sun.COM 		expiration += now;
10508048SMadhavan.Venkataraman@Sun.COM 	}
10519334SMadhavan.Venkataraman@Sun.COM 
10529334SMadhavan.Venkataraman@Sun.COM 	if (resolution > 1) {
10539334SMadhavan.Venkataraman@Sun.COM 		/*
10549334SMadhavan.Venkataraman@Sun.COM 		 * Align expiration to the specified resolution.
10559334SMadhavan.Venkataraman@Sun.COM 		 */
10569334SMadhavan.Venkataraman@Sun.COM 		if (flags & CALLOUT_FLAG_ROUNDUP)
10579334SMadhavan.Venkataraman@Sun.COM 			expiration += resolution - 1;
10589334SMadhavan.Venkataraman@Sun.COM 		expiration = (expiration / resolution) * resolution;
10599334SMadhavan.Venkataraman@Sun.COM 	}
10609334SMadhavan.Venkataraman@Sun.COM 
10618566SMadhavan.Venkataraman@Sun.COM 	if (expiration <= 0) {
10628566SMadhavan.Venkataraman@Sun.COM 		/*
10638566SMadhavan.Venkataraman@Sun.COM 		 * expiration hrtime overflow has occurred. Just set the
10648566SMadhavan.Venkataraman@Sun.COM 		 * expiration to infinity.
10658566SMadhavan.Venkataraman@Sun.COM 		 */
10668566SMadhavan.Venkataraman@Sun.COM 		expiration = CY_INFINITY;
10678566SMadhavan.Venkataraman@Sun.COM 	}
10688048SMadhavan.Venkataraman@Sun.COM 
10698048SMadhavan.Venkataraman@Sun.COM 	/*
10708048SMadhavan.Venkataraman@Sun.COM 	 * Assign an ID to this callout
10718048SMadhavan.Venkataraman@Sun.COM 	 */
10728048SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_FLAG_32BIT) {
10738048SMadhavan.Venkataraman@Sun.COM 		if (interval > callout_longterm) {
10748048SMadhavan.Venkataraman@Sun.COM 			id = (ct->ct_long_id - callout_counter_low);
10758048SMadhavan.Venkataraman@Sun.COM 			id |= CALLOUT_COUNTER_HIGH;
10768048SMadhavan.Venkataraman@Sun.COM 			ct->ct_long_id = id;
10778048SMadhavan.Venkataraman@Sun.COM 		} else {
10788048SMadhavan.Venkataraman@Sun.COM 			id = (ct->ct_short_id - callout_counter_low);
10798048SMadhavan.Venkataraman@Sun.COM 			id |= CALLOUT_COUNTER_HIGH;
10808048SMadhavan.Venkataraman@Sun.COM 			ct->ct_short_id = id;
10818048SMadhavan.Venkataraman@Sun.COM 		}
10828048SMadhavan.Venkataraman@Sun.COM 	} else {
10838048SMadhavan.Venkataraman@Sun.COM 		id = (ct->ct_gen_id - callout_counter_low);
10848048SMadhavan.Venkataraman@Sun.COM 		if ((id & CALLOUT_COUNTER_HIGH) == 0) {
10858048SMadhavan.Venkataraman@Sun.COM 			id |= CALLOUT_COUNTER_HIGH;
10868048SMadhavan.Venkataraman@Sun.COM 			id += CALLOUT_GENERATION_LOW;
10878048SMadhavan.Venkataraman@Sun.COM 		}
10888048SMadhavan.Venkataraman@Sun.COM 		ct->ct_gen_id = id;
10898048SMadhavan.Venkataraman@Sun.COM 	}
10908048SMadhavan.Venkataraman@Sun.COM 
10918048SMadhavan.Venkataraman@Sun.COM 	cp->c_xid = id;
10928048SMadhavan.Venkataraman@Sun.COM 
10939334SMadhavan.Venkataraman@Sun.COM 	clflags = 0;
10949334SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_FLAG_ABSOLUTE)
10959334SMadhavan.Venkataraman@Sun.COM 		clflags |= CALLOUT_LIST_FLAG_ABSOLUTE;
10969334SMadhavan.Venkataraman@Sun.COM 	if (flags & CALLOUT_FLAG_HRESTIME)
10979334SMadhavan.Venkataraman@Sun.COM 		clflags |= CALLOUT_LIST_FLAG_HRESTIME;
10989334SMadhavan.Venkataraman@Sun.COM 	if (resolution == 1)
10999334SMadhavan.Venkataraman@Sun.COM 		clflags |= CALLOUT_LIST_FLAG_NANO;
11008048SMadhavan.Venkataraman@Sun.COM 	hash = CALLOUT_CLHASH(expiration);
11018048SMadhavan.Venkataraman@Sun.COM 
11028048SMadhavan.Venkataraman@Sun.COM again:
11038048SMadhavan.Venkataraman@Sun.COM 	/*
11048048SMadhavan.Venkataraman@Sun.COM 	 * Try to see if a callout list already exists for this expiration.
11058048SMadhavan.Venkataraman@Sun.COM 	 */
11069334SMadhavan.Venkataraman@Sun.COM 	cl = callout_list_get(ct, expiration, clflags, hash);
11078048SMadhavan.Venkataraman@Sun.COM 	if (cl == NULL) {
11088048SMadhavan.Venkataraman@Sun.COM 		/*
11098048SMadhavan.Venkataraman@Sun.COM 		 * Check the free list. If we don't find one, we have to
11108048SMadhavan.Venkataraman@Sun.COM 		 * take the slow path and allocate from kmem.
11118048SMadhavan.Venkataraman@Sun.COM 		 */
11128048SMadhavan.Venkataraman@Sun.COM 		if ((cl = ct->ct_lfree) == NULL) {
11138048SMadhavan.Venkataraman@Sun.COM 			callout_list_alloc(ct);
11148048SMadhavan.Venkataraman@Sun.COM 			/*
11158048SMadhavan.Venkataraman@Sun.COM 			 * In the above call, we drop the lock, allocate and
11168048SMadhavan.Venkataraman@Sun.COM 			 * reacquire the lock. So, we could have been away
11178048SMadhavan.Venkataraman@Sun.COM 			 * for a while. In the meantime, someone could have
11188048SMadhavan.Venkataraman@Sun.COM 			 * inserted a callout list with the same expiration.
11198048SMadhavan.Venkataraman@Sun.COM 			 * Plus, the heap could have become full. So, the best
11208048SMadhavan.Venkataraman@Sun.COM 			 * course is to repeat the steps. This should be an
11218048SMadhavan.Venkataraman@Sun.COM 			 * infrequent event.
11228048SMadhavan.Venkataraman@Sun.COM 			 */
11238048SMadhavan.Venkataraman@Sun.COM 			goto again;
11248048SMadhavan.Venkataraman@Sun.COM 		}
11258048SMadhavan.Venkataraman@Sun.COM 		ct->ct_lfree = cl->cl_next;
11268048SMadhavan.Venkataraman@Sun.COM 		cl->cl_expiration = expiration;
11279334SMadhavan.Venkataraman@Sun.COM 		cl->cl_flags = clflags;
11288048SMadhavan.Venkataraman@Sun.COM 
1129*11745SMadhavan.Venkataraman@Sun.COM 		/*
1130*11745SMadhavan.Venkataraman@Sun.COM 		 * Check if we have enough space in the heap to insert one
1131*11745SMadhavan.Venkataraman@Sun.COM 		 * expiration. If not, expand the heap.
1132*11745SMadhavan.Venkataraman@Sun.COM 		 */
1133*11745SMadhavan.Venkataraman@Sun.COM 		if (ct->ct_heap_num == ct->ct_heap_max) {
1134*11745SMadhavan.Venkataraman@Sun.COM 			if (callout_heap_expand(ct) == 0) {
1135*11745SMadhavan.Venkataraman@Sun.COM 				/*
1136*11745SMadhavan.Venkataraman@Sun.COM 				 * Could not expand the heap. Just queue it.
1137*11745SMadhavan.Venkataraman@Sun.COM 				 */
1138*11745SMadhavan.Venkataraman@Sun.COM 				callout_queue_insert(ct, cl);
1139*11745SMadhavan.Venkataraman@Sun.COM 				goto out;
1140*11745SMadhavan.Venkataraman@Sun.COM 			}
1141*11745SMadhavan.Venkataraman@Sun.COM 
1142*11745SMadhavan.Venkataraman@Sun.COM 			/*
1143*11745SMadhavan.Venkataraman@Sun.COM 			 * In the above call, we drop the lock, allocate and
1144*11745SMadhavan.Venkataraman@Sun.COM 			 * reacquire the lock. So, we could have been away
1145*11745SMadhavan.Venkataraman@Sun.COM 			 * for a while. In the meantime, someone could have
1146*11745SMadhavan.Venkataraman@Sun.COM 			 * inserted a callout list with the same expiration.
1147*11745SMadhavan.Venkataraman@Sun.COM 			 * But we will not go back and check for it as this
1148*11745SMadhavan.Venkataraman@Sun.COM 			 * should be a really infrequent event. There is no
1149*11745SMadhavan.Venkataraman@Sun.COM 			 * point.
1150*11745SMadhavan.Venkataraman@Sun.COM 			 */
1151*11745SMadhavan.Venkataraman@Sun.COM 		}
1152*11745SMadhavan.Venkataraman@Sun.COM 
11539334SMadhavan.Venkataraman@Sun.COM 		if (clflags & CALLOUT_LIST_FLAG_NANO) {
11549334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_APPEND(ct->ct_clhash[hash], cl);
11559334SMadhavan.Venkataraman@Sun.COM 		} else {
11569334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl);
11579334SMadhavan.Venkataraman@Sun.COM 		}
11588048SMadhavan.Venkataraman@Sun.COM 
11598048SMadhavan.Venkataraman@Sun.COM 		/*
11608048SMadhavan.Venkataraman@Sun.COM 		 * This is a new expiration. So, insert it into the heap.
11618048SMadhavan.Venkataraman@Sun.COM 		 * This will also reprogram the cyclic, if the expiration
11628048SMadhavan.Venkataraman@Sun.COM 		 * propagated to the root of the heap.
11638048SMadhavan.Venkataraman@Sun.COM 		 */
11649334SMadhavan.Venkataraman@Sun.COM 		callout_heap_insert(ct, cl);
11659334SMadhavan.Venkataraman@Sun.COM 	} else {
11669334SMadhavan.Venkataraman@Sun.COM 		/*
11679334SMadhavan.Venkataraman@Sun.COM 		 * If the callout list was empty, untimeout_generic() would
11689334SMadhavan.Venkataraman@Sun.COM 		 * have incremented a reap count. Decrement the reap count
11699334SMadhavan.Venkataraman@Sun.COM 		 * as we are going to insert a callout into this list.
11709334SMadhavan.Venkataraman@Sun.COM 		 */
11719334SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_callouts.ch_head == NULL)
11729334SMadhavan.Venkataraman@Sun.COM 			ct->ct_nreap--;
11738048SMadhavan.Venkataraman@Sun.COM 	}
1174*11745SMadhavan.Venkataraman@Sun.COM out:
11758048SMadhavan.Venkataraman@Sun.COM 	cp->c_list = cl;
11768048SMadhavan.Venkataraman@Sun.COM 	CALLOUT_APPEND(ct, cp);
11778048SMadhavan.Venkataraman@Sun.COM 
11788048SMadhavan.Venkataraman@Sun.COM 	ct->ct_timeouts++;
11798048SMadhavan.Venkataraman@Sun.COM 	ct->ct_timeouts_pending++;
11808048SMadhavan.Venkataraman@Sun.COM 
11818048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
11828048SMadhavan.Venkataraman@Sun.COM 
11838048SMadhavan.Venkataraman@Sun.COM 	kpreempt_enable();
11848048SMadhavan.Venkataraman@Sun.COM 
11858048SMadhavan.Venkataraman@Sun.COM 	TRACE_4(TR_FAC_CALLOUT, TR_TIMEOUT,
11868048SMadhavan.Venkataraman@Sun.COM 	    "timeout:%K(%p) in %llx expiration, cp %p", func, arg, expiration,
11878048SMadhavan.Venkataraman@Sun.COM 	    cp);
11888048SMadhavan.Venkataraman@Sun.COM 
11898048SMadhavan.Venkataraman@Sun.COM 	return (id);
11908048SMadhavan.Venkataraman@Sun.COM }
11918048SMadhavan.Venkataraman@Sun.COM 
11928048SMadhavan.Venkataraman@Sun.COM timeout_id_t
11938048SMadhavan.Venkataraman@Sun.COM timeout(void (*func)(void *), void *arg, clock_t delta)
11948048SMadhavan.Venkataraman@Sun.COM {
11958048SMadhavan.Venkataraman@Sun.COM 	ulong_t id;
11968048SMadhavan.Venkataraman@Sun.COM 
11978048SMadhavan.Venkataraman@Sun.COM 	/*
11980Sstevel@tonic-gate 	 * Make sure the callout runs at least 1 tick in the future.
11990Sstevel@tonic-gate 	 */
12000Sstevel@tonic-gate 	if (delta <= 0)
12010Sstevel@tonic-gate 		delta = 1;
12028566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
12038566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
12040Sstevel@tonic-gate 
12058048SMadhavan.Venkataraman@Sun.COM 	id =  (ulong_t)timeout_generic(CALLOUT_NORMAL, func, arg,
12068048SMadhavan.Venkataraman@Sun.COM 	    TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	return ((timeout_id_t)id);
12090Sstevel@tonic-gate }
12100Sstevel@tonic-gate 
12118048SMadhavan.Venkataraman@Sun.COM /*
12128048SMadhavan.Venkataraman@Sun.COM  * Convenience function that creates a normal callout with default parameters
12138048SMadhavan.Venkataraman@Sun.COM  * and returns a full ID.
12148048SMadhavan.Venkataraman@Sun.COM  */
12158048SMadhavan.Venkataraman@Sun.COM callout_id_t
12168048SMadhavan.Venkataraman@Sun.COM timeout_default(void (*func)(void *), void *arg, clock_t delta)
12170Sstevel@tonic-gate {
12188048SMadhavan.Venkataraman@Sun.COM 	callout_id_t id;
12190Sstevel@tonic-gate 
12208048SMadhavan.Venkataraman@Sun.COM 	/*
12218048SMadhavan.Venkataraman@Sun.COM 	 * Make sure the callout runs at least 1 tick in the future.
12228048SMadhavan.Venkataraman@Sun.COM 	 */
12238048SMadhavan.Venkataraman@Sun.COM 	if (delta <= 0)
12248048SMadhavan.Venkataraman@Sun.COM 		delta = 1;
12258566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
12268566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
12278048SMadhavan.Venkataraman@Sun.COM 
12288048SMadhavan.Venkataraman@Sun.COM 	id = timeout_generic(CALLOUT_NORMAL, func, arg, TICK_TO_NSEC(delta),
12298048SMadhavan.Venkataraman@Sun.COM 	    nsec_per_tick, 0);
12308048SMadhavan.Venkataraman@Sun.COM 
12318048SMadhavan.Venkataraman@Sun.COM 	return (id);
12320Sstevel@tonic-gate }
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate timeout_id_t
12350Sstevel@tonic-gate realtime_timeout(void (*func)(void *), void *arg, clock_t delta)
12360Sstevel@tonic-gate {
12378048SMadhavan.Venkataraman@Sun.COM 	ulong_t id;
12388048SMadhavan.Venkataraman@Sun.COM 
12398048SMadhavan.Venkataraman@Sun.COM 	/*
12408048SMadhavan.Venkataraman@Sun.COM 	 * Make sure the callout runs at least 1 tick in the future.
12418048SMadhavan.Venkataraman@Sun.COM 	 */
12428048SMadhavan.Venkataraman@Sun.COM 	if (delta <= 0)
12438048SMadhavan.Venkataraman@Sun.COM 		delta = 1;
12448566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
12458566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
12468048SMadhavan.Venkataraman@Sun.COM 
12478048SMadhavan.Venkataraman@Sun.COM 	id =  (ulong_t)timeout_generic(CALLOUT_REALTIME, func, arg,
12488048SMadhavan.Venkataraman@Sun.COM 	    TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
12498048SMadhavan.Venkataraman@Sun.COM 
12508048SMadhavan.Venkataraman@Sun.COM 	return ((timeout_id_t)id);
12510Sstevel@tonic-gate }
12520Sstevel@tonic-gate 
12538048SMadhavan.Venkataraman@Sun.COM /*
12548048SMadhavan.Venkataraman@Sun.COM  * Convenience function that creates a realtime callout with default parameters
12558048SMadhavan.Venkataraman@Sun.COM  * and returns a full ID.
12568048SMadhavan.Venkataraman@Sun.COM  */
12578048SMadhavan.Venkataraman@Sun.COM callout_id_t
12588048SMadhavan.Venkataraman@Sun.COM realtime_timeout_default(void (*func)(void *), void *arg, clock_t delta)
12590Sstevel@tonic-gate {
12608048SMadhavan.Venkataraman@Sun.COM 	callout_id_t id;
12618048SMadhavan.Venkataraman@Sun.COM 
12628048SMadhavan.Venkataraman@Sun.COM 	/*
12638048SMadhavan.Venkataraman@Sun.COM 	 * Make sure the callout runs at least 1 tick in the future.
12648048SMadhavan.Venkataraman@Sun.COM 	 */
12658048SMadhavan.Venkataraman@Sun.COM 	if (delta <= 0)
12668048SMadhavan.Venkataraman@Sun.COM 		delta = 1;
12678566SMadhavan.Venkataraman@Sun.COM 	else if (delta > callout_max_ticks)
12688566SMadhavan.Venkataraman@Sun.COM 		delta = callout_max_ticks;
12698048SMadhavan.Venkataraman@Sun.COM 
12708048SMadhavan.Venkataraman@Sun.COM 	id = timeout_generic(CALLOUT_REALTIME, func, arg, TICK_TO_NSEC(delta),
12718048SMadhavan.Venkataraman@Sun.COM 	    nsec_per_tick, 0);
12728048SMadhavan.Venkataraman@Sun.COM 
12738048SMadhavan.Venkataraman@Sun.COM 	return (id);
12748048SMadhavan.Venkataraman@Sun.COM }
12758048SMadhavan.Venkataraman@Sun.COM 
12768048SMadhavan.Venkataraman@Sun.COM hrtime_t
12778048SMadhavan.Venkataraman@Sun.COM untimeout_generic(callout_id_t id, int nowait)
12788048SMadhavan.Venkataraman@Sun.COM {
12790Sstevel@tonic-gate 	callout_table_t *ct;
12800Sstevel@tonic-gate 	callout_t *cp;
12810Sstevel@tonic-gate 	callout_id_t xid;
12829334SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl;
1283*11745SMadhavan.Venkataraman@Sun.COM 	int hash, flags;
12848048SMadhavan.Venkataraman@Sun.COM 	callout_id_t bogus;
12850Sstevel@tonic-gate 
12868048SMadhavan.Venkataraman@Sun.COM 	ct = &callout_table[CALLOUT_ID_TO_TABLE(id)];
12878048SMadhavan.Venkataraman@Sun.COM 	hash = CALLOUT_IDHASH(id);
12888048SMadhavan.Venkataraman@Sun.COM 
12898048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
12900Sstevel@tonic-gate 
12918048SMadhavan.Venkataraman@Sun.COM 	/*
12928048SMadhavan.Venkataraman@Sun.COM 	 * Search the ID hash table for the callout.
12938048SMadhavan.Venkataraman@Sun.COM 	 */
12948048SMadhavan.Venkataraman@Sun.COM 	for (cp = ct->ct_idhash[hash].ch_head; cp; cp = cp->c_idnext) {
12950Sstevel@tonic-gate 
12968048SMadhavan.Venkataraman@Sun.COM 		xid = cp->c_xid;
12978048SMadhavan.Venkataraman@Sun.COM 
12988048SMadhavan.Venkataraman@Sun.COM 		/*
12998048SMadhavan.Venkataraman@Sun.COM 		 * Match the ID and generation number.
13008048SMadhavan.Venkataraman@Sun.COM 		 */
13018048SMadhavan.Venkataraman@Sun.COM 		if ((xid & CALLOUT_ID_MASK) != id)
13028048SMadhavan.Venkataraman@Sun.COM 			continue;
13030Sstevel@tonic-gate 
13048048SMadhavan.Venkataraman@Sun.COM 		if ((xid & CALLOUT_EXECUTING) == 0) {
13058048SMadhavan.Venkataraman@Sun.COM 			hrtime_t expiration;
13060Sstevel@tonic-gate 
13078048SMadhavan.Venkataraman@Sun.COM 			/*
13088048SMadhavan.Venkataraman@Sun.COM 			 * Delete the callout. If the callout list becomes
13098048SMadhavan.Venkataraman@Sun.COM 			 * NULL, we don't remove it from the table. This is
13108048SMadhavan.Venkataraman@Sun.COM 			 * so it can be reused. If the empty callout list
13118048SMadhavan.Venkataraman@Sun.COM 			 * corresponds to the top of the the callout heap, we
13128048SMadhavan.Venkataraman@Sun.COM 			 * don't reprogram the table cyclic here. This is in
13138048SMadhavan.Venkataraman@Sun.COM 			 * order to avoid lots of X-calls to the CPU associated
13148048SMadhavan.Venkataraman@Sun.COM 			 * with the callout table.
13158048SMadhavan.Venkataraman@Sun.COM 			 */
13169334SMadhavan.Venkataraman@Sun.COM 			cl = cp->c_list;
13179334SMadhavan.Venkataraman@Sun.COM 			expiration = cl->cl_expiration;
13188048SMadhavan.Venkataraman@Sun.COM 			CALLOUT_DELETE(ct, cp);
1319*11745SMadhavan.Venkataraman@Sun.COM 			CALLOUT_FREE(ct, cp);
13208048SMadhavan.Venkataraman@Sun.COM 			ct->ct_untimeouts_unexpired++;
13218048SMadhavan.Venkataraman@Sun.COM 			ct->ct_timeouts_pending--;
13229334SMadhavan.Venkataraman@Sun.COM 
13239334SMadhavan.Venkataraman@Sun.COM 			/*
1324*11745SMadhavan.Venkataraman@Sun.COM 			 * If the callout list has become empty, there are 3
1325*11745SMadhavan.Venkataraman@Sun.COM 			 * possibilities. If it is present:
1326*11745SMadhavan.Venkataraman@Sun.COM 			 *	- in the heap, it needs to be cleaned along
1327*11745SMadhavan.Venkataraman@Sun.COM 			 *	  with its heap entry. Increment a reap count.
1328*11745SMadhavan.Venkataraman@Sun.COM 			 *	- in the callout queue, free it.
1329*11745SMadhavan.Venkataraman@Sun.COM 			 *	- in the expired list, free it.
13309334SMadhavan.Venkataraman@Sun.COM 			 */
1331*11745SMadhavan.Venkataraman@Sun.COM 			if (cl->cl_callouts.ch_head == NULL) {
1332*11745SMadhavan.Venkataraman@Sun.COM 				flags = cl->cl_flags;
1333*11745SMadhavan.Venkataraman@Sun.COM 				if (flags & CALLOUT_LIST_FLAG_HEAPED) {
1334*11745SMadhavan.Venkataraman@Sun.COM 					ct->ct_nreap++;
1335*11745SMadhavan.Venkataraman@Sun.COM 				} else if (flags & CALLOUT_LIST_FLAG_QUEUED) {
1336*11745SMadhavan.Venkataraman@Sun.COM 					CALLOUT_LIST_DELETE(ct->ct_queue, cl);
1337*11745SMadhavan.Venkataraman@Sun.COM 					CALLOUT_LIST_FREE(ct, cl);
1338*11745SMadhavan.Venkataraman@Sun.COM 				} else {
1339*11745SMadhavan.Venkataraman@Sun.COM 					CALLOUT_LIST_DELETE(ct->ct_expired, cl);
1340*11745SMadhavan.Venkataraman@Sun.COM 					CALLOUT_LIST_FREE(ct, cl);
1341*11745SMadhavan.Venkataraman@Sun.COM 				}
1342*11745SMadhavan.Venkataraman@Sun.COM 			}
13438048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
13446422Sqiao 
13458048SMadhavan.Venkataraman@Sun.COM 			expiration -= gethrtime();
13460Sstevel@tonic-gate 			TRACE_2(TR_FAC_CALLOUT, TR_UNTIMEOUT,
13478048SMadhavan.Venkataraman@Sun.COM 			    "untimeout:ID %lx hrtime left %llx", id,
13488048SMadhavan.Venkataraman@Sun.COM 			    expiration);
13498048SMadhavan.Venkataraman@Sun.COM 			return (expiration < 0 ? 0 : expiration);
13500Sstevel@tonic-gate 		}
13510Sstevel@tonic-gate 
13528048SMadhavan.Venkataraman@Sun.COM 		ct->ct_untimeouts_executing++;
13530Sstevel@tonic-gate 		/*
13540Sstevel@tonic-gate 		 * The callout we want to delete is currently executing.
13550Sstevel@tonic-gate 		 * The DDI states that we must wait until the callout
13569039SMadhavan.Venkataraman@Sun.COM 		 * completes before returning, so we block on c_done until the
13578048SMadhavan.Venkataraman@Sun.COM 		 * callout ID changes (to the old ID if it's on the freelist,
13580Sstevel@tonic-gate 		 * or to a new callout ID if it's in use).  This implicitly
13590Sstevel@tonic-gate 		 * assumes that callout structures are persistent (they are).
13600Sstevel@tonic-gate 		 */
13619039SMadhavan.Venkataraman@Sun.COM 		if (cp->c_executor == curthread) {
13620Sstevel@tonic-gate 			/*
13630Sstevel@tonic-gate 			 * The timeout handler called untimeout() on itself.
13640Sstevel@tonic-gate 			 * Stupid, but legal.  We can't wait for the timeout
13650Sstevel@tonic-gate 			 * to complete without deadlocking, so we just return.
13660Sstevel@tonic-gate 			 */
13678048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
13680Sstevel@tonic-gate 			TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_SELF,
13690Sstevel@tonic-gate 			    "untimeout_self:ID %x", id);
13700Sstevel@tonic-gate 			return (-1);
13710Sstevel@tonic-gate 		}
13728048SMadhavan.Venkataraman@Sun.COM 		if (nowait == 0) {
13738048SMadhavan.Venkataraman@Sun.COM 			/*
13748048SMadhavan.Venkataraman@Sun.COM 			 * We need to wait. Indicate that we are waiting by
13759039SMadhavan.Venkataraman@Sun.COM 			 * incrementing c_waiting. This prevents the executor
13769039SMadhavan.Venkataraman@Sun.COM 			 * from doing a wakeup on c_done if there are no
13778048SMadhavan.Venkataraman@Sun.COM 			 * waiters.
13788048SMadhavan.Venkataraman@Sun.COM 			 */
13798048SMadhavan.Venkataraman@Sun.COM 			while (cp->c_xid == xid) {
13809039SMadhavan.Venkataraman@Sun.COM 				cp->c_waiting = 1;
13819039SMadhavan.Venkataraman@Sun.COM 				cv_wait(&cp->c_done, &ct->ct_mutex);
13828048SMadhavan.Venkataraman@Sun.COM 			}
13838048SMadhavan.Venkataraman@Sun.COM 		}
13848048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
13850Sstevel@tonic-gate 		TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_EXECUTING,
13860Sstevel@tonic-gate 		    "untimeout_executing:ID %lx", id);
13870Sstevel@tonic-gate 		return (-1);
13880Sstevel@tonic-gate 	}
13898048SMadhavan.Venkataraman@Sun.COM 	ct->ct_untimeouts_expired++;
13900Sstevel@tonic-gate 
13918048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
13920Sstevel@tonic-gate 	TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_BOGUS_ID,
13930Sstevel@tonic-gate 	    "untimeout_bogus_id:ID %lx", id);
13940Sstevel@tonic-gate 
13950Sstevel@tonic-gate 	/*
13960Sstevel@tonic-gate 	 * We didn't find the specified callout ID.  This means either
13970Sstevel@tonic-gate 	 * (1) the callout already fired, or (2) the caller passed us
13980Sstevel@tonic-gate 	 * a bogus value.  Perform a sanity check to detect case (2).
13990Sstevel@tonic-gate 	 */
14009334SMadhavan.Venkataraman@Sun.COM 	bogus = (CALLOUT_ID_FLAGS | CALLOUT_COUNTER_HIGH);
14018048SMadhavan.Venkataraman@Sun.COM 	if (((id & bogus) != CALLOUT_COUNTER_HIGH) && (id != 0))
14028048SMadhavan.Venkataraman@Sun.COM 		panic("untimeout: impossible timeout id %llx",
14038048SMadhavan.Venkataraman@Sun.COM 		    (unsigned long long)id);
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 	return (-1);
14060Sstevel@tonic-gate }
14070Sstevel@tonic-gate 
14088048SMadhavan.Venkataraman@Sun.COM clock_t
14098048SMadhavan.Venkataraman@Sun.COM untimeout(timeout_id_t id_arg)
14108048SMadhavan.Venkataraman@Sun.COM {
14118048SMadhavan.Venkataraman@Sun.COM 	hrtime_t hleft;
14128048SMadhavan.Venkataraman@Sun.COM 	clock_t tleft;
14138048SMadhavan.Venkataraman@Sun.COM 	callout_id_t id;
14148048SMadhavan.Venkataraman@Sun.COM 
14158048SMadhavan.Venkataraman@Sun.COM 	id = (ulong_t)id_arg;
14168048SMadhavan.Venkataraman@Sun.COM 	hleft = untimeout_generic(id, 0);
14178048SMadhavan.Venkataraman@Sun.COM 	if (hleft < 0)
14188048SMadhavan.Venkataraman@Sun.COM 		tleft = -1;
14198048SMadhavan.Venkataraman@Sun.COM 	else if (hleft == 0)
14208048SMadhavan.Venkataraman@Sun.COM 		tleft = 0;
14218048SMadhavan.Venkataraman@Sun.COM 	else
14228048SMadhavan.Venkataraman@Sun.COM 		tleft = NSEC_TO_TICK(hleft);
14238048SMadhavan.Venkataraman@Sun.COM 
14248048SMadhavan.Venkataraman@Sun.COM 	return (tleft);
14258048SMadhavan.Venkataraman@Sun.COM }
14268048SMadhavan.Venkataraman@Sun.COM 
14270Sstevel@tonic-gate /*
14288048SMadhavan.Venkataraman@Sun.COM  * Convenience function to untimeout a timeout with a full ID with default
14298048SMadhavan.Venkataraman@Sun.COM  * parameters.
14308048SMadhavan.Venkataraman@Sun.COM  */
14318048SMadhavan.Venkataraman@Sun.COM clock_t
14328048SMadhavan.Venkataraman@Sun.COM untimeout_default(callout_id_t id, int nowait)
14338048SMadhavan.Venkataraman@Sun.COM {
14348048SMadhavan.Venkataraman@Sun.COM 	hrtime_t hleft;
14358048SMadhavan.Venkataraman@Sun.COM 	clock_t tleft;
14368048SMadhavan.Venkataraman@Sun.COM 
14378048SMadhavan.Venkataraman@Sun.COM 	hleft = untimeout_generic(id, nowait);
14388048SMadhavan.Venkataraman@Sun.COM 	if (hleft < 0)
14398048SMadhavan.Venkataraman@Sun.COM 		tleft = -1;
14408048SMadhavan.Venkataraman@Sun.COM 	else if (hleft == 0)
14418048SMadhavan.Venkataraman@Sun.COM 		tleft = 0;
14428048SMadhavan.Venkataraman@Sun.COM 	else
14438048SMadhavan.Venkataraman@Sun.COM 		tleft = NSEC_TO_TICK(hleft);
14448048SMadhavan.Venkataraman@Sun.COM 
14458048SMadhavan.Venkataraman@Sun.COM 	return (tleft);
14468048SMadhavan.Venkataraman@Sun.COM }
14478048SMadhavan.Venkataraman@Sun.COM 
14488048SMadhavan.Venkataraman@Sun.COM /*
14498048SMadhavan.Venkataraman@Sun.COM  * Expire all the callouts queued in the specified callout list.
14500Sstevel@tonic-gate  */
14510Sstevel@tonic-gate static void
14528048SMadhavan.Venkataraman@Sun.COM callout_list_expire(callout_table_t *ct, callout_list_t *cl)
14530Sstevel@tonic-gate {
14549039SMadhavan.Venkataraman@Sun.COM 	callout_t *cp, *cnext;
14558048SMadhavan.Venkataraman@Sun.COM 
14568048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
14578048SMadhavan.Venkataraman@Sun.COM 	ASSERT(cl != NULL);
14588048SMadhavan.Venkataraman@Sun.COM 
14599039SMadhavan.Venkataraman@Sun.COM 	for (cp = cl->cl_callouts.ch_head; cp != NULL; cp = cnext) {
14609039SMadhavan.Venkataraman@Sun.COM 		/*
14619039SMadhavan.Venkataraman@Sun.COM 		 * Multiple executor threads could be running at the same
14629039SMadhavan.Venkataraman@Sun.COM 		 * time. If this callout is already being executed,
14639039SMadhavan.Venkataraman@Sun.COM 		 * go on to the next one.
14649039SMadhavan.Venkataraman@Sun.COM 		 */
14659039SMadhavan.Venkataraman@Sun.COM 		if (cp->c_xid & CALLOUT_EXECUTING) {
14669039SMadhavan.Venkataraman@Sun.COM 			cnext = cp->c_clnext;
14679039SMadhavan.Venkataraman@Sun.COM 			continue;
14689039SMadhavan.Venkataraman@Sun.COM 		}
14698048SMadhavan.Venkataraman@Sun.COM 
14708048SMadhavan.Venkataraman@Sun.COM 		/*
14718048SMadhavan.Venkataraman@Sun.COM 		 * Indicate to untimeout() that a callout is
14728048SMadhavan.Venkataraman@Sun.COM 		 * being expired by the executor.
14738048SMadhavan.Venkataraman@Sun.COM 		 */
14748048SMadhavan.Venkataraman@Sun.COM 		cp->c_xid |= CALLOUT_EXECUTING;
14759039SMadhavan.Venkataraman@Sun.COM 		cp->c_executor = curthread;
14768048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
14778048SMadhavan.Venkataraman@Sun.COM 
14788048SMadhavan.Venkataraman@Sun.COM 		DTRACE_PROBE1(callout__start, callout_t *, cp);
14798048SMadhavan.Venkataraman@Sun.COM 		(*cp->c_func)(cp->c_arg);
14808048SMadhavan.Venkataraman@Sun.COM 		DTRACE_PROBE1(callout__end, callout_t *, cp);
14810Sstevel@tonic-gate 
14828048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
14838048SMadhavan.Venkataraman@Sun.COM 
14848048SMadhavan.Venkataraman@Sun.COM 		ct->ct_expirations++;
14858048SMadhavan.Venkataraman@Sun.COM 		ct->ct_timeouts_pending--;
14868048SMadhavan.Venkataraman@Sun.COM 		/*
14879039SMadhavan.Venkataraman@Sun.COM 		 * Indicate completion for c_done.
14888048SMadhavan.Venkataraman@Sun.COM 		 */
14898048SMadhavan.Venkataraman@Sun.COM 		cp->c_xid &= ~CALLOUT_EXECUTING;
14909039SMadhavan.Venkataraman@Sun.COM 		cp->c_executor = NULL;
14919039SMadhavan.Venkataraman@Sun.COM 		cnext = cp->c_clnext;
14928048SMadhavan.Venkataraman@Sun.COM 
14938048SMadhavan.Venkataraman@Sun.COM 		/*
14948048SMadhavan.Venkataraman@Sun.COM 		 * Delete callout from ID hash table and the callout
14958048SMadhavan.Venkataraman@Sun.COM 		 * list, return to freelist, and tell any untimeout() that
14968048SMadhavan.Venkataraman@Sun.COM 		 * cares that we're done.
14978048SMadhavan.Venkataraman@Sun.COM 		 */
14988048SMadhavan.Venkataraman@Sun.COM 		CALLOUT_DELETE(ct, cp);
1499*11745SMadhavan.Venkataraman@Sun.COM 		CALLOUT_FREE(ct, cp);
15000Sstevel@tonic-gate 
15019039SMadhavan.Venkataraman@Sun.COM 		if (cp->c_waiting) {
15029039SMadhavan.Venkataraman@Sun.COM 			cp->c_waiting = 0;
15039039SMadhavan.Venkataraman@Sun.COM 			cv_broadcast(&cp->c_done);
15048048SMadhavan.Venkataraman@Sun.COM 		}
15058048SMadhavan.Venkataraman@Sun.COM 	}
15068048SMadhavan.Venkataraman@Sun.COM }
15078048SMadhavan.Venkataraman@Sun.COM 
15088048SMadhavan.Venkataraman@Sun.COM /*
15098048SMadhavan.Venkataraman@Sun.COM  * Execute all expired callout lists for a callout table.
15108048SMadhavan.Venkataraman@Sun.COM  */
15118048SMadhavan.Venkataraman@Sun.COM static void
15128048SMadhavan.Venkataraman@Sun.COM callout_expire(callout_table_t *ct)
15138048SMadhavan.Venkataraman@Sun.COM {
15148048SMadhavan.Venkataraman@Sun.COM 	callout_list_t *cl, *clnext;
15158048SMadhavan.Venkataraman@Sun.COM 
15168048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
15170Sstevel@tonic-gate 
15188048SMadhavan.Venkataraman@Sun.COM 	for (cl = ct->ct_expired.ch_head; (cl != NULL); cl = clnext) {
15198048SMadhavan.Venkataraman@Sun.COM 		/*
15208048SMadhavan.Venkataraman@Sun.COM 		 * Expire all the callouts in this callout list.
15218048SMadhavan.Venkataraman@Sun.COM 		 */
15228048SMadhavan.Venkataraman@Sun.COM 		callout_list_expire(ct, cl);
15238048SMadhavan.Venkataraman@Sun.COM 
15248048SMadhavan.Venkataraman@Sun.COM 		clnext = cl->cl_next;
15259039SMadhavan.Venkataraman@Sun.COM 		if (cl->cl_callouts.ch_head == NULL) {
15269039SMadhavan.Venkataraman@Sun.COM 			/*
15279039SMadhavan.Venkataraman@Sun.COM 			 * Free the callout list.
15289039SMadhavan.Venkataraman@Sun.COM 			 */
15299039SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_DELETE(ct->ct_expired, cl);
15309334SMadhavan.Venkataraman@Sun.COM 			CALLOUT_LIST_FREE(ct, cl);
15319039SMadhavan.Venkataraman@Sun.COM 		}
15320Sstevel@tonic-gate 	}
15330Sstevel@tonic-gate }
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate /*
15368048SMadhavan.Venkataraman@Sun.COM  * The cyclic handlers below process callouts in two steps:
15378048SMadhavan.Venkataraman@Sun.COM  *
15388048SMadhavan.Venkataraman@Sun.COM  *	1. Find all expired callout lists and queue them in a separate
15398048SMadhavan.Venkataraman@Sun.COM  *	   list of expired callouts.
15408048SMadhavan.Venkataraman@Sun.COM  *	2. Execute the expired callout lists.
15418048SMadhavan.Venkataraman@Sun.COM  *
15428048SMadhavan.Venkataraman@Sun.COM  * This is done for two reasons:
15438048SMadhavan.Venkataraman@Sun.COM  *
15448048SMadhavan.Venkataraman@Sun.COM  *	1. We want to quickly find the next earliest expiration to program
15458048SMadhavan.Venkataraman@Sun.COM  *	   the cyclic to and reprogram it. We can do this right at the end
15468048SMadhavan.Venkataraman@Sun.COM  *	   of step 1.
15478048SMadhavan.Venkataraman@Sun.COM  *	2. The realtime cyclic handler expires callouts in place. However,
15488048SMadhavan.Venkataraman@Sun.COM  *	   for normal callouts, callouts are expired by a taskq thread.
15498048SMadhavan.Venkataraman@Sun.COM  *	   So, it is simpler and more robust to have the taskq thread just
15508048SMadhavan.Venkataraman@Sun.COM  *	   do step 2.
15510Sstevel@tonic-gate  */
15526422Sqiao 
15538048SMadhavan.Venkataraman@Sun.COM /*
1554*11745SMadhavan.Venkataraman@Sun.COM  * Realtime callout cyclic handlers.
15558048SMadhavan.Venkataraman@Sun.COM  */
15568048SMadhavan.Venkataraman@Sun.COM void
15578048SMadhavan.Venkataraman@Sun.COM callout_realtime(callout_table_t *ct)
15588048SMadhavan.Venkataraman@Sun.COM {
15598048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
1560*11745SMadhavan.Venkataraman@Sun.COM 	(void) callout_heap_delete(ct);
1561*11745SMadhavan.Venkataraman@Sun.COM 	callout_expire(ct);
1562*11745SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
1563*11745SMadhavan.Venkataraman@Sun.COM }
1564*11745SMadhavan.Venkataraman@Sun.COM 
1565*11745SMadhavan.Venkataraman@Sun.COM void
1566*11745SMadhavan.Venkataraman@Sun.COM callout_queue_realtime(callout_table_t *ct)
1567*11745SMadhavan.Venkataraman@Sun.COM {
1568*11745SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
1569*11745SMadhavan.Venkataraman@Sun.COM 	(void) callout_queue_delete(ct);
15708048SMadhavan.Venkataraman@Sun.COM 	callout_expire(ct);
15718048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
15728048SMadhavan.Venkataraman@Sun.COM }
15738048SMadhavan.Venkataraman@Sun.COM 
15748048SMadhavan.Venkataraman@Sun.COM void
15758048SMadhavan.Venkataraman@Sun.COM callout_execute(callout_table_t *ct)
15768048SMadhavan.Venkataraman@Sun.COM {
15778048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
15788048SMadhavan.Venkataraman@Sun.COM 	callout_expire(ct);
15798048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
15808048SMadhavan.Venkataraman@Sun.COM }
15818048SMadhavan.Venkataraman@Sun.COM 
15828048SMadhavan.Venkataraman@Sun.COM /*
1583*11745SMadhavan.Venkataraman@Sun.COM  * Normal callout cyclic handlers.
15848048SMadhavan.Venkataraman@Sun.COM  */
15858048SMadhavan.Venkataraman@Sun.COM void
15868048SMadhavan.Venkataraman@Sun.COM callout_normal(callout_table_t *ct)
15878048SMadhavan.Venkataraman@Sun.COM {
15889039SMadhavan.Venkataraman@Sun.COM 	int i, exec;
1589*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t exp;
15908048SMadhavan.Venkataraman@Sun.COM 
15918048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
1592*11745SMadhavan.Venkataraman@Sun.COM 	exp = callout_heap_delete(ct);
1593*11745SMadhavan.Venkataraman@Sun.COM 	CALLOUT_EXEC_COMPUTE(ct, exp, exec);
1594*11745SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
1595*11745SMadhavan.Venkataraman@Sun.COM 
1596*11745SMadhavan.Venkataraman@Sun.COM 	for (i = 0; i < exec; i++) {
1597*11745SMadhavan.Venkataraman@Sun.COM 		ASSERT(ct->ct_taskq != NULL);
1598*11745SMadhavan.Venkataraman@Sun.COM 		(void) taskq_dispatch(ct->ct_taskq,
1599*11745SMadhavan.Venkataraman@Sun.COM 		    (task_func_t *)callout_execute, ct, TQ_NOSLEEP);
1600*11745SMadhavan.Venkataraman@Sun.COM 	}
1601*11745SMadhavan.Venkataraman@Sun.COM }
1602*11745SMadhavan.Venkataraman@Sun.COM 
1603*11745SMadhavan.Venkataraman@Sun.COM void
1604*11745SMadhavan.Venkataraman@Sun.COM callout_queue_normal(callout_table_t *ct)
1605*11745SMadhavan.Venkataraman@Sun.COM {
1606*11745SMadhavan.Venkataraman@Sun.COM 	int i, exec;
1607*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t exp;
1608*11745SMadhavan.Venkataraman@Sun.COM 
1609*11745SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
1610*11745SMadhavan.Venkataraman@Sun.COM 	exp = callout_queue_delete(ct);
1611*11745SMadhavan.Venkataraman@Sun.COM 	CALLOUT_EXEC_COMPUTE(ct, exp, exec);
16128048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
16138048SMadhavan.Venkataraman@Sun.COM 
16149039SMadhavan.Venkataraman@Sun.COM 	for (i = 0; i < exec; i++) {
16158048SMadhavan.Venkataraman@Sun.COM 		ASSERT(ct->ct_taskq != NULL);
16168048SMadhavan.Venkataraman@Sun.COM 		(void) taskq_dispatch(ct->ct_taskq,
16178048SMadhavan.Venkataraman@Sun.COM 		    (task_func_t *)callout_execute, ct, TQ_NOSLEEP);
16180Sstevel@tonic-gate 	}
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate /*
16228048SMadhavan.Venkataraman@Sun.COM  * Suspend callout processing.
16230Sstevel@tonic-gate  */
16248048SMadhavan.Venkataraman@Sun.COM static void
16258048SMadhavan.Venkataraman@Sun.COM callout_suspend(void)
16260Sstevel@tonic-gate {
16278048SMadhavan.Venkataraman@Sun.COM 	int t, f;
16288048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
16298048SMadhavan.Venkataraman@Sun.COM 
16308048SMadhavan.Venkataraman@Sun.COM 	/*
16318048SMadhavan.Venkataraman@Sun.COM 	 * Traverse every callout table in the system and suspend callout
16328048SMadhavan.Venkataraman@Sun.COM 	 * processing.
16338048SMadhavan.Venkataraman@Sun.COM 	 *
16348048SMadhavan.Venkataraman@Sun.COM 	 * We need to suspend all the tables (including the inactive ones)
16358048SMadhavan.Venkataraman@Sun.COM 	 * so that if a table is made active while the suspend is still on,
16368048SMadhavan.Venkataraman@Sun.COM 	 * the table remains suspended.
16378048SMadhavan.Venkataraman@Sun.COM 	 */
16388048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
16398048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
16408048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[CALLOUT_TABLE(t, f)];
16418048SMadhavan.Venkataraman@Sun.COM 
16428048SMadhavan.Venkataraman@Sun.COM 			mutex_enter(&ct->ct_mutex);
16438566SMadhavan.Venkataraman@Sun.COM 			ct->ct_suspend++;
16448048SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_cyclic == CYCLIC_NONE) {
16458048SMadhavan.Venkataraman@Sun.COM 				mutex_exit(&ct->ct_mutex);
16468048SMadhavan.Venkataraman@Sun.COM 				continue;
16478048SMadhavan.Venkataraman@Sun.COM 			}
1648*11745SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_suspend == 1) {
16498566SMadhavan.Venkataraman@Sun.COM 				(void) cyclic_reprogram(ct->ct_cyclic,
16508566SMadhavan.Venkataraman@Sun.COM 				    CY_INFINITY);
1651*11745SMadhavan.Venkataraman@Sun.COM 				(void) cyclic_reprogram(ct->ct_qcyclic,
1652*11745SMadhavan.Venkataraman@Sun.COM 				    CY_INFINITY);
1653*11745SMadhavan.Venkataraman@Sun.COM 			}
16548048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
16558048SMadhavan.Venkataraman@Sun.COM 		}
16568048SMadhavan.Venkataraman@Sun.COM 	}
16578048SMadhavan.Venkataraman@Sun.COM }
16588048SMadhavan.Venkataraman@Sun.COM 
16598048SMadhavan.Venkataraman@Sun.COM /*
16608048SMadhavan.Venkataraman@Sun.COM  * Resume callout processing.
16618048SMadhavan.Venkataraman@Sun.COM  */
16628048SMadhavan.Venkataraman@Sun.COM static void
16639334SMadhavan.Venkataraman@Sun.COM callout_resume(hrtime_t delta, int timechange)
16648048SMadhavan.Venkataraman@Sun.COM {
1665*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t hexp, qexp;
16668048SMadhavan.Venkataraman@Sun.COM 	int t, f;
16678048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
16680Sstevel@tonic-gate 
16698048SMadhavan.Venkataraman@Sun.COM 	/*
16708048SMadhavan.Venkataraman@Sun.COM 	 * Traverse every callout table in the system and resume callout
16718048SMadhavan.Venkataraman@Sun.COM 	 * processing. For active tables, perform any hrtime adjustments
16728048SMadhavan.Venkataraman@Sun.COM 	 * necessary.
16738048SMadhavan.Venkataraman@Sun.COM 	 */
16748048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
16758048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
16768048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[CALLOUT_TABLE(t, f)];
16778048SMadhavan.Venkataraman@Sun.COM 
16788048SMadhavan.Venkataraman@Sun.COM 			mutex_enter(&ct->ct_mutex);
16798048SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_cyclic == CYCLIC_NONE) {
16808566SMadhavan.Venkataraman@Sun.COM 				ct->ct_suspend--;
16818048SMadhavan.Venkataraman@Sun.COM 				mutex_exit(&ct->ct_mutex);
16828048SMadhavan.Venkataraman@Sun.COM 				continue;
16838048SMadhavan.Venkataraman@Sun.COM 			}
16848048SMadhavan.Venkataraman@Sun.COM 
16859334SMadhavan.Venkataraman@Sun.COM 			/*
16869334SMadhavan.Venkataraman@Sun.COM 			 * If a delta is specified, adjust the expirations in
16879334SMadhavan.Venkataraman@Sun.COM 			 * the heap by delta. Also, if the caller indicates
16889334SMadhavan.Venkataraman@Sun.COM 			 * a timechange, process that. This step also cleans
16899334SMadhavan.Venkataraman@Sun.COM 			 * out any empty callout lists that might happen to
16909334SMadhavan.Venkataraman@Sun.COM 			 * be there.
16919334SMadhavan.Venkataraman@Sun.COM 			 */
1692*11745SMadhavan.Venkataraman@Sun.COM 			hexp = callout_heap_process(ct, delta, timechange);
1693*11745SMadhavan.Venkataraman@Sun.COM 			qexp = callout_queue_process(ct, delta, timechange);
16948048SMadhavan.Venkataraman@Sun.COM 
16958566SMadhavan.Venkataraman@Sun.COM 			ct->ct_suspend--;
16968566SMadhavan.Venkataraman@Sun.COM 			if (ct->ct_suspend == 0) {
1697*11745SMadhavan.Venkataraman@Sun.COM 				(void) cyclic_reprogram(ct->ct_cyclic, hexp);
1698*11745SMadhavan.Venkataraman@Sun.COM 				(void) cyclic_reprogram(ct->ct_qcyclic, qexp);
16998566SMadhavan.Venkataraman@Sun.COM 			}
17009334SMadhavan.Venkataraman@Sun.COM 
17018048SMadhavan.Venkataraman@Sun.COM 			mutex_exit(&ct->ct_mutex);
17028048SMadhavan.Venkataraman@Sun.COM 		}
17038048SMadhavan.Venkataraman@Sun.COM 	}
17040Sstevel@tonic-gate }
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate /*
17070Sstevel@tonic-gate  * Callback handler used by CPR to stop and resume callouts.
17089334SMadhavan.Venkataraman@Sun.COM  * The cyclic subsystem saves and restores hrtime during CPR.
17099334SMadhavan.Venkataraman@Sun.COM  * That is why callout_resume() is called with a 0 delta.
17109334SMadhavan.Venkataraman@Sun.COM  * Although hrtime is the same, hrestime (system time) has
17119334SMadhavan.Venkataraman@Sun.COM  * progressed during CPR. So, we have to indicate a time change
17129334SMadhavan.Venkataraman@Sun.COM  * to expire the absolute hrestime timers.
17130Sstevel@tonic-gate  */
17140Sstevel@tonic-gate /*ARGSUSED*/
17150Sstevel@tonic-gate static boolean_t
17160Sstevel@tonic-gate callout_cpr_callb(void *arg, int code)
17170Sstevel@tonic-gate {
17188048SMadhavan.Venkataraman@Sun.COM 	if (code == CB_CODE_CPR_CHKPT)
17198048SMadhavan.Venkataraman@Sun.COM 		callout_suspend();
17208048SMadhavan.Venkataraman@Sun.COM 	else
17219334SMadhavan.Venkataraman@Sun.COM 		callout_resume(0, 1);
17228048SMadhavan.Venkataraman@Sun.COM 
17238048SMadhavan.Venkataraman@Sun.COM 	return (B_TRUE);
17248048SMadhavan.Venkataraman@Sun.COM }
17258048SMadhavan.Venkataraman@Sun.COM 
17268048SMadhavan.Venkataraman@Sun.COM /*
17278048SMadhavan.Venkataraman@Sun.COM  * Callback handler invoked when the debugger is entered or exited.
17288048SMadhavan.Venkataraman@Sun.COM  */
17298048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/
17308048SMadhavan.Venkataraman@Sun.COM static boolean_t
17318048SMadhavan.Venkataraman@Sun.COM callout_debug_callb(void *arg, int code)
17328048SMadhavan.Venkataraman@Sun.COM {
17338048SMadhavan.Venkataraman@Sun.COM 	hrtime_t delta;
17348048SMadhavan.Venkataraman@Sun.COM 
17358048SMadhavan.Venkataraman@Sun.COM 	/*
17368048SMadhavan.Venkataraman@Sun.COM 	 * When the system enters the debugger. make a note of the hrtime.
17378048SMadhavan.Venkataraman@Sun.COM 	 * When it is resumed, compute how long the system was in the
17388048SMadhavan.Venkataraman@Sun.COM 	 * debugger. This interval should not be counted for callouts.
17398048SMadhavan.Venkataraman@Sun.COM 	 */
17408048SMadhavan.Venkataraman@Sun.COM 	if (code == 0) {
17418048SMadhavan.Venkataraman@Sun.COM 		callout_suspend();
17428048SMadhavan.Venkataraman@Sun.COM 		callout_debug_hrtime = gethrtime();
17438048SMadhavan.Venkataraman@Sun.COM 	} else {
17448048SMadhavan.Venkataraman@Sun.COM 		delta = gethrtime() - callout_debug_hrtime;
17459334SMadhavan.Venkataraman@Sun.COM 		callout_resume(delta, 0);
17468048SMadhavan.Venkataraman@Sun.COM 	}
17478048SMadhavan.Venkataraman@Sun.COM 
17480Sstevel@tonic-gate 	return (B_TRUE);
17490Sstevel@tonic-gate }
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate /*
17529039SMadhavan.Venkataraman@Sun.COM  * Move the absolute hrestime callouts to the expired list. Then program the
17539039SMadhavan.Venkataraman@Sun.COM  * table's cyclic to expire immediately so that the callouts can be executed
17548048SMadhavan.Venkataraman@Sun.COM  * immediately.
17558048SMadhavan.Venkataraman@Sun.COM  */
17568048SMadhavan.Venkataraman@Sun.COM static void
17578048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(callout_table_t *ct)
17588048SMadhavan.Venkataraman@Sun.COM {
1759*11745SMadhavan.Venkataraman@Sun.COM 	hrtime_t hexp, qexp;
17608048SMadhavan.Venkataraman@Sun.COM 
17618048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
1762*11745SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_cyclic == CYCLIC_NONE) {
17638048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
17648048SMadhavan.Venkataraman@Sun.COM 		return;
17658048SMadhavan.Venkataraman@Sun.COM 	}
17668048SMadhavan.Venkataraman@Sun.COM 
17679334SMadhavan.Venkataraman@Sun.COM 	/*
17689334SMadhavan.Venkataraman@Sun.COM 	 * Walk the heap and process all the absolute hrestime entries.
17699334SMadhavan.Venkataraman@Sun.COM 	 */
1770*11745SMadhavan.Venkataraman@Sun.COM 	hexp = callout_heap_process(ct, 0, 1);
1771*11745SMadhavan.Venkataraman@Sun.COM 	qexp = callout_queue_process(ct, 0, 1);
17728048SMadhavan.Venkataraman@Sun.COM 
1773*11745SMadhavan.Venkataraman@Sun.COM 	if (ct->ct_suspend == 0) {
1774*11745SMadhavan.Venkataraman@Sun.COM 		(void) cyclic_reprogram(ct->ct_cyclic, hexp);
1775*11745SMadhavan.Venkataraman@Sun.COM 		(void) cyclic_reprogram(ct->ct_qcyclic, qexp);
1776*11745SMadhavan.Venkataraman@Sun.COM 	}
17779039SMadhavan.Venkataraman@Sun.COM 
17788048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
17798048SMadhavan.Venkataraman@Sun.COM }
17808048SMadhavan.Venkataraman@Sun.COM 
17818048SMadhavan.Venkataraman@Sun.COM /*
17828048SMadhavan.Venkataraman@Sun.COM  * This function is called whenever system time (hrestime) is changed
17838048SMadhavan.Venkataraman@Sun.COM  * explicitly. All the HRESTIME callouts must be expired at once.
17848048SMadhavan.Venkataraman@Sun.COM  */
17858048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/
17868048SMadhavan.Venkataraman@Sun.COM void
17878048SMadhavan.Venkataraman@Sun.COM callout_hrestime(void)
17888048SMadhavan.Venkataraman@Sun.COM {
17898048SMadhavan.Venkataraman@Sun.COM 	int t, f;
17908048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
17918048SMadhavan.Venkataraman@Sun.COM 
17928048SMadhavan.Venkataraman@Sun.COM 	/*
17938048SMadhavan.Venkataraman@Sun.COM 	 * Traverse every callout table in the system and process the hrestime
17948048SMadhavan.Venkataraman@Sun.COM 	 * callouts therein.
17958048SMadhavan.Venkataraman@Sun.COM 	 *
17968048SMadhavan.Venkataraman@Sun.COM 	 * We look at all the tables because we don't know which ones were
17978048SMadhavan.Venkataraman@Sun.COM 	 * onlined and offlined in the past. The offlined tables may still
17988048SMadhavan.Venkataraman@Sun.COM 	 * have active cyclics processing timers somewhere.
17998048SMadhavan.Venkataraman@Sun.COM 	 */
18008048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
18018048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
18028048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[CALLOUT_TABLE(t, f)];
18038048SMadhavan.Venkataraman@Sun.COM 			callout_hrestime_one(ct);
18048048SMadhavan.Venkataraman@Sun.COM 		}
18058048SMadhavan.Venkataraman@Sun.COM 	}
18068048SMadhavan.Venkataraman@Sun.COM }
18078048SMadhavan.Venkataraman@Sun.COM 
18088048SMadhavan.Venkataraman@Sun.COM /*
18098048SMadhavan.Venkataraman@Sun.COM  * Create the hash tables for this callout table.
18108048SMadhavan.Venkataraman@Sun.COM  */
18118048SMadhavan.Venkataraman@Sun.COM static void
18128048SMadhavan.Venkataraman@Sun.COM callout_hash_init(callout_table_t *ct)
18138048SMadhavan.Venkataraman@Sun.COM {
18148048SMadhavan.Venkataraman@Sun.COM 	size_t size;
18158048SMadhavan.Venkataraman@Sun.COM 
18168048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
18178048SMadhavan.Venkataraman@Sun.COM 	ASSERT((ct->ct_idhash == NULL) && (ct->ct_clhash == NULL));
18188048SMadhavan.Venkataraman@Sun.COM 
18198048SMadhavan.Venkataraman@Sun.COM 	size = sizeof (callout_hash_t) * CALLOUT_BUCKETS;
18208048SMadhavan.Venkataraman@Sun.COM 	ct->ct_idhash = kmem_zalloc(size, KM_SLEEP);
18218048SMadhavan.Venkataraman@Sun.COM 	ct->ct_clhash = kmem_zalloc(size, KM_SLEEP);
18228048SMadhavan.Venkataraman@Sun.COM }
18238048SMadhavan.Venkataraman@Sun.COM 
18248048SMadhavan.Venkataraman@Sun.COM /*
18258048SMadhavan.Venkataraman@Sun.COM  * Create per-callout table kstats.
18268048SMadhavan.Venkataraman@Sun.COM  */
18278048SMadhavan.Venkataraman@Sun.COM static void
18288048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(callout_table_t *ct)
18298048SMadhavan.Venkataraman@Sun.COM {
18308048SMadhavan.Venkataraman@Sun.COM 	callout_stat_type_t stat;
18318048SMadhavan.Venkataraman@Sun.COM 	kstat_t *ct_kstats;
18328048SMadhavan.Venkataraman@Sun.COM 	int ndx;
18338048SMadhavan.Venkataraman@Sun.COM 
18348048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
18358048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_kstats == NULL);
18368048SMadhavan.Venkataraman@Sun.COM 
18378048SMadhavan.Venkataraman@Sun.COM 	ndx = ct - callout_table;
18388048SMadhavan.Venkataraman@Sun.COM 	ct_kstats = kstat_create("unix", ndx, "callout",
18398048SMadhavan.Venkataraman@Sun.COM 	    "misc", KSTAT_TYPE_NAMED, CALLOUT_NUM_STATS, KSTAT_FLAG_VIRTUAL);
18408048SMadhavan.Venkataraman@Sun.COM 
18418048SMadhavan.Venkataraman@Sun.COM 	if (ct_kstats == NULL) {
18428048SMadhavan.Venkataraman@Sun.COM 		cmn_err(CE_WARN, "kstat_create for callout table %p failed",
18438048SMadhavan.Venkataraman@Sun.COM 		    (void *)ct);
18448048SMadhavan.Venkataraman@Sun.COM 	} else {
18458048SMadhavan.Venkataraman@Sun.COM 		ct_kstats->ks_data = ct->ct_kstat_data;
18468048SMadhavan.Venkataraman@Sun.COM 		for (stat = 0; stat < CALLOUT_NUM_STATS; stat++)
18478048SMadhavan.Venkataraman@Sun.COM 			kstat_named_init(&ct->ct_kstat_data[stat],
18488048SMadhavan.Venkataraman@Sun.COM 			    callout_kstat_names[stat], KSTAT_DATA_INT64);
18498048SMadhavan.Venkataraman@Sun.COM 		ct->ct_kstats = ct_kstats;
18508048SMadhavan.Venkataraman@Sun.COM 		kstat_install(ct_kstats);
18518048SMadhavan.Venkataraman@Sun.COM 	}
18528048SMadhavan.Venkataraman@Sun.COM }
18538048SMadhavan.Venkataraman@Sun.COM 
18548048SMadhavan.Venkataraman@Sun.COM static void
18558048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(callout_table_t *ct)
18568048SMadhavan.Venkataraman@Sun.COM {
18578048SMadhavan.Venkataraman@Sun.COM 	cyc_handler_t hdlr;
18588048SMadhavan.Venkataraman@Sun.COM 	cyc_time_t when;
18598048SMadhavan.Venkataraman@Sun.COM 	processorid_t seqid;
18608048SMadhavan.Venkataraman@Sun.COM 	int t;
1861*11745SMadhavan.Venkataraman@Sun.COM 	cyclic_id_t cyclic, qcyclic;
18628048SMadhavan.Venkataraman@Sun.COM 
18638048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&ct->ct_mutex));
18648048SMadhavan.Venkataraman@Sun.COM 
1865*11745SMadhavan.Venkataraman@Sun.COM 	t = ct->ct_type;
18668048SMadhavan.Venkataraman@Sun.COM 	seqid = CALLOUT_TABLE_SEQID(ct);
18678048SMadhavan.Venkataraman@Sun.COM 
18688048SMadhavan.Venkataraman@Sun.COM 	/*
18698048SMadhavan.Venkataraman@Sun.COM 	 * Create the taskq thread if the table type is normal.
18708048SMadhavan.Venkataraman@Sun.COM 	 * Realtime tables are handled at PIL1 by a softint
18718048SMadhavan.Venkataraman@Sun.COM 	 * handler.
18728048SMadhavan.Venkataraman@Sun.COM 	 */
18738048SMadhavan.Venkataraman@Sun.COM 	if (t == CALLOUT_NORMAL) {
18748048SMadhavan.Venkataraman@Sun.COM 		ASSERT(ct->ct_taskq == NULL);
18758048SMadhavan.Venkataraman@Sun.COM 		/*
18768048SMadhavan.Venkataraman@Sun.COM 		 * Each callout thread consumes exactly one
18778048SMadhavan.Venkataraman@Sun.COM 		 * task structure while active.  Therefore,
18789334SMadhavan.Venkataraman@Sun.COM 		 * prepopulating with 2 * callout_threads tasks
18798048SMadhavan.Venkataraman@Sun.COM 		 * ensures that there's at least one task per
18808048SMadhavan.Venkataraman@Sun.COM 		 * thread that's either scheduled or on the
18818048SMadhavan.Venkataraman@Sun.COM 		 * freelist.  In turn, this guarantees that
18828048SMadhavan.Venkataraman@Sun.COM 		 * taskq_dispatch() will always either succeed
18838048SMadhavan.Venkataraman@Sun.COM 		 * (because there's a free task structure) or
18848048SMadhavan.Venkataraman@Sun.COM 		 * be unnecessary (because "callout_excute(ct)"
18858048SMadhavan.Venkataraman@Sun.COM 		 * has already scheduled).
18868048SMadhavan.Venkataraman@Sun.COM 		 */
18878048SMadhavan.Venkataraman@Sun.COM 		ct->ct_taskq =
18888048SMadhavan.Venkataraman@Sun.COM 		    taskq_create_instance("callout_taskq", seqid,
18899334SMadhavan.Venkataraman@Sun.COM 		    callout_threads, maxclsyspri,
18909334SMadhavan.Venkataraman@Sun.COM 		    2 * callout_threads, 2 * callout_threads,
18918048SMadhavan.Venkataraman@Sun.COM 		    TASKQ_PREPOPULATE | TASKQ_CPR_SAFE);
18928048SMadhavan.Venkataraman@Sun.COM 	}
18938048SMadhavan.Venkataraman@Sun.COM 
18948048SMadhavan.Venkataraman@Sun.COM 	/*
18958048SMadhavan.Venkataraman@Sun.COM 	 * callouts can only be created in a table whose
18968048SMadhavan.Venkataraman@Sun.COM 	 * cyclic has been initialized.
18978048SMadhavan.Venkataraman@Sun.COM 	 */
18988048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_heap_num == 0);
18998048SMadhavan.Venkataraman@Sun.COM 
19008048SMadhavan.Venkataraman@Sun.COM 	/*
190111655SMadhavan.Venkataraman@Sun.COM 	 * Drop the mutex before creating the callout cyclics. cyclic_add()
190211655SMadhavan.Venkataraman@Sun.COM 	 * could potentially expand the cyclic heap. We don't want to be
190311655SMadhavan.Venkataraman@Sun.COM 	 * holding the callout table mutex in that case. Note that this
190411655SMadhavan.Venkataraman@Sun.COM 	 * function is called during CPU online. cpu_lock is held at this
190511655SMadhavan.Venkataraman@Sun.COM 	 * point. So, only one thread can be executing the cyclic add logic
190611655SMadhavan.Venkataraman@Sun.COM 	 * below at any time.
190711655SMadhavan.Venkataraman@Sun.COM 	 */
190811655SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&ct->ct_mutex);
190911655SMadhavan.Venkataraman@Sun.COM 
191011655SMadhavan.Venkataraman@Sun.COM 	/*
19118048SMadhavan.Venkataraman@Sun.COM 	 * Create the callout table cyclics.
19129039SMadhavan.Venkataraman@Sun.COM 	 *
19139039SMadhavan.Venkataraman@Sun.COM 	 * The realtime cyclic handler executes at low PIL. The normal cyclic
19149039SMadhavan.Venkataraman@Sun.COM 	 * handler executes at lock PIL. This is because there are cases
19159039SMadhavan.Venkataraman@Sun.COM 	 * where code can block at PIL > 1 waiting for a normal callout handler
19169039SMadhavan.Venkataraman@Sun.COM 	 * to unblock it directly or indirectly. If the normal cyclic were to
19179039SMadhavan.Venkataraman@Sun.COM 	 * be executed at low PIL, it could get blocked out by the waiter
19189039SMadhavan.Venkataraman@Sun.COM 	 * and cause a deadlock.
19198048SMadhavan.Venkataraman@Sun.COM 	 */
19208048SMadhavan.Venkataraman@Sun.COM 	ASSERT(ct->ct_cyclic == CYCLIC_NONE);
19218048SMadhavan.Venkataraman@Sun.COM 
1922*11745SMadhavan.Venkataraman@Sun.COM 	if (t == CALLOUT_REALTIME) {
19239039SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_level = callout_realtime_level;
1924*11745SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_func = (cyc_func_t)callout_realtime;
1925*11745SMadhavan.Venkataraman@Sun.COM 	} else {
19269039SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_level = callout_normal_level;
1927*11745SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_func = (cyc_func_t)callout_normal;
1928*11745SMadhavan.Venkataraman@Sun.COM 	}
19298048SMadhavan.Venkataraman@Sun.COM 	hdlr.cyh_arg = ct;
19308048SMadhavan.Venkataraman@Sun.COM 	when.cyt_when = CY_INFINITY;
19318048SMadhavan.Venkataraman@Sun.COM 	when.cyt_interval = CY_INFINITY;
19328048SMadhavan.Venkataraman@Sun.COM 
193311655SMadhavan.Venkataraman@Sun.COM 	cyclic = cyclic_add(&hdlr, &when);
193411655SMadhavan.Venkataraman@Sun.COM 
1935*11745SMadhavan.Venkataraman@Sun.COM 	if (t == CALLOUT_REALTIME)
1936*11745SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_func = (cyc_func_t)callout_queue_realtime;
1937*11745SMadhavan.Venkataraman@Sun.COM 	else
1938*11745SMadhavan.Venkataraman@Sun.COM 		hdlr.cyh_func = (cyc_func_t)callout_queue_normal;
1939*11745SMadhavan.Venkataraman@Sun.COM 
1940*11745SMadhavan.Venkataraman@Sun.COM 	qcyclic = cyclic_add(&hdlr, &when);
1941*11745SMadhavan.Venkataraman@Sun.COM 
194211655SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&ct->ct_mutex);
194311655SMadhavan.Venkataraman@Sun.COM 	ct->ct_cyclic = cyclic;
1944*11745SMadhavan.Venkataraman@Sun.COM 	ct->ct_qcyclic = qcyclic;
19458048SMadhavan.Venkataraman@Sun.COM }
19468048SMadhavan.Venkataraman@Sun.COM 
19478048SMadhavan.Venkataraman@Sun.COM void
19488048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cpu_t *cp)
19498048SMadhavan.Venkataraman@Sun.COM {
19508048SMadhavan.Venkataraman@Sun.COM 	lgrp_handle_t hand;
19518048SMadhavan.Venkataraman@Sun.COM 	callout_cache_t *cache;
19528048SMadhavan.Venkataraman@Sun.COM 	char s[KMEM_CACHE_NAMELEN];
19538048SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
19548048SMadhavan.Venkataraman@Sun.COM 	processorid_t seqid;
19558048SMadhavan.Venkataraman@Sun.COM 	int t;
19568048SMadhavan.Venkataraman@Sun.COM 
19578048SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
19588048SMadhavan.Venkataraman@Sun.COM 
19598048SMadhavan.Venkataraman@Sun.COM 	/*
19608048SMadhavan.Venkataraman@Sun.COM 	 * Locate the cache corresponding to the onlined CPU's lgroup.
19618048SMadhavan.Venkataraman@Sun.COM 	 * Note that access to callout_caches is protected by cpu_lock.
19628048SMadhavan.Venkataraman@Sun.COM 	 */
19638048SMadhavan.Venkataraman@Sun.COM 	hand = lgrp_plat_cpu_to_hand(cp->cpu_id);
19648048SMadhavan.Venkataraman@Sun.COM 	for (cache = callout_caches; cache != NULL; cache = cache->cc_next) {
19658048SMadhavan.Venkataraman@Sun.COM 		if (cache->cc_hand == hand)
19668048SMadhavan.Venkataraman@Sun.COM 			break;
19678048SMadhavan.Venkataraman@Sun.COM 	}
19688048SMadhavan.Venkataraman@Sun.COM 
19698048SMadhavan.Venkataraman@Sun.COM 	/*
19708048SMadhavan.Venkataraman@Sun.COM 	 * If not found, create one. The caches are never destroyed.
19718048SMadhavan.Venkataraman@Sun.COM 	 */
19728048SMadhavan.Venkataraman@Sun.COM 	if (cache == NULL) {
19738048SMadhavan.Venkataraman@Sun.COM 		cache = kmem_alloc(sizeof (callout_cache_t), KM_SLEEP);
19748048SMadhavan.Venkataraman@Sun.COM 		cache->cc_hand = hand;
19758048SMadhavan.Venkataraman@Sun.COM 		(void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_cache%lx",
19768048SMadhavan.Venkataraman@Sun.COM 		    (long)hand);
19778048SMadhavan.Venkataraman@Sun.COM 		cache->cc_cache = kmem_cache_create(s, sizeof (callout_t),
19788048SMadhavan.Venkataraman@Sun.COM 		    CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0);
19798048SMadhavan.Venkataraman@Sun.COM 		(void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_lcache%lx",
19808048SMadhavan.Venkataraman@Sun.COM 		    (long)hand);
19818048SMadhavan.Venkataraman@Sun.COM 		cache->cc_lcache = kmem_cache_create(s, sizeof (callout_list_t),
19828048SMadhavan.Venkataraman@Sun.COM 		    CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0);
19838048SMadhavan.Venkataraman@Sun.COM 		cache->cc_next = callout_caches;
19848048SMadhavan.Venkataraman@Sun.COM 		callout_caches = cache;
19858048SMadhavan.Venkataraman@Sun.COM 	}
19868048SMadhavan.Venkataraman@Sun.COM 
19878048SMadhavan.Venkataraman@Sun.COM 	seqid = cp->cpu_seqid;
19888048SMadhavan.Venkataraman@Sun.COM 
19898048SMadhavan.Venkataraman@Sun.COM 	for (t = 0; t < CALLOUT_NTYPES; t++) {
19908048SMadhavan.Venkataraman@Sun.COM 		ct = &callout_table[CALLOUT_TABLE(t, seqid)];
19918048SMadhavan.Venkataraman@Sun.COM 
19928048SMadhavan.Venkataraman@Sun.COM 		mutex_enter(&ct->ct_mutex);
19938048SMadhavan.Venkataraman@Sun.COM 		/*
19948048SMadhavan.Venkataraman@Sun.COM 		 * Store convinience pointers to the kmem caches
19958048SMadhavan.Venkataraman@Sun.COM 		 * in the callout table. These assignments should always be
19968048SMadhavan.Venkataraman@Sun.COM 		 * done as callout tables can map to different physical
19978048SMadhavan.Venkataraman@Sun.COM 		 * CPUs each time.
19988048SMadhavan.Venkataraman@Sun.COM 		 */
19998048SMadhavan.Venkataraman@Sun.COM 		ct->ct_cache = cache->cc_cache;
20008048SMadhavan.Venkataraman@Sun.COM 		ct->ct_lcache = cache->cc_lcache;
20018048SMadhavan.Venkataraman@Sun.COM 
20028048SMadhavan.Venkataraman@Sun.COM 		/*
20038048SMadhavan.Venkataraman@Sun.COM 		 * We use the heap pointer to check if stuff has been
20048048SMadhavan.Venkataraman@Sun.COM 		 * initialized for this callout table.
20058048SMadhavan.Venkataraman@Sun.COM 		 */
20068048SMadhavan.Venkataraman@Sun.COM 		if (ct->ct_heap == NULL) {
20078048SMadhavan.Venkataraman@Sun.COM 			callout_heap_init(ct);
20088048SMadhavan.Venkataraman@Sun.COM 			callout_hash_init(ct);
20098048SMadhavan.Venkataraman@Sun.COM 			callout_kstat_init(ct);
20108048SMadhavan.Venkataraman@Sun.COM 			callout_cyclic_init(ct);
20118048SMadhavan.Venkataraman@Sun.COM 		}
20128048SMadhavan.Venkataraman@Sun.COM 
20138048SMadhavan.Venkataraman@Sun.COM 		mutex_exit(&ct->ct_mutex);
20148048SMadhavan.Venkataraman@Sun.COM 
20158048SMadhavan.Venkataraman@Sun.COM 		/*
2016*11745SMadhavan.Venkataraman@Sun.COM 		 * Move the cyclics to this CPU by doing a bind.
20178048SMadhavan.Venkataraman@Sun.COM 		 */
20188048SMadhavan.Venkataraman@Sun.COM 		cyclic_bind(ct->ct_cyclic, cp, NULL);
2019*11745SMadhavan.Venkataraman@Sun.COM 		cyclic_bind(ct->ct_qcyclic, cp, NULL);
20208566SMadhavan.Venkataraman@Sun.COM 	}
20218566SMadhavan.Venkataraman@Sun.COM }
20228566SMadhavan.Venkataraman@Sun.COM 
20238566SMadhavan.Venkataraman@Sun.COM void
20248566SMadhavan.Venkataraman@Sun.COM callout_cpu_offline(cpu_t *cp)
20258566SMadhavan.Venkataraman@Sun.COM {
20268566SMadhavan.Venkataraman@Sun.COM 	callout_table_t *ct;
20278566SMadhavan.Venkataraman@Sun.COM 	processorid_t seqid;
20288566SMadhavan.Venkataraman@Sun.COM 	int t;
20298566SMadhavan.Venkataraman@Sun.COM 
20308566SMadhavan.Venkataraman@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
20318566SMadhavan.Venkataraman@Sun.COM 
20328566SMadhavan.Venkataraman@Sun.COM 	seqid = cp->cpu_seqid;
20338566SMadhavan.Venkataraman@Sun.COM 
20348566SMadhavan.Venkataraman@Sun.COM 	for (t = 0; t < CALLOUT_NTYPES; t++) {
20358566SMadhavan.Venkataraman@Sun.COM 		ct = &callout_table[CALLOUT_TABLE(t, seqid)];
20368566SMadhavan.Venkataraman@Sun.COM 
20378566SMadhavan.Venkataraman@Sun.COM 		/*
2038*11745SMadhavan.Venkataraman@Sun.COM 		 * Unbind the cyclics. This will allow the cyclic subsystem
2039*11745SMadhavan.Venkataraman@Sun.COM 		 * to juggle the cyclics during CPU offline.
20408566SMadhavan.Venkataraman@Sun.COM 		 */
20418048SMadhavan.Venkataraman@Sun.COM 		cyclic_bind(ct->ct_cyclic, NULL, NULL);
2042*11745SMadhavan.Venkataraman@Sun.COM 		cyclic_bind(ct->ct_qcyclic, NULL, NULL);
20438048SMadhavan.Venkataraman@Sun.COM 	}
20448048SMadhavan.Venkataraman@Sun.COM }
20458048SMadhavan.Venkataraman@Sun.COM 
20468048SMadhavan.Venkataraman@Sun.COM /*
20478048SMadhavan.Venkataraman@Sun.COM  * This is called to perform per-CPU initialization for slave CPUs at
20488048SMadhavan.Venkataraman@Sun.COM  * boot time.
20498048SMadhavan.Venkataraman@Sun.COM  */
20508048SMadhavan.Venkataraman@Sun.COM void
20518048SMadhavan.Venkataraman@Sun.COM callout_mp_init(void)
20528048SMadhavan.Venkataraman@Sun.COM {
20538048SMadhavan.Venkataraman@Sun.COM 	cpu_t *cp;
2054*11745SMadhavan.Venkataraman@Sun.COM 	size_t min, max;
2055*11745SMadhavan.Venkataraman@Sun.COM 
2056*11745SMadhavan.Venkataraman@Sun.COM 	if (callout_chunk == CALLOUT_CHUNK) {
2057*11745SMadhavan.Venkataraman@Sun.COM 		/*
2058*11745SMadhavan.Venkataraman@Sun.COM 		 * No one has specified a chunk in /etc/system. We need to
2059*11745SMadhavan.Venkataraman@Sun.COM 		 * compute it here based on the number of online CPUs and
2060*11745SMadhavan.Venkataraman@Sun.COM 		 * available physical memory.
2061*11745SMadhavan.Venkataraman@Sun.COM 		 */
2062*11745SMadhavan.Venkataraman@Sun.COM 		min = CALLOUT_MIN_HEAP_SIZE;
2063*11745SMadhavan.Venkataraman@Sun.COM 		max = ptob(physmem) / CALLOUT_MEM_FRACTION;
2064*11745SMadhavan.Venkataraman@Sun.COM 		if (min > max)
2065*11745SMadhavan.Venkataraman@Sun.COM 			min = max;
2066*11745SMadhavan.Venkataraman@Sun.COM 		callout_chunk = min / sizeof (callout_heap_t);
2067*11745SMadhavan.Venkataraman@Sun.COM 		callout_chunk /= ncpus_online;
2068*11745SMadhavan.Venkataraman@Sun.COM 		callout_chunk = P2ROUNDUP(callout_chunk, CALLOUT_CHUNK);
2069*11745SMadhavan.Venkataraman@Sun.COM 	}
20708048SMadhavan.Venkataraman@Sun.COM 
20718048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&cpu_lock);
20728048SMadhavan.Venkataraman@Sun.COM 
20738048SMadhavan.Venkataraman@Sun.COM 	cp = cpu_active;
20748048SMadhavan.Venkataraman@Sun.COM 	do {
20758048SMadhavan.Venkataraman@Sun.COM 		callout_cpu_online(cp);
20768048SMadhavan.Venkataraman@Sun.COM 	} while ((cp = cp->cpu_next_onln) != cpu_active);
20778048SMadhavan.Venkataraman@Sun.COM 
20788048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&cpu_lock);
20798048SMadhavan.Venkataraman@Sun.COM }
20808048SMadhavan.Venkataraman@Sun.COM 
20818048SMadhavan.Venkataraman@Sun.COM /*
20820Sstevel@tonic-gate  * Initialize all callout tables.  Called at boot time just before clkstart().
20830Sstevel@tonic-gate  */
20840Sstevel@tonic-gate void
20850Sstevel@tonic-gate callout_init(void)
20860Sstevel@tonic-gate {
20870Sstevel@tonic-gate 	int f, t;
20888048SMadhavan.Venkataraman@Sun.COM 	size_t size;
20890Sstevel@tonic-gate 	int table_id;
20900Sstevel@tonic-gate 	callout_table_t *ct;
20918048SMadhavan.Venkataraman@Sun.COM 	long bits, fanout;
20928048SMadhavan.Venkataraman@Sun.COM 	uintptr_t buf;
20930Sstevel@tonic-gate 
20948048SMadhavan.Venkataraman@Sun.COM 	/*
20958048SMadhavan.Venkataraman@Sun.COM 	 * Initialize callout globals.
20968048SMadhavan.Venkataraman@Sun.COM 	 */
20978048SMadhavan.Venkataraman@Sun.COM 	bits = 0;
20988048SMadhavan.Venkataraman@Sun.COM 	for (fanout = 1; (fanout < max_ncpus); fanout <<= 1)
20998048SMadhavan.Venkataraman@Sun.COM 		bits++;
21008048SMadhavan.Venkataraman@Sun.COM 	callout_table_bits = CALLOUT_TYPE_BITS + bits;
21018048SMadhavan.Venkataraman@Sun.COM 	callout_table_mask = (1 << callout_table_bits) - 1;
21028048SMadhavan.Venkataraman@Sun.COM 	callout_counter_low = 1 << CALLOUT_COUNTER_SHIFT;
21038048SMadhavan.Venkataraman@Sun.COM 	callout_longterm = TICK_TO_NSEC(CALLOUT_LONGTERM_TICKS);
21048566SMadhavan.Venkataraman@Sun.COM 	callout_max_ticks = CALLOUT_MAX_TICKS;
21059334SMadhavan.Venkataraman@Sun.COM 	if (callout_min_reap == 0)
21069334SMadhavan.Venkataraman@Sun.COM 		callout_min_reap = CALLOUT_MIN_REAP;
21070Sstevel@tonic-gate 
21089334SMadhavan.Venkataraman@Sun.COM 	if (callout_tolerance <= 0)
21099334SMadhavan.Venkataraman@Sun.COM 		callout_tolerance = CALLOUT_TOLERANCE;
21109334SMadhavan.Venkataraman@Sun.COM 	if (callout_threads <= 0)
21119334SMadhavan.Venkataraman@Sun.COM 		callout_threads = CALLOUT_THREADS;
2112*11745SMadhavan.Venkataraman@Sun.COM 	if (callout_chunk <= 0)
2113*11745SMadhavan.Venkataraman@Sun.COM 		callout_chunk = CALLOUT_CHUNK;
2114*11745SMadhavan.Venkataraman@Sun.COM 	else
2115*11745SMadhavan.Venkataraman@Sun.COM 		callout_chunk = P2ROUNDUP(callout_chunk, CALLOUT_CHUNK);
21168048SMadhavan.Venkataraman@Sun.COM 
21178048SMadhavan.Venkataraman@Sun.COM 	/*
21188048SMadhavan.Venkataraman@Sun.COM 	 * Allocate all the callout tables based on max_ncpus. We have chosen
21198048SMadhavan.Venkataraman@Sun.COM 	 * to do boot-time allocation instead of dynamic allocation because:
21208048SMadhavan.Venkataraman@Sun.COM 	 *
21218048SMadhavan.Venkataraman@Sun.COM 	 *	- the size of the callout tables is not too large.
21228048SMadhavan.Venkataraman@Sun.COM 	 *	- there are race conditions involved in making this dynamic.
21238048SMadhavan.Venkataraman@Sun.COM 	 *	- the hash tables that go with the callout tables consume
21248048SMadhavan.Venkataraman@Sun.COM 	 *	  most of the memory and they are only allocated in
21258048SMadhavan.Venkataraman@Sun.COM 	 *	  callout_cpu_online().
21268048SMadhavan.Venkataraman@Sun.COM 	 *
21278048SMadhavan.Venkataraman@Sun.COM 	 * Each CPU has two tables that are consecutive in the array. The first
21288048SMadhavan.Venkataraman@Sun.COM 	 * one is for realtime callouts and the second one is for normal ones.
21298048SMadhavan.Venkataraman@Sun.COM 	 *
21308048SMadhavan.Venkataraman@Sun.COM 	 * We do this alignment dance to make sure that callout table
21318048SMadhavan.Venkataraman@Sun.COM 	 * structures will always be on a cache line boundary.
21328048SMadhavan.Venkataraman@Sun.COM 	 */
21338048SMadhavan.Venkataraman@Sun.COM 	size = sizeof (callout_table_t) * CALLOUT_NTYPES * max_ncpus;
21348048SMadhavan.Venkataraman@Sun.COM 	size += CALLOUT_ALIGN;
21358048SMadhavan.Venkataraman@Sun.COM 	buf = (uintptr_t)kmem_zalloc(size, KM_SLEEP);
21368048SMadhavan.Venkataraman@Sun.COM 	callout_table = (callout_table_t *)P2ROUNDUP(buf, CALLOUT_ALIGN);
21378048SMadhavan.Venkataraman@Sun.COM 
21388048SMadhavan.Venkataraman@Sun.COM 	size = sizeof (kstat_named_t) * CALLOUT_NUM_STATS;
21398048SMadhavan.Venkataraman@Sun.COM 	/*
21408048SMadhavan.Venkataraman@Sun.COM 	 * Now, initialize the tables for all the CPUs.
21418048SMadhavan.Venkataraman@Sun.COM 	 */
21428048SMadhavan.Venkataraman@Sun.COM 	for (f = 0; f < max_ncpus; f++) {
21438048SMadhavan.Venkataraman@Sun.COM 		for (t = 0; t < CALLOUT_NTYPES; t++) {
21440Sstevel@tonic-gate 			table_id = CALLOUT_TABLE(t, f);
21458048SMadhavan.Venkataraman@Sun.COM 			ct = &callout_table[table_id];
21468566SMadhavan.Venkataraman@Sun.COM 			ct->ct_type = t;
21478048SMadhavan.Venkataraman@Sun.COM 			mutex_init(&ct->ct_mutex, NULL, MUTEX_DEFAULT, NULL);
21486422Sqiao 			/*
21498048SMadhavan.Venkataraman@Sun.COM 			 * Precompute the base IDs for long and short-term
21508048SMadhavan.Venkataraman@Sun.COM 			 * legacy IDs. This makes ID generation during
21518048SMadhavan.Venkataraman@Sun.COM 			 * timeout() fast.
21526422Sqiao 			 */
21538048SMadhavan.Venkataraman@Sun.COM 			ct->ct_short_id = CALLOUT_SHORT_ID(table_id);
21548048SMadhavan.Venkataraman@Sun.COM 			ct->ct_long_id = CALLOUT_LONG_ID(table_id);
21558048SMadhavan.Venkataraman@Sun.COM 			/*
21568048SMadhavan.Venkataraman@Sun.COM 			 * Precompute the base ID for generation-based IDs.
21578048SMadhavan.Venkataraman@Sun.COM 			 * Note that when the first ID gets allocated, the
21588048SMadhavan.Venkataraman@Sun.COM 			 * ID will wrap. This will cause the generation
21598048SMadhavan.Venkataraman@Sun.COM 			 * number to be incremented to 1.
21608048SMadhavan.Venkataraman@Sun.COM 			 */
21618048SMadhavan.Venkataraman@Sun.COM 			ct->ct_gen_id = CALLOUT_SHORT_ID(table_id);
21628048SMadhavan.Venkataraman@Sun.COM 			/*
2163*11745SMadhavan.Venkataraman@Sun.COM 			 * Initialize the cyclics as NONE. This will get set
21648048SMadhavan.Venkataraman@Sun.COM 			 * during CPU online. This is so that partially
21658048SMadhavan.Venkataraman@Sun.COM 			 * populated systems will only have the required
21668048SMadhavan.Venkataraman@Sun.COM 			 * number of cyclics, not more.
21678048SMadhavan.Venkataraman@Sun.COM 			 */
21688048SMadhavan.Venkataraman@Sun.COM 			ct->ct_cyclic = CYCLIC_NONE;
2169*11745SMadhavan.Venkataraman@Sun.COM 			ct->ct_qcyclic = CYCLIC_NONE;
21708048SMadhavan.Venkataraman@Sun.COM 			ct->ct_kstat_data = kmem_zalloc(size, KM_SLEEP);
21710Sstevel@tonic-gate 		}
21720Sstevel@tonic-gate 	}
21738048SMadhavan.Venkataraman@Sun.COM 
21748048SMadhavan.Venkataraman@Sun.COM 	/*
21758048SMadhavan.Venkataraman@Sun.COM 	 * Add the callback for CPR. This is called during checkpoint
21768048SMadhavan.Venkataraman@Sun.COM 	 * resume to suspend and resume callouts.
21778048SMadhavan.Venkataraman@Sun.COM 	 */
21788048SMadhavan.Venkataraman@Sun.COM 	(void) callb_add(callout_cpr_callb, 0, CB_CL_CPR_CALLOUT,
21798048SMadhavan.Venkataraman@Sun.COM 	    "callout_cpr");
21808048SMadhavan.Venkataraman@Sun.COM 	(void) callb_add(callout_debug_callb, 0, CB_CL_ENTER_DEBUGGER,
21818048SMadhavan.Venkataraman@Sun.COM 	    "callout_debug");
21828048SMadhavan.Venkataraman@Sun.COM 
21838048SMadhavan.Venkataraman@Sun.COM 	/*
21848048SMadhavan.Venkataraman@Sun.COM 	 * Call the per-CPU initialization function for the boot CPU. This
21858048SMadhavan.Venkataraman@Sun.COM 	 * is done here because the function is not called automatically for
21868048SMadhavan.Venkataraman@Sun.COM 	 * the boot CPU from the CPU online/offline hooks. Note that the
21878048SMadhavan.Venkataraman@Sun.COM 	 * CPU lock is taken here because of convention.
21888048SMadhavan.Venkataraman@Sun.COM 	 */
21898048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&cpu_lock);
21908048SMadhavan.Venkataraman@Sun.COM 	callout_boot_ct = &callout_table[CALLOUT_TABLE(0, CPU->cpu_seqid)];
21918048SMadhavan.Venkataraman@Sun.COM 	callout_cpu_online(CPU);
21928048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&cpu_lock);
21930Sstevel@tonic-gate }
2194