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