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 /*
22*12681SPeter.Telford@Sun.COM * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate */
240Sstevel@tonic-gate
250Sstevel@tonic-gate #include <sys/callo.h>
260Sstevel@tonic-gate #include <sys/param.h>
270Sstevel@tonic-gate #include <sys/types.h>
280Sstevel@tonic-gate #include <sys/cpuvar.h>
290Sstevel@tonic-gate #include <sys/thread.h>
300Sstevel@tonic-gate #include <sys/kmem.h>
318048SMadhavan.Venkataraman@Sun.COM #include <sys/kmem_impl.h>
320Sstevel@tonic-gate #include <sys/cmn_err.h>
330Sstevel@tonic-gate #include <sys/callb.h>
340Sstevel@tonic-gate #include <sys/debug.h>
350Sstevel@tonic-gate #include <sys/vtrace.h>
360Sstevel@tonic-gate #include <sys/sysmacros.h>
370Sstevel@tonic-gate #include <sys/sdt.h>
380Sstevel@tonic-gate
39*12681SPeter.Telford@Sun.COM int callout_init_done; /* useful during boot */
40*12681SPeter.Telford@Sun.COM
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate * Callout tables. See timeout(9F) for details.
430Sstevel@tonic-gate */
449334SMadhavan.Venkataraman@Sun.COM static int callout_threads; /* callout normal threads */
458048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_debug_hrtime; /* debugger entry time */
4611745SMadhavan.Venkataraman@Sun.COM static int callout_chunk; /* callout heap chunk size */
479334SMadhavan.Venkataraman@Sun.COM static int callout_min_reap; /* callout minimum reap count */
489334SMadhavan.Venkataraman@Sun.COM static int callout_tolerance; /* callout hires tolerance */
498048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_boot_ct; /* Boot CPU's callout tables */
508566SMadhavan.Venkataraman@Sun.COM static clock_t callout_max_ticks; /* max interval */
518048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_longterm; /* longterm nanoseconds */
528048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_counter_low; /* callout ID increment */
538048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_bits; /* number of table bits in ID */
548048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_mask; /* mask for the table bits */
558048SMadhavan.Venkataraman@Sun.COM static callout_cache_t *callout_caches; /* linked list of caches */
568048SMadhavan.Venkataraman@Sun.COM #pragma align 64(callout_table)
578048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_table; /* global callout table array */
580Sstevel@tonic-gate
599039SMadhavan.Venkataraman@Sun.COM /*
6010696SDavid.Hollister@Sun.COM * We run 'realtime' callouts at PIL 1 (CY_LOW_LEVEL). For 'normal'
6110696SDavid.Hollister@Sun.COM * callouts, from PIL 10 (CY_LOCK_LEVEL) we dispatch the callout,
6210696SDavid.Hollister@Sun.COM * via taskq, to a thread that executes at PIL 0 - so we end up running
6310696SDavid.Hollister@Sun.COM * 'normal' callouts at PIL 0.
649039SMadhavan.Venkataraman@Sun.COM */
659334SMadhavan.Venkataraman@Sun.COM static volatile int callout_realtime_level = CY_LOW_LEVEL;
669334SMadhavan.Venkataraman@Sun.COM static volatile int callout_normal_level = CY_LOCK_LEVEL;
679039SMadhavan.Venkataraman@Sun.COM
688048SMadhavan.Venkataraman@Sun.COM static char *callout_kstat_names[] = {
698048SMadhavan.Venkataraman@Sun.COM "callout_timeouts",
708048SMadhavan.Venkataraman@Sun.COM "callout_timeouts_pending",
718048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_unexpired",
728048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_executing",
738048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_expired",
748048SMadhavan.Venkataraman@Sun.COM "callout_expirations",
758048SMadhavan.Venkataraman@Sun.COM "callout_allocations",
769334SMadhavan.Venkataraman@Sun.COM "callout_cleanups",
778048SMadhavan.Venkataraman@Sun.COM };
788048SMadhavan.Venkataraman@Sun.COM
799334SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_heap_process(callout_table_t *, hrtime_t, int);
809334SMadhavan.Venkataraman@Sun.COM
818048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_INSERT(hash, cp, cnext, cprev) \
820Sstevel@tonic-gate { \
838048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \
848048SMadhavan.Venkataraman@Sun.COM \
850Sstevel@tonic-gate cp->cprev = NULL; \
868048SMadhavan.Venkataraman@Sun.COM cp->cnext = hashp->ch_head; \
878048SMadhavan.Venkataraman@Sun.COM if (hashp->ch_head == NULL) \
888048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp; \
898048SMadhavan.Venkataraman@Sun.COM else \
908048SMadhavan.Venkataraman@Sun.COM cp->cnext->cprev = cp; \
918048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp; \
928048SMadhavan.Venkataraman@Sun.COM }
938048SMadhavan.Venkataraman@Sun.COM
948048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_APPEND(hash, cp, cnext, cprev) \
958048SMadhavan.Venkataraman@Sun.COM { \
968048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \
978048SMadhavan.Venkataraman@Sun.COM \
988048SMadhavan.Venkataraman@Sun.COM cp->cnext = NULL; \
998048SMadhavan.Venkataraman@Sun.COM cp->cprev = hashp->ch_tail; \
1008048SMadhavan.Venkataraman@Sun.COM if (hashp->ch_tail == NULL) \
1018048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp; \
1028048SMadhavan.Venkataraman@Sun.COM else \
1038048SMadhavan.Venkataraman@Sun.COM cp->cprev->cnext = cp; \
1048048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp; \
1050Sstevel@tonic-gate }
1060Sstevel@tonic-gate
1078048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_DELETE(hash, cp, cnext, cprev) \
1080Sstevel@tonic-gate { \
1098048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \
1108048SMadhavan.Venkataraman@Sun.COM \
1118048SMadhavan.Venkataraman@Sun.COM if (cp->cnext == NULL) \
1128048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp->cprev; \
1130Sstevel@tonic-gate else \
1148048SMadhavan.Venkataraman@Sun.COM cp->cnext->cprev = cp->cprev; \
1158048SMadhavan.Venkataraman@Sun.COM if (cp->cprev == NULL) \
1168048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp->cnext; \
1178048SMadhavan.Venkataraman@Sun.COM else \
1188048SMadhavan.Venkataraman@Sun.COM cp->cprev->cnext = cp->cnext; \
1190Sstevel@tonic-gate }
1200Sstevel@tonic-gate
1218048SMadhavan.Venkataraman@Sun.COM /*
1228048SMadhavan.Venkataraman@Sun.COM * These definitions help us queue callouts and callout lists. Here is
1238048SMadhavan.Venkataraman@Sun.COM * the queueing rationale:
1248048SMadhavan.Venkataraman@Sun.COM *
1258048SMadhavan.Venkataraman@Sun.COM * - callouts are queued in a FIFO manner in the ID hash table.
1268048SMadhavan.Venkataraman@Sun.COM * TCP timers are typically cancelled in the same order that they
1278048SMadhavan.Venkataraman@Sun.COM * were issued. The FIFO queueing shortens the search for a callout
1288048SMadhavan.Venkataraman@Sun.COM * during untimeout().
1298048SMadhavan.Venkataraman@Sun.COM *
1308048SMadhavan.Venkataraman@Sun.COM * - callouts are queued in a FIFO manner in their callout lists.
1318048SMadhavan.Venkataraman@Sun.COM * This ensures that the callouts are executed in the same order that
1328048SMadhavan.Venkataraman@Sun.COM * they were queued. This is fair. Plus, it helps to make each
1338048SMadhavan.Venkataraman@Sun.COM * callout expiration timely. It also favors cancellations.
1348048SMadhavan.Venkataraman@Sun.COM *
1359334SMadhavan.Venkataraman@Sun.COM * - callout lists are queued in the following manner in the callout
1369334SMadhavan.Venkataraman@Sun.COM * hash table buckets:
1379334SMadhavan.Venkataraman@Sun.COM *
1389334SMadhavan.Venkataraman@Sun.COM * - appended, if the callout list is a 1-nanosecond resolution
1399334SMadhavan.Venkataraman@Sun.COM * callout list. When a callout is created, we first look for
1409334SMadhavan.Venkataraman@Sun.COM * a callout list that has the same expiration so we can avoid
1419334SMadhavan.Venkataraman@Sun.COM * allocating a callout list and inserting the expiration into
1429334SMadhavan.Venkataraman@Sun.COM * the heap. However, we do not want to look at 1-nanosecond
1439334SMadhavan.Venkataraman@Sun.COM * resolution callout lists as we will seldom find a match in
1449334SMadhavan.Venkataraman@Sun.COM * them. Keeping these callout lists in the rear of the hash
1459334SMadhavan.Venkataraman@Sun.COM * buckets allows us to skip these during the lookup.
1469334SMadhavan.Venkataraman@Sun.COM *
1479334SMadhavan.Venkataraman@Sun.COM * - inserted at the beginning, if the callout list is not a
1489334SMadhavan.Venkataraman@Sun.COM * 1-nanosecond resolution callout list. This also has the
1499334SMadhavan.Venkataraman@Sun.COM * side-effect of keeping the long term timers away from the
1509334SMadhavan.Venkataraman@Sun.COM * front of the buckets.
1518048SMadhavan.Venkataraman@Sun.COM *
1528048SMadhavan.Venkataraman@Sun.COM * - callout lists are queued in a FIFO manner in the expired callouts
1538048SMadhavan.Venkataraman@Sun.COM * list. This ensures that callout lists are executed in the order
1548048SMadhavan.Venkataraman@Sun.COM * of expiration.
1558048SMadhavan.Venkataraman@Sun.COM */
1568048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_APPEND(ct, cp) \
1578048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)], \
1588048SMadhavan.Venkataraman@Sun.COM cp, c_idnext, c_idprev); \
1598048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(cp->c_list->cl_callouts, cp, c_clnext, c_clprev)
1608048SMadhavan.Venkataraman@Sun.COM
1618048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_DELETE(ct, cp) \
1628048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)], \
1638048SMadhavan.Venkataraman@Sun.COM cp, c_idnext, c_idprev); \
1648048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(cp->c_list->cl_callouts, cp, c_clnext, c_clprev)
1658048SMadhavan.Venkataraman@Sun.COM
1668048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_INSERT(hash, cl) \
1678048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_INSERT(hash, cl, cl_next, cl_prev)
1688048SMadhavan.Venkataraman@Sun.COM
1698048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_APPEND(hash, cl) \
1708048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(hash, cl, cl_next, cl_prev)
1718048SMadhavan.Venkataraman@Sun.COM
1728048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_DELETE(hash, cl) \
1738048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(hash, cl, cl_next, cl_prev)
1740Sstevel@tonic-gate
17511745SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_BEFORE(cl, nextcl) \
17611745SMadhavan.Venkataraman@Sun.COM { \
17711745SMadhavan.Venkataraman@Sun.COM (cl)->cl_prev = (nextcl)->cl_prev; \
17811745SMadhavan.Venkataraman@Sun.COM (cl)->cl_next = (nextcl); \
17911745SMadhavan.Venkataraman@Sun.COM (nextcl)->cl_prev = (cl); \
18011745SMadhavan.Venkataraman@Sun.COM if (cl->cl_prev != NULL) \
18111745SMadhavan.Venkataraman@Sun.COM cl->cl_prev->cl_next = cl; \
18211745SMadhavan.Venkataraman@Sun.COM }
18311745SMadhavan.Venkataraman@Sun.COM
1840Sstevel@tonic-gate /*
1859039SMadhavan.Venkataraman@Sun.COM * For normal callouts, there is a deadlock scenario if two callouts that
1869039SMadhavan.Venkataraman@Sun.COM * have an inter-dependency end up on the same callout list. To break the
1879039SMadhavan.Venkataraman@Sun.COM * deadlock, you need two taskq threads running in parallel. We compute
1889039SMadhavan.Venkataraman@Sun.COM * the number of taskq threads here using a bunch of conditions to make
1899039SMadhavan.Venkataraman@Sun.COM * it optimal for the common case. This is an ugly hack, but one that is
1909039SMadhavan.Venkataraman@Sun.COM * necessary (sigh).
1919039SMadhavan.Venkataraman@Sun.COM */
1929039SMadhavan.Venkataraman@Sun.COM #define CALLOUT_THRESHOLD 100000000
19311745SMadhavan.Venkataraman@Sun.COM #define CALLOUT_EXEC_COMPUTE(ct, nextexp, exec) \
1949039SMadhavan.Venkataraman@Sun.COM { \
1959039SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; \
1969039SMadhavan.Venkataraman@Sun.COM \
1979039SMadhavan.Venkataraman@Sun.COM cl = ct->ct_expired.ch_head; \
1989039SMadhavan.Venkataraman@Sun.COM if (cl == NULL) { \
1999039SMadhavan.Venkataraman@Sun.COM /* \
2009039SMadhavan.Venkataraman@Sun.COM * If the expired list is NULL, there is nothing to \
2019039SMadhavan.Venkataraman@Sun.COM * process. \
2029039SMadhavan.Venkataraman@Sun.COM */ \
2039039SMadhavan.Venkataraman@Sun.COM exec = 0; \
2049039SMadhavan.Venkataraman@Sun.COM } else if ((cl->cl_next == NULL) && \
2059039SMadhavan.Venkataraman@Sun.COM (cl->cl_callouts.ch_head == cl->cl_callouts.ch_tail)) { \
2069039SMadhavan.Venkataraman@Sun.COM /* \
2079039SMadhavan.Venkataraman@Sun.COM * If there is only one callout list and it contains \
2089039SMadhavan.Venkataraman@Sun.COM * only one callout, there is no need for two threads. \
2099039SMadhavan.Venkataraman@Sun.COM */ \
2109039SMadhavan.Venkataraman@Sun.COM exec = 1; \
21111745SMadhavan.Venkataraman@Sun.COM } else if ((nextexp) > (gethrtime() + CALLOUT_THRESHOLD)) { \
2129039SMadhavan.Venkataraman@Sun.COM /* \
21311745SMadhavan.Venkataraman@Sun.COM * If the next expiration of the cyclic is way out into \
21411745SMadhavan.Venkataraman@Sun.COM * the future, we need two threads. \
2159039SMadhavan.Venkataraman@Sun.COM */ \
2169039SMadhavan.Venkataraman@Sun.COM exec = 2; \
2179039SMadhavan.Venkataraman@Sun.COM } else { \
2189039SMadhavan.Venkataraman@Sun.COM /* \
2199039SMadhavan.Venkataraman@Sun.COM * We have multiple callouts to process. But the cyclic \
2209039SMadhavan.Venkataraman@Sun.COM * will fire in the near future. So, we only need one \
2219039SMadhavan.Venkataraman@Sun.COM * thread for now. \
2229039SMadhavan.Venkataraman@Sun.COM */ \
2239039SMadhavan.Venkataraman@Sun.COM exec = 1; \
2249039SMadhavan.Venkataraman@Sun.COM } \
2259039SMadhavan.Venkataraman@Sun.COM }
2269039SMadhavan.Venkataraman@Sun.COM
2279039SMadhavan.Venkataraman@Sun.COM /*
2289334SMadhavan.Venkataraman@Sun.COM * Macro to swap two heap items.
2299334SMadhavan.Venkataraman@Sun.COM */
2309334SMadhavan.Venkataraman@Sun.COM #define CALLOUT_SWAP(h1, h2) \
2319334SMadhavan.Venkataraman@Sun.COM { \
2329334SMadhavan.Venkataraman@Sun.COM callout_heap_t tmp; \
2339334SMadhavan.Venkataraman@Sun.COM \
2349334SMadhavan.Venkataraman@Sun.COM tmp = *h1; \
2359334SMadhavan.Venkataraman@Sun.COM *h1 = *h2; \
2369334SMadhavan.Venkataraman@Sun.COM *h2 = tmp; \
2379334SMadhavan.Venkataraman@Sun.COM }
2389334SMadhavan.Venkataraman@Sun.COM
2399334SMadhavan.Venkataraman@Sun.COM /*
2409334SMadhavan.Venkataraman@Sun.COM * Macro to free a callout list.
2419334SMadhavan.Venkataraman@Sun.COM */
2429334SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_FREE(ct, cl) \
2439334SMadhavan.Venkataraman@Sun.COM { \
2449334SMadhavan.Venkataraman@Sun.COM cl->cl_next = ct->ct_lfree; \
2459334SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl; \
2469334SMadhavan.Venkataraman@Sun.COM cl->cl_flags |= CALLOUT_LIST_FLAG_FREE; \
2479334SMadhavan.Venkataraman@Sun.COM }
2489334SMadhavan.Venkataraman@Sun.COM
2499334SMadhavan.Venkataraman@Sun.COM /*
25011745SMadhavan.Venkataraman@Sun.COM * Macro to free a callout.
25111745SMadhavan.Venkataraman@Sun.COM */
25211745SMadhavan.Venkataraman@Sun.COM #define CALLOUT_FREE(ct, cl) \
25311745SMadhavan.Venkataraman@Sun.COM { \
25411745SMadhavan.Venkataraman@Sun.COM cp->c_idnext = ct->ct_free; \
25511745SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp; \
25611745SMadhavan.Venkataraman@Sun.COM cp->c_xid |= CALLOUT_ID_FREE; \
25711745SMadhavan.Venkataraman@Sun.COM }
25811745SMadhavan.Venkataraman@Sun.COM
25911745SMadhavan.Venkataraman@Sun.COM /*
2600Sstevel@tonic-gate * Allocate a callout structure. We try quite hard because we
2610Sstevel@tonic-gate * can't sleep, and if we can't do the allocation, we're toast.
2628048SMadhavan.Venkataraman@Sun.COM * Failing all, we try a KM_PANIC allocation. Note that we never
2638048SMadhavan.Venkataraman@Sun.COM * deallocate a callout. See untimeout() for the reasoning.
2640Sstevel@tonic-gate */
2650Sstevel@tonic-gate static callout_t *
callout_alloc(callout_table_t * ct)2660Sstevel@tonic-gate callout_alloc(callout_table_t *ct)
2670Sstevel@tonic-gate {
2688048SMadhavan.Venkataraman@Sun.COM size_t size;
2698048SMadhavan.Venkataraman@Sun.COM callout_t *cp;
2708048SMadhavan.Venkataraman@Sun.COM
2718048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
2728048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
2730Sstevel@tonic-gate
2748048SMadhavan.Venkataraman@Sun.COM cp = kmem_cache_alloc(ct->ct_cache, KM_NOSLEEP);
2758048SMadhavan.Venkataraman@Sun.COM if (cp == NULL) {
2768048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_t);
2778048SMadhavan.Venkataraman@Sun.COM cp = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC);
2788048SMadhavan.Venkataraman@Sun.COM }
2798048SMadhavan.Venkataraman@Sun.COM cp->c_xid = 0;
2809039SMadhavan.Venkataraman@Sun.COM cp->c_executor = NULL;
2819039SMadhavan.Venkataraman@Sun.COM cv_init(&cp->c_done, NULL, CV_DEFAULT, NULL);
2829039SMadhavan.Venkataraman@Sun.COM cp->c_waiting = 0;
2838048SMadhavan.Venkataraman@Sun.COM
2848048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
2858048SMadhavan.Venkataraman@Sun.COM ct->ct_allocations++;
2860Sstevel@tonic-gate return (cp);
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate
2890Sstevel@tonic-gate /*
2908048SMadhavan.Venkataraman@Sun.COM * Allocate a callout list structure. We try quite hard because we
2918048SMadhavan.Venkataraman@Sun.COM * can't sleep, and if we can't do the allocation, we're toast.
2928048SMadhavan.Venkataraman@Sun.COM * Failing all, we try a KM_PANIC allocation. Note that we never
2938048SMadhavan.Venkataraman@Sun.COM * deallocate a callout list.
2948048SMadhavan.Venkataraman@Sun.COM */
2958048SMadhavan.Venkataraman@Sun.COM static void
callout_list_alloc(callout_table_t * ct)2968048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(callout_table_t *ct)
2978048SMadhavan.Venkataraman@Sun.COM {
2988048SMadhavan.Venkataraman@Sun.COM size_t size;
2998048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl;
3008048SMadhavan.Venkataraman@Sun.COM
3018048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
3028048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
3038048SMadhavan.Venkataraman@Sun.COM
3048048SMadhavan.Venkataraman@Sun.COM cl = kmem_cache_alloc(ct->ct_lcache, KM_NOSLEEP);
3058048SMadhavan.Venkataraman@Sun.COM if (cl == NULL) {
3068048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_list_t);
3078048SMadhavan.Venkataraman@Sun.COM cl = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC);
3088048SMadhavan.Venkataraman@Sun.COM }
3098048SMadhavan.Venkataraman@Sun.COM bzero(cl, sizeof (callout_list_t));
3108048SMadhavan.Venkataraman@Sun.COM
3118048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
3129334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl);
3138048SMadhavan.Venkataraman@Sun.COM }
3148048SMadhavan.Venkataraman@Sun.COM
3158048SMadhavan.Venkataraman@Sun.COM /*
3169334SMadhavan.Venkataraman@Sun.COM * Find a callout list that corresponds to an expiration and matching flags.
3178048SMadhavan.Venkataraman@Sun.COM */
3188048SMadhavan.Venkataraman@Sun.COM static callout_list_t *
callout_list_get(callout_table_t * ct,hrtime_t expiration,int flags,int hash)3199039SMadhavan.Venkataraman@Sun.COM callout_list_get(callout_table_t *ct, hrtime_t expiration, int flags, int hash)
3208048SMadhavan.Venkataraman@Sun.COM {
3218048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl;
3229334SMadhavan.Venkataraman@Sun.COM int clflags;
3238048SMadhavan.Venkataraman@Sun.COM
3248048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
3258048SMadhavan.Venkataraman@Sun.COM
3269334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_LIST_FLAG_NANO) {
3279334SMadhavan.Venkataraman@Sun.COM /*
3289334SMadhavan.Venkataraman@Sun.COM * This is a 1-nanosecond resolution callout. We will rarely
3299334SMadhavan.Venkataraman@Sun.COM * find a match for this. So, bail out.
3309334SMadhavan.Venkataraman@Sun.COM */
3319334SMadhavan.Venkataraman@Sun.COM return (NULL);
3329334SMadhavan.Venkataraman@Sun.COM }
3339334SMadhavan.Venkataraman@Sun.COM
3349334SMadhavan.Venkataraman@Sun.COM clflags = (CALLOUT_LIST_FLAG_ABSOLUTE | CALLOUT_LIST_FLAG_HRESTIME);
3358048SMadhavan.Venkataraman@Sun.COM for (cl = ct->ct_clhash[hash].ch_head; (cl != NULL); cl = cl->cl_next) {
3369334SMadhavan.Venkataraman@Sun.COM /*
3379334SMadhavan.Venkataraman@Sun.COM * If we have reached a 1-nanosecond resolution callout list,
3389334SMadhavan.Venkataraman@Sun.COM * we don't have much hope of finding a match in this hash
3399334SMadhavan.Venkataraman@Sun.COM * bucket. So, just bail out.
3409334SMadhavan.Venkataraman@Sun.COM */
3419334SMadhavan.Venkataraman@Sun.COM if (cl->cl_flags & CALLOUT_LIST_FLAG_NANO)
3429334SMadhavan.Venkataraman@Sun.COM return (NULL);
3439334SMadhavan.Venkataraman@Sun.COM
3449039SMadhavan.Venkataraman@Sun.COM if ((cl->cl_expiration == expiration) &&
3459334SMadhavan.Venkataraman@Sun.COM ((cl->cl_flags & clflags) == (flags & clflags)))
3468048SMadhavan.Venkataraman@Sun.COM return (cl);
3478048SMadhavan.Venkataraman@Sun.COM }
3488048SMadhavan.Venkataraman@Sun.COM
3498048SMadhavan.Venkataraman@Sun.COM return (NULL);
3508048SMadhavan.Venkataraman@Sun.COM }
3518048SMadhavan.Venkataraman@Sun.COM
3528048SMadhavan.Venkataraman@Sun.COM /*
35311745SMadhavan.Venkataraman@Sun.COM * Add a new callout list into a callout table's queue in sorted order by
35411745SMadhavan.Venkataraman@Sun.COM * expiration.
35511745SMadhavan.Venkataraman@Sun.COM */
35611745SMadhavan.Venkataraman@Sun.COM static int
callout_queue_add(callout_table_t * ct,callout_list_t * cl)35711745SMadhavan.Venkataraman@Sun.COM callout_queue_add(callout_table_t *ct, callout_list_t *cl)
35811745SMadhavan.Venkataraman@Sun.COM {
35911745SMadhavan.Venkataraman@Sun.COM callout_list_t *nextcl;
36011745SMadhavan.Venkataraman@Sun.COM hrtime_t expiration;
36111745SMadhavan.Venkataraman@Sun.COM
36211745SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration;
36311745SMadhavan.Venkataraman@Sun.COM nextcl = ct->ct_queue.ch_head;
36411745SMadhavan.Venkataraman@Sun.COM if ((nextcl == NULL) || (expiration < nextcl->cl_expiration)) {
36511745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_INSERT(ct->ct_queue, cl);
36611745SMadhavan.Venkataraman@Sun.COM return (1);
36711745SMadhavan.Venkataraman@Sun.COM }
36811745SMadhavan.Venkataraman@Sun.COM
36911745SMadhavan.Venkataraman@Sun.COM while (nextcl != NULL) {
37011745SMadhavan.Venkataraman@Sun.COM if (expiration < nextcl->cl_expiration) {
37111745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_BEFORE(cl, nextcl);
37211745SMadhavan.Venkataraman@Sun.COM return (0);
37311745SMadhavan.Venkataraman@Sun.COM }
37411745SMadhavan.Venkataraman@Sun.COM nextcl = nextcl->cl_next;
37511745SMadhavan.Venkataraman@Sun.COM }
37611745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_queue, cl);
37711745SMadhavan.Venkataraman@Sun.COM
37811745SMadhavan.Venkataraman@Sun.COM return (0);
37911745SMadhavan.Venkataraman@Sun.COM }
38011745SMadhavan.Venkataraman@Sun.COM
38111745SMadhavan.Venkataraman@Sun.COM /*
38211745SMadhavan.Venkataraman@Sun.COM * Insert a callout list into a callout table's queue and reprogram the queue
38311745SMadhavan.Venkataraman@Sun.COM * cyclic if needed.
38411745SMadhavan.Venkataraman@Sun.COM */
38511745SMadhavan.Venkataraman@Sun.COM static void
callout_queue_insert(callout_table_t * ct,callout_list_t * cl)38611745SMadhavan.Venkataraman@Sun.COM callout_queue_insert(callout_table_t *ct, callout_list_t *cl)
38711745SMadhavan.Venkataraman@Sun.COM {
38811745SMadhavan.Venkataraman@Sun.COM cl->cl_flags |= CALLOUT_LIST_FLAG_QUEUED;
38911745SMadhavan.Venkataraman@Sun.COM
39011745SMadhavan.Venkataraman@Sun.COM /*
39111745SMadhavan.Venkataraman@Sun.COM * Add the callout to the callout queue. If it ends up at the head,
39211745SMadhavan.Venkataraman@Sun.COM * the cyclic needs to be reprogrammed as we have an earlier
39311745SMadhavan.Venkataraman@Sun.COM * expiration.
39411745SMadhavan.Venkataraman@Sun.COM *
39511745SMadhavan.Venkataraman@Sun.COM * Also, during the CPR suspend phase, do not reprogram the cyclic.
39611745SMadhavan.Venkataraman@Sun.COM * We don't want any callout activity. When the CPR resume phase is
39711745SMadhavan.Venkataraman@Sun.COM * entered, the cyclic will be programmed for the earliest expiration
39811745SMadhavan.Venkataraman@Sun.COM * in the queue.
39911745SMadhavan.Venkataraman@Sun.COM */
40011745SMadhavan.Venkataraman@Sun.COM if (callout_queue_add(ct, cl) && (ct->ct_suspend == 0))
40111745SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_qcyclic, cl->cl_expiration);
40211745SMadhavan.Venkataraman@Sun.COM }
40311745SMadhavan.Venkataraman@Sun.COM
40411745SMadhavan.Venkataraman@Sun.COM /*
40511745SMadhavan.Venkataraman@Sun.COM * Delete and handle all past expirations in a callout table's queue.
40611745SMadhavan.Venkataraman@Sun.COM */
40711745SMadhavan.Venkataraman@Sun.COM static hrtime_t
callout_queue_delete(callout_table_t * ct)40811745SMadhavan.Venkataraman@Sun.COM callout_queue_delete(callout_table_t *ct)
40911745SMadhavan.Venkataraman@Sun.COM {
41011745SMadhavan.Venkataraman@Sun.COM callout_list_t *cl;
41111745SMadhavan.Venkataraman@Sun.COM hrtime_t now;
41211745SMadhavan.Venkataraman@Sun.COM
41311745SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
41411745SMadhavan.Venkataraman@Sun.COM
41511745SMadhavan.Venkataraman@Sun.COM now = gethrtime();
41611745SMadhavan.Venkataraman@Sun.COM while ((cl = ct->ct_queue.ch_head) != NULL) {
41711745SMadhavan.Venkataraman@Sun.COM if (cl->cl_expiration > now)
41811745SMadhavan.Venkataraman@Sun.COM break;
41911745SMadhavan.Venkataraman@Sun.COM cl->cl_flags &= ~CALLOUT_LIST_FLAG_QUEUED;
42011745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_queue, cl);
42111745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, cl);
42211745SMadhavan.Venkataraman@Sun.COM }
42311745SMadhavan.Venkataraman@Sun.COM
42411745SMadhavan.Venkataraman@Sun.COM /*
42511745SMadhavan.Venkataraman@Sun.COM * If this callout queue is empty or callouts have been suspended,
42611745SMadhavan.Venkataraman@Sun.COM * just return.
42711745SMadhavan.Venkataraman@Sun.COM */
42811745SMadhavan.Venkataraman@Sun.COM if ((cl == NULL) || (ct->ct_suspend > 0))
42911745SMadhavan.Venkataraman@Sun.COM return (CY_INFINITY);
43011745SMadhavan.Venkataraman@Sun.COM
43111745SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_qcyclic, cl->cl_expiration);
43211745SMadhavan.Venkataraman@Sun.COM
43311745SMadhavan.Venkataraman@Sun.COM return (cl->cl_expiration);
43411745SMadhavan.Venkataraman@Sun.COM }
43511745SMadhavan.Venkataraman@Sun.COM
43611745SMadhavan.Venkataraman@Sun.COM static hrtime_t
callout_queue_process(callout_table_t * ct,hrtime_t delta,int timechange)43711745SMadhavan.Venkataraman@Sun.COM callout_queue_process(callout_table_t *ct, hrtime_t delta, int timechange)
43811745SMadhavan.Venkataraman@Sun.COM {
43911745SMadhavan.Venkataraman@Sun.COM callout_list_t *firstcl, *cl;
44011745SMadhavan.Venkataraman@Sun.COM hrtime_t expiration, now;
44111745SMadhavan.Venkataraman@Sun.COM int clflags;
44211745SMadhavan.Venkataraman@Sun.COM callout_hash_t temp;
44311745SMadhavan.Venkataraman@Sun.COM
44411745SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
44511745SMadhavan.Venkataraman@Sun.COM
44611745SMadhavan.Venkataraman@Sun.COM firstcl = ct->ct_queue.ch_head;
44711745SMadhavan.Venkataraman@Sun.COM if (firstcl == NULL)
44811745SMadhavan.Venkataraman@Sun.COM return (CY_INFINITY);
44911745SMadhavan.Venkataraman@Sun.COM
45011745SMadhavan.Venkataraman@Sun.COM /*
45111745SMadhavan.Venkataraman@Sun.COM * We walk the callout queue. If we encounter a hrestime entry that
45211745SMadhavan.Venkataraman@Sun.COM * must be removed, we clean it out. Otherwise, we apply any
45311745SMadhavan.Venkataraman@Sun.COM * adjustments needed to it. Because of the latter, we need to
45411745SMadhavan.Venkataraman@Sun.COM * recreate the list as we go along.
45511745SMadhavan.Venkataraman@Sun.COM */
45611745SMadhavan.Venkataraman@Sun.COM temp = ct->ct_queue;
45711745SMadhavan.Venkataraman@Sun.COM ct->ct_queue.ch_head = NULL;
45811745SMadhavan.Venkataraman@Sun.COM ct->ct_queue.ch_tail = NULL;
45911745SMadhavan.Venkataraman@Sun.COM
46011745SMadhavan.Venkataraman@Sun.COM clflags = (CALLOUT_LIST_FLAG_HRESTIME | CALLOUT_LIST_FLAG_ABSOLUTE);
46111745SMadhavan.Venkataraman@Sun.COM now = gethrtime();
46211745SMadhavan.Venkataraman@Sun.COM while ((cl = temp.ch_head) != NULL) {
46311745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(temp, cl);
46411745SMadhavan.Venkataraman@Sun.COM
46511745SMadhavan.Venkataraman@Sun.COM /*
46611745SMadhavan.Venkataraman@Sun.COM * Delete the callout and expire it, if one of the following
46711745SMadhavan.Venkataraman@Sun.COM * is true:
46811745SMadhavan.Venkataraman@Sun.COM * - the callout has expired
46911745SMadhavan.Venkataraman@Sun.COM * - the callout is an absolute hrestime one and
47011745SMadhavan.Venkataraman@Sun.COM * there has been a system time change
47111745SMadhavan.Venkataraman@Sun.COM */
47211745SMadhavan.Venkataraman@Sun.COM if ((cl->cl_expiration <= now) ||
47311745SMadhavan.Venkataraman@Sun.COM (timechange && ((cl->cl_flags & clflags) == clflags))) {
47411745SMadhavan.Venkataraman@Sun.COM cl->cl_flags &= ~CALLOUT_LIST_FLAG_QUEUED;
47511745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, cl);
47611745SMadhavan.Venkataraman@Sun.COM continue;
47711745SMadhavan.Venkataraman@Sun.COM }
47811745SMadhavan.Venkataraman@Sun.COM
47911745SMadhavan.Venkataraman@Sun.COM /*
48011745SMadhavan.Venkataraman@Sun.COM * Apply adjustments, if any. Adjustments are applied after
48111745SMadhavan.Venkataraman@Sun.COM * the system returns from KMDB or OBP. They are only applied
48211745SMadhavan.Venkataraman@Sun.COM * to relative callout lists.
48311745SMadhavan.Venkataraman@Sun.COM */
48411745SMadhavan.Venkataraman@Sun.COM if (delta && !(cl->cl_flags & CALLOUT_LIST_FLAG_ABSOLUTE)) {
48511745SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration + delta;
48611745SMadhavan.Venkataraman@Sun.COM if (expiration <= 0)
48711745SMadhavan.Venkataraman@Sun.COM expiration = CY_INFINITY;
48811745SMadhavan.Venkataraman@Sun.COM cl->cl_expiration = expiration;
48911745SMadhavan.Venkataraman@Sun.COM }
49011745SMadhavan.Venkataraman@Sun.COM
49111745SMadhavan.Venkataraman@Sun.COM (void) callout_queue_add(ct, cl);
49211745SMadhavan.Venkataraman@Sun.COM }
49311745SMadhavan.Venkataraman@Sun.COM
49411745SMadhavan.Venkataraman@Sun.COM /*
49511745SMadhavan.Venkataraman@Sun.COM * We need to return the expiration to help program the cyclic.
49611745SMadhavan.Venkataraman@Sun.COM * If there are expired callouts, the cyclic needs to go off
49711745SMadhavan.Venkataraman@Sun.COM * immediately. If the queue has become empty, then we return infinity.
49811745SMadhavan.Venkataraman@Sun.COM * Else, we return the expiration of the earliest callout in the queue.
49911745SMadhavan.Venkataraman@Sun.COM */
50011745SMadhavan.Venkataraman@Sun.COM if (ct->ct_expired.ch_head != NULL)
50111745SMadhavan.Venkataraman@Sun.COM return (gethrtime());
50211745SMadhavan.Venkataraman@Sun.COM
50311745SMadhavan.Venkataraman@Sun.COM cl = ct->ct_queue.ch_head;
50411745SMadhavan.Venkataraman@Sun.COM if (cl == NULL)
50511745SMadhavan.Venkataraman@Sun.COM return (CY_INFINITY);
50611745SMadhavan.Venkataraman@Sun.COM
50711745SMadhavan.Venkataraman@Sun.COM return (cl->cl_expiration);
50811745SMadhavan.Venkataraman@Sun.COM }
50911745SMadhavan.Venkataraman@Sun.COM
51011745SMadhavan.Venkataraman@Sun.COM /*
5118048SMadhavan.Venkataraman@Sun.COM * Initialize a callout table's heap, if necessary. Preallocate some free
5128048SMadhavan.Venkataraman@Sun.COM * entries so we don't have to check for NULL elsewhere.
5138048SMadhavan.Venkataraman@Sun.COM */
5148048SMadhavan.Venkataraman@Sun.COM static void
callout_heap_init(callout_table_t * ct)5158048SMadhavan.Venkataraman@Sun.COM callout_heap_init(callout_table_t *ct)
5168048SMadhavan.Venkataraman@Sun.COM {
5178048SMadhavan.Venkataraman@Sun.COM size_t size;
5188048SMadhavan.Venkataraman@Sun.COM
5198048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
5208048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap == NULL);
5218048SMadhavan.Venkataraman@Sun.COM
5228048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num = 0;
52311745SMadhavan.Venkataraman@Sun.COM ct->ct_heap_max = callout_chunk;
52411745SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_heap_t) * callout_chunk;
5258048SMadhavan.Venkataraman@Sun.COM ct->ct_heap = kmem_alloc(size, KM_SLEEP);
5268048SMadhavan.Venkataraman@Sun.COM }
5278048SMadhavan.Venkataraman@Sun.COM
5288048SMadhavan.Venkataraman@Sun.COM /*
52911745SMadhavan.Venkataraman@Sun.COM * Reallocate the heap. Return 0 if the heap is still full at the end of it.
53011745SMadhavan.Venkataraman@Sun.COM * Return 1 otherwise. Note that the heap only expands, it never contracts.
5318048SMadhavan.Venkataraman@Sun.COM */
53211745SMadhavan.Venkataraman@Sun.COM static int
callout_heap_expand(callout_table_t * ct)5338048SMadhavan.Venkataraman@Sun.COM callout_heap_expand(callout_table_t *ct)
5348048SMadhavan.Venkataraman@Sun.COM {
5358048SMadhavan.Venkataraman@Sun.COM size_t max, size, osize;
5369334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap;
5378048SMadhavan.Venkataraman@Sun.COM
5388048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
5398048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num <= ct->ct_heap_max);
5408048SMadhavan.Venkataraman@Sun.COM
5418048SMadhavan.Venkataraman@Sun.COM while (ct->ct_heap_num == ct->ct_heap_max) {
5428048SMadhavan.Venkataraman@Sun.COM max = ct->ct_heap_max;
5438048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
5448048SMadhavan.Venkataraman@Sun.COM
5459334SMadhavan.Venkataraman@Sun.COM osize = sizeof (callout_heap_t) * max;
54611745SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_heap_t) * (max + callout_chunk);
54711745SMadhavan.Venkataraman@Sun.COM heap = kmem_alloc(size, KM_NOSLEEP);
5488048SMadhavan.Venkataraman@Sun.COM
5498048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
55011745SMadhavan.Venkataraman@Sun.COM if (heap == NULL) {
55111745SMadhavan.Venkataraman@Sun.COM /*
55211745SMadhavan.Venkataraman@Sun.COM * We could not allocate memory. If we can free up
55311745SMadhavan.Venkataraman@Sun.COM * some entries, that would be great.
55411745SMadhavan.Venkataraman@Sun.COM */
55511745SMadhavan.Venkataraman@Sun.COM if (ct->ct_nreap > 0)
55611745SMadhavan.Venkataraman@Sun.COM (void) callout_heap_process(ct, 0, 0);
55711745SMadhavan.Venkataraman@Sun.COM /*
55811745SMadhavan.Venkataraman@Sun.COM * If we still have no space in the heap, inform the
55911745SMadhavan.Venkataraman@Sun.COM * caller.
56011745SMadhavan.Venkataraman@Sun.COM */
56111745SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == ct->ct_heap_max)
56211745SMadhavan.Venkataraman@Sun.COM return (0);
56311745SMadhavan.Venkataraman@Sun.COM return (1);
56411745SMadhavan.Venkataraman@Sun.COM }
5658048SMadhavan.Venkataraman@Sun.COM if (max < ct->ct_heap_max) {
5668048SMadhavan.Venkataraman@Sun.COM /*
5678048SMadhavan.Venkataraman@Sun.COM * Someone beat us to the allocation. Free what we
5688048SMadhavan.Venkataraman@Sun.COM * just allocated and proceed.
5698048SMadhavan.Venkataraman@Sun.COM */
5708048SMadhavan.Venkataraman@Sun.COM kmem_free(heap, size);
5718048SMadhavan.Venkataraman@Sun.COM continue;
5728048SMadhavan.Venkataraman@Sun.COM }
5738048SMadhavan.Venkataraman@Sun.COM
5748048SMadhavan.Venkataraman@Sun.COM bcopy(ct->ct_heap, heap, osize);
5758048SMadhavan.Venkataraman@Sun.COM kmem_free(ct->ct_heap, osize);
5768048SMadhavan.Venkataraman@Sun.COM ct->ct_heap = heap;
5779334SMadhavan.Venkataraman@Sun.COM ct->ct_heap_max = size / sizeof (callout_heap_t);
5788048SMadhavan.Venkataraman@Sun.COM }
57911745SMadhavan.Venkataraman@Sun.COM
58011745SMadhavan.Venkataraman@Sun.COM return (1);
5818048SMadhavan.Venkataraman@Sun.COM }
5828048SMadhavan.Venkataraman@Sun.COM
5838048SMadhavan.Venkataraman@Sun.COM /*
5848048SMadhavan.Venkataraman@Sun.COM * Move an expiration from the bottom of the heap to its correct place
5858048SMadhavan.Venkataraman@Sun.COM * in the heap. If we reached the root doing this, return 1. Else,
5868048SMadhavan.Venkataraman@Sun.COM * return 0.
5870Sstevel@tonic-gate */
5888048SMadhavan.Venkataraman@Sun.COM static int
callout_upheap(callout_table_t * ct)5898048SMadhavan.Venkataraman@Sun.COM callout_upheap(callout_table_t *ct)
5908048SMadhavan.Venkataraman@Sun.COM {
5918048SMadhavan.Venkataraman@Sun.COM int current, parent;
5929334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap, *hcurrent, *hparent;
5938048SMadhavan.Venkataraman@Sun.COM
5948048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
5958048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num >= 1);
5968048SMadhavan.Venkataraman@Sun.COM
5978048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 1) {
5988048SMadhavan.Venkataraman@Sun.COM return (1);
5998048SMadhavan.Venkataraman@Sun.COM }
6008048SMadhavan.Venkataraman@Sun.COM
6018048SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap;
6028048SMadhavan.Venkataraman@Sun.COM current = ct->ct_heap_num - 1;
6038048SMadhavan.Venkataraman@Sun.COM
6048048SMadhavan.Venkataraman@Sun.COM for (;;) {
6058048SMadhavan.Venkataraman@Sun.COM parent = CALLOUT_HEAP_PARENT(current);
6069334SMadhavan.Venkataraman@Sun.COM hparent = &heap[parent];
6079334SMadhavan.Venkataraman@Sun.COM hcurrent = &heap[current];
6088048SMadhavan.Venkataraman@Sun.COM
6098048SMadhavan.Venkataraman@Sun.COM /*
6108048SMadhavan.Venkataraman@Sun.COM * We have an expiration later than our parent; we're done.
6118048SMadhavan.Venkataraman@Sun.COM */
6129334SMadhavan.Venkataraman@Sun.COM if (hcurrent->ch_expiration >= hparent->ch_expiration) {
6138048SMadhavan.Venkataraman@Sun.COM return (0);
6148048SMadhavan.Venkataraman@Sun.COM }
6158048SMadhavan.Venkataraman@Sun.COM
6168048SMadhavan.Venkataraman@Sun.COM /*
6178048SMadhavan.Venkataraman@Sun.COM * We need to swap with our parent, and continue up the heap.
6188048SMadhavan.Venkataraman@Sun.COM */
6199334SMadhavan.Venkataraman@Sun.COM CALLOUT_SWAP(hparent, hcurrent);
6208048SMadhavan.Venkataraman@Sun.COM
6218048SMadhavan.Venkataraman@Sun.COM /*
6228048SMadhavan.Venkataraman@Sun.COM * If we just reached the root, we're done.
6238048SMadhavan.Venkataraman@Sun.COM */
6248048SMadhavan.Venkataraman@Sun.COM if (parent == 0) {
6258048SMadhavan.Venkataraman@Sun.COM return (1);
6268048SMadhavan.Venkataraman@Sun.COM }
6278048SMadhavan.Venkataraman@Sun.COM
6288048SMadhavan.Venkataraman@Sun.COM current = parent;
6298048SMadhavan.Venkataraman@Sun.COM }
6308048SMadhavan.Venkataraman@Sun.COM /*NOTREACHED*/
6318048SMadhavan.Venkataraman@Sun.COM }
6328048SMadhavan.Venkataraman@Sun.COM
6338048SMadhavan.Venkataraman@Sun.COM /*
6349334SMadhavan.Venkataraman@Sun.COM * Insert a new heap item into a callout table's heap.
6358048SMadhavan.Venkataraman@Sun.COM */
6368048SMadhavan.Venkataraman@Sun.COM static void
callout_heap_insert(callout_table_t * ct,callout_list_t * cl)6379334SMadhavan.Venkataraman@Sun.COM callout_heap_insert(callout_table_t *ct, callout_list_t *cl)
6388048SMadhavan.Venkataraman@Sun.COM {
6398048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
6408048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num < ct->ct_heap_max);
6418048SMadhavan.Venkataraman@Sun.COM
64211745SMadhavan.Venkataraman@Sun.COM cl->cl_flags |= CALLOUT_LIST_FLAG_HEAPED;
6438048SMadhavan.Venkataraman@Sun.COM /*
6449334SMadhavan.Venkataraman@Sun.COM * First, copy the expiration and callout list pointer to the bottom
6459334SMadhavan.Venkataraman@Sun.COM * of the heap.
6468048SMadhavan.Venkataraman@Sun.COM */
6479334SMadhavan.Venkataraman@Sun.COM ct->ct_heap[ct->ct_heap_num].ch_expiration = cl->cl_expiration;
6489334SMadhavan.Venkataraman@Sun.COM ct->ct_heap[ct->ct_heap_num].ch_list = cl;
6498048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num++;
6508048SMadhavan.Venkataraman@Sun.COM
6518048SMadhavan.Venkataraman@Sun.COM /*
6528048SMadhavan.Venkataraman@Sun.COM * Now, perform an upheap operation. If we reached the root, then
6538048SMadhavan.Venkataraman@Sun.COM * the cyclic needs to be reprogrammed as we have an earlier
6548048SMadhavan.Venkataraman@Sun.COM * expiration.
6558048SMadhavan.Venkataraman@Sun.COM *
6568048SMadhavan.Venkataraman@Sun.COM * Also, during the CPR suspend phase, do not reprogram the cyclic.
6578048SMadhavan.Venkataraman@Sun.COM * We don't want any callout activity. When the CPR resume phase is
6588048SMadhavan.Venkataraman@Sun.COM * entered, the cyclic will be programmed for the earliest expiration
6598048SMadhavan.Venkataraman@Sun.COM * in the heap.
6608048SMadhavan.Venkataraman@Sun.COM */
6618566SMadhavan.Venkataraman@Sun.COM if (callout_upheap(ct) && (ct->ct_suspend == 0))
6629334SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, cl->cl_expiration);
6638048SMadhavan.Venkataraman@Sun.COM }
6648048SMadhavan.Venkataraman@Sun.COM
6658048SMadhavan.Venkataraman@Sun.COM /*
6668048SMadhavan.Venkataraman@Sun.COM * Move an expiration from the top of the heap to its correct place
6678048SMadhavan.Venkataraman@Sun.COM * in the heap.
6688048SMadhavan.Venkataraman@Sun.COM */
6698048SMadhavan.Venkataraman@Sun.COM static void
callout_downheap(callout_table_t * ct)6708048SMadhavan.Venkataraman@Sun.COM callout_downheap(callout_table_t *ct)
6710Sstevel@tonic-gate {
6729334SMadhavan.Venkataraman@Sun.COM int current, left, right, nelems;
6739334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap, *hleft, *hright, *hcurrent;
6748048SMadhavan.Venkataraman@Sun.COM
6758048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
6768048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num >= 1);
6778048SMadhavan.Venkataraman@Sun.COM
6788048SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap;
6798048SMadhavan.Venkataraman@Sun.COM current = 0;
6808048SMadhavan.Venkataraman@Sun.COM nelems = ct->ct_heap_num;
6818048SMadhavan.Venkataraman@Sun.COM
6828048SMadhavan.Venkataraman@Sun.COM for (;;) {
6838048SMadhavan.Venkataraman@Sun.COM /*
6848048SMadhavan.Venkataraman@Sun.COM * If we don't have a left child (i.e., we're a leaf), we're
6858048SMadhavan.Venkataraman@Sun.COM * done.
6868048SMadhavan.Venkataraman@Sun.COM */
6878048SMadhavan.Venkataraman@Sun.COM if ((left = CALLOUT_HEAP_LEFT(current)) >= nelems)
6888048SMadhavan.Venkataraman@Sun.COM return;
6898048SMadhavan.Venkataraman@Sun.COM
6909334SMadhavan.Venkataraman@Sun.COM hleft = &heap[left];
6919334SMadhavan.Venkataraman@Sun.COM hcurrent = &heap[current];
6928048SMadhavan.Venkataraman@Sun.COM
6938048SMadhavan.Venkataraman@Sun.COM right = CALLOUT_HEAP_RIGHT(current);
6948048SMadhavan.Venkataraman@Sun.COM
6958048SMadhavan.Venkataraman@Sun.COM /*
6968048SMadhavan.Venkataraman@Sun.COM * Even if we don't have a right child, we still need to compare
6978048SMadhavan.Venkataraman@Sun.COM * our expiration against that of our left child.
6988048SMadhavan.Venkataraman@Sun.COM */
6998048SMadhavan.Venkataraman@Sun.COM if (right >= nelems)
7008048SMadhavan.Venkataraman@Sun.COM goto comp_left;
7018048SMadhavan.Venkataraman@Sun.COM
7029334SMadhavan.Venkataraman@Sun.COM hright = &heap[right];
7038048SMadhavan.Venkataraman@Sun.COM
7048048SMadhavan.Venkataraman@Sun.COM /*
7058048SMadhavan.Venkataraman@Sun.COM * We have both a left and a right child. We need to compare
7068048SMadhavan.Venkataraman@Sun.COM * the expiration of the children to determine which
7078048SMadhavan.Venkataraman@Sun.COM * expires earlier.
7088048SMadhavan.Venkataraman@Sun.COM */
7099334SMadhavan.Venkataraman@Sun.COM if (hright->ch_expiration < hleft->ch_expiration) {
7108048SMadhavan.Venkataraman@Sun.COM /*
7118048SMadhavan.Venkataraman@Sun.COM * Our right child is the earlier of our children.
7128048SMadhavan.Venkataraman@Sun.COM * We'll now compare our expiration to its expiration.
7138048SMadhavan.Venkataraman@Sun.COM * If ours is the earlier one, we're done.
7148048SMadhavan.Venkataraman@Sun.COM */
7159334SMadhavan.Venkataraman@Sun.COM if (hcurrent->ch_expiration <= hright->ch_expiration)
7168048SMadhavan.Venkataraman@Sun.COM return;
7178048SMadhavan.Venkataraman@Sun.COM
7188048SMadhavan.Venkataraman@Sun.COM /*
7198048SMadhavan.Venkataraman@Sun.COM * Our right child expires earlier than we do; swap
7208048SMadhavan.Venkataraman@Sun.COM * with our right child, and descend right.
7218048SMadhavan.Venkataraman@Sun.COM */
7229334SMadhavan.Venkataraman@Sun.COM CALLOUT_SWAP(hright, hcurrent);
7238048SMadhavan.Venkataraman@Sun.COM current = right;
7248048SMadhavan.Venkataraman@Sun.COM continue;
7258048SMadhavan.Venkataraman@Sun.COM }
7268048SMadhavan.Venkataraman@Sun.COM
7278048SMadhavan.Venkataraman@Sun.COM comp_left:
7288048SMadhavan.Venkataraman@Sun.COM /*
7298048SMadhavan.Venkataraman@Sun.COM * Our left child is the earlier of our children (or we have
7308048SMadhavan.Venkataraman@Sun.COM * no right child). We'll now compare our expiration
7318048SMadhavan.Venkataraman@Sun.COM * to its expiration. If ours is the earlier one, we're done.
7328048SMadhavan.Venkataraman@Sun.COM */
7339334SMadhavan.Venkataraman@Sun.COM if (hcurrent->ch_expiration <= hleft->ch_expiration)
7348048SMadhavan.Venkataraman@Sun.COM return;
7358048SMadhavan.Venkataraman@Sun.COM
7368048SMadhavan.Venkataraman@Sun.COM /*
7378048SMadhavan.Venkataraman@Sun.COM * Our left child expires earlier than we do; swap with our
7388048SMadhavan.Venkataraman@Sun.COM * left child, and descend left.
7398048SMadhavan.Venkataraman@Sun.COM */
7409334SMadhavan.Venkataraman@Sun.COM CALLOUT_SWAP(hleft, hcurrent);
7418048SMadhavan.Venkataraman@Sun.COM current = left;
7428048SMadhavan.Venkataraman@Sun.COM }
7438048SMadhavan.Venkataraman@Sun.COM }
7448048SMadhavan.Venkataraman@Sun.COM
7458048SMadhavan.Venkataraman@Sun.COM /*
7468048SMadhavan.Venkataraman@Sun.COM * Delete and handle all past expirations in a callout table's heap.
7478048SMadhavan.Venkataraman@Sun.COM */
74811745SMadhavan.Venkataraman@Sun.COM static hrtime_t
callout_heap_delete(callout_table_t * ct)7498048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(callout_table_t *ct)
7508048SMadhavan.Venkataraman@Sun.COM {
7519334SMadhavan.Venkataraman@Sun.COM hrtime_t now, expiration, next;
7528048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl;
7539334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap;
7548048SMadhavan.Venkataraman@Sun.COM int hash;
7558048SMadhavan.Venkataraman@Sun.COM
7568048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
7578048SMadhavan.Venkataraman@Sun.COM
7589334SMadhavan.Venkataraman@Sun.COM if (CALLOUT_CLEANUP(ct)) {
7599334SMadhavan.Venkataraman@Sun.COM /*
7609334SMadhavan.Venkataraman@Sun.COM * There are too many heap elements pointing to empty callout
7619334SMadhavan.Venkataraman@Sun.COM * lists. Clean them out.
7629334SMadhavan.Venkataraman@Sun.COM */
7639334SMadhavan.Venkataraman@Sun.COM (void) callout_heap_process(ct, 0, 0);
7649334SMadhavan.Venkataraman@Sun.COM }
7659334SMadhavan.Venkataraman@Sun.COM
7668048SMadhavan.Venkataraman@Sun.COM now = gethrtime();
7679334SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap;
7688048SMadhavan.Venkataraman@Sun.COM
7698048SMadhavan.Venkataraman@Sun.COM while (ct->ct_heap_num > 0) {
7709334SMadhavan.Venkataraman@Sun.COM expiration = heap->ch_expiration;
7718048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(expiration);
7729334SMadhavan.Venkataraman@Sun.COM cl = heap->ch_list;
7739334SMadhavan.Venkataraman@Sun.COM ASSERT(expiration == cl->cl_expiration);
7749334SMadhavan.Venkataraman@Sun.COM
7759334SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) {
7768048SMadhavan.Venkataraman@Sun.COM /*
7779334SMadhavan.Venkataraman@Sun.COM * If the callout list is empty, reap it.
7789334SMadhavan.Venkataraman@Sun.COM * Decrement the reap count.
7799334SMadhavan.Venkataraman@Sun.COM */
7809334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
7819334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl);
7829334SMadhavan.Venkataraman@Sun.COM ct->ct_nreap--;
7839334SMadhavan.Venkataraman@Sun.COM } else {
7849334SMadhavan.Venkataraman@Sun.COM /*
7859334SMadhavan.Venkataraman@Sun.COM * If the root of the heap expires in the future,
7869334SMadhavan.Venkataraman@Sun.COM * bail out.
7878048SMadhavan.Venkataraman@Sun.COM */
7888048SMadhavan.Venkataraman@Sun.COM if (expiration > now)
7898048SMadhavan.Venkataraman@Sun.COM break;
7908048SMadhavan.Venkataraman@Sun.COM
7918048SMadhavan.Venkataraman@Sun.COM /*
7928048SMadhavan.Venkataraman@Sun.COM * Move the callout list for this expiration to the
7938048SMadhavan.Venkataraman@Sun.COM * list of expired callout lists. It will be processed
7948048SMadhavan.Venkataraman@Sun.COM * by the callout executor.
7958048SMadhavan.Venkataraman@Sun.COM */
79611745SMadhavan.Venkataraman@Sun.COM cl->cl_flags &= ~CALLOUT_LIST_FLAG_HEAPED;
7978048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
7988048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, cl);
7998048SMadhavan.Venkataraman@Sun.COM }
8008048SMadhavan.Venkataraman@Sun.COM
8018048SMadhavan.Venkataraman@Sun.COM /*
8028048SMadhavan.Venkataraman@Sun.COM * Now delete the root. This is done by swapping the root with
8038048SMadhavan.Venkataraman@Sun.COM * the last item in the heap and downheaping the item.
8048048SMadhavan.Venkataraman@Sun.COM */
8058048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num--;
8068048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num > 0) {
8079334SMadhavan.Venkataraman@Sun.COM heap[0] = heap[ct->ct_heap_num];
8088048SMadhavan.Venkataraman@Sun.COM callout_downheap(ct);
8098048SMadhavan.Venkataraman@Sun.COM }
8108048SMadhavan.Venkataraman@Sun.COM }
8118048SMadhavan.Venkataraman@Sun.COM
8128048SMadhavan.Venkataraman@Sun.COM /*
8139334SMadhavan.Venkataraman@Sun.COM * If this callout table is empty or callouts have been suspended,
8149334SMadhavan.Venkataraman@Sun.COM * just return. The cyclic has already been programmed to
8158048SMadhavan.Venkataraman@Sun.COM * infinity by the cyclic subsystem.
8168048SMadhavan.Venkataraman@Sun.COM */
8178566SMadhavan.Venkataraman@Sun.COM if ((ct->ct_heap_num == 0) || (ct->ct_suspend > 0))
81811745SMadhavan.Venkataraman@Sun.COM return (CY_INFINITY);
8198048SMadhavan.Venkataraman@Sun.COM
8209334SMadhavan.Venkataraman@Sun.COM /*
8219334SMadhavan.Venkataraman@Sun.COM * If the top expirations are within callout_tolerance of each other,
8229334SMadhavan.Venkataraman@Sun.COM * delay the cyclic expire so that they can be processed together.
8239334SMadhavan.Venkataraman@Sun.COM * This is to prevent high resolution timers from swamping the system
8249334SMadhavan.Venkataraman@Sun.COM * with cyclic activity.
8259334SMadhavan.Venkataraman@Sun.COM */
8269334SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num > 2) {
8279334SMadhavan.Venkataraman@Sun.COM next = expiration + callout_tolerance;
8289334SMadhavan.Venkataraman@Sun.COM if ((heap[1].ch_expiration < next) ||
8299334SMadhavan.Venkataraman@Sun.COM (heap[2].ch_expiration < next))
8309334SMadhavan.Venkataraman@Sun.COM expiration = next;
8319334SMadhavan.Venkataraman@Sun.COM }
8329334SMadhavan.Venkataraman@Sun.COM
8338048SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, expiration);
83411745SMadhavan.Venkataraman@Sun.COM
83511745SMadhavan.Venkataraman@Sun.COM return (expiration);
8368048SMadhavan.Venkataraman@Sun.COM }
8378048SMadhavan.Venkataraman@Sun.COM
8388566SMadhavan.Venkataraman@Sun.COM /*
8399334SMadhavan.Venkataraman@Sun.COM * There are some situations when the entire heap is walked and processed.
8409334SMadhavan.Venkataraman@Sun.COM * This function is called to do the processing. These are the situations:
8419334SMadhavan.Venkataraman@Sun.COM *
8429334SMadhavan.Venkataraman@Sun.COM * 1. When the reap count reaches its threshold, the heap has to be cleared
8439334SMadhavan.Venkataraman@Sun.COM * of all empty callout lists.
8449334SMadhavan.Venkataraman@Sun.COM *
8459334SMadhavan.Venkataraman@Sun.COM * 2. When the system enters and exits KMDB/OBP, all entries in the heap
8469334SMadhavan.Venkataraman@Sun.COM * need to be adjusted by the interval spent in KMDB/OBP.
8479334SMadhavan.Venkataraman@Sun.COM *
8489334SMadhavan.Venkataraman@Sun.COM * 3. When system time is changed, the heap has to be scanned for
8499334SMadhavan.Venkataraman@Sun.COM * absolute hrestime timers. These need to be removed from the heap
8509334SMadhavan.Venkataraman@Sun.COM * and expired immediately.
8519334SMadhavan.Venkataraman@Sun.COM *
8529334SMadhavan.Venkataraman@Sun.COM * In cases 2 and 3, it is a good idea to do 1 as well since we are
8539334SMadhavan.Venkataraman@Sun.COM * scanning the heap anyway.
8549334SMadhavan.Venkataraman@Sun.COM *
8559334SMadhavan.Venkataraman@Sun.COM * If the root gets changed and/or callout lists are expired, return the
8569334SMadhavan.Venkataraman@Sun.COM * new expiration to the caller so he can reprogram the cyclic accordingly.
8579334SMadhavan.Venkataraman@Sun.COM */
8589334SMadhavan.Venkataraman@Sun.COM static hrtime_t
callout_heap_process(callout_table_t * ct,hrtime_t delta,int timechange)8599334SMadhavan.Venkataraman@Sun.COM callout_heap_process(callout_table_t *ct, hrtime_t delta, int timechange)
8609334SMadhavan.Venkataraman@Sun.COM {
8619334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap;
86211745SMadhavan.Venkataraman@Sun.COM callout_list_t *cl;
8639334SMadhavan.Venkataraman@Sun.COM hrtime_t expiration, now;
86411745SMadhavan.Venkataraman@Sun.COM int i, hash, clflags;
8659334SMadhavan.Venkataraman@Sun.COM ulong_t num;
8669334SMadhavan.Venkataraman@Sun.COM
8679334SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
8689334SMadhavan.Venkataraman@Sun.COM
8699334SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 0)
87011745SMadhavan.Venkataraman@Sun.COM return (CY_INFINITY);
8719334SMadhavan.Venkataraman@Sun.COM
8729334SMadhavan.Venkataraman@Sun.COM if (ct->ct_nreap > 0)
8739334SMadhavan.Venkataraman@Sun.COM ct->ct_cleanups++;
8749334SMadhavan.Venkataraman@Sun.COM
8759334SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap;
8769334SMadhavan.Venkataraman@Sun.COM
8779334SMadhavan.Venkataraman@Sun.COM /*
8789334SMadhavan.Venkataraman@Sun.COM * We walk the heap from the top to the bottom. If we encounter
8799334SMadhavan.Venkataraman@Sun.COM * a heap item that points to an empty callout list, we clean
8809334SMadhavan.Venkataraman@Sun.COM * it out. If we encounter a hrestime entry that must be removed,
8819334SMadhavan.Venkataraman@Sun.COM * again we clean it out. Otherwise, we apply any adjustments needed
8829334SMadhavan.Venkataraman@Sun.COM * to an element.
8839334SMadhavan.Venkataraman@Sun.COM *
8849334SMadhavan.Venkataraman@Sun.COM * During the walk, we also compact the heap from the bottom and
8859334SMadhavan.Venkataraman@Sun.COM * reconstruct the heap using upheap operations. This is very
8869334SMadhavan.Venkataraman@Sun.COM * efficient if the number of elements to be cleaned is greater than
8879334SMadhavan.Venkataraman@Sun.COM * or equal to half the heap. This is the common case.
8889334SMadhavan.Venkataraman@Sun.COM *
8899334SMadhavan.Venkataraman@Sun.COM * Even in the non-common case, the upheap operations should be short
8909334SMadhavan.Venkataraman@Sun.COM * as the entries below generally tend to be bigger than the entries
8919334SMadhavan.Venkataraman@Sun.COM * above.
8929334SMadhavan.Venkataraman@Sun.COM */
8939334SMadhavan.Venkataraman@Sun.COM num = ct->ct_heap_num;
8949334SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num = 0;
8959334SMadhavan.Venkataraman@Sun.COM clflags = (CALLOUT_LIST_FLAG_HRESTIME | CALLOUT_LIST_FLAG_ABSOLUTE);
8969334SMadhavan.Venkataraman@Sun.COM now = gethrtime();
8979334SMadhavan.Venkataraman@Sun.COM for (i = 0; i < num; i++) {
8989334SMadhavan.Venkataraman@Sun.COM cl = heap[i].ch_list;
8999334SMadhavan.Venkataraman@Sun.COM /*
9009334SMadhavan.Venkataraman@Sun.COM * If the callout list is empty, delete the heap element and
9019334SMadhavan.Venkataraman@Sun.COM * free the callout list.
9029334SMadhavan.Venkataraman@Sun.COM */
9039334SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) {
9049334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration);
9059334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
9069334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl);
9079334SMadhavan.Venkataraman@Sun.COM continue;
9089334SMadhavan.Venkataraman@Sun.COM }
9099334SMadhavan.Venkataraman@Sun.COM
9109334SMadhavan.Venkataraman@Sun.COM /*
9119334SMadhavan.Venkataraman@Sun.COM * Delete the heap element and expire the callout list, if
9129334SMadhavan.Venkataraman@Sun.COM * one of the following is true:
9139334SMadhavan.Venkataraman@Sun.COM * - the callout list has expired
9149334SMadhavan.Venkataraman@Sun.COM * - the callout list is an absolute hrestime one and
9159334SMadhavan.Venkataraman@Sun.COM * there has been a system time change
9169334SMadhavan.Venkataraman@Sun.COM */
9179334SMadhavan.Venkataraman@Sun.COM if ((cl->cl_expiration <= now) ||
9189334SMadhavan.Venkataraman@Sun.COM (timechange && ((cl->cl_flags & clflags) == clflags))) {
9199334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration);
92011745SMadhavan.Venkataraman@Sun.COM cl->cl_flags &= ~CALLOUT_LIST_FLAG_HEAPED;
9219334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
9229334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, cl);
9239334SMadhavan.Venkataraman@Sun.COM continue;
9249334SMadhavan.Venkataraman@Sun.COM }
9259334SMadhavan.Venkataraman@Sun.COM
9269334SMadhavan.Venkataraman@Sun.COM /*
9279334SMadhavan.Venkataraman@Sun.COM * Apply adjustments, if any. Adjustments are applied after
9289334SMadhavan.Venkataraman@Sun.COM * the system returns from KMDB or OBP. They are only applied
9299334SMadhavan.Venkataraman@Sun.COM * to relative callout lists.
9309334SMadhavan.Venkataraman@Sun.COM */
9319334SMadhavan.Venkataraman@Sun.COM if (delta && !(cl->cl_flags & CALLOUT_LIST_FLAG_ABSOLUTE)) {
9329334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration);
9339334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl);
9349334SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration + delta;
9359334SMadhavan.Venkataraman@Sun.COM if (expiration <= 0)
9369334SMadhavan.Venkataraman@Sun.COM expiration = CY_INFINITY;
9379334SMadhavan.Venkataraman@Sun.COM heap[i].ch_expiration = expiration;
9389334SMadhavan.Venkataraman@Sun.COM cl->cl_expiration = expiration;
9399334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration);
9409334SMadhavan.Venkataraman@Sun.COM if (cl->cl_flags & CALLOUT_LIST_FLAG_NANO) {
9419334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_clhash[hash], cl);
9429334SMadhavan.Venkataraman@Sun.COM } else {
9439334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl);
9449334SMadhavan.Venkataraman@Sun.COM }
9459334SMadhavan.Venkataraman@Sun.COM }
9469334SMadhavan.Venkataraman@Sun.COM
9479334SMadhavan.Venkataraman@Sun.COM heap[ct->ct_heap_num] = heap[i];
9489334SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num++;
9499334SMadhavan.Venkataraman@Sun.COM (void) callout_upheap(ct);
9509334SMadhavan.Venkataraman@Sun.COM }
9519334SMadhavan.Venkataraman@Sun.COM
9529334SMadhavan.Venkataraman@Sun.COM ct->ct_nreap = 0;
9539334SMadhavan.Venkataraman@Sun.COM
95411745SMadhavan.Venkataraman@Sun.COM /*
95511745SMadhavan.Venkataraman@Sun.COM * We need to return the expiration to help program the cyclic.
95611745SMadhavan.Venkataraman@Sun.COM * If there are expired callouts, the cyclic needs to go off
95711745SMadhavan.Venkataraman@Sun.COM * immediately. If the heap has become empty, then we return infinity.
95811745SMadhavan.Venkataraman@Sun.COM * Else, return the expiration of the earliest callout in the heap.
95911745SMadhavan.Venkataraman@Sun.COM */
96011745SMadhavan.Venkataraman@Sun.COM if (ct->ct_expired.ch_head != NULL)
96111745SMadhavan.Venkataraman@Sun.COM return (gethrtime());
9629334SMadhavan.Venkataraman@Sun.COM
96311745SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 0)
96411745SMadhavan.Venkataraman@Sun.COM return (CY_INFINITY);
96511745SMadhavan.Venkataraman@Sun.COM
96611745SMadhavan.Venkataraman@Sun.COM return (heap->ch_expiration);
9679334SMadhavan.Venkataraman@Sun.COM }
9689334SMadhavan.Venkataraman@Sun.COM
9699334SMadhavan.Venkataraman@Sun.COM /*
9708566SMadhavan.Venkataraman@Sun.COM * Common function used to create normal and realtime callouts.
9718566SMadhavan.Venkataraman@Sun.COM *
9728566SMadhavan.Venkataraman@Sun.COM * Realtime callouts are handled at CY_LOW_PIL by a cyclic handler. So,
9738566SMadhavan.Venkataraman@Sun.COM * there is one restriction on a realtime callout handler - it should not
9748566SMadhavan.Venkataraman@Sun.COM * directly or indirectly acquire cpu_lock. CPU offline waits for pending
9758566SMadhavan.Venkataraman@Sun.COM * cyclic handlers to complete while holding cpu_lock. So, if a realtime
9768566SMadhavan.Venkataraman@Sun.COM * callout handler were to try to get cpu_lock, there would be a deadlock
9778566SMadhavan.Venkataraman@Sun.COM * during CPU offline.
9788566SMadhavan.Venkataraman@Sun.COM */
9798048SMadhavan.Venkataraman@Sun.COM callout_id_t
timeout_generic(int type,void (* func)(void *),void * arg,hrtime_t expiration,hrtime_t resolution,int flags)9808048SMadhavan.Venkataraman@Sun.COM timeout_generic(int type, void (*func)(void *), void *arg,
9818048SMadhavan.Venkataraman@Sun.COM hrtime_t expiration, hrtime_t resolution, int flags)
9828048SMadhavan.Venkataraman@Sun.COM {
9838048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct;
9844123Sdm120769 callout_t *cp;
9854123Sdm120769 callout_id_t id;
9868048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl;
98711745SMadhavan.Venkataraman@Sun.COM hrtime_t now, interval;
9889334SMadhavan.Venkataraman@Sun.COM int hash, clflags;
9898048SMadhavan.Venkataraman@Sun.COM
9908048SMadhavan.Venkataraman@Sun.COM ASSERT(resolution > 0);
9918048SMadhavan.Venkataraman@Sun.COM ASSERT(func != NULL);
9928048SMadhavan.Venkataraman@Sun.COM
9938048SMadhavan.Venkataraman@Sun.COM /*
9949334SMadhavan.Venkataraman@Sun.COM * We get the current hrtime right upfront so that latencies in
9959334SMadhavan.Venkataraman@Sun.COM * this function do not affect the accuracy of the callout.
9968048SMadhavan.Venkataraman@Sun.COM */
9979334SMadhavan.Venkataraman@Sun.COM now = gethrtime();
9986422Sqiao
9998048SMadhavan.Venkataraman@Sun.COM /*
10008048SMadhavan.Venkataraman@Sun.COM * We disable kernel preemption so that we remain on the same CPU
10018048SMadhavan.Venkataraman@Sun.COM * throughout. If we needed to reprogram the callout table's cyclic,
10028048SMadhavan.Venkataraman@Sun.COM * we can avoid X-calls if we are on the same CPU.
10038048SMadhavan.Venkataraman@Sun.COM *
10048048SMadhavan.Venkataraman@Sun.COM * Note that callout_alloc() releases and reacquires the callout
10058048SMadhavan.Venkataraman@Sun.COM * table mutex. While reacquiring the mutex, it is possible for us
10068048SMadhavan.Venkataraman@Sun.COM * to go to sleep and later migrate to another CPU. This should be
10078048SMadhavan.Venkataraman@Sun.COM * pretty rare, though.
10088048SMadhavan.Venkataraman@Sun.COM */
10098048SMadhavan.Venkataraman@Sun.COM kpreempt_disable();
10100Sstevel@tonic-gate
10118048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(type, CPU->cpu_seqid)];
10128048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
10130Sstevel@tonic-gate
10148048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) {
10158048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
10168048SMadhavan.Venkataraman@Sun.COM /*
10178048SMadhavan.Venkataraman@Sun.COM * The callout table has not yet been initialized fully.
10188048SMadhavan.Venkataraman@Sun.COM * So, put this one on the boot callout table which is
10198048SMadhavan.Venkataraman@Sun.COM * always initialized.
10208048SMadhavan.Venkataraman@Sun.COM */
10218048SMadhavan.Venkataraman@Sun.COM ct = &callout_boot_ct[type];
10228048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
10238048SMadhavan.Venkataraman@Sun.COM }
10248048SMadhavan.Venkataraman@Sun.COM
10259334SMadhavan.Venkataraman@Sun.COM if (CALLOUT_CLEANUP(ct)) {
10269334SMadhavan.Venkataraman@Sun.COM /*
10279334SMadhavan.Venkataraman@Sun.COM * There are too many heap elements pointing to empty callout
102811745SMadhavan.Venkataraman@Sun.COM * lists. Clean them out. Since cleanup is only done once
102911745SMadhavan.Venkataraman@Sun.COM * in a while, no need to reprogram the cyclic if the root
103011745SMadhavan.Venkataraman@Sun.COM * of the heap gets cleaned out.
10319334SMadhavan.Venkataraman@Sun.COM */
103211745SMadhavan.Venkataraman@Sun.COM (void) callout_heap_process(ct, 0, 0);
10339334SMadhavan.Venkataraman@Sun.COM }
10349334SMadhavan.Venkataraman@Sun.COM
10358048SMadhavan.Venkataraman@Sun.COM if ((cp = ct->ct_free) == NULL)
10360Sstevel@tonic-gate cp = callout_alloc(ct);
10370Sstevel@tonic-gate else
10388048SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp->c_idnext;
10390Sstevel@tonic-gate
10400Sstevel@tonic-gate cp->c_func = func;
10410Sstevel@tonic-gate cp->c_arg = arg;
10420Sstevel@tonic-gate
10430Sstevel@tonic-gate /*
10448048SMadhavan.Venkataraman@Sun.COM * Compute the expiration hrtime.
10458048SMadhavan.Venkataraman@Sun.COM */
10468048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ABSOLUTE) {
10478048SMadhavan.Venkataraman@Sun.COM interval = expiration - now;
10488048SMadhavan.Venkataraman@Sun.COM } else {
10498048SMadhavan.Venkataraman@Sun.COM interval = expiration;
10508048SMadhavan.Venkataraman@Sun.COM expiration += now;
10518048SMadhavan.Venkataraman@Sun.COM }
10529334SMadhavan.Venkataraman@Sun.COM
10539334SMadhavan.Venkataraman@Sun.COM if (resolution > 1) {
10549334SMadhavan.Venkataraman@Sun.COM /*
10559334SMadhavan.Venkataraman@Sun.COM * Align expiration to the specified resolution.
10569334SMadhavan.Venkataraman@Sun.COM */
10579334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ROUNDUP)
10589334SMadhavan.Venkataraman@Sun.COM expiration += resolution - 1;
10599334SMadhavan.Venkataraman@Sun.COM expiration = (expiration / resolution) * resolution;
10609334SMadhavan.Venkataraman@Sun.COM }
10619334SMadhavan.Venkataraman@Sun.COM
10628566SMadhavan.Venkataraman@Sun.COM if (expiration <= 0) {
10638566SMadhavan.Venkataraman@Sun.COM /*
10648566SMadhavan.Venkataraman@Sun.COM * expiration hrtime overflow has occurred. Just set the
10658566SMadhavan.Venkataraman@Sun.COM * expiration to infinity.
10668566SMadhavan.Venkataraman@Sun.COM */
10678566SMadhavan.Venkataraman@Sun.COM expiration = CY_INFINITY;
10688566SMadhavan.Venkataraman@Sun.COM }
10698048SMadhavan.Venkataraman@Sun.COM
10708048SMadhavan.Venkataraman@Sun.COM /*
10718048SMadhavan.Venkataraman@Sun.COM * Assign an ID to this callout
10728048SMadhavan.Venkataraman@Sun.COM */
10738048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_32BIT) {
10748048SMadhavan.Venkataraman@Sun.COM if (interval > callout_longterm) {
10758048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_long_id - callout_counter_low);
10768048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH;
10778048SMadhavan.Venkataraman@Sun.COM ct->ct_long_id = id;
10788048SMadhavan.Venkataraman@Sun.COM } else {
10798048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_short_id - callout_counter_low);
10808048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH;
10818048SMadhavan.Venkataraman@Sun.COM ct->ct_short_id = id;
10828048SMadhavan.Venkataraman@Sun.COM }
10838048SMadhavan.Venkataraman@Sun.COM } else {
10848048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_gen_id - callout_counter_low);
10858048SMadhavan.Venkataraman@Sun.COM if ((id & CALLOUT_COUNTER_HIGH) == 0) {
10868048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH;
10878048SMadhavan.Venkataraman@Sun.COM id += CALLOUT_GENERATION_LOW;
10888048SMadhavan.Venkataraman@Sun.COM }
10898048SMadhavan.Venkataraman@Sun.COM ct->ct_gen_id = id;
10908048SMadhavan.Venkataraman@Sun.COM }
10918048SMadhavan.Venkataraman@Sun.COM
10928048SMadhavan.Venkataraman@Sun.COM cp->c_xid = id;
10938048SMadhavan.Venkataraman@Sun.COM
10949334SMadhavan.Venkataraman@Sun.COM clflags = 0;
10959334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ABSOLUTE)
10969334SMadhavan.Venkataraman@Sun.COM clflags |= CALLOUT_LIST_FLAG_ABSOLUTE;
10979334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_HRESTIME)
10989334SMadhavan.Venkataraman@Sun.COM clflags |= CALLOUT_LIST_FLAG_HRESTIME;
10999334SMadhavan.Venkataraman@Sun.COM if (resolution == 1)
11009334SMadhavan.Venkataraman@Sun.COM clflags |= CALLOUT_LIST_FLAG_NANO;
11018048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(expiration);
11028048SMadhavan.Venkataraman@Sun.COM
11038048SMadhavan.Venkataraman@Sun.COM again:
11048048SMadhavan.Venkataraman@Sun.COM /*
11058048SMadhavan.Venkataraman@Sun.COM * Try to see if a callout list already exists for this expiration.
11068048SMadhavan.Venkataraman@Sun.COM */
11079334SMadhavan.Venkataraman@Sun.COM cl = callout_list_get(ct, expiration, clflags, hash);
11088048SMadhavan.Venkataraman@Sun.COM if (cl == NULL) {
11098048SMadhavan.Venkataraman@Sun.COM /*
11108048SMadhavan.Venkataraman@Sun.COM * Check the free list. If we don't find one, we have to
11118048SMadhavan.Venkataraman@Sun.COM * take the slow path and allocate from kmem.
11128048SMadhavan.Venkataraman@Sun.COM */
11138048SMadhavan.Venkataraman@Sun.COM if ((cl = ct->ct_lfree) == NULL) {
11148048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(ct);
11158048SMadhavan.Venkataraman@Sun.COM /*
11168048SMadhavan.Venkataraman@Sun.COM * In the above call, we drop the lock, allocate and
11178048SMadhavan.Venkataraman@Sun.COM * reacquire the lock. So, we could have been away
11188048SMadhavan.Venkataraman@Sun.COM * for a while. In the meantime, someone could have
11198048SMadhavan.Venkataraman@Sun.COM * inserted a callout list with the same expiration.
11208048SMadhavan.Venkataraman@Sun.COM * Plus, the heap could have become full. So, the best
11218048SMadhavan.Venkataraman@Sun.COM * course is to repeat the steps. This should be an
11228048SMadhavan.Venkataraman@Sun.COM * infrequent event.
11238048SMadhavan.Venkataraman@Sun.COM */
11248048SMadhavan.Venkataraman@Sun.COM goto again;
11258048SMadhavan.Venkataraman@Sun.COM }
11268048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl->cl_next;
11278048SMadhavan.Venkataraman@Sun.COM cl->cl_expiration = expiration;
11289334SMadhavan.Venkataraman@Sun.COM cl->cl_flags = clflags;
11298048SMadhavan.Venkataraman@Sun.COM
113011745SMadhavan.Venkataraman@Sun.COM /*
113111745SMadhavan.Venkataraman@Sun.COM * Check if we have enough space in the heap to insert one
113211745SMadhavan.Venkataraman@Sun.COM * expiration. If not, expand the heap.
113311745SMadhavan.Venkataraman@Sun.COM */
113411745SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == ct->ct_heap_max) {
113511745SMadhavan.Venkataraman@Sun.COM if (callout_heap_expand(ct) == 0) {
113611745SMadhavan.Venkataraman@Sun.COM /*
113711745SMadhavan.Venkataraman@Sun.COM * Could not expand the heap. Just queue it.
113811745SMadhavan.Venkataraman@Sun.COM */
113911745SMadhavan.Venkataraman@Sun.COM callout_queue_insert(ct, cl);
114011745SMadhavan.Venkataraman@Sun.COM goto out;
114111745SMadhavan.Venkataraman@Sun.COM }
114211745SMadhavan.Venkataraman@Sun.COM
114311745SMadhavan.Venkataraman@Sun.COM /*
114411745SMadhavan.Venkataraman@Sun.COM * In the above call, we drop the lock, allocate and
114511745SMadhavan.Venkataraman@Sun.COM * reacquire the lock. So, we could have been away
114611745SMadhavan.Venkataraman@Sun.COM * for a while. In the meantime, someone could have
114711745SMadhavan.Venkataraman@Sun.COM * inserted a callout list with the same expiration.
114811745SMadhavan.Venkataraman@Sun.COM * But we will not go back and check for it as this
114911745SMadhavan.Venkataraman@Sun.COM * should be a really infrequent event. There is no
115011745SMadhavan.Venkataraman@Sun.COM * point.
115111745SMadhavan.Venkataraman@Sun.COM */
115211745SMadhavan.Venkataraman@Sun.COM }
115311745SMadhavan.Venkataraman@Sun.COM
11549334SMadhavan.Venkataraman@Sun.COM if (clflags & CALLOUT_LIST_FLAG_NANO) {
11559334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_clhash[hash], cl);
11569334SMadhavan.Venkataraman@Sun.COM } else {
11579334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl);
11589334SMadhavan.Venkataraman@Sun.COM }
11598048SMadhavan.Venkataraman@Sun.COM
11608048SMadhavan.Venkataraman@Sun.COM /*
11618048SMadhavan.Venkataraman@Sun.COM * This is a new expiration. So, insert it into the heap.
11628048SMadhavan.Venkataraman@Sun.COM * This will also reprogram the cyclic, if the expiration
11638048SMadhavan.Venkataraman@Sun.COM * propagated to the root of the heap.
11648048SMadhavan.Venkataraman@Sun.COM */
11659334SMadhavan.Venkataraman@Sun.COM callout_heap_insert(ct, cl);
11669334SMadhavan.Venkataraman@Sun.COM } else {
11679334SMadhavan.Venkataraman@Sun.COM /*
11689334SMadhavan.Venkataraman@Sun.COM * If the callout list was empty, untimeout_generic() would
11699334SMadhavan.Venkataraman@Sun.COM * have incremented a reap count. Decrement the reap count
11709334SMadhavan.Venkataraman@Sun.COM * as we are going to insert a callout into this list.
11719334SMadhavan.Venkataraman@Sun.COM */
11729334SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL)
11739334SMadhavan.Venkataraman@Sun.COM ct->ct_nreap--;
11748048SMadhavan.Venkataraman@Sun.COM }
117511745SMadhavan.Venkataraman@Sun.COM out:
11768048SMadhavan.Venkataraman@Sun.COM cp->c_list = cl;
11778048SMadhavan.Venkataraman@Sun.COM CALLOUT_APPEND(ct, cp);
11788048SMadhavan.Venkataraman@Sun.COM
11798048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts++;
11808048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending++;
11818048SMadhavan.Venkataraman@Sun.COM
11828048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
11838048SMadhavan.Venkataraman@Sun.COM
11848048SMadhavan.Venkataraman@Sun.COM kpreempt_enable();
11858048SMadhavan.Venkataraman@Sun.COM
11868048SMadhavan.Venkataraman@Sun.COM TRACE_4(TR_FAC_CALLOUT, TR_TIMEOUT,
11878048SMadhavan.Venkataraman@Sun.COM "timeout:%K(%p) in %llx expiration, cp %p", func, arg, expiration,
11888048SMadhavan.Venkataraman@Sun.COM cp);
11898048SMadhavan.Venkataraman@Sun.COM
11908048SMadhavan.Venkataraman@Sun.COM return (id);
11918048SMadhavan.Venkataraman@Sun.COM }
11928048SMadhavan.Venkataraman@Sun.COM
11938048SMadhavan.Venkataraman@Sun.COM timeout_id_t
timeout(void (* func)(void *),void * arg,clock_t delta)11948048SMadhavan.Venkataraman@Sun.COM timeout(void (*func)(void *), void *arg, clock_t delta)
11958048SMadhavan.Venkataraman@Sun.COM {
11968048SMadhavan.Venkataraman@Sun.COM ulong_t id;
11978048SMadhavan.Venkataraman@Sun.COM
11988048SMadhavan.Venkataraman@Sun.COM /*
11990Sstevel@tonic-gate * Make sure the callout runs at least 1 tick in the future.
12000Sstevel@tonic-gate */
12010Sstevel@tonic-gate if (delta <= 0)
12020Sstevel@tonic-gate delta = 1;
12038566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks)
12048566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks;
12050Sstevel@tonic-gate
12068048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)timeout_generic(CALLOUT_NORMAL, func, arg,
12078048SMadhavan.Venkataraman@Sun.COM TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
12080Sstevel@tonic-gate
12090Sstevel@tonic-gate return ((timeout_id_t)id);
12100Sstevel@tonic-gate }
12110Sstevel@tonic-gate
12128048SMadhavan.Venkataraman@Sun.COM /*
12138048SMadhavan.Venkataraman@Sun.COM * Convenience function that creates a normal callout with default parameters
12148048SMadhavan.Venkataraman@Sun.COM * and returns a full ID.
12158048SMadhavan.Venkataraman@Sun.COM */
12168048SMadhavan.Venkataraman@Sun.COM callout_id_t
timeout_default(void (* func)(void *),void * arg,clock_t delta)12178048SMadhavan.Venkataraman@Sun.COM timeout_default(void (*func)(void *), void *arg, clock_t delta)
12180Sstevel@tonic-gate {
12198048SMadhavan.Venkataraman@Sun.COM callout_id_t id;
12200Sstevel@tonic-gate
12218048SMadhavan.Venkataraman@Sun.COM /*
12228048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future.
12238048SMadhavan.Venkataraman@Sun.COM */
12248048SMadhavan.Venkataraman@Sun.COM if (delta <= 0)
12258048SMadhavan.Venkataraman@Sun.COM delta = 1;
12268566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks)
12278566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks;
12288048SMadhavan.Venkataraman@Sun.COM
12298048SMadhavan.Venkataraman@Sun.COM id = timeout_generic(CALLOUT_NORMAL, func, arg, TICK_TO_NSEC(delta),
12308048SMadhavan.Venkataraman@Sun.COM nsec_per_tick, 0);
12318048SMadhavan.Venkataraman@Sun.COM
12328048SMadhavan.Venkataraman@Sun.COM return (id);
12330Sstevel@tonic-gate }
12340Sstevel@tonic-gate
12350Sstevel@tonic-gate timeout_id_t
realtime_timeout(void (* func)(void *),void * arg,clock_t delta)12360Sstevel@tonic-gate realtime_timeout(void (*func)(void *), void *arg, clock_t delta)
12370Sstevel@tonic-gate {
12388048SMadhavan.Venkataraman@Sun.COM ulong_t id;
12398048SMadhavan.Venkataraman@Sun.COM
12408048SMadhavan.Venkataraman@Sun.COM /*
12418048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future.
12428048SMadhavan.Venkataraman@Sun.COM */
12438048SMadhavan.Venkataraman@Sun.COM if (delta <= 0)
12448048SMadhavan.Venkataraman@Sun.COM delta = 1;
12458566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks)
12468566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks;
12478048SMadhavan.Venkataraman@Sun.COM
12488048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)timeout_generic(CALLOUT_REALTIME, func, arg,
12498048SMadhavan.Venkataraman@Sun.COM TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY);
12508048SMadhavan.Venkataraman@Sun.COM
12518048SMadhavan.Venkataraman@Sun.COM return ((timeout_id_t)id);
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate
12548048SMadhavan.Venkataraman@Sun.COM /*
12558048SMadhavan.Venkataraman@Sun.COM * Convenience function that creates a realtime callout with default parameters
12568048SMadhavan.Venkataraman@Sun.COM * and returns a full ID.
12578048SMadhavan.Venkataraman@Sun.COM */
12588048SMadhavan.Venkataraman@Sun.COM callout_id_t
realtime_timeout_default(void (* func)(void *),void * arg,clock_t delta)12598048SMadhavan.Venkataraman@Sun.COM realtime_timeout_default(void (*func)(void *), void *arg, clock_t delta)
12600Sstevel@tonic-gate {
12618048SMadhavan.Venkataraman@Sun.COM callout_id_t id;
12628048SMadhavan.Venkataraman@Sun.COM
12638048SMadhavan.Venkataraman@Sun.COM /*
12648048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future.
12658048SMadhavan.Venkataraman@Sun.COM */
12668048SMadhavan.Venkataraman@Sun.COM if (delta <= 0)
12678048SMadhavan.Venkataraman@Sun.COM delta = 1;
12688566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks)
12698566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks;
12708048SMadhavan.Venkataraman@Sun.COM
12718048SMadhavan.Venkataraman@Sun.COM id = timeout_generic(CALLOUT_REALTIME, func, arg, TICK_TO_NSEC(delta),
12728048SMadhavan.Venkataraman@Sun.COM nsec_per_tick, 0);
12738048SMadhavan.Venkataraman@Sun.COM
12748048SMadhavan.Venkataraman@Sun.COM return (id);
12758048SMadhavan.Venkataraman@Sun.COM }
12768048SMadhavan.Venkataraman@Sun.COM
12778048SMadhavan.Venkataraman@Sun.COM hrtime_t
untimeout_generic(callout_id_t id,int nowait)12788048SMadhavan.Venkataraman@Sun.COM untimeout_generic(callout_id_t id, int nowait)
12798048SMadhavan.Venkataraman@Sun.COM {
12800Sstevel@tonic-gate callout_table_t *ct;
12810Sstevel@tonic-gate callout_t *cp;
12820Sstevel@tonic-gate callout_id_t xid;
12839334SMadhavan.Venkataraman@Sun.COM callout_list_t *cl;
128411745SMadhavan.Venkataraman@Sun.COM int hash, flags;
12858048SMadhavan.Venkataraman@Sun.COM callout_id_t bogus;
12860Sstevel@tonic-gate
12878048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_ID_TO_TABLE(id)];
12888048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_IDHASH(id);
12898048SMadhavan.Venkataraman@Sun.COM
12908048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
12910Sstevel@tonic-gate
12928048SMadhavan.Venkataraman@Sun.COM /*
12938048SMadhavan.Venkataraman@Sun.COM * Search the ID hash table for the callout.
12948048SMadhavan.Venkataraman@Sun.COM */
12958048SMadhavan.Venkataraman@Sun.COM for (cp = ct->ct_idhash[hash].ch_head; cp; cp = cp->c_idnext) {
12960Sstevel@tonic-gate
12978048SMadhavan.Venkataraman@Sun.COM xid = cp->c_xid;
12988048SMadhavan.Venkataraman@Sun.COM
12998048SMadhavan.Venkataraman@Sun.COM /*
13008048SMadhavan.Venkataraman@Sun.COM * Match the ID and generation number.
13018048SMadhavan.Venkataraman@Sun.COM */
13028048SMadhavan.Venkataraman@Sun.COM if ((xid & CALLOUT_ID_MASK) != id)
13038048SMadhavan.Venkataraman@Sun.COM continue;
13040Sstevel@tonic-gate
13058048SMadhavan.Venkataraman@Sun.COM if ((xid & CALLOUT_EXECUTING) == 0) {
13068048SMadhavan.Venkataraman@Sun.COM hrtime_t expiration;
13070Sstevel@tonic-gate
13088048SMadhavan.Venkataraman@Sun.COM /*
13098048SMadhavan.Venkataraman@Sun.COM * Delete the callout. If the callout list becomes
13108048SMadhavan.Venkataraman@Sun.COM * NULL, we don't remove it from the table. This is
13118048SMadhavan.Venkataraman@Sun.COM * so it can be reused. If the empty callout list
13128048SMadhavan.Venkataraman@Sun.COM * corresponds to the top of the the callout heap, we
13138048SMadhavan.Venkataraman@Sun.COM * don't reprogram the table cyclic here. This is in
13148048SMadhavan.Venkataraman@Sun.COM * order to avoid lots of X-calls to the CPU associated
13158048SMadhavan.Venkataraman@Sun.COM * with the callout table.
13168048SMadhavan.Venkataraman@Sun.COM */
13179334SMadhavan.Venkataraman@Sun.COM cl = cp->c_list;
13189334SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration;
13198048SMadhavan.Venkataraman@Sun.COM CALLOUT_DELETE(ct, cp);
132011745SMadhavan.Venkataraman@Sun.COM CALLOUT_FREE(ct, cp);
13218048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_unexpired++;
13228048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending--;
13239334SMadhavan.Venkataraman@Sun.COM
13249334SMadhavan.Venkataraman@Sun.COM /*
132511745SMadhavan.Venkataraman@Sun.COM * If the callout list has become empty, there are 3
132611745SMadhavan.Venkataraman@Sun.COM * possibilities. If it is present:
132711745SMadhavan.Venkataraman@Sun.COM * - in the heap, it needs to be cleaned along
132811745SMadhavan.Venkataraman@Sun.COM * with its heap entry. Increment a reap count.
132911745SMadhavan.Venkataraman@Sun.COM * - in the callout queue, free it.
133011745SMadhavan.Venkataraman@Sun.COM * - in the expired list, free it.
13319334SMadhavan.Venkataraman@Sun.COM */
133211745SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) {
133311745SMadhavan.Venkataraman@Sun.COM flags = cl->cl_flags;
133411745SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_LIST_FLAG_HEAPED) {
133511745SMadhavan.Venkataraman@Sun.COM ct->ct_nreap++;
133611745SMadhavan.Venkataraman@Sun.COM } else if (flags & CALLOUT_LIST_FLAG_QUEUED) {
133711745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_queue, cl);
133811745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl);
133911745SMadhavan.Venkataraman@Sun.COM } else {
134011745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_expired, cl);
134111745SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl);
134211745SMadhavan.Venkataraman@Sun.COM }
134311745SMadhavan.Venkataraman@Sun.COM }
13448048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
13456422Sqiao
13468048SMadhavan.Venkataraman@Sun.COM expiration -= gethrtime();
13470Sstevel@tonic-gate TRACE_2(TR_FAC_CALLOUT, TR_UNTIMEOUT,
13488048SMadhavan.Venkataraman@Sun.COM "untimeout:ID %lx hrtime left %llx", id,
13498048SMadhavan.Venkataraman@Sun.COM expiration);
13508048SMadhavan.Venkataraman@Sun.COM return (expiration < 0 ? 0 : expiration);
13510Sstevel@tonic-gate }
13520Sstevel@tonic-gate
13538048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_executing++;
13540Sstevel@tonic-gate /*
13550Sstevel@tonic-gate * The callout we want to delete is currently executing.
13560Sstevel@tonic-gate * The DDI states that we must wait until the callout
13579039SMadhavan.Venkataraman@Sun.COM * completes before returning, so we block on c_done until the
13588048SMadhavan.Venkataraman@Sun.COM * callout ID changes (to the old ID if it's on the freelist,
13590Sstevel@tonic-gate * or to a new callout ID if it's in use). This implicitly
13600Sstevel@tonic-gate * assumes that callout structures are persistent (they are).
13610Sstevel@tonic-gate */
13629039SMadhavan.Venkataraman@Sun.COM if (cp->c_executor == curthread) {
13630Sstevel@tonic-gate /*
13640Sstevel@tonic-gate * The timeout handler called untimeout() on itself.
13650Sstevel@tonic-gate * Stupid, but legal. We can't wait for the timeout
13660Sstevel@tonic-gate * to complete without deadlocking, so we just return.
13670Sstevel@tonic-gate */
13688048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
13690Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_SELF,
13700Sstevel@tonic-gate "untimeout_self:ID %x", id);
13710Sstevel@tonic-gate return (-1);
13720Sstevel@tonic-gate }
13738048SMadhavan.Venkataraman@Sun.COM if (nowait == 0) {
13748048SMadhavan.Venkataraman@Sun.COM /*
13758048SMadhavan.Venkataraman@Sun.COM * We need to wait. Indicate that we are waiting by
13769039SMadhavan.Venkataraman@Sun.COM * incrementing c_waiting. This prevents the executor
13779039SMadhavan.Venkataraman@Sun.COM * from doing a wakeup on c_done if there are no
13788048SMadhavan.Venkataraman@Sun.COM * waiters.
13798048SMadhavan.Venkataraman@Sun.COM */
13808048SMadhavan.Venkataraman@Sun.COM while (cp->c_xid == xid) {
13819039SMadhavan.Venkataraman@Sun.COM cp->c_waiting = 1;
13829039SMadhavan.Venkataraman@Sun.COM cv_wait(&cp->c_done, &ct->ct_mutex);
13838048SMadhavan.Venkataraman@Sun.COM }
13848048SMadhavan.Venkataraman@Sun.COM }
13858048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
13860Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_EXECUTING,
13870Sstevel@tonic-gate "untimeout_executing:ID %lx", id);
13880Sstevel@tonic-gate return (-1);
13890Sstevel@tonic-gate }
13908048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_expired++;
13910Sstevel@tonic-gate
13928048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
13930Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_BOGUS_ID,
13940Sstevel@tonic-gate "untimeout_bogus_id:ID %lx", id);
13950Sstevel@tonic-gate
13960Sstevel@tonic-gate /*
13970Sstevel@tonic-gate * We didn't find the specified callout ID. This means either
13980Sstevel@tonic-gate * (1) the callout already fired, or (2) the caller passed us
13990Sstevel@tonic-gate * a bogus value. Perform a sanity check to detect case (2).
14000Sstevel@tonic-gate */
14019334SMadhavan.Venkataraman@Sun.COM bogus = (CALLOUT_ID_FLAGS | CALLOUT_COUNTER_HIGH);
14028048SMadhavan.Venkataraman@Sun.COM if (((id & bogus) != CALLOUT_COUNTER_HIGH) && (id != 0))
14038048SMadhavan.Venkataraman@Sun.COM panic("untimeout: impossible timeout id %llx",
14048048SMadhavan.Venkataraman@Sun.COM (unsigned long long)id);
14050Sstevel@tonic-gate
14060Sstevel@tonic-gate return (-1);
14070Sstevel@tonic-gate }
14080Sstevel@tonic-gate
14098048SMadhavan.Venkataraman@Sun.COM clock_t
untimeout(timeout_id_t id_arg)14108048SMadhavan.Venkataraman@Sun.COM untimeout(timeout_id_t id_arg)
14118048SMadhavan.Venkataraman@Sun.COM {
14128048SMadhavan.Venkataraman@Sun.COM hrtime_t hleft;
14138048SMadhavan.Venkataraman@Sun.COM clock_t tleft;
14148048SMadhavan.Venkataraman@Sun.COM callout_id_t id;
14158048SMadhavan.Venkataraman@Sun.COM
14168048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)id_arg;
14178048SMadhavan.Venkataraman@Sun.COM hleft = untimeout_generic(id, 0);
14188048SMadhavan.Venkataraman@Sun.COM if (hleft < 0)
14198048SMadhavan.Venkataraman@Sun.COM tleft = -1;
14208048SMadhavan.Venkataraman@Sun.COM else if (hleft == 0)
14218048SMadhavan.Venkataraman@Sun.COM tleft = 0;
14228048SMadhavan.Venkataraman@Sun.COM else
14238048SMadhavan.Venkataraman@Sun.COM tleft = NSEC_TO_TICK(hleft);
14248048SMadhavan.Venkataraman@Sun.COM
14258048SMadhavan.Venkataraman@Sun.COM return (tleft);
14268048SMadhavan.Venkataraman@Sun.COM }
14278048SMadhavan.Venkataraman@Sun.COM
14280Sstevel@tonic-gate /*
14298048SMadhavan.Venkataraman@Sun.COM * Convenience function to untimeout a timeout with a full ID with default
14308048SMadhavan.Venkataraman@Sun.COM * parameters.
14318048SMadhavan.Venkataraman@Sun.COM */
14328048SMadhavan.Venkataraman@Sun.COM clock_t
untimeout_default(callout_id_t id,int nowait)14338048SMadhavan.Venkataraman@Sun.COM untimeout_default(callout_id_t id, int nowait)
14348048SMadhavan.Venkataraman@Sun.COM {
14358048SMadhavan.Venkataraman@Sun.COM hrtime_t hleft;
14368048SMadhavan.Venkataraman@Sun.COM clock_t tleft;
14378048SMadhavan.Venkataraman@Sun.COM
14388048SMadhavan.Venkataraman@Sun.COM hleft = untimeout_generic(id, nowait);
14398048SMadhavan.Venkataraman@Sun.COM if (hleft < 0)
14408048SMadhavan.Venkataraman@Sun.COM tleft = -1;
14418048SMadhavan.Venkataraman@Sun.COM else if (hleft == 0)
14428048SMadhavan.Venkataraman@Sun.COM tleft = 0;
14438048SMadhavan.Venkataraman@Sun.COM else
14448048SMadhavan.Venkataraman@Sun.COM tleft = NSEC_TO_TICK(hleft);
14458048SMadhavan.Venkataraman@Sun.COM
14468048SMadhavan.Venkataraman@Sun.COM return (tleft);
14478048SMadhavan.Venkataraman@Sun.COM }
14488048SMadhavan.Venkataraman@Sun.COM
14498048SMadhavan.Venkataraman@Sun.COM /*
14508048SMadhavan.Venkataraman@Sun.COM * Expire all the callouts queued in the specified callout list.
14510Sstevel@tonic-gate */
14520Sstevel@tonic-gate static void
callout_list_expire(callout_table_t * ct,callout_list_t * cl)14538048SMadhavan.Venkataraman@Sun.COM callout_list_expire(callout_table_t *ct, callout_list_t *cl)
14540Sstevel@tonic-gate {
14559039SMadhavan.Venkataraman@Sun.COM callout_t *cp, *cnext;
14568048SMadhavan.Venkataraman@Sun.COM
14578048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
14588048SMadhavan.Venkataraman@Sun.COM ASSERT(cl != NULL);
14598048SMadhavan.Venkataraman@Sun.COM
14609039SMadhavan.Venkataraman@Sun.COM for (cp = cl->cl_callouts.ch_head; cp != NULL; cp = cnext) {
14619039SMadhavan.Venkataraman@Sun.COM /*
14629039SMadhavan.Venkataraman@Sun.COM * Multiple executor threads could be running at the same
14639039SMadhavan.Venkataraman@Sun.COM * time. If this callout is already being executed,
14649039SMadhavan.Venkataraman@Sun.COM * go on to the next one.
14659039SMadhavan.Venkataraman@Sun.COM */
14669039SMadhavan.Venkataraman@Sun.COM if (cp->c_xid & CALLOUT_EXECUTING) {
14679039SMadhavan.Venkataraman@Sun.COM cnext = cp->c_clnext;
14689039SMadhavan.Venkataraman@Sun.COM continue;
14699039SMadhavan.Venkataraman@Sun.COM }
14708048SMadhavan.Venkataraman@Sun.COM
14718048SMadhavan.Venkataraman@Sun.COM /*
14728048SMadhavan.Venkataraman@Sun.COM * Indicate to untimeout() that a callout is
14738048SMadhavan.Venkataraman@Sun.COM * being expired by the executor.
14748048SMadhavan.Venkataraman@Sun.COM */
14758048SMadhavan.Venkataraman@Sun.COM cp->c_xid |= CALLOUT_EXECUTING;
14769039SMadhavan.Venkataraman@Sun.COM cp->c_executor = curthread;
14778048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
14788048SMadhavan.Venkataraman@Sun.COM
14798048SMadhavan.Venkataraman@Sun.COM DTRACE_PROBE1(callout__start, callout_t *, cp);
14808048SMadhavan.Venkataraman@Sun.COM (*cp->c_func)(cp->c_arg);
14818048SMadhavan.Venkataraman@Sun.COM DTRACE_PROBE1(callout__end, callout_t *, cp);
14820Sstevel@tonic-gate
14838048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
14848048SMadhavan.Venkataraman@Sun.COM
14858048SMadhavan.Venkataraman@Sun.COM ct->ct_expirations++;
14868048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending--;
14878048SMadhavan.Venkataraman@Sun.COM /*
14889039SMadhavan.Venkataraman@Sun.COM * Indicate completion for c_done.
14898048SMadhavan.Venkataraman@Sun.COM */
14908048SMadhavan.Venkataraman@Sun.COM cp->c_xid &= ~CALLOUT_EXECUTING;
14919039SMadhavan.Venkataraman@Sun.COM cp->c_executor = NULL;
14929039SMadhavan.Venkataraman@Sun.COM cnext = cp->c_clnext;
14938048SMadhavan.Venkataraman@Sun.COM
14948048SMadhavan.Venkataraman@Sun.COM /*
14958048SMadhavan.Venkataraman@Sun.COM * Delete callout from ID hash table and the callout
14968048SMadhavan.Venkataraman@Sun.COM * list, return to freelist, and tell any untimeout() that
14978048SMadhavan.Venkataraman@Sun.COM * cares that we're done.
14988048SMadhavan.Venkataraman@Sun.COM */
14998048SMadhavan.Venkataraman@Sun.COM CALLOUT_DELETE(ct, cp);
150011745SMadhavan.Venkataraman@Sun.COM CALLOUT_FREE(ct, cp);
15010Sstevel@tonic-gate
15029039SMadhavan.Venkataraman@Sun.COM if (cp->c_waiting) {
15039039SMadhavan.Venkataraman@Sun.COM cp->c_waiting = 0;
15049039SMadhavan.Venkataraman@Sun.COM cv_broadcast(&cp->c_done);
15058048SMadhavan.Venkataraman@Sun.COM }
15068048SMadhavan.Venkataraman@Sun.COM }
15078048SMadhavan.Venkataraman@Sun.COM }
15088048SMadhavan.Venkataraman@Sun.COM
15098048SMadhavan.Venkataraman@Sun.COM /*
15108048SMadhavan.Venkataraman@Sun.COM * Execute all expired callout lists for a callout table.
15118048SMadhavan.Venkataraman@Sun.COM */
15128048SMadhavan.Venkataraman@Sun.COM static void
callout_expire(callout_table_t * ct)15138048SMadhavan.Venkataraman@Sun.COM callout_expire(callout_table_t *ct)
15148048SMadhavan.Venkataraman@Sun.COM {
15158048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl, *clnext;
15168048SMadhavan.Venkataraman@Sun.COM
15178048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
15180Sstevel@tonic-gate
15198048SMadhavan.Venkataraman@Sun.COM for (cl = ct->ct_expired.ch_head; (cl != NULL); cl = clnext) {
15208048SMadhavan.Venkataraman@Sun.COM /*
15218048SMadhavan.Venkataraman@Sun.COM * Expire all the callouts in this callout list.
15228048SMadhavan.Venkataraman@Sun.COM */
15238048SMadhavan.Venkataraman@Sun.COM callout_list_expire(ct, cl);
15248048SMadhavan.Venkataraman@Sun.COM
15258048SMadhavan.Venkataraman@Sun.COM clnext = cl->cl_next;
15269039SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) {
15279039SMadhavan.Venkataraman@Sun.COM /*
15289039SMadhavan.Venkataraman@Sun.COM * Free the callout list.
15299039SMadhavan.Venkataraman@Sun.COM */
15309039SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_expired, cl);
15319334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl);
15329039SMadhavan.Venkataraman@Sun.COM }
15330Sstevel@tonic-gate }
15340Sstevel@tonic-gate }
15350Sstevel@tonic-gate
15360Sstevel@tonic-gate /*
15378048SMadhavan.Venkataraman@Sun.COM * The cyclic handlers below process callouts in two steps:
15388048SMadhavan.Venkataraman@Sun.COM *
15398048SMadhavan.Venkataraman@Sun.COM * 1. Find all expired callout lists and queue them in a separate
15408048SMadhavan.Venkataraman@Sun.COM * list of expired callouts.
15418048SMadhavan.Venkataraman@Sun.COM * 2. Execute the expired callout lists.
15428048SMadhavan.Venkataraman@Sun.COM *
15438048SMadhavan.Venkataraman@Sun.COM * This is done for two reasons:
15448048SMadhavan.Venkataraman@Sun.COM *
15458048SMadhavan.Venkataraman@Sun.COM * 1. We want to quickly find the next earliest expiration to program
15468048SMadhavan.Venkataraman@Sun.COM * the cyclic to and reprogram it. We can do this right at the end
15478048SMadhavan.Venkataraman@Sun.COM * of step 1.
15488048SMadhavan.Venkataraman@Sun.COM * 2. The realtime cyclic handler expires callouts in place. However,
15498048SMadhavan.Venkataraman@Sun.COM * for normal callouts, callouts are expired by a taskq thread.
15508048SMadhavan.Venkataraman@Sun.COM * So, it is simpler and more robust to have the taskq thread just
15518048SMadhavan.Venkataraman@Sun.COM * do step 2.
15520Sstevel@tonic-gate */
15536422Sqiao
15548048SMadhavan.Venkataraman@Sun.COM /*
155511745SMadhavan.Venkataraman@Sun.COM * Realtime callout cyclic handlers.
15568048SMadhavan.Venkataraman@Sun.COM */
15578048SMadhavan.Venkataraman@Sun.COM void
callout_realtime(callout_table_t * ct)15588048SMadhavan.Venkataraman@Sun.COM callout_realtime(callout_table_t *ct)
15598048SMadhavan.Venkataraman@Sun.COM {
15608048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
156111745SMadhavan.Venkataraman@Sun.COM (void) callout_heap_delete(ct);
156211745SMadhavan.Venkataraman@Sun.COM callout_expire(ct);
156311745SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
156411745SMadhavan.Venkataraman@Sun.COM }
156511745SMadhavan.Venkataraman@Sun.COM
156611745SMadhavan.Venkataraman@Sun.COM void
callout_queue_realtime(callout_table_t * ct)156711745SMadhavan.Venkataraman@Sun.COM callout_queue_realtime(callout_table_t *ct)
156811745SMadhavan.Venkataraman@Sun.COM {
156911745SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
157011745SMadhavan.Venkataraman@Sun.COM (void) callout_queue_delete(ct);
15718048SMadhavan.Venkataraman@Sun.COM callout_expire(ct);
15728048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
15738048SMadhavan.Venkataraman@Sun.COM }
15748048SMadhavan.Venkataraman@Sun.COM
15758048SMadhavan.Venkataraman@Sun.COM void
callout_execute(callout_table_t * ct)15768048SMadhavan.Venkataraman@Sun.COM callout_execute(callout_table_t *ct)
15778048SMadhavan.Venkataraman@Sun.COM {
15788048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
15798048SMadhavan.Venkataraman@Sun.COM callout_expire(ct);
15808048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
15818048SMadhavan.Venkataraman@Sun.COM }
15828048SMadhavan.Venkataraman@Sun.COM
15838048SMadhavan.Venkataraman@Sun.COM /*
158411745SMadhavan.Venkataraman@Sun.COM * Normal callout cyclic handlers.
15858048SMadhavan.Venkataraman@Sun.COM */
15868048SMadhavan.Venkataraman@Sun.COM void
callout_normal(callout_table_t * ct)15878048SMadhavan.Venkataraman@Sun.COM callout_normal(callout_table_t *ct)
15888048SMadhavan.Venkataraman@Sun.COM {
15899039SMadhavan.Venkataraman@Sun.COM int i, exec;
159011745SMadhavan.Venkataraman@Sun.COM hrtime_t exp;
15918048SMadhavan.Venkataraman@Sun.COM
15928048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
159311745SMadhavan.Venkataraman@Sun.COM exp = callout_heap_delete(ct);
159411745SMadhavan.Venkataraman@Sun.COM CALLOUT_EXEC_COMPUTE(ct, exp, exec);
159511745SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
159611745SMadhavan.Venkataraman@Sun.COM
159711745SMadhavan.Venkataraman@Sun.COM for (i = 0; i < exec; i++) {
159811745SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_taskq != NULL);
159911745SMadhavan.Venkataraman@Sun.COM (void) taskq_dispatch(ct->ct_taskq,
160011745SMadhavan.Venkataraman@Sun.COM (task_func_t *)callout_execute, ct, TQ_NOSLEEP);
160111745SMadhavan.Venkataraman@Sun.COM }
160211745SMadhavan.Venkataraman@Sun.COM }
160311745SMadhavan.Venkataraman@Sun.COM
160411745SMadhavan.Venkataraman@Sun.COM void
callout_queue_normal(callout_table_t * ct)160511745SMadhavan.Venkataraman@Sun.COM callout_queue_normal(callout_table_t *ct)
160611745SMadhavan.Venkataraman@Sun.COM {
160711745SMadhavan.Venkataraman@Sun.COM int i, exec;
160811745SMadhavan.Venkataraman@Sun.COM hrtime_t exp;
160911745SMadhavan.Venkataraman@Sun.COM
161011745SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
161111745SMadhavan.Venkataraman@Sun.COM exp = callout_queue_delete(ct);
161211745SMadhavan.Venkataraman@Sun.COM CALLOUT_EXEC_COMPUTE(ct, exp, exec);
16138048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
16148048SMadhavan.Venkataraman@Sun.COM
16159039SMadhavan.Venkataraman@Sun.COM for (i = 0; i < exec; i++) {
16168048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_taskq != NULL);
16178048SMadhavan.Venkataraman@Sun.COM (void) taskq_dispatch(ct->ct_taskq,
16188048SMadhavan.Venkataraman@Sun.COM (task_func_t *)callout_execute, ct, TQ_NOSLEEP);
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate }
16210Sstevel@tonic-gate
16220Sstevel@tonic-gate /*
16238048SMadhavan.Venkataraman@Sun.COM * Suspend callout processing.
16240Sstevel@tonic-gate */
16258048SMadhavan.Venkataraman@Sun.COM static void
callout_suspend(void)16268048SMadhavan.Venkataraman@Sun.COM callout_suspend(void)
16270Sstevel@tonic-gate {
16288048SMadhavan.Venkataraman@Sun.COM int t, f;
16298048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct;
16308048SMadhavan.Venkataraman@Sun.COM
16318048SMadhavan.Venkataraman@Sun.COM /*
16328048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and suspend callout
16338048SMadhavan.Venkataraman@Sun.COM * processing.
16348048SMadhavan.Venkataraman@Sun.COM *
16358048SMadhavan.Venkataraman@Sun.COM * We need to suspend all the tables (including the inactive ones)
16368048SMadhavan.Venkataraman@Sun.COM * so that if a table is made active while the suspend is still on,
16378048SMadhavan.Venkataraman@Sun.COM * the table remains suspended.
16388048SMadhavan.Venkataraman@Sun.COM */
16398048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) {
16408048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) {
16418048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)];
16428048SMadhavan.Venkataraman@Sun.COM
16438048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
16448566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend++;
16458048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) {
16468048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
16478048SMadhavan.Venkataraman@Sun.COM continue;
16488048SMadhavan.Venkataraman@Sun.COM }
164911745SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 1) {
16508566SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic,
16518566SMadhavan.Venkataraman@Sun.COM CY_INFINITY);
165211745SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_qcyclic,
165311745SMadhavan.Venkataraman@Sun.COM CY_INFINITY);
165411745SMadhavan.Venkataraman@Sun.COM }
16558048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
16568048SMadhavan.Venkataraman@Sun.COM }
16578048SMadhavan.Venkataraman@Sun.COM }
16588048SMadhavan.Venkataraman@Sun.COM }
16598048SMadhavan.Venkataraman@Sun.COM
16608048SMadhavan.Venkataraman@Sun.COM /*
16618048SMadhavan.Venkataraman@Sun.COM * Resume callout processing.
16628048SMadhavan.Venkataraman@Sun.COM */
16638048SMadhavan.Venkataraman@Sun.COM static void
callout_resume(hrtime_t delta,int timechange)16649334SMadhavan.Venkataraman@Sun.COM callout_resume(hrtime_t delta, int timechange)
16658048SMadhavan.Venkataraman@Sun.COM {
166611745SMadhavan.Venkataraman@Sun.COM hrtime_t hexp, qexp;
16678048SMadhavan.Venkataraman@Sun.COM int t, f;
16688048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct;
16690Sstevel@tonic-gate
16708048SMadhavan.Venkataraman@Sun.COM /*
16718048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and resume callout
16728048SMadhavan.Venkataraman@Sun.COM * processing. For active tables, perform any hrtime adjustments
16738048SMadhavan.Venkataraman@Sun.COM * necessary.
16748048SMadhavan.Venkataraman@Sun.COM */
16758048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) {
16768048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) {
16778048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)];
16788048SMadhavan.Venkataraman@Sun.COM
16798048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
16808048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) {
16818566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend--;
16828048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
16838048SMadhavan.Venkataraman@Sun.COM continue;
16848048SMadhavan.Venkataraman@Sun.COM }
16858048SMadhavan.Venkataraman@Sun.COM
16869334SMadhavan.Venkataraman@Sun.COM /*
16879334SMadhavan.Venkataraman@Sun.COM * If a delta is specified, adjust the expirations in
16889334SMadhavan.Venkataraman@Sun.COM * the heap by delta. Also, if the caller indicates
16899334SMadhavan.Venkataraman@Sun.COM * a timechange, process that. This step also cleans
16909334SMadhavan.Venkataraman@Sun.COM * out any empty callout lists that might happen to
16919334SMadhavan.Venkataraman@Sun.COM * be there.
16929334SMadhavan.Venkataraman@Sun.COM */
169311745SMadhavan.Venkataraman@Sun.COM hexp = callout_heap_process(ct, delta, timechange);
169411745SMadhavan.Venkataraman@Sun.COM qexp = callout_queue_process(ct, delta, timechange);
16958048SMadhavan.Venkataraman@Sun.COM
16968566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend--;
16978566SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 0) {
169811745SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, hexp);
169911745SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_qcyclic, qexp);
17008566SMadhavan.Venkataraman@Sun.COM }
17019334SMadhavan.Venkataraman@Sun.COM
17028048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
17038048SMadhavan.Venkataraman@Sun.COM }
17048048SMadhavan.Venkataraman@Sun.COM }
17050Sstevel@tonic-gate }
17060Sstevel@tonic-gate
17070Sstevel@tonic-gate /*
17080Sstevel@tonic-gate * Callback handler used by CPR to stop and resume callouts.
17099334SMadhavan.Venkataraman@Sun.COM * The cyclic subsystem saves and restores hrtime during CPR.
17109334SMadhavan.Venkataraman@Sun.COM * That is why callout_resume() is called with a 0 delta.
17119334SMadhavan.Venkataraman@Sun.COM * Although hrtime is the same, hrestime (system time) has
17129334SMadhavan.Venkataraman@Sun.COM * progressed during CPR. So, we have to indicate a time change
17139334SMadhavan.Venkataraman@Sun.COM * to expire the absolute hrestime timers.
17140Sstevel@tonic-gate */
17150Sstevel@tonic-gate /*ARGSUSED*/
17160Sstevel@tonic-gate static boolean_t
callout_cpr_callb(void * arg,int code)17170Sstevel@tonic-gate callout_cpr_callb(void *arg, int code)
17180Sstevel@tonic-gate {
17198048SMadhavan.Venkataraman@Sun.COM if (code == CB_CODE_CPR_CHKPT)
17208048SMadhavan.Venkataraman@Sun.COM callout_suspend();
17218048SMadhavan.Venkataraman@Sun.COM else
17229334SMadhavan.Venkataraman@Sun.COM callout_resume(0, 1);
17238048SMadhavan.Venkataraman@Sun.COM
17248048SMadhavan.Venkataraman@Sun.COM return (B_TRUE);
17258048SMadhavan.Venkataraman@Sun.COM }
17268048SMadhavan.Venkataraman@Sun.COM
17278048SMadhavan.Venkataraman@Sun.COM /*
17288048SMadhavan.Venkataraman@Sun.COM * Callback handler invoked when the debugger is entered or exited.
17298048SMadhavan.Venkataraman@Sun.COM */
17308048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/
17318048SMadhavan.Venkataraman@Sun.COM static boolean_t
callout_debug_callb(void * arg,int code)17328048SMadhavan.Venkataraman@Sun.COM callout_debug_callb(void *arg, int code)
17338048SMadhavan.Venkataraman@Sun.COM {
17348048SMadhavan.Venkataraman@Sun.COM hrtime_t delta;
17358048SMadhavan.Venkataraman@Sun.COM
17368048SMadhavan.Venkataraman@Sun.COM /*
17378048SMadhavan.Venkataraman@Sun.COM * When the system enters the debugger. make a note of the hrtime.
17388048SMadhavan.Venkataraman@Sun.COM * When it is resumed, compute how long the system was in the
17398048SMadhavan.Venkataraman@Sun.COM * debugger. This interval should not be counted for callouts.
17408048SMadhavan.Venkataraman@Sun.COM */
17418048SMadhavan.Venkataraman@Sun.COM if (code == 0) {
17428048SMadhavan.Venkataraman@Sun.COM callout_suspend();
17438048SMadhavan.Venkataraman@Sun.COM callout_debug_hrtime = gethrtime();
17448048SMadhavan.Venkataraman@Sun.COM } else {
17458048SMadhavan.Venkataraman@Sun.COM delta = gethrtime() - callout_debug_hrtime;
17469334SMadhavan.Venkataraman@Sun.COM callout_resume(delta, 0);
17478048SMadhavan.Venkataraman@Sun.COM }
17488048SMadhavan.Venkataraman@Sun.COM
17490Sstevel@tonic-gate return (B_TRUE);
17500Sstevel@tonic-gate }
17510Sstevel@tonic-gate
17520Sstevel@tonic-gate /*
17539039SMadhavan.Venkataraman@Sun.COM * Move the absolute hrestime callouts to the expired list. Then program the
17549039SMadhavan.Venkataraman@Sun.COM * table's cyclic to expire immediately so that the callouts can be executed
17558048SMadhavan.Venkataraman@Sun.COM * immediately.
17568048SMadhavan.Venkataraman@Sun.COM */
17578048SMadhavan.Venkataraman@Sun.COM static void
callout_hrestime_one(callout_table_t * ct)17588048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(callout_table_t *ct)
17598048SMadhavan.Venkataraman@Sun.COM {
176011745SMadhavan.Venkataraman@Sun.COM hrtime_t hexp, qexp;
17618048SMadhavan.Venkataraman@Sun.COM
17628048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
176311745SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) {
17648048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
17658048SMadhavan.Venkataraman@Sun.COM return;
17668048SMadhavan.Venkataraman@Sun.COM }
17678048SMadhavan.Venkataraman@Sun.COM
17689334SMadhavan.Venkataraman@Sun.COM /*
17699334SMadhavan.Venkataraman@Sun.COM * Walk the heap and process all the absolute hrestime entries.
17709334SMadhavan.Venkataraman@Sun.COM */
177111745SMadhavan.Venkataraman@Sun.COM hexp = callout_heap_process(ct, 0, 1);
177211745SMadhavan.Venkataraman@Sun.COM qexp = callout_queue_process(ct, 0, 1);
17738048SMadhavan.Venkataraman@Sun.COM
177411745SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 0) {
177511745SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, hexp);
177611745SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_qcyclic, qexp);
177711745SMadhavan.Venkataraman@Sun.COM }
17789039SMadhavan.Venkataraman@Sun.COM
17798048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
17808048SMadhavan.Venkataraman@Sun.COM }
17818048SMadhavan.Venkataraman@Sun.COM
17828048SMadhavan.Venkataraman@Sun.COM /*
17838048SMadhavan.Venkataraman@Sun.COM * This function is called whenever system time (hrestime) is changed
17848048SMadhavan.Venkataraman@Sun.COM * explicitly. All the HRESTIME callouts must be expired at once.
17858048SMadhavan.Venkataraman@Sun.COM */
17868048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/
17878048SMadhavan.Venkataraman@Sun.COM void
callout_hrestime(void)17888048SMadhavan.Venkataraman@Sun.COM callout_hrestime(void)
17898048SMadhavan.Venkataraman@Sun.COM {
17908048SMadhavan.Venkataraman@Sun.COM int t, f;
17918048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct;
17928048SMadhavan.Venkataraman@Sun.COM
17938048SMadhavan.Venkataraman@Sun.COM /*
17948048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and process the hrestime
17958048SMadhavan.Venkataraman@Sun.COM * callouts therein.
17968048SMadhavan.Venkataraman@Sun.COM *
17978048SMadhavan.Venkataraman@Sun.COM * We look at all the tables because we don't know which ones were
17988048SMadhavan.Venkataraman@Sun.COM * onlined and offlined in the past. The offlined tables may still
17998048SMadhavan.Venkataraman@Sun.COM * have active cyclics processing timers somewhere.
18008048SMadhavan.Venkataraman@Sun.COM */
18018048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) {
18028048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) {
18038048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)];
18048048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(ct);
18058048SMadhavan.Venkataraman@Sun.COM }
18068048SMadhavan.Venkataraman@Sun.COM }
18078048SMadhavan.Venkataraman@Sun.COM }
18088048SMadhavan.Venkataraman@Sun.COM
18098048SMadhavan.Venkataraman@Sun.COM /*
18108048SMadhavan.Venkataraman@Sun.COM * Create the hash tables for this callout table.
18118048SMadhavan.Venkataraman@Sun.COM */
18128048SMadhavan.Venkataraman@Sun.COM static void
callout_hash_init(callout_table_t * ct)18138048SMadhavan.Venkataraman@Sun.COM callout_hash_init(callout_table_t *ct)
18148048SMadhavan.Venkataraman@Sun.COM {
18158048SMadhavan.Venkataraman@Sun.COM size_t size;
18168048SMadhavan.Venkataraman@Sun.COM
18178048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
18188048SMadhavan.Venkataraman@Sun.COM ASSERT((ct->ct_idhash == NULL) && (ct->ct_clhash == NULL));
18198048SMadhavan.Venkataraman@Sun.COM
18208048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_hash_t) * CALLOUT_BUCKETS;
18218048SMadhavan.Venkataraman@Sun.COM ct->ct_idhash = kmem_zalloc(size, KM_SLEEP);
18228048SMadhavan.Venkataraman@Sun.COM ct->ct_clhash = kmem_zalloc(size, KM_SLEEP);
18238048SMadhavan.Venkataraman@Sun.COM }
18248048SMadhavan.Venkataraman@Sun.COM
18258048SMadhavan.Venkataraman@Sun.COM /*
18268048SMadhavan.Venkataraman@Sun.COM * Create per-callout table kstats.
18278048SMadhavan.Venkataraman@Sun.COM */
18288048SMadhavan.Venkataraman@Sun.COM static void
callout_kstat_init(callout_table_t * ct)18298048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(callout_table_t *ct)
18308048SMadhavan.Venkataraman@Sun.COM {
18318048SMadhavan.Venkataraman@Sun.COM callout_stat_type_t stat;
18328048SMadhavan.Venkataraman@Sun.COM kstat_t *ct_kstats;
18338048SMadhavan.Venkataraman@Sun.COM int ndx;
18348048SMadhavan.Venkataraman@Sun.COM
18358048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
18368048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_kstats == NULL);
18378048SMadhavan.Venkataraman@Sun.COM
18388048SMadhavan.Venkataraman@Sun.COM ndx = ct - callout_table;
18398048SMadhavan.Venkataraman@Sun.COM ct_kstats = kstat_create("unix", ndx, "callout",
18408048SMadhavan.Venkataraman@Sun.COM "misc", KSTAT_TYPE_NAMED, CALLOUT_NUM_STATS, KSTAT_FLAG_VIRTUAL);
18418048SMadhavan.Venkataraman@Sun.COM
18428048SMadhavan.Venkataraman@Sun.COM if (ct_kstats == NULL) {
18438048SMadhavan.Venkataraman@Sun.COM cmn_err(CE_WARN, "kstat_create for callout table %p failed",
18448048SMadhavan.Venkataraman@Sun.COM (void *)ct);
18458048SMadhavan.Venkataraman@Sun.COM } else {
18468048SMadhavan.Venkataraman@Sun.COM ct_kstats->ks_data = ct->ct_kstat_data;
18478048SMadhavan.Venkataraman@Sun.COM for (stat = 0; stat < CALLOUT_NUM_STATS; stat++)
18488048SMadhavan.Venkataraman@Sun.COM kstat_named_init(&ct->ct_kstat_data[stat],
18498048SMadhavan.Venkataraman@Sun.COM callout_kstat_names[stat], KSTAT_DATA_INT64);
18508048SMadhavan.Venkataraman@Sun.COM ct->ct_kstats = ct_kstats;
18518048SMadhavan.Venkataraman@Sun.COM kstat_install(ct_kstats);
18528048SMadhavan.Venkataraman@Sun.COM }
18538048SMadhavan.Venkataraman@Sun.COM }
18548048SMadhavan.Venkataraman@Sun.COM
18558048SMadhavan.Venkataraman@Sun.COM static void
callout_cyclic_init(callout_table_t * ct)18568048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(callout_table_t *ct)
18578048SMadhavan.Venkataraman@Sun.COM {
18588048SMadhavan.Venkataraman@Sun.COM cyc_handler_t hdlr;
18598048SMadhavan.Venkataraman@Sun.COM cyc_time_t when;
18608048SMadhavan.Venkataraman@Sun.COM processorid_t seqid;
18618048SMadhavan.Venkataraman@Sun.COM int t;
186211745SMadhavan.Venkataraman@Sun.COM cyclic_id_t cyclic, qcyclic;
18638048SMadhavan.Venkataraman@Sun.COM
18648048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex));
18658048SMadhavan.Venkataraman@Sun.COM
186611745SMadhavan.Venkataraman@Sun.COM t = ct->ct_type;
18678048SMadhavan.Venkataraman@Sun.COM seqid = CALLOUT_TABLE_SEQID(ct);
18688048SMadhavan.Venkataraman@Sun.COM
18698048SMadhavan.Venkataraman@Sun.COM /*
18708048SMadhavan.Venkataraman@Sun.COM * Create the taskq thread if the table type is normal.
18718048SMadhavan.Venkataraman@Sun.COM * Realtime tables are handled at PIL1 by a softint
18728048SMadhavan.Venkataraman@Sun.COM * handler.
18738048SMadhavan.Venkataraman@Sun.COM */
18748048SMadhavan.Venkataraman@Sun.COM if (t == CALLOUT_NORMAL) {
18758048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_taskq == NULL);
18768048SMadhavan.Venkataraman@Sun.COM /*
18778048SMadhavan.Venkataraman@Sun.COM * Each callout thread consumes exactly one
18788048SMadhavan.Venkataraman@Sun.COM * task structure while active. Therefore,
18799334SMadhavan.Venkataraman@Sun.COM * prepopulating with 2 * callout_threads tasks
18808048SMadhavan.Venkataraman@Sun.COM * ensures that there's at least one task per
18818048SMadhavan.Venkataraman@Sun.COM * thread that's either scheduled or on the
18828048SMadhavan.Venkataraman@Sun.COM * freelist. In turn, this guarantees that
18838048SMadhavan.Venkataraman@Sun.COM * taskq_dispatch() will always either succeed
18848048SMadhavan.Venkataraman@Sun.COM * (because there's a free task structure) or
18858048SMadhavan.Venkataraman@Sun.COM * be unnecessary (because "callout_excute(ct)"
18868048SMadhavan.Venkataraman@Sun.COM * has already scheduled).
18878048SMadhavan.Venkataraman@Sun.COM */
18888048SMadhavan.Venkataraman@Sun.COM ct->ct_taskq =
18898048SMadhavan.Venkataraman@Sun.COM taskq_create_instance("callout_taskq", seqid,
18909334SMadhavan.Venkataraman@Sun.COM callout_threads, maxclsyspri,
18919334SMadhavan.Venkataraman@Sun.COM 2 * callout_threads, 2 * callout_threads,
18928048SMadhavan.Venkataraman@Sun.COM TASKQ_PREPOPULATE | TASKQ_CPR_SAFE);
18938048SMadhavan.Venkataraman@Sun.COM }
18948048SMadhavan.Venkataraman@Sun.COM
18958048SMadhavan.Venkataraman@Sun.COM /*
18968048SMadhavan.Venkataraman@Sun.COM * callouts can only be created in a table whose
18978048SMadhavan.Venkataraman@Sun.COM * cyclic has been initialized.
18988048SMadhavan.Venkataraman@Sun.COM */
18998048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num == 0);
19008048SMadhavan.Venkataraman@Sun.COM
19018048SMadhavan.Venkataraman@Sun.COM /*
190211655SMadhavan.Venkataraman@Sun.COM * Drop the mutex before creating the callout cyclics. cyclic_add()
190311655SMadhavan.Venkataraman@Sun.COM * could potentially expand the cyclic heap. We don't want to be
190411655SMadhavan.Venkataraman@Sun.COM * holding the callout table mutex in that case. Note that this
190511655SMadhavan.Venkataraman@Sun.COM * function is called during CPU online. cpu_lock is held at this
190611655SMadhavan.Venkataraman@Sun.COM * point. So, only one thread can be executing the cyclic add logic
190711655SMadhavan.Venkataraman@Sun.COM * below at any time.
190811655SMadhavan.Venkataraman@Sun.COM */
190911655SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
191011655SMadhavan.Venkataraman@Sun.COM
191111655SMadhavan.Venkataraman@Sun.COM /*
19128048SMadhavan.Venkataraman@Sun.COM * Create the callout table cyclics.
19139039SMadhavan.Venkataraman@Sun.COM *
19149039SMadhavan.Venkataraman@Sun.COM * The realtime cyclic handler executes at low PIL. The normal cyclic
19159039SMadhavan.Venkataraman@Sun.COM * handler executes at lock PIL. This is because there are cases
19169039SMadhavan.Venkataraman@Sun.COM * where code can block at PIL > 1 waiting for a normal callout handler
19179039SMadhavan.Venkataraman@Sun.COM * to unblock it directly or indirectly. If the normal cyclic were to
19189039SMadhavan.Venkataraman@Sun.COM * be executed at low PIL, it could get blocked out by the waiter
19199039SMadhavan.Venkataraman@Sun.COM * and cause a deadlock.
19208048SMadhavan.Venkataraman@Sun.COM */
19218048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_cyclic == CYCLIC_NONE);
19228048SMadhavan.Venkataraman@Sun.COM
192311745SMadhavan.Venkataraman@Sun.COM if (t == CALLOUT_REALTIME) {
19249039SMadhavan.Venkataraman@Sun.COM hdlr.cyh_level = callout_realtime_level;
192511745SMadhavan.Venkataraman@Sun.COM hdlr.cyh_func = (cyc_func_t)callout_realtime;
192611745SMadhavan.Venkataraman@Sun.COM } else {
19279039SMadhavan.Venkataraman@Sun.COM hdlr.cyh_level = callout_normal_level;
192811745SMadhavan.Venkataraman@Sun.COM hdlr.cyh_func = (cyc_func_t)callout_normal;
192911745SMadhavan.Venkataraman@Sun.COM }
19308048SMadhavan.Venkataraman@Sun.COM hdlr.cyh_arg = ct;
19318048SMadhavan.Venkataraman@Sun.COM when.cyt_when = CY_INFINITY;
19328048SMadhavan.Venkataraman@Sun.COM when.cyt_interval = CY_INFINITY;
19338048SMadhavan.Venkataraman@Sun.COM
193411655SMadhavan.Venkataraman@Sun.COM cyclic = cyclic_add(&hdlr, &when);
193511655SMadhavan.Venkataraman@Sun.COM
193611745SMadhavan.Venkataraman@Sun.COM if (t == CALLOUT_REALTIME)
193711745SMadhavan.Venkataraman@Sun.COM hdlr.cyh_func = (cyc_func_t)callout_queue_realtime;
193811745SMadhavan.Venkataraman@Sun.COM else
193911745SMadhavan.Venkataraman@Sun.COM hdlr.cyh_func = (cyc_func_t)callout_queue_normal;
194011745SMadhavan.Venkataraman@Sun.COM
194111745SMadhavan.Venkataraman@Sun.COM qcyclic = cyclic_add(&hdlr, &when);
194211745SMadhavan.Venkataraman@Sun.COM
194311655SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
194411655SMadhavan.Venkataraman@Sun.COM ct->ct_cyclic = cyclic;
194511745SMadhavan.Venkataraman@Sun.COM ct->ct_qcyclic = qcyclic;
19468048SMadhavan.Venkataraman@Sun.COM }
19478048SMadhavan.Venkataraman@Sun.COM
19488048SMadhavan.Venkataraman@Sun.COM void
callout_cpu_online(cpu_t * cp)19498048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cpu_t *cp)
19508048SMadhavan.Venkataraman@Sun.COM {
19518048SMadhavan.Venkataraman@Sun.COM lgrp_handle_t hand;
19528048SMadhavan.Venkataraman@Sun.COM callout_cache_t *cache;
19538048SMadhavan.Venkataraman@Sun.COM char s[KMEM_CACHE_NAMELEN];
19548048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct;
19558048SMadhavan.Venkataraman@Sun.COM processorid_t seqid;
19568048SMadhavan.Venkataraman@Sun.COM int t;
19578048SMadhavan.Venkataraman@Sun.COM
19588048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
19598048SMadhavan.Venkataraman@Sun.COM
19608048SMadhavan.Venkataraman@Sun.COM /*
19618048SMadhavan.Venkataraman@Sun.COM * Locate the cache corresponding to the onlined CPU's lgroup.
19628048SMadhavan.Venkataraman@Sun.COM * Note that access to callout_caches is protected by cpu_lock.
19638048SMadhavan.Venkataraman@Sun.COM */
19648048SMadhavan.Venkataraman@Sun.COM hand = lgrp_plat_cpu_to_hand(cp->cpu_id);
19658048SMadhavan.Venkataraman@Sun.COM for (cache = callout_caches; cache != NULL; cache = cache->cc_next) {
19668048SMadhavan.Venkataraman@Sun.COM if (cache->cc_hand == hand)
19678048SMadhavan.Venkataraman@Sun.COM break;
19688048SMadhavan.Venkataraman@Sun.COM }
19698048SMadhavan.Venkataraman@Sun.COM
19708048SMadhavan.Venkataraman@Sun.COM /*
19718048SMadhavan.Venkataraman@Sun.COM * If not found, create one. The caches are never destroyed.
19728048SMadhavan.Venkataraman@Sun.COM */
19738048SMadhavan.Venkataraman@Sun.COM if (cache == NULL) {
19748048SMadhavan.Venkataraman@Sun.COM cache = kmem_alloc(sizeof (callout_cache_t), KM_SLEEP);
19758048SMadhavan.Venkataraman@Sun.COM cache->cc_hand = hand;
19768048SMadhavan.Venkataraman@Sun.COM (void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_cache%lx",
19778048SMadhavan.Venkataraman@Sun.COM (long)hand);
19788048SMadhavan.Venkataraman@Sun.COM cache->cc_cache = kmem_cache_create(s, sizeof (callout_t),
19798048SMadhavan.Venkataraman@Sun.COM CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0);
19808048SMadhavan.Venkataraman@Sun.COM (void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_lcache%lx",
19818048SMadhavan.Venkataraman@Sun.COM (long)hand);
19828048SMadhavan.Venkataraman@Sun.COM cache->cc_lcache = kmem_cache_create(s, sizeof (callout_list_t),
19838048SMadhavan.Venkataraman@Sun.COM CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0);
19848048SMadhavan.Venkataraman@Sun.COM cache->cc_next = callout_caches;
19858048SMadhavan.Venkataraman@Sun.COM callout_caches = cache;
19868048SMadhavan.Venkataraman@Sun.COM }
19878048SMadhavan.Venkataraman@Sun.COM
19888048SMadhavan.Venkataraman@Sun.COM seqid = cp->cpu_seqid;
19898048SMadhavan.Venkataraman@Sun.COM
19908048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) {
19918048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, seqid)];
19928048SMadhavan.Venkataraman@Sun.COM
19938048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex);
19948048SMadhavan.Venkataraman@Sun.COM /*
19958048SMadhavan.Venkataraman@Sun.COM * Store convinience pointers to the kmem caches
19968048SMadhavan.Venkataraman@Sun.COM * in the callout table. These assignments should always be
19978048SMadhavan.Venkataraman@Sun.COM * done as callout tables can map to different physical
19988048SMadhavan.Venkataraman@Sun.COM * CPUs each time.
19998048SMadhavan.Venkataraman@Sun.COM */
20008048SMadhavan.Venkataraman@Sun.COM ct->ct_cache = cache->cc_cache;
20018048SMadhavan.Venkataraman@Sun.COM ct->ct_lcache = cache->cc_lcache;
20028048SMadhavan.Venkataraman@Sun.COM
20038048SMadhavan.Venkataraman@Sun.COM /*
20048048SMadhavan.Venkataraman@Sun.COM * We use the heap pointer to check if stuff has been
20058048SMadhavan.Venkataraman@Sun.COM * initialized for this callout table.
20068048SMadhavan.Venkataraman@Sun.COM */
20078048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap == NULL) {
20088048SMadhavan.Venkataraman@Sun.COM callout_heap_init(ct);
20098048SMadhavan.Venkataraman@Sun.COM callout_hash_init(ct);
20108048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(ct);
20118048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(ct);
20128048SMadhavan.Venkataraman@Sun.COM }
20138048SMadhavan.Venkataraman@Sun.COM
20148048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex);
20158048SMadhavan.Venkataraman@Sun.COM
20168048SMadhavan.Venkataraman@Sun.COM /*
201711745SMadhavan.Venkataraman@Sun.COM * Move the cyclics to this CPU by doing a bind.
20188048SMadhavan.Venkataraman@Sun.COM */
20198048SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_cyclic, cp, NULL);
202011745SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_qcyclic, cp, NULL);
20218566SMadhavan.Venkataraman@Sun.COM }
20228566SMadhavan.Venkataraman@Sun.COM }
20238566SMadhavan.Venkataraman@Sun.COM
20248566SMadhavan.Venkataraman@Sun.COM void
callout_cpu_offline(cpu_t * cp)20258566SMadhavan.Venkataraman@Sun.COM callout_cpu_offline(cpu_t *cp)
20268566SMadhavan.Venkataraman@Sun.COM {
20278566SMadhavan.Venkataraman@Sun.COM callout_table_t *ct;
20288566SMadhavan.Venkataraman@Sun.COM processorid_t seqid;
20298566SMadhavan.Venkataraman@Sun.COM int t;
20308566SMadhavan.Venkataraman@Sun.COM
20318566SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock));
20328566SMadhavan.Venkataraman@Sun.COM
20338566SMadhavan.Venkataraman@Sun.COM seqid = cp->cpu_seqid;
20348566SMadhavan.Venkataraman@Sun.COM
20358566SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) {
20368566SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, seqid)];
20378566SMadhavan.Venkataraman@Sun.COM
20388566SMadhavan.Venkataraman@Sun.COM /*
203911745SMadhavan.Venkataraman@Sun.COM * Unbind the cyclics. This will allow the cyclic subsystem
204011745SMadhavan.Venkataraman@Sun.COM * to juggle the cyclics during CPU offline.
20418566SMadhavan.Venkataraman@Sun.COM */
20428048SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_cyclic, NULL, NULL);
204311745SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_qcyclic, NULL, NULL);
20448048SMadhavan.Venkataraman@Sun.COM }
20458048SMadhavan.Venkataraman@Sun.COM }
20468048SMadhavan.Venkataraman@Sun.COM
20478048SMadhavan.Venkataraman@Sun.COM /*
20488048SMadhavan.Venkataraman@Sun.COM * This is called to perform per-CPU initialization for slave CPUs at
20498048SMadhavan.Venkataraman@Sun.COM * boot time.
20508048SMadhavan.Venkataraman@Sun.COM */
20518048SMadhavan.Venkataraman@Sun.COM void
callout_mp_init(void)20528048SMadhavan.Venkataraman@Sun.COM callout_mp_init(void)
20538048SMadhavan.Venkataraman@Sun.COM {
20548048SMadhavan.Venkataraman@Sun.COM cpu_t *cp;
205511745SMadhavan.Venkataraman@Sun.COM size_t min, max;
205611745SMadhavan.Venkataraman@Sun.COM
205711745SMadhavan.Venkataraman@Sun.COM if (callout_chunk == CALLOUT_CHUNK) {
205811745SMadhavan.Venkataraman@Sun.COM /*
205911745SMadhavan.Venkataraman@Sun.COM * No one has specified a chunk in /etc/system. We need to
206011745SMadhavan.Venkataraman@Sun.COM * compute it here based on the number of online CPUs and
206111745SMadhavan.Venkataraman@Sun.COM * available physical memory.
206211745SMadhavan.Venkataraman@Sun.COM */
206311745SMadhavan.Venkataraman@Sun.COM min = CALLOUT_MIN_HEAP_SIZE;
206411950SMadhavan.Venkataraman@Sun.COM max = ptob(physmem / CALLOUT_MEM_FRACTION);
206511745SMadhavan.Venkataraman@Sun.COM if (min > max)
206611745SMadhavan.Venkataraman@Sun.COM min = max;
206711745SMadhavan.Venkataraman@Sun.COM callout_chunk = min / sizeof (callout_heap_t);
206811745SMadhavan.Venkataraman@Sun.COM callout_chunk /= ncpus_online;
206911745SMadhavan.Venkataraman@Sun.COM callout_chunk = P2ROUNDUP(callout_chunk, CALLOUT_CHUNK);
207011745SMadhavan.Venkataraman@Sun.COM }
20718048SMadhavan.Venkataraman@Sun.COM
20728048SMadhavan.Venkataraman@Sun.COM mutex_enter(&cpu_lock);
20738048SMadhavan.Venkataraman@Sun.COM
20748048SMadhavan.Venkataraman@Sun.COM cp = cpu_active;
20758048SMadhavan.Venkataraman@Sun.COM do {
20768048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cp);
20778048SMadhavan.Venkataraman@Sun.COM } while ((cp = cp->cpu_next_onln) != cpu_active);
20788048SMadhavan.Venkataraman@Sun.COM
20798048SMadhavan.Venkataraman@Sun.COM mutex_exit(&cpu_lock);
20808048SMadhavan.Venkataraman@Sun.COM }
20818048SMadhavan.Venkataraman@Sun.COM
20828048SMadhavan.Venkataraman@Sun.COM /*
20830Sstevel@tonic-gate * Initialize all callout tables. Called at boot time just before clkstart().
20840Sstevel@tonic-gate */
20850Sstevel@tonic-gate void
callout_init(void)20860Sstevel@tonic-gate callout_init(void)
20870Sstevel@tonic-gate {
20880Sstevel@tonic-gate int f, t;
20898048SMadhavan.Venkataraman@Sun.COM size_t size;
20900Sstevel@tonic-gate int table_id;
20910Sstevel@tonic-gate callout_table_t *ct;
20928048SMadhavan.Venkataraman@Sun.COM long bits, fanout;
20938048SMadhavan.Venkataraman@Sun.COM uintptr_t buf;
20940Sstevel@tonic-gate
20958048SMadhavan.Venkataraman@Sun.COM /*
20968048SMadhavan.Venkataraman@Sun.COM * Initialize callout globals.
20978048SMadhavan.Venkataraman@Sun.COM */
20988048SMadhavan.Venkataraman@Sun.COM bits = 0;
20998048SMadhavan.Venkataraman@Sun.COM for (fanout = 1; (fanout < max_ncpus); fanout <<= 1)
21008048SMadhavan.Venkataraman@Sun.COM bits++;
21018048SMadhavan.Venkataraman@Sun.COM callout_table_bits = CALLOUT_TYPE_BITS + bits;
21028048SMadhavan.Venkataraman@Sun.COM callout_table_mask = (1 << callout_table_bits) - 1;
21038048SMadhavan.Venkataraman@Sun.COM callout_counter_low = 1 << CALLOUT_COUNTER_SHIFT;
21048048SMadhavan.Venkataraman@Sun.COM callout_longterm = TICK_TO_NSEC(CALLOUT_LONGTERM_TICKS);
21058566SMadhavan.Venkataraman@Sun.COM callout_max_ticks = CALLOUT_MAX_TICKS;
21069334SMadhavan.Venkataraman@Sun.COM if (callout_min_reap == 0)
21079334SMadhavan.Venkataraman@Sun.COM callout_min_reap = CALLOUT_MIN_REAP;
21080Sstevel@tonic-gate
21099334SMadhavan.Venkataraman@Sun.COM if (callout_tolerance <= 0)
21109334SMadhavan.Venkataraman@Sun.COM callout_tolerance = CALLOUT_TOLERANCE;
21119334SMadhavan.Venkataraman@Sun.COM if (callout_threads <= 0)
21129334SMadhavan.Venkataraman@Sun.COM callout_threads = CALLOUT_THREADS;
211311745SMadhavan.Venkataraman@Sun.COM if (callout_chunk <= 0)
211411745SMadhavan.Venkataraman@Sun.COM callout_chunk = CALLOUT_CHUNK;
211511745SMadhavan.Venkataraman@Sun.COM else
211611745SMadhavan.Venkataraman@Sun.COM callout_chunk = P2ROUNDUP(callout_chunk, CALLOUT_CHUNK);
21178048SMadhavan.Venkataraman@Sun.COM
21188048SMadhavan.Venkataraman@Sun.COM /*
21198048SMadhavan.Venkataraman@Sun.COM * Allocate all the callout tables based on max_ncpus. We have chosen
21208048SMadhavan.Venkataraman@Sun.COM * to do boot-time allocation instead of dynamic allocation because:
21218048SMadhavan.Venkataraman@Sun.COM *
21228048SMadhavan.Venkataraman@Sun.COM * - the size of the callout tables is not too large.
21238048SMadhavan.Venkataraman@Sun.COM * - there are race conditions involved in making this dynamic.
21248048SMadhavan.Venkataraman@Sun.COM * - the hash tables that go with the callout tables consume
21258048SMadhavan.Venkataraman@Sun.COM * most of the memory and they are only allocated in
21268048SMadhavan.Venkataraman@Sun.COM * callout_cpu_online().
21278048SMadhavan.Venkataraman@Sun.COM *
21288048SMadhavan.Venkataraman@Sun.COM * Each CPU has two tables that are consecutive in the array. The first
21298048SMadhavan.Venkataraman@Sun.COM * one is for realtime callouts and the second one is for normal ones.
21308048SMadhavan.Venkataraman@Sun.COM *
21318048SMadhavan.Venkataraman@Sun.COM * We do this alignment dance to make sure that callout table
21328048SMadhavan.Venkataraman@Sun.COM * structures will always be on a cache line boundary.
21338048SMadhavan.Venkataraman@Sun.COM */
21348048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_table_t) * CALLOUT_NTYPES * max_ncpus;
21358048SMadhavan.Venkataraman@Sun.COM size += CALLOUT_ALIGN;
21368048SMadhavan.Venkataraman@Sun.COM buf = (uintptr_t)kmem_zalloc(size, KM_SLEEP);
21378048SMadhavan.Venkataraman@Sun.COM callout_table = (callout_table_t *)P2ROUNDUP(buf, CALLOUT_ALIGN);
21388048SMadhavan.Venkataraman@Sun.COM
21398048SMadhavan.Venkataraman@Sun.COM size = sizeof (kstat_named_t) * CALLOUT_NUM_STATS;
21408048SMadhavan.Venkataraman@Sun.COM /*
21418048SMadhavan.Venkataraman@Sun.COM * Now, initialize the tables for all the CPUs.
21428048SMadhavan.Venkataraman@Sun.COM */
21438048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) {
21448048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) {
21450Sstevel@tonic-gate table_id = CALLOUT_TABLE(t, f);
21468048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[table_id];
21478566SMadhavan.Venkataraman@Sun.COM ct->ct_type = t;
21488048SMadhavan.Venkataraman@Sun.COM mutex_init(&ct->ct_mutex, NULL, MUTEX_DEFAULT, NULL);
21496422Sqiao /*
21508048SMadhavan.Venkataraman@Sun.COM * Precompute the base IDs for long and short-term
21518048SMadhavan.Venkataraman@Sun.COM * legacy IDs. This makes ID generation during
21528048SMadhavan.Venkataraman@Sun.COM * timeout() fast.
21536422Sqiao */
21548048SMadhavan.Venkataraman@Sun.COM ct->ct_short_id = CALLOUT_SHORT_ID(table_id);
21558048SMadhavan.Venkataraman@Sun.COM ct->ct_long_id = CALLOUT_LONG_ID(table_id);
21568048SMadhavan.Venkataraman@Sun.COM /*
21578048SMadhavan.Venkataraman@Sun.COM * Precompute the base ID for generation-based IDs.
21588048SMadhavan.Venkataraman@Sun.COM * Note that when the first ID gets allocated, the
21598048SMadhavan.Venkataraman@Sun.COM * ID will wrap. This will cause the generation
21608048SMadhavan.Venkataraman@Sun.COM * number to be incremented to 1.
21618048SMadhavan.Venkataraman@Sun.COM */
21628048SMadhavan.Venkataraman@Sun.COM ct->ct_gen_id = CALLOUT_SHORT_ID(table_id);
21638048SMadhavan.Venkataraman@Sun.COM /*
216411745SMadhavan.Venkataraman@Sun.COM * Initialize the cyclics as NONE. This will get set
21658048SMadhavan.Venkataraman@Sun.COM * during CPU online. This is so that partially
21668048SMadhavan.Venkataraman@Sun.COM * populated systems will only have the required
21678048SMadhavan.Venkataraman@Sun.COM * number of cyclics, not more.
21688048SMadhavan.Venkataraman@Sun.COM */
21698048SMadhavan.Venkataraman@Sun.COM ct->ct_cyclic = CYCLIC_NONE;
217011745SMadhavan.Venkataraman@Sun.COM ct->ct_qcyclic = CYCLIC_NONE;
21718048SMadhavan.Venkataraman@Sun.COM ct->ct_kstat_data = kmem_zalloc(size, KM_SLEEP);
21720Sstevel@tonic-gate }
21730Sstevel@tonic-gate }
21748048SMadhavan.Venkataraman@Sun.COM
21758048SMadhavan.Venkataraman@Sun.COM /*
21768048SMadhavan.Venkataraman@Sun.COM * Add the callback for CPR. This is called during checkpoint
21778048SMadhavan.Venkataraman@Sun.COM * resume to suspend and resume callouts.
21788048SMadhavan.Venkataraman@Sun.COM */
21798048SMadhavan.Venkataraman@Sun.COM (void) callb_add(callout_cpr_callb, 0, CB_CL_CPR_CALLOUT,
21808048SMadhavan.Venkataraman@Sun.COM "callout_cpr");
21818048SMadhavan.Venkataraman@Sun.COM (void) callb_add(callout_debug_callb, 0, CB_CL_ENTER_DEBUGGER,
21828048SMadhavan.Venkataraman@Sun.COM "callout_debug");
21838048SMadhavan.Venkataraman@Sun.COM
21848048SMadhavan.Venkataraman@Sun.COM /*
21858048SMadhavan.Venkataraman@Sun.COM * Call the per-CPU initialization function for the boot CPU. This
21868048SMadhavan.Venkataraman@Sun.COM * is done here because the function is not called automatically for
21878048SMadhavan.Venkataraman@Sun.COM * the boot CPU from the CPU online/offline hooks. Note that the
21888048SMadhavan.Venkataraman@Sun.COM * CPU lock is taken here because of convention.
21898048SMadhavan.Venkataraman@Sun.COM */
21908048SMadhavan.Venkataraman@Sun.COM mutex_enter(&cpu_lock);
21918048SMadhavan.Venkataraman@Sun.COM callout_boot_ct = &callout_table[CALLOUT_TABLE(0, CPU->cpu_seqid)];
21928048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(CPU);
21938048SMadhavan.Venkataraman@Sun.COM mutex_exit(&cpu_lock);
2194*12681SPeter.Telford@Sun.COM
2195*12681SPeter.Telford@Sun.COM /* heads-up to boot-time clients that timeouts now available */
2196*12681SPeter.Telford@Sun.COM callout_init_done = 1;
21970Sstevel@tonic-gate }
2198