10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 56422Sqiao * Common Development and Distribution License (the "License"). 66422Sqiao * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 228566SMadhavan.Venkataraman@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #include <sys/callo.h> 270Sstevel@tonic-gate #include <sys/param.h> 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <sys/cpuvar.h> 300Sstevel@tonic-gate #include <sys/thread.h> 310Sstevel@tonic-gate #include <sys/kmem.h> 328048SMadhavan.Venkataraman@Sun.COM #include <sys/kmem_impl.h> 330Sstevel@tonic-gate #include <sys/cmn_err.h> 340Sstevel@tonic-gate #include <sys/callb.h> 350Sstevel@tonic-gate #include <sys/debug.h> 360Sstevel@tonic-gate #include <sys/vtrace.h> 370Sstevel@tonic-gate #include <sys/sysmacros.h> 380Sstevel@tonic-gate #include <sys/sdt.h> 390Sstevel@tonic-gate 400Sstevel@tonic-gate /* 410Sstevel@tonic-gate * Callout tables. See timeout(9F) for details. 420Sstevel@tonic-gate */ 43*9334SMadhavan.Venkataraman@Sun.COM static int callout_threads; /* callout normal threads */ 448048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_debug_hrtime; /* debugger entry time */ 45*9334SMadhavan.Venkataraman@Sun.COM static int callout_min_reap; /* callout minimum reap count */ 46*9334SMadhavan.Venkataraman@Sun.COM static int callout_tolerance; /* callout hires tolerance */ 478048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_boot_ct; /* Boot CPU's callout tables */ 488566SMadhavan.Venkataraman@Sun.COM static clock_t callout_max_ticks; /* max interval */ 498048SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_longterm; /* longterm nanoseconds */ 508048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_counter_low; /* callout ID increment */ 518048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_bits; /* number of table bits in ID */ 528048SMadhavan.Venkataraman@Sun.COM static ulong_t callout_table_mask; /* mask for the table bits */ 538048SMadhavan.Venkataraman@Sun.COM static callout_cache_t *callout_caches; /* linked list of caches */ 548048SMadhavan.Venkataraman@Sun.COM #pragma align 64(callout_table) 558048SMadhavan.Venkataraman@Sun.COM static callout_table_t *callout_table; /* global callout table array */ 560Sstevel@tonic-gate 579039SMadhavan.Venkataraman@Sun.COM /* 589039SMadhavan.Venkataraman@Sun.COM * We run normal callouts from PIL 10. This means that no other handler that 599039SMadhavan.Venkataraman@Sun.COM * runs at PIL 10 is allowed to wait for normal callouts directly or indirectly 609039SMadhavan.Venkataraman@Sun.COM * as it will cause a deadlock. This has always been an unwritten rule. 619039SMadhavan.Venkataraman@Sun.COM * We are making it explicit here. 629039SMadhavan.Venkataraman@Sun.COM */ 63*9334SMadhavan.Venkataraman@Sun.COM static volatile int callout_realtime_level = CY_LOW_LEVEL; 64*9334SMadhavan.Venkataraman@Sun.COM static volatile int callout_normal_level = CY_LOCK_LEVEL; 659039SMadhavan.Venkataraman@Sun.COM 668048SMadhavan.Venkataraman@Sun.COM static char *callout_kstat_names[] = { 678048SMadhavan.Venkataraman@Sun.COM "callout_timeouts", 688048SMadhavan.Venkataraman@Sun.COM "callout_timeouts_pending", 698048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_unexpired", 708048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_executing", 718048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_expired", 728048SMadhavan.Venkataraman@Sun.COM "callout_expirations", 738048SMadhavan.Venkataraman@Sun.COM "callout_allocations", 74*9334SMadhavan.Venkataraman@Sun.COM "callout_cleanups", 758048SMadhavan.Venkataraman@Sun.COM }; 768048SMadhavan.Venkataraman@Sun.COM 77*9334SMadhavan.Venkataraman@Sun.COM static hrtime_t callout_heap_process(callout_table_t *, hrtime_t, int); 78*9334SMadhavan.Venkataraman@Sun.COM 798048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_INSERT(hash, cp, cnext, cprev) \ 800Sstevel@tonic-gate { \ 818048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \ 828048SMadhavan.Venkataraman@Sun.COM \ 830Sstevel@tonic-gate cp->cprev = NULL; \ 848048SMadhavan.Venkataraman@Sun.COM cp->cnext = hashp->ch_head; \ 858048SMadhavan.Venkataraman@Sun.COM if (hashp->ch_head == NULL) \ 868048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp; \ 878048SMadhavan.Venkataraman@Sun.COM else \ 888048SMadhavan.Venkataraman@Sun.COM cp->cnext->cprev = cp; \ 898048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp; \ 908048SMadhavan.Venkataraman@Sun.COM } 918048SMadhavan.Venkataraman@Sun.COM 928048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_APPEND(hash, cp, cnext, cprev) \ 938048SMadhavan.Venkataraman@Sun.COM { \ 948048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \ 958048SMadhavan.Venkataraman@Sun.COM \ 968048SMadhavan.Venkataraman@Sun.COM cp->cnext = NULL; \ 978048SMadhavan.Venkataraman@Sun.COM cp->cprev = hashp->ch_tail; \ 988048SMadhavan.Venkataraman@Sun.COM if (hashp->ch_tail == NULL) \ 998048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp; \ 1008048SMadhavan.Venkataraman@Sun.COM else \ 1018048SMadhavan.Venkataraman@Sun.COM cp->cprev->cnext = cp; \ 1028048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp; \ 1030Sstevel@tonic-gate } 1040Sstevel@tonic-gate 1058048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_DELETE(hash, cp, cnext, cprev) \ 1060Sstevel@tonic-gate { \ 1078048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \ 1088048SMadhavan.Venkataraman@Sun.COM \ 1098048SMadhavan.Venkataraman@Sun.COM if (cp->cnext == NULL) \ 1108048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp->cprev; \ 1110Sstevel@tonic-gate else \ 1128048SMadhavan.Venkataraman@Sun.COM cp->cnext->cprev = cp->cprev; \ 1138048SMadhavan.Venkataraman@Sun.COM if (cp->cprev == NULL) \ 1148048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp->cnext; \ 1158048SMadhavan.Venkataraman@Sun.COM else \ 1168048SMadhavan.Venkataraman@Sun.COM cp->cprev->cnext = cp->cnext; \ 1170Sstevel@tonic-gate } 1180Sstevel@tonic-gate 1198048SMadhavan.Venkataraman@Sun.COM /* 1208048SMadhavan.Venkataraman@Sun.COM * These definitions help us queue callouts and callout lists. Here is 1218048SMadhavan.Venkataraman@Sun.COM * the queueing rationale: 1228048SMadhavan.Venkataraman@Sun.COM * 1238048SMadhavan.Venkataraman@Sun.COM * - callouts are queued in a FIFO manner in the ID hash table. 1248048SMadhavan.Venkataraman@Sun.COM * TCP timers are typically cancelled in the same order that they 1258048SMadhavan.Venkataraman@Sun.COM * were issued. The FIFO queueing shortens the search for a callout 1268048SMadhavan.Venkataraman@Sun.COM * during untimeout(). 1278048SMadhavan.Venkataraman@Sun.COM * 1288048SMadhavan.Venkataraman@Sun.COM * - callouts are queued in a FIFO manner in their callout lists. 1298048SMadhavan.Venkataraman@Sun.COM * This ensures that the callouts are executed in the same order that 1308048SMadhavan.Venkataraman@Sun.COM * they were queued. This is fair. Plus, it helps to make each 1318048SMadhavan.Venkataraman@Sun.COM * callout expiration timely. It also favors cancellations. 1328048SMadhavan.Venkataraman@Sun.COM * 133*9334SMadhavan.Venkataraman@Sun.COM * - callout lists are queued in the following manner in the callout 134*9334SMadhavan.Venkataraman@Sun.COM * hash table buckets: 135*9334SMadhavan.Venkataraman@Sun.COM * 136*9334SMadhavan.Venkataraman@Sun.COM * - appended, if the callout list is a 1-nanosecond resolution 137*9334SMadhavan.Venkataraman@Sun.COM * callout list. When a callout is created, we first look for 138*9334SMadhavan.Venkataraman@Sun.COM * a callout list that has the same expiration so we can avoid 139*9334SMadhavan.Venkataraman@Sun.COM * allocating a callout list and inserting the expiration into 140*9334SMadhavan.Venkataraman@Sun.COM * the heap. However, we do not want to look at 1-nanosecond 141*9334SMadhavan.Venkataraman@Sun.COM * resolution callout lists as we will seldom find a match in 142*9334SMadhavan.Venkataraman@Sun.COM * them. Keeping these callout lists in the rear of the hash 143*9334SMadhavan.Venkataraman@Sun.COM * buckets allows us to skip these during the lookup. 144*9334SMadhavan.Venkataraman@Sun.COM * 145*9334SMadhavan.Venkataraman@Sun.COM * - inserted at the beginning, if the callout list is not a 146*9334SMadhavan.Venkataraman@Sun.COM * 1-nanosecond resolution callout list. This also has the 147*9334SMadhavan.Venkataraman@Sun.COM * side-effect of keeping the long term timers away from the 148*9334SMadhavan.Venkataraman@Sun.COM * front of the buckets. 1498048SMadhavan.Venkataraman@Sun.COM * 1508048SMadhavan.Venkataraman@Sun.COM * - callout lists are queued in a FIFO manner in the expired callouts 1518048SMadhavan.Venkataraman@Sun.COM * list. This ensures that callout lists are executed in the order 1528048SMadhavan.Venkataraman@Sun.COM * of expiration. 1538048SMadhavan.Venkataraman@Sun.COM */ 1548048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_APPEND(ct, cp) \ 1558048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)], \ 1568048SMadhavan.Venkataraman@Sun.COM cp, c_idnext, c_idprev); \ 1578048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(cp->c_list->cl_callouts, cp, c_clnext, c_clprev) 1588048SMadhavan.Venkataraman@Sun.COM 1598048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_DELETE(ct, cp) \ 1608048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)], \ 1618048SMadhavan.Venkataraman@Sun.COM cp, c_idnext, c_idprev); \ 1628048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(cp->c_list->cl_callouts, cp, c_clnext, c_clprev) 1638048SMadhavan.Venkataraman@Sun.COM 1648048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_INSERT(hash, cl) \ 1658048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_INSERT(hash, cl, cl_next, cl_prev) 1668048SMadhavan.Venkataraman@Sun.COM 1678048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_APPEND(hash, cl) \ 1688048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(hash, cl, cl_next, cl_prev) 1698048SMadhavan.Venkataraman@Sun.COM 1708048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_DELETE(hash, cl) \ 1718048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(hash, cl, cl_next, cl_prev) 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1749039SMadhavan.Venkataraman@Sun.COM * For normal callouts, there is a deadlock scenario if two callouts that 1759039SMadhavan.Venkataraman@Sun.COM * have an inter-dependency end up on the same callout list. To break the 1769039SMadhavan.Venkataraman@Sun.COM * deadlock, you need two taskq threads running in parallel. We compute 1779039SMadhavan.Venkataraman@Sun.COM * the number of taskq threads here using a bunch of conditions to make 1789039SMadhavan.Venkataraman@Sun.COM * it optimal for the common case. This is an ugly hack, but one that is 1799039SMadhavan.Venkataraman@Sun.COM * necessary (sigh). 1809039SMadhavan.Venkataraman@Sun.COM */ 1819039SMadhavan.Venkataraman@Sun.COM #define CALLOUT_THRESHOLD 100000000 1829039SMadhavan.Venkataraman@Sun.COM #define CALLOUT_EXEC_COMPUTE(ct, exec) \ 1839039SMadhavan.Venkataraman@Sun.COM { \ 1849039SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; \ 1859039SMadhavan.Venkataraman@Sun.COM \ 1869039SMadhavan.Venkataraman@Sun.COM cl = ct->ct_expired.ch_head; \ 1879039SMadhavan.Venkataraman@Sun.COM if (cl == NULL) { \ 1889039SMadhavan.Venkataraman@Sun.COM /* \ 1899039SMadhavan.Venkataraman@Sun.COM * If the expired list is NULL, there is nothing to \ 1909039SMadhavan.Venkataraman@Sun.COM * process. \ 1919039SMadhavan.Venkataraman@Sun.COM */ \ 1929039SMadhavan.Venkataraman@Sun.COM exec = 0; \ 1939039SMadhavan.Venkataraman@Sun.COM } else if ((cl->cl_next == NULL) && \ 1949039SMadhavan.Venkataraman@Sun.COM (cl->cl_callouts.ch_head == cl->cl_callouts.ch_tail)) { \ 1959039SMadhavan.Venkataraman@Sun.COM /* \ 1969039SMadhavan.Venkataraman@Sun.COM * If there is only one callout list and it contains \ 1979039SMadhavan.Venkataraman@Sun.COM * only one callout, there is no need for two threads. \ 1989039SMadhavan.Venkataraman@Sun.COM */ \ 1999039SMadhavan.Venkataraman@Sun.COM exec = 1; \ 2009039SMadhavan.Venkataraman@Sun.COM } else if ((ct->ct_heap_num == 0) || \ 201*9334SMadhavan.Venkataraman@Sun.COM (ct->ct_heap[0].ch_expiration > gethrtime() + CALLOUT_THRESHOLD)) {\ 2029039SMadhavan.Venkataraman@Sun.COM /* \ 2039039SMadhavan.Venkataraman@Sun.COM * If the heap has become empty, we need two threads as \ 2049039SMadhavan.Venkataraman@Sun.COM * there is no one to kick off the second thread in the \ 2059039SMadhavan.Venkataraman@Sun.COM * future. If the heap is not empty and the top of the \ 2069039SMadhavan.Venkataraman@Sun.COM * heap does not expire in the near future, we need two \ 2079039SMadhavan.Venkataraman@Sun.COM * threads. \ 2089039SMadhavan.Venkataraman@Sun.COM */ \ 2099039SMadhavan.Venkataraman@Sun.COM exec = 2; \ 2109039SMadhavan.Venkataraman@Sun.COM } else { \ 2119039SMadhavan.Venkataraman@Sun.COM /* \ 2129039SMadhavan.Venkataraman@Sun.COM * We have multiple callouts to process. But the cyclic \ 2139039SMadhavan.Venkataraman@Sun.COM * will fire in the near future. So, we only need one \ 2149039SMadhavan.Venkataraman@Sun.COM * thread for now. \ 2159039SMadhavan.Venkataraman@Sun.COM */ \ 2169039SMadhavan.Venkataraman@Sun.COM exec = 1; \ 2179039SMadhavan.Venkataraman@Sun.COM } \ 2189039SMadhavan.Venkataraman@Sun.COM } 2199039SMadhavan.Venkataraman@Sun.COM 2209039SMadhavan.Venkataraman@Sun.COM /* 221*9334SMadhavan.Venkataraman@Sun.COM * Macro to swap two heap items. 222*9334SMadhavan.Venkataraman@Sun.COM */ 223*9334SMadhavan.Venkataraman@Sun.COM #define CALLOUT_SWAP(h1, h2) \ 224*9334SMadhavan.Venkataraman@Sun.COM { \ 225*9334SMadhavan.Venkataraman@Sun.COM callout_heap_t tmp; \ 226*9334SMadhavan.Venkataraman@Sun.COM \ 227*9334SMadhavan.Venkataraman@Sun.COM tmp = *h1; \ 228*9334SMadhavan.Venkataraman@Sun.COM *h1 = *h2; \ 229*9334SMadhavan.Venkataraman@Sun.COM *h2 = tmp; \ 230*9334SMadhavan.Venkataraman@Sun.COM } 231*9334SMadhavan.Venkataraman@Sun.COM 232*9334SMadhavan.Venkataraman@Sun.COM /* 233*9334SMadhavan.Venkataraman@Sun.COM * Macro to free a callout list. 234*9334SMadhavan.Venkataraman@Sun.COM */ 235*9334SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_FREE(ct, cl) \ 236*9334SMadhavan.Venkataraman@Sun.COM { \ 237*9334SMadhavan.Venkataraman@Sun.COM cl->cl_next = ct->ct_lfree; \ 238*9334SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl; \ 239*9334SMadhavan.Venkataraman@Sun.COM cl->cl_flags |= CALLOUT_LIST_FLAG_FREE; \ 240*9334SMadhavan.Venkataraman@Sun.COM } 241*9334SMadhavan.Venkataraman@Sun.COM 242*9334SMadhavan.Venkataraman@Sun.COM /* 2430Sstevel@tonic-gate * Allocate a callout structure. We try quite hard because we 2440Sstevel@tonic-gate * can't sleep, and if we can't do the allocation, we're toast. 2458048SMadhavan.Venkataraman@Sun.COM * Failing all, we try a KM_PANIC allocation. Note that we never 2468048SMadhavan.Venkataraman@Sun.COM * deallocate a callout. See untimeout() for the reasoning. 2470Sstevel@tonic-gate */ 2480Sstevel@tonic-gate static callout_t * 2490Sstevel@tonic-gate callout_alloc(callout_table_t *ct) 2500Sstevel@tonic-gate { 2518048SMadhavan.Venkataraman@Sun.COM size_t size; 2528048SMadhavan.Venkataraman@Sun.COM callout_t *cp; 2538048SMadhavan.Venkataraman@Sun.COM 2548048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 2558048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 2560Sstevel@tonic-gate 2578048SMadhavan.Venkataraman@Sun.COM cp = kmem_cache_alloc(ct->ct_cache, KM_NOSLEEP); 2588048SMadhavan.Venkataraman@Sun.COM if (cp == NULL) { 2598048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_t); 2608048SMadhavan.Venkataraman@Sun.COM cp = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC); 2618048SMadhavan.Venkataraman@Sun.COM } 2628048SMadhavan.Venkataraman@Sun.COM cp->c_xid = 0; 2639039SMadhavan.Venkataraman@Sun.COM cp->c_executor = NULL; 2649039SMadhavan.Venkataraman@Sun.COM cv_init(&cp->c_done, NULL, CV_DEFAULT, NULL); 2659039SMadhavan.Venkataraman@Sun.COM cp->c_waiting = 0; 2668048SMadhavan.Venkataraman@Sun.COM 2678048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 2688048SMadhavan.Venkataraman@Sun.COM ct->ct_allocations++; 2690Sstevel@tonic-gate return (cp); 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate /* 2738048SMadhavan.Venkataraman@Sun.COM * Allocate a callout list structure. We try quite hard because we 2748048SMadhavan.Venkataraman@Sun.COM * can't sleep, and if we can't do the allocation, we're toast. 2758048SMadhavan.Venkataraman@Sun.COM * Failing all, we try a KM_PANIC allocation. Note that we never 2768048SMadhavan.Venkataraman@Sun.COM * deallocate a callout list. 2778048SMadhavan.Venkataraman@Sun.COM */ 2788048SMadhavan.Venkataraman@Sun.COM static void 2798048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(callout_table_t *ct) 2808048SMadhavan.Venkataraman@Sun.COM { 2818048SMadhavan.Venkataraman@Sun.COM size_t size; 2828048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 2838048SMadhavan.Venkataraman@Sun.COM 2848048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 2858048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 2868048SMadhavan.Venkataraman@Sun.COM 2878048SMadhavan.Venkataraman@Sun.COM cl = kmem_cache_alloc(ct->ct_lcache, KM_NOSLEEP); 2888048SMadhavan.Venkataraman@Sun.COM if (cl == NULL) { 2898048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_list_t); 2908048SMadhavan.Venkataraman@Sun.COM cl = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC); 2918048SMadhavan.Venkataraman@Sun.COM } 2928048SMadhavan.Venkataraman@Sun.COM bzero(cl, sizeof (callout_list_t)); 2938048SMadhavan.Venkataraman@Sun.COM 2948048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 295*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl); 2968048SMadhavan.Venkataraman@Sun.COM } 2978048SMadhavan.Venkataraman@Sun.COM 2988048SMadhavan.Venkataraman@Sun.COM /* 299*9334SMadhavan.Venkataraman@Sun.COM * Find a callout list that corresponds to an expiration and matching flags. 3008048SMadhavan.Venkataraman@Sun.COM */ 3018048SMadhavan.Venkataraman@Sun.COM static callout_list_t * 3029039SMadhavan.Venkataraman@Sun.COM callout_list_get(callout_table_t *ct, hrtime_t expiration, int flags, int hash) 3038048SMadhavan.Venkataraman@Sun.COM { 3048048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 305*9334SMadhavan.Venkataraman@Sun.COM int clflags; 3068048SMadhavan.Venkataraman@Sun.COM 3078048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 3088048SMadhavan.Venkataraman@Sun.COM 309*9334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_LIST_FLAG_NANO) { 310*9334SMadhavan.Venkataraman@Sun.COM /* 311*9334SMadhavan.Venkataraman@Sun.COM * This is a 1-nanosecond resolution callout. We will rarely 312*9334SMadhavan.Venkataraman@Sun.COM * find a match for this. So, bail out. 313*9334SMadhavan.Venkataraman@Sun.COM */ 314*9334SMadhavan.Venkataraman@Sun.COM return (NULL); 315*9334SMadhavan.Venkataraman@Sun.COM } 316*9334SMadhavan.Venkataraman@Sun.COM 317*9334SMadhavan.Venkataraman@Sun.COM clflags = (CALLOUT_LIST_FLAG_ABSOLUTE | CALLOUT_LIST_FLAG_HRESTIME); 3188048SMadhavan.Venkataraman@Sun.COM for (cl = ct->ct_clhash[hash].ch_head; (cl != NULL); cl = cl->cl_next) { 319*9334SMadhavan.Venkataraman@Sun.COM /* 320*9334SMadhavan.Venkataraman@Sun.COM * If we have reached a 1-nanosecond resolution callout list, 321*9334SMadhavan.Venkataraman@Sun.COM * we don't have much hope of finding a match in this hash 322*9334SMadhavan.Venkataraman@Sun.COM * bucket. So, just bail out. 323*9334SMadhavan.Venkataraman@Sun.COM */ 324*9334SMadhavan.Venkataraman@Sun.COM if (cl->cl_flags & CALLOUT_LIST_FLAG_NANO) 325*9334SMadhavan.Venkataraman@Sun.COM return (NULL); 326*9334SMadhavan.Venkataraman@Sun.COM 3279039SMadhavan.Venkataraman@Sun.COM if ((cl->cl_expiration == expiration) && 328*9334SMadhavan.Venkataraman@Sun.COM ((cl->cl_flags & clflags) == (flags & clflags))) 3298048SMadhavan.Venkataraman@Sun.COM return (cl); 3308048SMadhavan.Venkataraman@Sun.COM } 3318048SMadhavan.Venkataraman@Sun.COM 3328048SMadhavan.Venkataraman@Sun.COM return (NULL); 3338048SMadhavan.Venkataraman@Sun.COM } 3348048SMadhavan.Venkataraman@Sun.COM 3358048SMadhavan.Venkataraman@Sun.COM /* 3368048SMadhavan.Venkataraman@Sun.COM * Initialize a callout table's heap, if necessary. Preallocate some free 3378048SMadhavan.Venkataraman@Sun.COM * entries so we don't have to check for NULL elsewhere. 3388048SMadhavan.Venkataraman@Sun.COM */ 3398048SMadhavan.Venkataraman@Sun.COM static void 3408048SMadhavan.Venkataraman@Sun.COM callout_heap_init(callout_table_t *ct) 3418048SMadhavan.Venkataraman@Sun.COM { 3428048SMadhavan.Venkataraman@Sun.COM size_t size; 3438048SMadhavan.Venkataraman@Sun.COM 3448048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 3458048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap == NULL); 3468048SMadhavan.Venkataraman@Sun.COM 3478048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num = 0; 3488048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_max = CALLOUT_CHUNK; 349*9334SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_heap_t) * CALLOUT_CHUNK; 3508048SMadhavan.Venkataraman@Sun.COM ct->ct_heap = kmem_alloc(size, KM_SLEEP); 3518048SMadhavan.Venkataraman@Sun.COM } 3528048SMadhavan.Venkataraman@Sun.COM 3538048SMadhavan.Venkataraman@Sun.COM /* 3548048SMadhavan.Venkataraman@Sun.COM * Reallocate the heap. We try quite hard because we can't sleep, and if 3558048SMadhavan.Venkataraman@Sun.COM * we can't do the allocation, we're toast. Failing all, we try a KM_PANIC 3568048SMadhavan.Venkataraman@Sun.COM * allocation. Note that the heap only expands, it never contracts. 3578048SMadhavan.Venkataraman@Sun.COM */ 3588048SMadhavan.Venkataraman@Sun.COM static void 3598048SMadhavan.Venkataraman@Sun.COM callout_heap_expand(callout_table_t *ct) 3608048SMadhavan.Venkataraman@Sun.COM { 3618048SMadhavan.Venkataraman@Sun.COM size_t max, size, osize; 362*9334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap; 3638048SMadhavan.Venkataraman@Sun.COM 3648048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 3658048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num <= ct->ct_heap_max); 3668048SMadhavan.Venkataraman@Sun.COM 3678048SMadhavan.Venkataraman@Sun.COM while (ct->ct_heap_num == ct->ct_heap_max) { 3688048SMadhavan.Venkataraman@Sun.COM max = ct->ct_heap_max; 3698048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 3708048SMadhavan.Venkataraman@Sun.COM 371*9334SMadhavan.Venkataraman@Sun.COM osize = sizeof (callout_heap_t) * max; 372*9334SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_heap_t) * (max + CALLOUT_CHUNK); 3738048SMadhavan.Venkataraman@Sun.COM heap = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC); 3748048SMadhavan.Venkataraman@Sun.COM 3758048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 3768048SMadhavan.Venkataraman@Sun.COM if (max < ct->ct_heap_max) { 3778048SMadhavan.Venkataraman@Sun.COM /* 3788048SMadhavan.Venkataraman@Sun.COM * Someone beat us to the allocation. Free what we 3798048SMadhavan.Venkataraman@Sun.COM * just allocated and proceed. 3808048SMadhavan.Venkataraman@Sun.COM */ 3818048SMadhavan.Venkataraman@Sun.COM kmem_free(heap, size); 3828048SMadhavan.Venkataraman@Sun.COM continue; 3838048SMadhavan.Venkataraman@Sun.COM } 3848048SMadhavan.Venkataraman@Sun.COM 3858048SMadhavan.Venkataraman@Sun.COM bcopy(ct->ct_heap, heap, osize); 3868048SMadhavan.Venkataraman@Sun.COM kmem_free(ct->ct_heap, osize); 3878048SMadhavan.Venkataraman@Sun.COM ct->ct_heap = heap; 388*9334SMadhavan.Venkataraman@Sun.COM ct->ct_heap_max = size / sizeof (callout_heap_t); 3898048SMadhavan.Venkataraman@Sun.COM } 3908048SMadhavan.Venkataraman@Sun.COM } 3918048SMadhavan.Venkataraman@Sun.COM 3928048SMadhavan.Venkataraman@Sun.COM /* 3938048SMadhavan.Venkataraman@Sun.COM * Move an expiration from the bottom of the heap to its correct place 3948048SMadhavan.Venkataraman@Sun.COM * in the heap. If we reached the root doing this, return 1. Else, 3958048SMadhavan.Venkataraman@Sun.COM * return 0. 3960Sstevel@tonic-gate */ 3978048SMadhavan.Venkataraman@Sun.COM static int 3988048SMadhavan.Venkataraman@Sun.COM callout_upheap(callout_table_t *ct) 3998048SMadhavan.Venkataraman@Sun.COM { 4008048SMadhavan.Venkataraman@Sun.COM int current, parent; 401*9334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap, *hcurrent, *hparent; 4028048SMadhavan.Venkataraman@Sun.COM 4038048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 4048048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num >= 1); 4058048SMadhavan.Venkataraman@Sun.COM 4068048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 1) { 4078048SMadhavan.Venkataraman@Sun.COM return (1); 4088048SMadhavan.Venkataraman@Sun.COM } 4098048SMadhavan.Venkataraman@Sun.COM 4108048SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap; 4118048SMadhavan.Venkataraman@Sun.COM current = ct->ct_heap_num - 1; 4128048SMadhavan.Venkataraman@Sun.COM 4138048SMadhavan.Venkataraman@Sun.COM for (;;) { 4148048SMadhavan.Venkataraman@Sun.COM parent = CALLOUT_HEAP_PARENT(current); 415*9334SMadhavan.Venkataraman@Sun.COM hparent = &heap[parent]; 416*9334SMadhavan.Venkataraman@Sun.COM hcurrent = &heap[current]; 4178048SMadhavan.Venkataraman@Sun.COM 4188048SMadhavan.Venkataraman@Sun.COM /* 4198048SMadhavan.Venkataraman@Sun.COM * We have an expiration later than our parent; we're done. 4208048SMadhavan.Venkataraman@Sun.COM */ 421*9334SMadhavan.Venkataraman@Sun.COM if (hcurrent->ch_expiration >= hparent->ch_expiration) { 4228048SMadhavan.Venkataraman@Sun.COM return (0); 4238048SMadhavan.Venkataraman@Sun.COM } 4248048SMadhavan.Venkataraman@Sun.COM 4258048SMadhavan.Venkataraman@Sun.COM /* 4268048SMadhavan.Venkataraman@Sun.COM * We need to swap with our parent, and continue up the heap. 4278048SMadhavan.Venkataraman@Sun.COM */ 428*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_SWAP(hparent, hcurrent); 4298048SMadhavan.Venkataraman@Sun.COM 4308048SMadhavan.Venkataraman@Sun.COM /* 4318048SMadhavan.Venkataraman@Sun.COM * If we just reached the root, we're done. 4328048SMadhavan.Venkataraman@Sun.COM */ 4338048SMadhavan.Venkataraman@Sun.COM if (parent == 0) { 4348048SMadhavan.Venkataraman@Sun.COM return (1); 4358048SMadhavan.Venkataraman@Sun.COM } 4368048SMadhavan.Venkataraman@Sun.COM 4378048SMadhavan.Venkataraman@Sun.COM current = parent; 4388048SMadhavan.Venkataraman@Sun.COM } 4398048SMadhavan.Venkataraman@Sun.COM /*NOTREACHED*/ 4408048SMadhavan.Venkataraman@Sun.COM } 4418048SMadhavan.Venkataraman@Sun.COM 4428048SMadhavan.Venkataraman@Sun.COM /* 443*9334SMadhavan.Venkataraman@Sun.COM * Insert a new heap item into a callout table's heap. 4448048SMadhavan.Venkataraman@Sun.COM */ 4458048SMadhavan.Venkataraman@Sun.COM static void 446*9334SMadhavan.Venkataraman@Sun.COM callout_heap_insert(callout_table_t *ct, callout_list_t *cl) 4478048SMadhavan.Venkataraman@Sun.COM { 4488048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 4498048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num < ct->ct_heap_max); 4508048SMadhavan.Venkataraman@Sun.COM 4518048SMadhavan.Venkataraman@Sun.COM /* 452*9334SMadhavan.Venkataraman@Sun.COM * First, copy the expiration and callout list pointer to the bottom 453*9334SMadhavan.Venkataraman@Sun.COM * of the heap. 4548048SMadhavan.Venkataraman@Sun.COM */ 455*9334SMadhavan.Venkataraman@Sun.COM ct->ct_heap[ct->ct_heap_num].ch_expiration = cl->cl_expiration; 456*9334SMadhavan.Venkataraman@Sun.COM ct->ct_heap[ct->ct_heap_num].ch_list = cl; 4578048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num++; 4588048SMadhavan.Venkataraman@Sun.COM 4598048SMadhavan.Venkataraman@Sun.COM /* 4608048SMadhavan.Venkataraman@Sun.COM * Now, perform an upheap operation. If we reached the root, then 4618048SMadhavan.Venkataraman@Sun.COM * the cyclic needs to be reprogrammed as we have an earlier 4628048SMadhavan.Venkataraman@Sun.COM * expiration. 4638048SMadhavan.Venkataraman@Sun.COM * 4648048SMadhavan.Venkataraman@Sun.COM * Also, during the CPR suspend phase, do not reprogram the cyclic. 4658048SMadhavan.Venkataraman@Sun.COM * We don't want any callout activity. When the CPR resume phase is 4668048SMadhavan.Venkataraman@Sun.COM * entered, the cyclic will be programmed for the earliest expiration 4678048SMadhavan.Venkataraman@Sun.COM * in the heap. 4688048SMadhavan.Venkataraman@Sun.COM */ 4698566SMadhavan.Venkataraman@Sun.COM if (callout_upheap(ct) && (ct->ct_suspend == 0)) 470*9334SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, cl->cl_expiration); 4718048SMadhavan.Venkataraman@Sun.COM } 4728048SMadhavan.Venkataraman@Sun.COM 4738048SMadhavan.Venkataraman@Sun.COM /* 4748048SMadhavan.Venkataraman@Sun.COM * Move an expiration from the top of the heap to its correct place 4758048SMadhavan.Venkataraman@Sun.COM * in the heap. 4768048SMadhavan.Venkataraman@Sun.COM */ 4778048SMadhavan.Venkataraman@Sun.COM static void 4788048SMadhavan.Venkataraman@Sun.COM callout_downheap(callout_table_t *ct) 4790Sstevel@tonic-gate { 480*9334SMadhavan.Venkataraman@Sun.COM int current, left, right, nelems; 481*9334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap, *hleft, *hright, *hcurrent; 4828048SMadhavan.Venkataraman@Sun.COM 4838048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 4848048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num >= 1); 4858048SMadhavan.Venkataraman@Sun.COM 4868048SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap; 4878048SMadhavan.Venkataraman@Sun.COM current = 0; 4888048SMadhavan.Venkataraman@Sun.COM nelems = ct->ct_heap_num; 4898048SMadhavan.Venkataraman@Sun.COM 4908048SMadhavan.Venkataraman@Sun.COM for (;;) { 4918048SMadhavan.Venkataraman@Sun.COM /* 4928048SMadhavan.Venkataraman@Sun.COM * If we don't have a left child (i.e., we're a leaf), we're 4938048SMadhavan.Venkataraman@Sun.COM * done. 4948048SMadhavan.Venkataraman@Sun.COM */ 4958048SMadhavan.Venkataraman@Sun.COM if ((left = CALLOUT_HEAP_LEFT(current)) >= nelems) 4968048SMadhavan.Venkataraman@Sun.COM return; 4978048SMadhavan.Venkataraman@Sun.COM 498*9334SMadhavan.Venkataraman@Sun.COM hleft = &heap[left]; 499*9334SMadhavan.Venkataraman@Sun.COM hcurrent = &heap[current]; 5008048SMadhavan.Venkataraman@Sun.COM 5018048SMadhavan.Venkataraman@Sun.COM right = CALLOUT_HEAP_RIGHT(current); 5028048SMadhavan.Venkataraman@Sun.COM 5038048SMadhavan.Venkataraman@Sun.COM /* 5048048SMadhavan.Venkataraman@Sun.COM * Even if we don't have a right child, we still need to compare 5058048SMadhavan.Venkataraman@Sun.COM * our expiration against that of our left child. 5068048SMadhavan.Venkataraman@Sun.COM */ 5078048SMadhavan.Venkataraman@Sun.COM if (right >= nelems) 5088048SMadhavan.Venkataraman@Sun.COM goto comp_left; 5098048SMadhavan.Venkataraman@Sun.COM 510*9334SMadhavan.Venkataraman@Sun.COM hright = &heap[right]; 5118048SMadhavan.Venkataraman@Sun.COM 5128048SMadhavan.Venkataraman@Sun.COM /* 5138048SMadhavan.Venkataraman@Sun.COM * We have both a left and a right child. We need to compare 5148048SMadhavan.Venkataraman@Sun.COM * the expiration of the children to determine which 5158048SMadhavan.Venkataraman@Sun.COM * expires earlier. 5168048SMadhavan.Venkataraman@Sun.COM */ 517*9334SMadhavan.Venkataraman@Sun.COM if (hright->ch_expiration < hleft->ch_expiration) { 5188048SMadhavan.Venkataraman@Sun.COM /* 5198048SMadhavan.Venkataraman@Sun.COM * Our right child is the earlier of our children. 5208048SMadhavan.Venkataraman@Sun.COM * We'll now compare our expiration to its expiration. 5218048SMadhavan.Venkataraman@Sun.COM * If ours is the earlier one, we're done. 5228048SMadhavan.Venkataraman@Sun.COM */ 523*9334SMadhavan.Venkataraman@Sun.COM if (hcurrent->ch_expiration <= hright->ch_expiration) 5248048SMadhavan.Venkataraman@Sun.COM return; 5258048SMadhavan.Venkataraman@Sun.COM 5268048SMadhavan.Venkataraman@Sun.COM /* 5278048SMadhavan.Venkataraman@Sun.COM * Our right child expires earlier than we do; swap 5288048SMadhavan.Venkataraman@Sun.COM * with our right child, and descend right. 5298048SMadhavan.Venkataraman@Sun.COM */ 530*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_SWAP(hright, hcurrent); 5318048SMadhavan.Venkataraman@Sun.COM current = right; 5328048SMadhavan.Venkataraman@Sun.COM continue; 5338048SMadhavan.Venkataraman@Sun.COM } 5348048SMadhavan.Venkataraman@Sun.COM 5358048SMadhavan.Venkataraman@Sun.COM comp_left: 5368048SMadhavan.Venkataraman@Sun.COM /* 5378048SMadhavan.Venkataraman@Sun.COM * Our left child is the earlier of our children (or we have 5388048SMadhavan.Venkataraman@Sun.COM * no right child). We'll now compare our expiration 5398048SMadhavan.Venkataraman@Sun.COM * to its expiration. If ours is the earlier one, we're done. 5408048SMadhavan.Venkataraman@Sun.COM */ 541*9334SMadhavan.Venkataraman@Sun.COM if (hcurrent->ch_expiration <= hleft->ch_expiration) 5428048SMadhavan.Venkataraman@Sun.COM return; 5438048SMadhavan.Venkataraman@Sun.COM 5448048SMadhavan.Venkataraman@Sun.COM /* 5458048SMadhavan.Venkataraman@Sun.COM * Our left child expires earlier than we do; swap with our 5468048SMadhavan.Venkataraman@Sun.COM * left child, and descend left. 5478048SMadhavan.Venkataraman@Sun.COM */ 548*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_SWAP(hleft, hcurrent); 5498048SMadhavan.Venkataraman@Sun.COM current = left; 5508048SMadhavan.Venkataraman@Sun.COM } 5518048SMadhavan.Venkataraman@Sun.COM } 5528048SMadhavan.Venkataraman@Sun.COM 5538048SMadhavan.Venkataraman@Sun.COM /* 5548048SMadhavan.Venkataraman@Sun.COM * Delete and handle all past expirations in a callout table's heap. 5558048SMadhavan.Venkataraman@Sun.COM */ 5568048SMadhavan.Venkataraman@Sun.COM static void 5578048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(callout_table_t *ct) 5588048SMadhavan.Venkataraman@Sun.COM { 559*9334SMadhavan.Venkataraman@Sun.COM hrtime_t now, expiration, next; 5608048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 561*9334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap; 5628048SMadhavan.Venkataraman@Sun.COM int hash; 5638048SMadhavan.Venkataraman@Sun.COM 5648048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 5658048SMadhavan.Venkataraman@Sun.COM 566*9334SMadhavan.Venkataraman@Sun.COM if (CALLOUT_CLEANUP(ct)) { 567*9334SMadhavan.Venkataraman@Sun.COM /* 568*9334SMadhavan.Venkataraman@Sun.COM * There are too many heap elements pointing to empty callout 569*9334SMadhavan.Venkataraman@Sun.COM * lists. Clean them out. 570*9334SMadhavan.Venkataraman@Sun.COM */ 571*9334SMadhavan.Venkataraman@Sun.COM (void) callout_heap_process(ct, 0, 0); 572*9334SMadhavan.Venkataraman@Sun.COM } 573*9334SMadhavan.Venkataraman@Sun.COM 5748048SMadhavan.Venkataraman@Sun.COM now = gethrtime(); 575*9334SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap; 5768048SMadhavan.Venkataraman@Sun.COM 5778048SMadhavan.Venkataraman@Sun.COM while (ct->ct_heap_num > 0) { 578*9334SMadhavan.Venkataraman@Sun.COM expiration = heap->ch_expiration; 5798048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(expiration); 580*9334SMadhavan.Venkataraman@Sun.COM cl = heap->ch_list; 581*9334SMadhavan.Venkataraman@Sun.COM ASSERT(expiration == cl->cl_expiration); 582*9334SMadhavan.Venkataraman@Sun.COM 583*9334SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) { 5848048SMadhavan.Venkataraman@Sun.COM /* 585*9334SMadhavan.Venkataraman@Sun.COM * If the callout list is empty, reap it. 586*9334SMadhavan.Venkataraman@Sun.COM * Decrement the reap count. 587*9334SMadhavan.Venkataraman@Sun.COM */ 588*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 589*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl); 590*9334SMadhavan.Venkataraman@Sun.COM ct->ct_nreap--; 591*9334SMadhavan.Venkataraman@Sun.COM } else { 592*9334SMadhavan.Venkataraman@Sun.COM /* 593*9334SMadhavan.Venkataraman@Sun.COM * If the root of the heap expires in the future, 594*9334SMadhavan.Venkataraman@Sun.COM * bail out. 5958048SMadhavan.Venkataraman@Sun.COM */ 5968048SMadhavan.Venkataraman@Sun.COM if (expiration > now) 5978048SMadhavan.Venkataraman@Sun.COM break; 5988048SMadhavan.Venkataraman@Sun.COM 5998048SMadhavan.Venkataraman@Sun.COM /* 6008048SMadhavan.Venkataraman@Sun.COM * Move the callout list for this expiration to the 6018048SMadhavan.Venkataraman@Sun.COM * list of expired callout lists. It will be processed 6028048SMadhavan.Venkataraman@Sun.COM * by the callout executor. 6038048SMadhavan.Venkataraman@Sun.COM */ 6048048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 6058048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, cl); 6068048SMadhavan.Venkataraman@Sun.COM } 6078048SMadhavan.Venkataraman@Sun.COM 6088048SMadhavan.Venkataraman@Sun.COM /* 6098048SMadhavan.Venkataraman@Sun.COM * Now delete the root. This is done by swapping the root with 6108048SMadhavan.Venkataraman@Sun.COM * the last item in the heap and downheaping the item. 6118048SMadhavan.Venkataraman@Sun.COM */ 6128048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num--; 6138048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num > 0) { 614*9334SMadhavan.Venkataraman@Sun.COM heap[0] = heap[ct->ct_heap_num]; 6158048SMadhavan.Venkataraman@Sun.COM callout_downheap(ct); 6168048SMadhavan.Venkataraman@Sun.COM } 6178048SMadhavan.Venkataraman@Sun.COM } 6188048SMadhavan.Venkataraman@Sun.COM 6198048SMadhavan.Venkataraman@Sun.COM /* 620*9334SMadhavan.Venkataraman@Sun.COM * If this callout table is empty or callouts have been suspended, 621*9334SMadhavan.Venkataraman@Sun.COM * just return. The cyclic has already been programmed to 6228048SMadhavan.Venkataraman@Sun.COM * infinity by the cyclic subsystem. 6238048SMadhavan.Venkataraman@Sun.COM */ 6248566SMadhavan.Venkataraman@Sun.COM if ((ct->ct_heap_num == 0) || (ct->ct_suspend > 0)) 6258048SMadhavan.Venkataraman@Sun.COM return; 6268048SMadhavan.Venkataraman@Sun.COM 627*9334SMadhavan.Venkataraman@Sun.COM /* 628*9334SMadhavan.Venkataraman@Sun.COM * If the top expirations are within callout_tolerance of each other, 629*9334SMadhavan.Venkataraman@Sun.COM * delay the cyclic expire so that they can be processed together. 630*9334SMadhavan.Venkataraman@Sun.COM * This is to prevent high resolution timers from swamping the system 631*9334SMadhavan.Venkataraman@Sun.COM * with cyclic activity. 632*9334SMadhavan.Venkataraman@Sun.COM */ 633*9334SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num > 2) { 634*9334SMadhavan.Venkataraman@Sun.COM next = expiration + callout_tolerance; 635*9334SMadhavan.Venkataraman@Sun.COM if ((heap[1].ch_expiration < next) || 636*9334SMadhavan.Venkataraman@Sun.COM (heap[2].ch_expiration < next)) 637*9334SMadhavan.Venkataraman@Sun.COM expiration = next; 638*9334SMadhavan.Venkataraman@Sun.COM } 639*9334SMadhavan.Venkataraman@Sun.COM 6408048SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, expiration); 6418048SMadhavan.Venkataraman@Sun.COM } 6428048SMadhavan.Venkataraman@Sun.COM 6438566SMadhavan.Venkataraman@Sun.COM /* 644*9334SMadhavan.Venkataraman@Sun.COM * There are some situations when the entire heap is walked and processed. 645*9334SMadhavan.Venkataraman@Sun.COM * This function is called to do the processing. These are the situations: 646*9334SMadhavan.Venkataraman@Sun.COM * 647*9334SMadhavan.Venkataraman@Sun.COM * 1. When the reap count reaches its threshold, the heap has to be cleared 648*9334SMadhavan.Venkataraman@Sun.COM * of all empty callout lists. 649*9334SMadhavan.Venkataraman@Sun.COM * 650*9334SMadhavan.Venkataraman@Sun.COM * 2. When the system enters and exits KMDB/OBP, all entries in the heap 651*9334SMadhavan.Venkataraman@Sun.COM * need to be adjusted by the interval spent in KMDB/OBP. 652*9334SMadhavan.Venkataraman@Sun.COM * 653*9334SMadhavan.Venkataraman@Sun.COM * 3. When system time is changed, the heap has to be scanned for 654*9334SMadhavan.Venkataraman@Sun.COM * absolute hrestime timers. These need to be removed from the heap 655*9334SMadhavan.Venkataraman@Sun.COM * and expired immediately. 656*9334SMadhavan.Venkataraman@Sun.COM * 657*9334SMadhavan.Venkataraman@Sun.COM * In cases 2 and 3, it is a good idea to do 1 as well since we are 658*9334SMadhavan.Venkataraman@Sun.COM * scanning the heap anyway. 659*9334SMadhavan.Venkataraman@Sun.COM * 660*9334SMadhavan.Venkataraman@Sun.COM * If the root gets changed and/or callout lists are expired, return the 661*9334SMadhavan.Venkataraman@Sun.COM * new expiration to the caller so he can reprogram the cyclic accordingly. 662*9334SMadhavan.Venkataraman@Sun.COM */ 663*9334SMadhavan.Venkataraman@Sun.COM static hrtime_t 664*9334SMadhavan.Venkataraman@Sun.COM callout_heap_process(callout_table_t *ct, hrtime_t delta, int timechange) 665*9334SMadhavan.Venkataraman@Sun.COM { 666*9334SMadhavan.Venkataraman@Sun.COM callout_heap_t *heap; 667*9334SMadhavan.Venkataraman@Sun.COM callout_list_t *cl, *rootcl; 668*9334SMadhavan.Venkataraman@Sun.COM hrtime_t expiration, now; 669*9334SMadhavan.Venkataraman@Sun.COM int i, hash, clflags, expired; 670*9334SMadhavan.Venkataraman@Sun.COM ulong_t num; 671*9334SMadhavan.Venkataraman@Sun.COM 672*9334SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 673*9334SMadhavan.Venkataraman@Sun.COM 674*9334SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 0) 675*9334SMadhavan.Venkataraman@Sun.COM return (0); 676*9334SMadhavan.Venkataraman@Sun.COM 677*9334SMadhavan.Venkataraman@Sun.COM if (ct->ct_nreap > 0) 678*9334SMadhavan.Venkataraman@Sun.COM ct->ct_cleanups++; 679*9334SMadhavan.Venkataraman@Sun.COM 680*9334SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap; 681*9334SMadhavan.Venkataraman@Sun.COM rootcl = heap->ch_list; 682*9334SMadhavan.Venkataraman@Sun.COM 683*9334SMadhavan.Venkataraman@Sun.COM /* 684*9334SMadhavan.Venkataraman@Sun.COM * We walk the heap from the top to the bottom. If we encounter 685*9334SMadhavan.Venkataraman@Sun.COM * a heap item that points to an empty callout list, we clean 686*9334SMadhavan.Venkataraman@Sun.COM * it out. If we encounter a hrestime entry that must be removed, 687*9334SMadhavan.Venkataraman@Sun.COM * again we clean it out. Otherwise, we apply any adjustments needed 688*9334SMadhavan.Venkataraman@Sun.COM * to an element. 689*9334SMadhavan.Venkataraman@Sun.COM * 690*9334SMadhavan.Venkataraman@Sun.COM * During the walk, we also compact the heap from the bottom and 691*9334SMadhavan.Venkataraman@Sun.COM * reconstruct the heap using upheap operations. This is very 692*9334SMadhavan.Venkataraman@Sun.COM * efficient if the number of elements to be cleaned is greater than 693*9334SMadhavan.Venkataraman@Sun.COM * or equal to half the heap. This is the common case. 694*9334SMadhavan.Venkataraman@Sun.COM * 695*9334SMadhavan.Venkataraman@Sun.COM * Even in the non-common case, the upheap operations should be short 696*9334SMadhavan.Venkataraman@Sun.COM * as the entries below generally tend to be bigger than the entries 697*9334SMadhavan.Venkataraman@Sun.COM * above. 698*9334SMadhavan.Venkataraman@Sun.COM */ 699*9334SMadhavan.Venkataraman@Sun.COM num = ct->ct_heap_num; 700*9334SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num = 0; 701*9334SMadhavan.Venkataraman@Sun.COM clflags = (CALLOUT_LIST_FLAG_HRESTIME | CALLOUT_LIST_FLAG_ABSOLUTE); 702*9334SMadhavan.Venkataraman@Sun.COM now = gethrtime(); 703*9334SMadhavan.Venkataraman@Sun.COM expired = 0; 704*9334SMadhavan.Venkataraman@Sun.COM for (i = 0; i < num; i++) { 705*9334SMadhavan.Venkataraman@Sun.COM cl = heap[i].ch_list; 706*9334SMadhavan.Venkataraman@Sun.COM /* 707*9334SMadhavan.Venkataraman@Sun.COM * If the callout list is empty, delete the heap element and 708*9334SMadhavan.Venkataraman@Sun.COM * free the callout list. 709*9334SMadhavan.Venkataraman@Sun.COM */ 710*9334SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) { 711*9334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration); 712*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 713*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl); 714*9334SMadhavan.Venkataraman@Sun.COM continue; 715*9334SMadhavan.Venkataraman@Sun.COM } 716*9334SMadhavan.Venkataraman@Sun.COM 717*9334SMadhavan.Venkataraman@Sun.COM /* 718*9334SMadhavan.Venkataraman@Sun.COM * Delete the heap element and expire the callout list, if 719*9334SMadhavan.Venkataraman@Sun.COM * one of the following is true: 720*9334SMadhavan.Venkataraman@Sun.COM * - the callout list has expired 721*9334SMadhavan.Venkataraman@Sun.COM * - the callout list is an absolute hrestime one and 722*9334SMadhavan.Venkataraman@Sun.COM * there has been a system time change 723*9334SMadhavan.Venkataraman@Sun.COM */ 724*9334SMadhavan.Venkataraman@Sun.COM if ((cl->cl_expiration <= now) || 725*9334SMadhavan.Venkataraman@Sun.COM (timechange && ((cl->cl_flags & clflags) == clflags))) { 726*9334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration); 727*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 728*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, cl); 729*9334SMadhavan.Venkataraman@Sun.COM expired = 1; 730*9334SMadhavan.Venkataraman@Sun.COM continue; 731*9334SMadhavan.Venkataraman@Sun.COM } 732*9334SMadhavan.Venkataraman@Sun.COM 733*9334SMadhavan.Venkataraman@Sun.COM /* 734*9334SMadhavan.Venkataraman@Sun.COM * Apply adjustments, if any. Adjustments are applied after 735*9334SMadhavan.Venkataraman@Sun.COM * the system returns from KMDB or OBP. They are only applied 736*9334SMadhavan.Venkataraman@Sun.COM * to relative callout lists. 737*9334SMadhavan.Venkataraman@Sun.COM */ 738*9334SMadhavan.Venkataraman@Sun.COM if (delta && !(cl->cl_flags & CALLOUT_LIST_FLAG_ABSOLUTE)) { 739*9334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration); 740*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 741*9334SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration + delta; 742*9334SMadhavan.Venkataraman@Sun.COM if (expiration <= 0) 743*9334SMadhavan.Venkataraman@Sun.COM expiration = CY_INFINITY; 744*9334SMadhavan.Venkataraman@Sun.COM heap[i].ch_expiration = expiration; 745*9334SMadhavan.Venkataraman@Sun.COM cl->cl_expiration = expiration; 746*9334SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(cl->cl_expiration); 747*9334SMadhavan.Venkataraman@Sun.COM if (cl->cl_flags & CALLOUT_LIST_FLAG_NANO) { 748*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_clhash[hash], cl); 749*9334SMadhavan.Venkataraman@Sun.COM } else { 750*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl); 751*9334SMadhavan.Venkataraman@Sun.COM } 752*9334SMadhavan.Venkataraman@Sun.COM } 753*9334SMadhavan.Venkataraman@Sun.COM 754*9334SMadhavan.Venkataraman@Sun.COM heap[ct->ct_heap_num] = heap[i]; 755*9334SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num++; 756*9334SMadhavan.Venkataraman@Sun.COM (void) callout_upheap(ct); 757*9334SMadhavan.Venkataraman@Sun.COM } 758*9334SMadhavan.Venkataraman@Sun.COM 759*9334SMadhavan.Venkataraman@Sun.COM ct->ct_nreap = 0; 760*9334SMadhavan.Venkataraman@Sun.COM 761*9334SMadhavan.Venkataraman@Sun.COM if (expired) 762*9334SMadhavan.Venkataraman@Sun.COM expiration = gethrtime(); 763*9334SMadhavan.Venkataraman@Sun.COM else if (ct->ct_heap_num == 0) 764*9334SMadhavan.Venkataraman@Sun.COM expiration = CY_INFINITY; 765*9334SMadhavan.Venkataraman@Sun.COM else if (rootcl != heap->ch_list) 766*9334SMadhavan.Venkataraman@Sun.COM expiration = heap->ch_expiration; 767*9334SMadhavan.Venkataraman@Sun.COM else 768*9334SMadhavan.Venkataraman@Sun.COM expiration = 0; 769*9334SMadhavan.Venkataraman@Sun.COM 770*9334SMadhavan.Venkataraman@Sun.COM return (expiration); 771*9334SMadhavan.Venkataraman@Sun.COM } 772*9334SMadhavan.Venkataraman@Sun.COM 773*9334SMadhavan.Venkataraman@Sun.COM /* 7748566SMadhavan.Venkataraman@Sun.COM * Common function used to create normal and realtime callouts. 7758566SMadhavan.Venkataraman@Sun.COM * 7768566SMadhavan.Venkataraman@Sun.COM * Realtime callouts are handled at CY_LOW_PIL by a cyclic handler. So, 7778566SMadhavan.Venkataraman@Sun.COM * there is one restriction on a realtime callout handler - it should not 7788566SMadhavan.Venkataraman@Sun.COM * directly or indirectly acquire cpu_lock. CPU offline waits for pending 7798566SMadhavan.Venkataraman@Sun.COM * cyclic handlers to complete while holding cpu_lock. So, if a realtime 7808566SMadhavan.Venkataraman@Sun.COM * callout handler were to try to get cpu_lock, there would be a deadlock 7818566SMadhavan.Venkataraman@Sun.COM * during CPU offline. 7828566SMadhavan.Venkataraman@Sun.COM */ 7838048SMadhavan.Venkataraman@Sun.COM callout_id_t 7848048SMadhavan.Venkataraman@Sun.COM timeout_generic(int type, void (*func)(void *), void *arg, 7858048SMadhavan.Venkataraman@Sun.COM hrtime_t expiration, hrtime_t resolution, int flags) 7868048SMadhavan.Venkataraman@Sun.COM { 7878048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 7884123Sdm120769 callout_t *cp; 7894123Sdm120769 callout_id_t id; 7908048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 791*9334SMadhavan.Venkataraman@Sun.COM hrtime_t now, interval, rexpiration; 792*9334SMadhavan.Venkataraman@Sun.COM int hash, clflags; 7938048SMadhavan.Venkataraman@Sun.COM 7948048SMadhavan.Venkataraman@Sun.COM ASSERT(resolution > 0); 7958048SMadhavan.Venkataraman@Sun.COM ASSERT(func != NULL); 7968048SMadhavan.Venkataraman@Sun.COM 7978048SMadhavan.Venkataraman@Sun.COM /* 798*9334SMadhavan.Venkataraman@Sun.COM * We get the current hrtime right upfront so that latencies in 799*9334SMadhavan.Venkataraman@Sun.COM * this function do not affect the accuracy of the callout. 8008048SMadhavan.Venkataraman@Sun.COM */ 801*9334SMadhavan.Venkataraman@Sun.COM now = gethrtime(); 8026422Sqiao 8038048SMadhavan.Venkataraman@Sun.COM /* 8048048SMadhavan.Venkataraman@Sun.COM * We disable kernel preemption so that we remain on the same CPU 8058048SMadhavan.Venkataraman@Sun.COM * throughout. If we needed to reprogram the callout table's cyclic, 8068048SMadhavan.Venkataraman@Sun.COM * we can avoid X-calls if we are on the same CPU. 8078048SMadhavan.Venkataraman@Sun.COM * 8088048SMadhavan.Venkataraman@Sun.COM * Note that callout_alloc() releases and reacquires the callout 8098048SMadhavan.Venkataraman@Sun.COM * table mutex. While reacquiring the mutex, it is possible for us 8108048SMadhavan.Venkataraman@Sun.COM * to go to sleep and later migrate to another CPU. This should be 8118048SMadhavan.Venkataraman@Sun.COM * pretty rare, though. 8128048SMadhavan.Venkataraman@Sun.COM */ 8138048SMadhavan.Venkataraman@Sun.COM kpreempt_disable(); 8140Sstevel@tonic-gate 8158048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(type, CPU->cpu_seqid)]; 8168048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 8170Sstevel@tonic-gate 8188048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) { 8198048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 8208048SMadhavan.Venkataraman@Sun.COM /* 8218048SMadhavan.Venkataraman@Sun.COM * The callout table has not yet been initialized fully. 8228048SMadhavan.Venkataraman@Sun.COM * So, put this one on the boot callout table which is 8238048SMadhavan.Venkataraman@Sun.COM * always initialized. 8248048SMadhavan.Venkataraman@Sun.COM */ 8258048SMadhavan.Venkataraman@Sun.COM ct = &callout_boot_ct[type]; 8268048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 8278048SMadhavan.Venkataraman@Sun.COM } 8288048SMadhavan.Venkataraman@Sun.COM 829*9334SMadhavan.Venkataraman@Sun.COM if (CALLOUT_CLEANUP(ct)) { 830*9334SMadhavan.Venkataraman@Sun.COM /* 831*9334SMadhavan.Venkataraman@Sun.COM * There are too many heap elements pointing to empty callout 832*9334SMadhavan.Venkataraman@Sun.COM * lists. Clean them out. 833*9334SMadhavan.Venkataraman@Sun.COM */ 834*9334SMadhavan.Venkataraman@Sun.COM rexpiration = callout_heap_process(ct, 0, 0); 835*9334SMadhavan.Venkataraman@Sun.COM if ((rexpiration != 0) && (ct->ct_suspend == 0)) 836*9334SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, rexpiration); 837*9334SMadhavan.Venkataraman@Sun.COM } 838*9334SMadhavan.Venkataraman@Sun.COM 8398048SMadhavan.Venkataraman@Sun.COM if ((cp = ct->ct_free) == NULL) 8400Sstevel@tonic-gate cp = callout_alloc(ct); 8410Sstevel@tonic-gate else 8428048SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp->c_idnext; 8430Sstevel@tonic-gate 8440Sstevel@tonic-gate cp->c_func = func; 8450Sstevel@tonic-gate cp->c_arg = arg; 8460Sstevel@tonic-gate 8470Sstevel@tonic-gate /* 8488048SMadhavan.Venkataraman@Sun.COM * Compute the expiration hrtime. 8498048SMadhavan.Venkataraman@Sun.COM */ 8508048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ABSOLUTE) { 8518048SMadhavan.Venkataraman@Sun.COM interval = expiration - now; 8528048SMadhavan.Venkataraman@Sun.COM } else { 8538048SMadhavan.Venkataraman@Sun.COM interval = expiration; 8548048SMadhavan.Venkataraman@Sun.COM expiration += now; 8558048SMadhavan.Venkataraman@Sun.COM } 856*9334SMadhavan.Venkataraman@Sun.COM 857*9334SMadhavan.Venkataraman@Sun.COM if (resolution > 1) { 858*9334SMadhavan.Venkataraman@Sun.COM /* 859*9334SMadhavan.Venkataraman@Sun.COM * Align expiration to the specified resolution. 860*9334SMadhavan.Venkataraman@Sun.COM */ 861*9334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ROUNDUP) 862*9334SMadhavan.Venkataraman@Sun.COM expiration += resolution - 1; 863*9334SMadhavan.Venkataraman@Sun.COM expiration = (expiration / resolution) * resolution; 864*9334SMadhavan.Venkataraman@Sun.COM } 865*9334SMadhavan.Venkataraman@Sun.COM 8668566SMadhavan.Venkataraman@Sun.COM if (expiration <= 0) { 8678566SMadhavan.Venkataraman@Sun.COM /* 8688566SMadhavan.Venkataraman@Sun.COM * expiration hrtime overflow has occurred. Just set the 8698566SMadhavan.Venkataraman@Sun.COM * expiration to infinity. 8708566SMadhavan.Venkataraman@Sun.COM */ 8718566SMadhavan.Venkataraman@Sun.COM expiration = CY_INFINITY; 8728566SMadhavan.Venkataraman@Sun.COM } 8738048SMadhavan.Venkataraman@Sun.COM 8748048SMadhavan.Venkataraman@Sun.COM /* 8758048SMadhavan.Venkataraman@Sun.COM * Assign an ID to this callout 8768048SMadhavan.Venkataraman@Sun.COM */ 8778048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_32BIT) { 8788048SMadhavan.Venkataraman@Sun.COM if (interval > callout_longterm) { 8798048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_long_id - callout_counter_low); 8808048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH; 8818048SMadhavan.Venkataraman@Sun.COM ct->ct_long_id = id; 8828048SMadhavan.Venkataraman@Sun.COM } else { 8838048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_short_id - callout_counter_low); 8848048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH; 8858048SMadhavan.Venkataraman@Sun.COM ct->ct_short_id = id; 8868048SMadhavan.Venkataraman@Sun.COM } 8878048SMadhavan.Venkataraman@Sun.COM } else { 8888048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_gen_id - callout_counter_low); 8898048SMadhavan.Venkataraman@Sun.COM if ((id & CALLOUT_COUNTER_HIGH) == 0) { 8908048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH; 8918048SMadhavan.Venkataraman@Sun.COM id += CALLOUT_GENERATION_LOW; 8928048SMadhavan.Venkataraman@Sun.COM } 8938048SMadhavan.Venkataraman@Sun.COM ct->ct_gen_id = id; 8948048SMadhavan.Venkataraman@Sun.COM } 8958048SMadhavan.Venkataraman@Sun.COM 8968048SMadhavan.Venkataraman@Sun.COM cp->c_xid = id; 8978048SMadhavan.Venkataraman@Sun.COM 898*9334SMadhavan.Venkataraman@Sun.COM clflags = 0; 899*9334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ABSOLUTE) 900*9334SMadhavan.Venkataraman@Sun.COM clflags |= CALLOUT_LIST_FLAG_ABSOLUTE; 901*9334SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_HRESTIME) 902*9334SMadhavan.Venkataraman@Sun.COM clflags |= CALLOUT_LIST_FLAG_HRESTIME; 903*9334SMadhavan.Venkataraman@Sun.COM if (resolution == 1) 904*9334SMadhavan.Venkataraman@Sun.COM clflags |= CALLOUT_LIST_FLAG_NANO; 9058048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(expiration); 9068048SMadhavan.Venkataraman@Sun.COM 9078048SMadhavan.Venkataraman@Sun.COM again: 9088048SMadhavan.Venkataraman@Sun.COM /* 9098048SMadhavan.Venkataraman@Sun.COM * Try to see if a callout list already exists for this expiration. 9108048SMadhavan.Venkataraman@Sun.COM */ 911*9334SMadhavan.Venkataraman@Sun.COM cl = callout_list_get(ct, expiration, clflags, hash); 9128048SMadhavan.Venkataraman@Sun.COM if (cl == NULL) { 9138048SMadhavan.Venkataraman@Sun.COM /* 9148048SMadhavan.Venkataraman@Sun.COM * Check if we have enough space in the heap to insert one 9158048SMadhavan.Venkataraman@Sun.COM * expiration. If not, expand the heap. 9168048SMadhavan.Venkataraman@Sun.COM */ 9178048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == ct->ct_heap_max) { 9188048SMadhavan.Venkataraman@Sun.COM callout_heap_expand(ct); 9198048SMadhavan.Venkataraman@Sun.COM /* 9208048SMadhavan.Venkataraman@Sun.COM * In the above call, we drop the lock, allocate and 9218048SMadhavan.Venkataraman@Sun.COM * reacquire the lock. So, we could have been away 9228048SMadhavan.Venkataraman@Sun.COM * for a while. In the meantime, someone could have 9238048SMadhavan.Venkataraman@Sun.COM * inserted a callout list with the same expiration. 9248048SMadhavan.Venkataraman@Sun.COM * So, the best course is to repeat the steps. This 9258048SMadhavan.Venkataraman@Sun.COM * should be an infrequent event. 9268048SMadhavan.Venkataraman@Sun.COM */ 9278048SMadhavan.Venkataraman@Sun.COM goto again; 9288048SMadhavan.Venkataraman@Sun.COM } 9298048SMadhavan.Venkataraman@Sun.COM 9308048SMadhavan.Venkataraman@Sun.COM /* 9318048SMadhavan.Venkataraman@Sun.COM * Check the free list. If we don't find one, we have to 9328048SMadhavan.Venkataraman@Sun.COM * take the slow path and allocate from kmem. 9338048SMadhavan.Venkataraman@Sun.COM */ 9348048SMadhavan.Venkataraman@Sun.COM if ((cl = ct->ct_lfree) == NULL) { 9358048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(ct); 9368048SMadhavan.Venkataraman@Sun.COM /* 9378048SMadhavan.Venkataraman@Sun.COM * In the above call, we drop the lock, allocate and 9388048SMadhavan.Venkataraman@Sun.COM * reacquire the lock. So, we could have been away 9398048SMadhavan.Venkataraman@Sun.COM * for a while. In the meantime, someone could have 9408048SMadhavan.Venkataraman@Sun.COM * inserted a callout list with the same expiration. 9418048SMadhavan.Venkataraman@Sun.COM * Plus, the heap could have become full. So, the best 9428048SMadhavan.Venkataraman@Sun.COM * course is to repeat the steps. This should be an 9438048SMadhavan.Venkataraman@Sun.COM * infrequent event. 9448048SMadhavan.Venkataraman@Sun.COM */ 9458048SMadhavan.Venkataraman@Sun.COM goto again; 9468048SMadhavan.Venkataraman@Sun.COM } 9478048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl->cl_next; 9488048SMadhavan.Venkataraman@Sun.COM cl->cl_expiration = expiration; 949*9334SMadhavan.Venkataraman@Sun.COM cl->cl_flags = clflags; 9508048SMadhavan.Venkataraman@Sun.COM 951*9334SMadhavan.Venkataraman@Sun.COM if (clflags & CALLOUT_LIST_FLAG_NANO) { 952*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_clhash[hash], cl); 953*9334SMadhavan.Venkataraman@Sun.COM } else { 954*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl); 955*9334SMadhavan.Venkataraman@Sun.COM } 9568048SMadhavan.Venkataraman@Sun.COM 9578048SMadhavan.Venkataraman@Sun.COM /* 9588048SMadhavan.Venkataraman@Sun.COM * This is a new expiration. So, insert it into the heap. 9598048SMadhavan.Venkataraman@Sun.COM * This will also reprogram the cyclic, if the expiration 9608048SMadhavan.Venkataraman@Sun.COM * propagated to the root of the heap. 9618048SMadhavan.Venkataraman@Sun.COM */ 962*9334SMadhavan.Venkataraman@Sun.COM callout_heap_insert(ct, cl); 963*9334SMadhavan.Venkataraman@Sun.COM } else { 964*9334SMadhavan.Venkataraman@Sun.COM /* 965*9334SMadhavan.Venkataraman@Sun.COM * If the callout list was empty, untimeout_generic() would 966*9334SMadhavan.Venkataraman@Sun.COM * have incremented a reap count. Decrement the reap count 967*9334SMadhavan.Venkataraman@Sun.COM * as we are going to insert a callout into this list. 968*9334SMadhavan.Venkataraman@Sun.COM */ 969*9334SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) 970*9334SMadhavan.Venkataraman@Sun.COM ct->ct_nreap--; 9718048SMadhavan.Venkataraman@Sun.COM } 9728048SMadhavan.Venkataraman@Sun.COM cp->c_list = cl; 9738048SMadhavan.Venkataraman@Sun.COM CALLOUT_APPEND(ct, cp); 9748048SMadhavan.Venkataraman@Sun.COM 9758048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts++; 9768048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending++; 9778048SMadhavan.Venkataraman@Sun.COM 9788048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 9798048SMadhavan.Venkataraman@Sun.COM 9808048SMadhavan.Venkataraman@Sun.COM kpreempt_enable(); 9818048SMadhavan.Venkataraman@Sun.COM 9828048SMadhavan.Venkataraman@Sun.COM TRACE_4(TR_FAC_CALLOUT, TR_TIMEOUT, 9838048SMadhavan.Venkataraman@Sun.COM "timeout:%K(%p) in %llx expiration, cp %p", func, arg, expiration, 9848048SMadhavan.Venkataraman@Sun.COM cp); 9858048SMadhavan.Venkataraman@Sun.COM 9868048SMadhavan.Venkataraman@Sun.COM return (id); 9878048SMadhavan.Venkataraman@Sun.COM } 9888048SMadhavan.Venkataraman@Sun.COM 9898048SMadhavan.Venkataraman@Sun.COM timeout_id_t 9908048SMadhavan.Venkataraman@Sun.COM timeout(void (*func)(void *), void *arg, clock_t delta) 9918048SMadhavan.Venkataraman@Sun.COM { 9928048SMadhavan.Venkataraman@Sun.COM ulong_t id; 9938048SMadhavan.Venkataraman@Sun.COM 9948048SMadhavan.Venkataraman@Sun.COM /* 9950Sstevel@tonic-gate * Make sure the callout runs at least 1 tick in the future. 9960Sstevel@tonic-gate */ 9970Sstevel@tonic-gate if (delta <= 0) 9980Sstevel@tonic-gate delta = 1; 9998566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 10008566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 10010Sstevel@tonic-gate 10028048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)timeout_generic(CALLOUT_NORMAL, func, arg, 10038048SMadhavan.Venkataraman@Sun.COM TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY); 10040Sstevel@tonic-gate 10050Sstevel@tonic-gate return ((timeout_id_t)id); 10060Sstevel@tonic-gate } 10070Sstevel@tonic-gate 10088048SMadhavan.Venkataraman@Sun.COM /* 10098048SMadhavan.Venkataraman@Sun.COM * Convenience function that creates a normal callout with default parameters 10108048SMadhavan.Venkataraman@Sun.COM * and returns a full ID. 10118048SMadhavan.Venkataraman@Sun.COM */ 10128048SMadhavan.Venkataraman@Sun.COM callout_id_t 10138048SMadhavan.Venkataraman@Sun.COM timeout_default(void (*func)(void *), void *arg, clock_t delta) 10140Sstevel@tonic-gate { 10158048SMadhavan.Venkataraman@Sun.COM callout_id_t id; 10160Sstevel@tonic-gate 10178048SMadhavan.Venkataraman@Sun.COM /* 10188048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future. 10198048SMadhavan.Venkataraman@Sun.COM */ 10208048SMadhavan.Venkataraman@Sun.COM if (delta <= 0) 10218048SMadhavan.Venkataraman@Sun.COM delta = 1; 10228566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 10238566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 10248048SMadhavan.Venkataraman@Sun.COM 10258048SMadhavan.Venkataraman@Sun.COM id = timeout_generic(CALLOUT_NORMAL, func, arg, TICK_TO_NSEC(delta), 10268048SMadhavan.Venkataraman@Sun.COM nsec_per_tick, 0); 10278048SMadhavan.Venkataraman@Sun.COM 10288048SMadhavan.Venkataraman@Sun.COM return (id); 10290Sstevel@tonic-gate } 10300Sstevel@tonic-gate 10310Sstevel@tonic-gate timeout_id_t 10320Sstevel@tonic-gate realtime_timeout(void (*func)(void *), void *arg, clock_t delta) 10330Sstevel@tonic-gate { 10348048SMadhavan.Venkataraman@Sun.COM ulong_t id; 10358048SMadhavan.Venkataraman@Sun.COM 10368048SMadhavan.Venkataraman@Sun.COM /* 10378048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future. 10388048SMadhavan.Venkataraman@Sun.COM */ 10398048SMadhavan.Venkataraman@Sun.COM if (delta <= 0) 10408048SMadhavan.Venkataraman@Sun.COM delta = 1; 10418566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 10428566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 10438048SMadhavan.Venkataraman@Sun.COM 10448048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)timeout_generic(CALLOUT_REALTIME, func, arg, 10458048SMadhavan.Venkataraman@Sun.COM TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY); 10468048SMadhavan.Venkataraman@Sun.COM 10478048SMadhavan.Venkataraman@Sun.COM return ((timeout_id_t)id); 10480Sstevel@tonic-gate } 10490Sstevel@tonic-gate 10508048SMadhavan.Venkataraman@Sun.COM /* 10518048SMadhavan.Venkataraman@Sun.COM * Convenience function that creates a realtime callout with default parameters 10528048SMadhavan.Venkataraman@Sun.COM * and returns a full ID. 10538048SMadhavan.Venkataraman@Sun.COM */ 10548048SMadhavan.Venkataraman@Sun.COM callout_id_t 10558048SMadhavan.Venkataraman@Sun.COM realtime_timeout_default(void (*func)(void *), void *arg, clock_t delta) 10560Sstevel@tonic-gate { 10578048SMadhavan.Venkataraman@Sun.COM callout_id_t id; 10588048SMadhavan.Venkataraman@Sun.COM 10598048SMadhavan.Venkataraman@Sun.COM /* 10608048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future. 10618048SMadhavan.Venkataraman@Sun.COM */ 10628048SMadhavan.Venkataraman@Sun.COM if (delta <= 0) 10638048SMadhavan.Venkataraman@Sun.COM delta = 1; 10648566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 10658566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 10668048SMadhavan.Venkataraman@Sun.COM 10678048SMadhavan.Venkataraman@Sun.COM id = timeout_generic(CALLOUT_REALTIME, func, arg, TICK_TO_NSEC(delta), 10688048SMadhavan.Venkataraman@Sun.COM nsec_per_tick, 0); 10698048SMadhavan.Venkataraman@Sun.COM 10708048SMadhavan.Venkataraman@Sun.COM return (id); 10718048SMadhavan.Venkataraman@Sun.COM } 10728048SMadhavan.Venkataraman@Sun.COM 10738048SMadhavan.Venkataraman@Sun.COM hrtime_t 10748048SMadhavan.Venkataraman@Sun.COM untimeout_generic(callout_id_t id, int nowait) 10758048SMadhavan.Venkataraman@Sun.COM { 10760Sstevel@tonic-gate callout_table_t *ct; 10770Sstevel@tonic-gate callout_t *cp; 10780Sstevel@tonic-gate callout_id_t xid; 1079*9334SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 10808048SMadhavan.Venkataraman@Sun.COM int hash; 10818048SMadhavan.Venkataraman@Sun.COM callout_id_t bogus; 10820Sstevel@tonic-gate 10838048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_ID_TO_TABLE(id)]; 10848048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_IDHASH(id); 10858048SMadhavan.Venkataraman@Sun.COM 10868048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 10870Sstevel@tonic-gate 10888048SMadhavan.Venkataraman@Sun.COM /* 10898048SMadhavan.Venkataraman@Sun.COM * Search the ID hash table for the callout. 10908048SMadhavan.Venkataraman@Sun.COM */ 10918048SMadhavan.Venkataraman@Sun.COM for (cp = ct->ct_idhash[hash].ch_head; cp; cp = cp->c_idnext) { 10920Sstevel@tonic-gate 10938048SMadhavan.Venkataraman@Sun.COM xid = cp->c_xid; 10948048SMadhavan.Venkataraman@Sun.COM 10958048SMadhavan.Venkataraman@Sun.COM /* 10968048SMadhavan.Venkataraman@Sun.COM * Match the ID and generation number. 10978048SMadhavan.Venkataraman@Sun.COM */ 10988048SMadhavan.Venkataraman@Sun.COM if ((xid & CALLOUT_ID_MASK) != id) 10998048SMadhavan.Venkataraman@Sun.COM continue; 11000Sstevel@tonic-gate 11018048SMadhavan.Venkataraman@Sun.COM if ((xid & CALLOUT_EXECUTING) == 0) { 11028048SMadhavan.Venkataraman@Sun.COM hrtime_t expiration; 11030Sstevel@tonic-gate 11048048SMadhavan.Venkataraman@Sun.COM /* 11058048SMadhavan.Venkataraman@Sun.COM * Delete the callout. If the callout list becomes 11068048SMadhavan.Venkataraman@Sun.COM * NULL, we don't remove it from the table. This is 11078048SMadhavan.Venkataraman@Sun.COM * so it can be reused. If the empty callout list 11088048SMadhavan.Venkataraman@Sun.COM * corresponds to the top of the the callout heap, we 11098048SMadhavan.Venkataraman@Sun.COM * don't reprogram the table cyclic here. This is in 11108048SMadhavan.Venkataraman@Sun.COM * order to avoid lots of X-calls to the CPU associated 11118048SMadhavan.Venkataraman@Sun.COM * with the callout table. 11128048SMadhavan.Venkataraman@Sun.COM */ 1113*9334SMadhavan.Venkataraman@Sun.COM cl = cp->c_list; 1114*9334SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration; 11158048SMadhavan.Venkataraman@Sun.COM CALLOUT_DELETE(ct, cp); 11168048SMadhavan.Venkataraman@Sun.COM cp->c_idnext = ct->ct_free; 11178048SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp; 1118*9334SMadhavan.Venkataraman@Sun.COM cp->c_xid |= CALLOUT_FREE; 11198048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_unexpired++; 11208048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending--; 1121*9334SMadhavan.Venkataraman@Sun.COM 1122*9334SMadhavan.Venkataraman@Sun.COM /* 1123*9334SMadhavan.Venkataraman@Sun.COM * If the callout list has become empty, it needs 1124*9334SMadhavan.Venkataraman@Sun.COM * to be cleaned along with its heap entry. Increment 1125*9334SMadhavan.Venkataraman@Sun.COM * a reap count. 1126*9334SMadhavan.Venkataraman@Sun.COM */ 1127*9334SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) 1128*9334SMadhavan.Venkataraman@Sun.COM ct->ct_nreap++; 11298048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 11306422Sqiao 11318048SMadhavan.Venkataraman@Sun.COM expiration -= gethrtime(); 11320Sstevel@tonic-gate TRACE_2(TR_FAC_CALLOUT, TR_UNTIMEOUT, 11338048SMadhavan.Venkataraman@Sun.COM "untimeout:ID %lx hrtime left %llx", id, 11348048SMadhavan.Venkataraman@Sun.COM expiration); 11358048SMadhavan.Venkataraman@Sun.COM return (expiration < 0 ? 0 : expiration); 11360Sstevel@tonic-gate } 11370Sstevel@tonic-gate 11388048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_executing++; 11390Sstevel@tonic-gate /* 11400Sstevel@tonic-gate * The callout we want to delete is currently executing. 11410Sstevel@tonic-gate * The DDI states that we must wait until the callout 11429039SMadhavan.Venkataraman@Sun.COM * completes before returning, so we block on c_done until the 11438048SMadhavan.Venkataraman@Sun.COM * callout ID changes (to the old ID if it's on the freelist, 11440Sstevel@tonic-gate * or to a new callout ID if it's in use). This implicitly 11450Sstevel@tonic-gate * assumes that callout structures are persistent (they are). 11460Sstevel@tonic-gate */ 11479039SMadhavan.Venkataraman@Sun.COM if (cp->c_executor == curthread) { 11480Sstevel@tonic-gate /* 11490Sstevel@tonic-gate * The timeout handler called untimeout() on itself. 11500Sstevel@tonic-gate * Stupid, but legal. We can't wait for the timeout 11510Sstevel@tonic-gate * to complete without deadlocking, so we just return. 11520Sstevel@tonic-gate */ 11538048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 11540Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_SELF, 11550Sstevel@tonic-gate "untimeout_self:ID %x", id); 11560Sstevel@tonic-gate return (-1); 11570Sstevel@tonic-gate } 11588048SMadhavan.Venkataraman@Sun.COM if (nowait == 0) { 11598048SMadhavan.Venkataraman@Sun.COM /* 11608048SMadhavan.Venkataraman@Sun.COM * We need to wait. Indicate that we are waiting by 11619039SMadhavan.Venkataraman@Sun.COM * incrementing c_waiting. This prevents the executor 11629039SMadhavan.Venkataraman@Sun.COM * from doing a wakeup on c_done if there are no 11638048SMadhavan.Venkataraman@Sun.COM * waiters. 11648048SMadhavan.Venkataraman@Sun.COM */ 11658048SMadhavan.Venkataraman@Sun.COM while (cp->c_xid == xid) { 11669039SMadhavan.Venkataraman@Sun.COM cp->c_waiting = 1; 11679039SMadhavan.Venkataraman@Sun.COM cv_wait(&cp->c_done, &ct->ct_mutex); 11688048SMadhavan.Venkataraman@Sun.COM } 11698048SMadhavan.Venkataraman@Sun.COM } 11708048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 11710Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_EXECUTING, 11720Sstevel@tonic-gate "untimeout_executing:ID %lx", id); 11730Sstevel@tonic-gate return (-1); 11740Sstevel@tonic-gate } 11758048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_expired++; 11760Sstevel@tonic-gate 11778048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 11780Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_BOGUS_ID, 11790Sstevel@tonic-gate "untimeout_bogus_id:ID %lx", id); 11800Sstevel@tonic-gate 11810Sstevel@tonic-gate /* 11820Sstevel@tonic-gate * We didn't find the specified callout ID. This means either 11830Sstevel@tonic-gate * (1) the callout already fired, or (2) the caller passed us 11840Sstevel@tonic-gate * a bogus value. Perform a sanity check to detect case (2). 11850Sstevel@tonic-gate */ 1186*9334SMadhavan.Venkataraman@Sun.COM bogus = (CALLOUT_ID_FLAGS | CALLOUT_COUNTER_HIGH); 11878048SMadhavan.Venkataraman@Sun.COM if (((id & bogus) != CALLOUT_COUNTER_HIGH) && (id != 0)) 11888048SMadhavan.Venkataraman@Sun.COM panic("untimeout: impossible timeout id %llx", 11898048SMadhavan.Venkataraman@Sun.COM (unsigned long long)id); 11900Sstevel@tonic-gate 11910Sstevel@tonic-gate return (-1); 11920Sstevel@tonic-gate } 11930Sstevel@tonic-gate 11948048SMadhavan.Venkataraman@Sun.COM clock_t 11958048SMadhavan.Venkataraman@Sun.COM untimeout(timeout_id_t id_arg) 11968048SMadhavan.Venkataraman@Sun.COM { 11978048SMadhavan.Venkataraman@Sun.COM hrtime_t hleft; 11988048SMadhavan.Venkataraman@Sun.COM clock_t tleft; 11998048SMadhavan.Venkataraman@Sun.COM callout_id_t id; 12008048SMadhavan.Venkataraman@Sun.COM 12018048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)id_arg; 12028048SMadhavan.Venkataraman@Sun.COM hleft = untimeout_generic(id, 0); 12038048SMadhavan.Venkataraman@Sun.COM if (hleft < 0) 12048048SMadhavan.Venkataraman@Sun.COM tleft = -1; 12058048SMadhavan.Venkataraman@Sun.COM else if (hleft == 0) 12068048SMadhavan.Venkataraman@Sun.COM tleft = 0; 12078048SMadhavan.Venkataraman@Sun.COM else 12088048SMadhavan.Venkataraman@Sun.COM tleft = NSEC_TO_TICK(hleft); 12098048SMadhavan.Venkataraman@Sun.COM 12108048SMadhavan.Venkataraman@Sun.COM return (tleft); 12118048SMadhavan.Venkataraman@Sun.COM } 12128048SMadhavan.Venkataraman@Sun.COM 12130Sstevel@tonic-gate /* 12148048SMadhavan.Venkataraman@Sun.COM * Convenience function to untimeout a timeout with a full ID with default 12158048SMadhavan.Venkataraman@Sun.COM * parameters. 12168048SMadhavan.Venkataraman@Sun.COM */ 12178048SMadhavan.Venkataraman@Sun.COM clock_t 12188048SMadhavan.Venkataraman@Sun.COM untimeout_default(callout_id_t id, int nowait) 12198048SMadhavan.Venkataraman@Sun.COM { 12208048SMadhavan.Venkataraman@Sun.COM hrtime_t hleft; 12218048SMadhavan.Venkataraman@Sun.COM clock_t tleft; 12228048SMadhavan.Venkataraman@Sun.COM 12238048SMadhavan.Venkataraman@Sun.COM hleft = untimeout_generic(id, nowait); 12248048SMadhavan.Venkataraman@Sun.COM if (hleft < 0) 12258048SMadhavan.Venkataraman@Sun.COM tleft = -1; 12268048SMadhavan.Venkataraman@Sun.COM else if (hleft == 0) 12278048SMadhavan.Venkataraman@Sun.COM tleft = 0; 12288048SMadhavan.Venkataraman@Sun.COM else 12298048SMadhavan.Venkataraman@Sun.COM tleft = NSEC_TO_TICK(hleft); 12308048SMadhavan.Venkataraman@Sun.COM 12318048SMadhavan.Venkataraman@Sun.COM return (tleft); 12328048SMadhavan.Venkataraman@Sun.COM } 12338048SMadhavan.Venkataraman@Sun.COM 12348048SMadhavan.Venkataraman@Sun.COM /* 12358048SMadhavan.Venkataraman@Sun.COM * Expire all the callouts queued in the specified callout list. 12360Sstevel@tonic-gate */ 12370Sstevel@tonic-gate static void 12388048SMadhavan.Venkataraman@Sun.COM callout_list_expire(callout_table_t *ct, callout_list_t *cl) 12390Sstevel@tonic-gate { 12409039SMadhavan.Venkataraman@Sun.COM callout_t *cp, *cnext; 12418048SMadhavan.Venkataraman@Sun.COM 12428048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 12438048SMadhavan.Venkataraman@Sun.COM ASSERT(cl != NULL); 12448048SMadhavan.Venkataraman@Sun.COM 12459039SMadhavan.Venkataraman@Sun.COM for (cp = cl->cl_callouts.ch_head; cp != NULL; cp = cnext) { 12469039SMadhavan.Venkataraman@Sun.COM /* 12479039SMadhavan.Venkataraman@Sun.COM * Multiple executor threads could be running at the same 12489039SMadhavan.Venkataraman@Sun.COM * time. If this callout is already being executed, 12499039SMadhavan.Venkataraman@Sun.COM * go on to the next one. 12509039SMadhavan.Venkataraman@Sun.COM */ 12519039SMadhavan.Venkataraman@Sun.COM if (cp->c_xid & CALLOUT_EXECUTING) { 12529039SMadhavan.Venkataraman@Sun.COM cnext = cp->c_clnext; 12539039SMadhavan.Venkataraman@Sun.COM continue; 12549039SMadhavan.Venkataraman@Sun.COM } 12558048SMadhavan.Venkataraman@Sun.COM 12568048SMadhavan.Venkataraman@Sun.COM /* 12578048SMadhavan.Venkataraman@Sun.COM * Indicate to untimeout() that a callout is 12588048SMadhavan.Venkataraman@Sun.COM * being expired by the executor. 12598048SMadhavan.Venkataraman@Sun.COM */ 12608048SMadhavan.Venkataraman@Sun.COM cp->c_xid |= CALLOUT_EXECUTING; 12619039SMadhavan.Venkataraman@Sun.COM cp->c_executor = curthread; 12628048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 12638048SMadhavan.Venkataraman@Sun.COM 12648048SMadhavan.Venkataraman@Sun.COM DTRACE_PROBE1(callout__start, callout_t *, cp); 12658048SMadhavan.Venkataraman@Sun.COM (*cp->c_func)(cp->c_arg); 12668048SMadhavan.Venkataraman@Sun.COM DTRACE_PROBE1(callout__end, callout_t *, cp); 12670Sstevel@tonic-gate 12688048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 12698048SMadhavan.Venkataraman@Sun.COM 12708048SMadhavan.Venkataraman@Sun.COM ct->ct_expirations++; 12718048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending--; 12728048SMadhavan.Venkataraman@Sun.COM /* 12739039SMadhavan.Venkataraman@Sun.COM * Indicate completion for c_done. 12748048SMadhavan.Venkataraman@Sun.COM */ 12758048SMadhavan.Venkataraman@Sun.COM cp->c_xid &= ~CALLOUT_EXECUTING; 12769039SMadhavan.Venkataraman@Sun.COM cp->c_executor = NULL; 12779039SMadhavan.Venkataraman@Sun.COM cnext = cp->c_clnext; 12788048SMadhavan.Venkataraman@Sun.COM 12798048SMadhavan.Venkataraman@Sun.COM /* 12808048SMadhavan.Venkataraman@Sun.COM * Delete callout from ID hash table and the callout 12818048SMadhavan.Venkataraman@Sun.COM * list, return to freelist, and tell any untimeout() that 12828048SMadhavan.Venkataraman@Sun.COM * cares that we're done. 12838048SMadhavan.Venkataraman@Sun.COM */ 12848048SMadhavan.Venkataraman@Sun.COM CALLOUT_DELETE(ct, cp); 12858048SMadhavan.Venkataraman@Sun.COM cp->c_idnext = ct->ct_free; 12868048SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp; 1287*9334SMadhavan.Venkataraman@Sun.COM cp->c_xid |= CALLOUT_FREE; 12880Sstevel@tonic-gate 12899039SMadhavan.Venkataraman@Sun.COM if (cp->c_waiting) { 12909039SMadhavan.Venkataraman@Sun.COM cp->c_waiting = 0; 12919039SMadhavan.Venkataraman@Sun.COM cv_broadcast(&cp->c_done); 12928048SMadhavan.Venkataraman@Sun.COM } 12938048SMadhavan.Venkataraman@Sun.COM } 12948048SMadhavan.Venkataraman@Sun.COM } 12958048SMadhavan.Venkataraman@Sun.COM 12968048SMadhavan.Venkataraman@Sun.COM /* 12978048SMadhavan.Venkataraman@Sun.COM * Execute all expired callout lists for a callout table. 12988048SMadhavan.Venkataraman@Sun.COM */ 12998048SMadhavan.Venkataraman@Sun.COM static void 13008048SMadhavan.Venkataraman@Sun.COM callout_expire(callout_table_t *ct) 13018048SMadhavan.Venkataraman@Sun.COM { 13028048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl, *clnext; 13038048SMadhavan.Venkataraman@Sun.COM 13048048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 13050Sstevel@tonic-gate 13068048SMadhavan.Venkataraman@Sun.COM for (cl = ct->ct_expired.ch_head; (cl != NULL); cl = clnext) { 13078048SMadhavan.Venkataraman@Sun.COM /* 13088048SMadhavan.Venkataraman@Sun.COM * Expire all the callouts in this callout list. 13098048SMadhavan.Venkataraman@Sun.COM */ 13108048SMadhavan.Venkataraman@Sun.COM callout_list_expire(ct, cl); 13118048SMadhavan.Venkataraman@Sun.COM 13128048SMadhavan.Venkataraman@Sun.COM clnext = cl->cl_next; 13139039SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head == NULL) { 13149039SMadhavan.Venkataraman@Sun.COM /* 13159039SMadhavan.Venkataraman@Sun.COM * Free the callout list. 13169039SMadhavan.Venkataraman@Sun.COM */ 13179039SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_expired, cl); 1318*9334SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_FREE(ct, cl); 13199039SMadhavan.Venkataraman@Sun.COM } 13200Sstevel@tonic-gate } 13210Sstevel@tonic-gate } 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate /* 13248048SMadhavan.Venkataraman@Sun.COM * The cyclic handlers below process callouts in two steps: 13258048SMadhavan.Venkataraman@Sun.COM * 13268048SMadhavan.Venkataraman@Sun.COM * 1. Find all expired callout lists and queue them in a separate 13278048SMadhavan.Venkataraman@Sun.COM * list of expired callouts. 13288048SMadhavan.Venkataraman@Sun.COM * 2. Execute the expired callout lists. 13298048SMadhavan.Venkataraman@Sun.COM * 13308048SMadhavan.Venkataraman@Sun.COM * This is done for two reasons: 13318048SMadhavan.Venkataraman@Sun.COM * 13328048SMadhavan.Venkataraman@Sun.COM * 1. We want to quickly find the next earliest expiration to program 13338048SMadhavan.Venkataraman@Sun.COM * the cyclic to and reprogram it. We can do this right at the end 13348048SMadhavan.Venkataraman@Sun.COM * of step 1. 13358048SMadhavan.Venkataraman@Sun.COM * 2. The realtime cyclic handler expires callouts in place. However, 13368048SMadhavan.Venkataraman@Sun.COM * for normal callouts, callouts are expired by a taskq thread. 13378048SMadhavan.Venkataraman@Sun.COM * So, it is simpler and more robust to have the taskq thread just 13388048SMadhavan.Venkataraman@Sun.COM * do step 2. 13390Sstevel@tonic-gate */ 13406422Sqiao 13418048SMadhavan.Venkataraman@Sun.COM /* 13428048SMadhavan.Venkataraman@Sun.COM * Realtime callout cyclic handler. 13438048SMadhavan.Venkataraman@Sun.COM */ 13448048SMadhavan.Venkataraman@Sun.COM void 13458048SMadhavan.Venkataraman@Sun.COM callout_realtime(callout_table_t *ct) 13468048SMadhavan.Venkataraman@Sun.COM { 13478048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 13488048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(ct); 13498048SMadhavan.Venkataraman@Sun.COM callout_expire(ct); 13508048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 13518048SMadhavan.Venkataraman@Sun.COM } 13528048SMadhavan.Venkataraman@Sun.COM 13538048SMadhavan.Venkataraman@Sun.COM void 13548048SMadhavan.Venkataraman@Sun.COM callout_execute(callout_table_t *ct) 13558048SMadhavan.Venkataraman@Sun.COM { 13568048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 13578048SMadhavan.Venkataraman@Sun.COM callout_expire(ct); 13588048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 13598048SMadhavan.Venkataraman@Sun.COM } 13608048SMadhavan.Venkataraman@Sun.COM 13618048SMadhavan.Venkataraman@Sun.COM /* 13628048SMadhavan.Venkataraman@Sun.COM * Normal callout cyclic handler. 13638048SMadhavan.Venkataraman@Sun.COM */ 13648048SMadhavan.Venkataraman@Sun.COM void 13658048SMadhavan.Venkataraman@Sun.COM callout_normal(callout_table_t *ct) 13668048SMadhavan.Venkataraman@Sun.COM { 13679039SMadhavan.Venkataraman@Sun.COM int i, exec; 13688048SMadhavan.Venkataraman@Sun.COM 13698048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 13708048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(ct); 13719039SMadhavan.Venkataraman@Sun.COM CALLOUT_EXEC_COMPUTE(ct, exec); 13728048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 13738048SMadhavan.Venkataraman@Sun.COM 13749039SMadhavan.Venkataraman@Sun.COM for (i = 0; i < exec; i++) { 13758048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_taskq != NULL); 13768048SMadhavan.Venkataraman@Sun.COM (void) taskq_dispatch(ct->ct_taskq, 13778048SMadhavan.Venkataraman@Sun.COM (task_func_t *)callout_execute, ct, TQ_NOSLEEP); 13780Sstevel@tonic-gate } 13790Sstevel@tonic-gate } 13800Sstevel@tonic-gate 13810Sstevel@tonic-gate /* 13828048SMadhavan.Venkataraman@Sun.COM * Suspend callout processing. 13830Sstevel@tonic-gate */ 13848048SMadhavan.Venkataraman@Sun.COM static void 13858048SMadhavan.Venkataraman@Sun.COM callout_suspend(void) 13860Sstevel@tonic-gate { 13878048SMadhavan.Venkataraman@Sun.COM int t, f; 13888048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 13898048SMadhavan.Venkataraman@Sun.COM 13908048SMadhavan.Venkataraman@Sun.COM /* 13918048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and suspend callout 13928048SMadhavan.Venkataraman@Sun.COM * processing. 13938048SMadhavan.Venkataraman@Sun.COM * 13948048SMadhavan.Venkataraman@Sun.COM * We need to suspend all the tables (including the inactive ones) 13958048SMadhavan.Venkataraman@Sun.COM * so that if a table is made active while the suspend is still on, 13968048SMadhavan.Venkataraman@Sun.COM * the table remains suspended. 13978048SMadhavan.Venkataraman@Sun.COM */ 13988048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 13998048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 14008048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)]; 14018048SMadhavan.Venkataraman@Sun.COM 14028048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 14038566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend++; 14048048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) { 14058048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 14068048SMadhavan.Venkataraman@Sun.COM continue; 14078048SMadhavan.Venkataraman@Sun.COM } 14088566SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 1) 14098566SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, 14108566SMadhavan.Venkataraman@Sun.COM CY_INFINITY); 14118048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 14128048SMadhavan.Venkataraman@Sun.COM } 14138048SMadhavan.Venkataraman@Sun.COM } 14148048SMadhavan.Venkataraman@Sun.COM } 14158048SMadhavan.Venkataraman@Sun.COM 14168048SMadhavan.Venkataraman@Sun.COM /* 14178048SMadhavan.Venkataraman@Sun.COM * Resume callout processing. 14188048SMadhavan.Venkataraman@Sun.COM */ 14198048SMadhavan.Venkataraman@Sun.COM static void 1420*9334SMadhavan.Venkataraman@Sun.COM callout_resume(hrtime_t delta, int timechange) 14218048SMadhavan.Venkataraman@Sun.COM { 14228048SMadhavan.Venkataraman@Sun.COM hrtime_t exp; 14238048SMadhavan.Venkataraman@Sun.COM int t, f; 14248048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 14250Sstevel@tonic-gate 14268048SMadhavan.Venkataraman@Sun.COM /* 14278048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and resume callout 14288048SMadhavan.Venkataraman@Sun.COM * processing. For active tables, perform any hrtime adjustments 14298048SMadhavan.Venkataraman@Sun.COM * necessary. 14308048SMadhavan.Venkataraman@Sun.COM */ 14318048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 14328048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 14338048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)]; 14348048SMadhavan.Venkataraman@Sun.COM 14358048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 14368048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) { 14378566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend--; 14388048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 14398048SMadhavan.Venkataraman@Sun.COM continue; 14408048SMadhavan.Venkataraman@Sun.COM } 14418048SMadhavan.Venkataraman@Sun.COM 1442*9334SMadhavan.Venkataraman@Sun.COM /* 1443*9334SMadhavan.Venkataraman@Sun.COM * If a delta is specified, adjust the expirations in 1444*9334SMadhavan.Venkataraman@Sun.COM * the heap by delta. Also, if the caller indicates 1445*9334SMadhavan.Venkataraman@Sun.COM * a timechange, process that. This step also cleans 1446*9334SMadhavan.Venkataraman@Sun.COM * out any empty callout lists that might happen to 1447*9334SMadhavan.Venkataraman@Sun.COM * be there. 1448*9334SMadhavan.Venkataraman@Sun.COM */ 1449*9334SMadhavan.Venkataraman@Sun.COM (void) callout_heap_process(ct, delta, timechange); 14508048SMadhavan.Venkataraman@Sun.COM 14518566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend--; 14528566SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 0) { 14538566SMadhavan.Venkataraman@Sun.COM /* 14548566SMadhavan.Venkataraman@Sun.COM * If the expired list is non-empty, then have 14558566SMadhavan.Venkataraman@Sun.COM * the cyclic expire immediately. Else, program 14568566SMadhavan.Venkataraman@Sun.COM * the cyclic based on the heap. 14578566SMadhavan.Venkataraman@Sun.COM */ 14588566SMadhavan.Venkataraman@Sun.COM if (ct->ct_expired.ch_head != NULL) 14598566SMadhavan.Venkataraman@Sun.COM exp = gethrtime(); 14608566SMadhavan.Venkataraman@Sun.COM else if (ct->ct_heap_num > 0) 1461*9334SMadhavan.Venkataraman@Sun.COM exp = ct->ct_heap[0].ch_expiration; 14628566SMadhavan.Venkataraman@Sun.COM else 14638566SMadhavan.Venkataraman@Sun.COM exp = 0; 14648566SMadhavan.Venkataraman@Sun.COM if (exp != 0) 14658566SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, 14668566SMadhavan.Venkataraman@Sun.COM exp); 14678566SMadhavan.Venkataraman@Sun.COM } 1468*9334SMadhavan.Venkataraman@Sun.COM 14698048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 14708048SMadhavan.Venkataraman@Sun.COM } 14718048SMadhavan.Venkataraman@Sun.COM } 14720Sstevel@tonic-gate } 14730Sstevel@tonic-gate 14740Sstevel@tonic-gate /* 14750Sstevel@tonic-gate * Callback handler used by CPR to stop and resume callouts. 1476*9334SMadhavan.Venkataraman@Sun.COM * The cyclic subsystem saves and restores hrtime during CPR. 1477*9334SMadhavan.Venkataraman@Sun.COM * That is why callout_resume() is called with a 0 delta. 1478*9334SMadhavan.Venkataraman@Sun.COM * Although hrtime is the same, hrestime (system time) has 1479*9334SMadhavan.Venkataraman@Sun.COM * progressed during CPR. So, we have to indicate a time change 1480*9334SMadhavan.Venkataraman@Sun.COM * to expire the absolute hrestime timers. 14810Sstevel@tonic-gate */ 14820Sstevel@tonic-gate /*ARGSUSED*/ 14830Sstevel@tonic-gate static boolean_t 14840Sstevel@tonic-gate callout_cpr_callb(void *arg, int code) 14850Sstevel@tonic-gate { 14868048SMadhavan.Venkataraman@Sun.COM if (code == CB_CODE_CPR_CHKPT) 14878048SMadhavan.Venkataraman@Sun.COM callout_suspend(); 14888048SMadhavan.Venkataraman@Sun.COM else 1489*9334SMadhavan.Venkataraman@Sun.COM callout_resume(0, 1); 14908048SMadhavan.Venkataraman@Sun.COM 14918048SMadhavan.Venkataraman@Sun.COM return (B_TRUE); 14928048SMadhavan.Venkataraman@Sun.COM } 14938048SMadhavan.Venkataraman@Sun.COM 14948048SMadhavan.Venkataraman@Sun.COM /* 14958048SMadhavan.Venkataraman@Sun.COM * Callback handler invoked when the debugger is entered or exited. 14968048SMadhavan.Venkataraman@Sun.COM */ 14978048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/ 14988048SMadhavan.Venkataraman@Sun.COM static boolean_t 14998048SMadhavan.Venkataraman@Sun.COM callout_debug_callb(void *arg, int code) 15008048SMadhavan.Venkataraman@Sun.COM { 15018048SMadhavan.Venkataraman@Sun.COM hrtime_t delta; 15028048SMadhavan.Venkataraman@Sun.COM 15038048SMadhavan.Venkataraman@Sun.COM /* 15048048SMadhavan.Venkataraman@Sun.COM * When the system enters the debugger. make a note of the hrtime. 15058048SMadhavan.Venkataraman@Sun.COM * When it is resumed, compute how long the system was in the 15068048SMadhavan.Venkataraman@Sun.COM * debugger. This interval should not be counted for callouts. 15078048SMadhavan.Venkataraman@Sun.COM */ 15088048SMadhavan.Venkataraman@Sun.COM if (code == 0) { 15098048SMadhavan.Venkataraman@Sun.COM callout_suspend(); 15108048SMadhavan.Venkataraman@Sun.COM callout_debug_hrtime = gethrtime(); 15118048SMadhavan.Venkataraman@Sun.COM } else { 15128048SMadhavan.Venkataraman@Sun.COM delta = gethrtime() - callout_debug_hrtime; 1513*9334SMadhavan.Venkataraman@Sun.COM callout_resume(delta, 0); 15148048SMadhavan.Venkataraman@Sun.COM } 15158048SMadhavan.Venkataraman@Sun.COM 15160Sstevel@tonic-gate return (B_TRUE); 15170Sstevel@tonic-gate } 15180Sstevel@tonic-gate 15190Sstevel@tonic-gate /* 15209039SMadhavan.Venkataraman@Sun.COM * Move the absolute hrestime callouts to the expired list. Then program the 15219039SMadhavan.Venkataraman@Sun.COM * table's cyclic to expire immediately so that the callouts can be executed 15228048SMadhavan.Venkataraman@Sun.COM * immediately. 15238048SMadhavan.Venkataraman@Sun.COM */ 15248048SMadhavan.Venkataraman@Sun.COM static void 15258048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(callout_table_t *ct) 15268048SMadhavan.Venkataraman@Sun.COM { 1527*9334SMadhavan.Venkataraman@Sun.COM hrtime_t expiration; 15288048SMadhavan.Venkataraman@Sun.COM 15298048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 15308048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 0) { 15318048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 15328048SMadhavan.Venkataraman@Sun.COM return; 15338048SMadhavan.Venkataraman@Sun.COM } 15348048SMadhavan.Venkataraman@Sun.COM 1535*9334SMadhavan.Venkataraman@Sun.COM /* 1536*9334SMadhavan.Venkataraman@Sun.COM * Walk the heap and process all the absolute hrestime entries. 1537*9334SMadhavan.Venkataraman@Sun.COM */ 1538*9334SMadhavan.Venkataraman@Sun.COM expiration = callout_heap_process(ct, 0, 1); 15398048SMadhavan.Venkataraman@Sun.COM 1540*9334SMadhavan.Venkataraman@Sun.COM if ((expiration != 0) && (ct->ct_suspend == 0)) 1541*9334SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, expiration); 15429039SMadhavan.Venkataraman@Sun.COM 15438048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 15448048SMadhavan.Venkataraman@Sun.COM } 15458048SMadhavan.Venkataraman@Sun.COM 15468048SMadhavan.Venkataraman@Sun.COM /* 15478048SMadhavan.Venkataraman@Sun.COM * This function is called whenever system time (hrestime) is changed 15488048SMadhavan.Venkataraman@Sun.COM * explicitly. All the HRESTIME callouts must be expired at once. 15498048SMadhavan.Venkataraman@Sun.COM */ 15508048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/ 15518048SMadhavan.Venkataraman@Sun.COM void 15528048SMadhavan.Venkataraman@Sun.COM callout_hrestime(void) 15538048SMadhavan.Venkataraman@Sun.COM { 15548048SMadhavan.Venkataraman@Sun.COM int t, f; 15558048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 15568048SMadhavan.Venkataraman@Sun.COM 15578048SMadhavan.Venkataraman@Sun.COM /* 15588048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and process the hrestime 15598048SMadhavan.Venkataraman@Sun.COM * callouts therein. 15608048SMadhavan.Venkataraman@Sun.COM * 15618048SMadhavan.Venkataraman@Sun.COM * We look at all the tables because we don't know which ones were 15628048SMadhavan.Venkataraman@Sun.COM * onlined and offlined in the past. The offlined tables may still 15638048SMadhavan.Venkataraman@Sun.COM * have active cyclics processing timers somewhere. 15648048SMadhavan.Venkataraman@Sun.COM */ 15658048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 15668048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 15678048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)]; 15688048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(ct); 15698048SMadhavan.Venkataraman@Sun.COM } 15708048SMadhavan.Venkataraman@Sun.COM } 15718048SMadhavan.Venkataraman@Sun.COM } 15728048SMadhavan.Venkataraman@Sun.COM 15738048SMadhavan.Venkataraman@Sun.COM /* 15748048SMadhavan.Venkataraman@Sun.COM * Create the hash tables for this callout table. 15758048SMadhavan.Venkataraman@Sun.COM */ 15768048SMadhavan.Venkataraman@Sun.COM static void 15778048SMadhavan.Venkataraman@Sun.COM callout_hash_init(callout_table_t *ct) 15788048SMadhavan.Venkataraman@Sun.COM { 15798048SMadhavan.Venkataraman@Sun.COM size_t size; 15808048SMadhavan.Venkataraman@Sun.COM 15818048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 15828048SMadhavan.Venkataraman@Sun.COM ASSERT((ct->ct_idhash == NULL) && (ct->ct_clhash == NULL)); 15838048SMadhavan.Venkataraman@Sun.COM 15848048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_hash_t) * CALLOUT_BUCKETS; 15858048SMadhavan.Venkataraman@Sun.COM ct->ct_idhash = kmem_zalloc(size, KM_SLEEP); 15868048SMadhavan.Venkataraman@Sun.COM ct->ct_clhash = kmem_zalloc(size, KM_SLEEP); 15878048SMadhavan.Venkataraman@Sun.COM } 15888048SMadhavan.Venkataraman@Sun.COM 15898048SMadhavan.Venkataraman@Sun.COM /* 15908048SMadhavan.Venkataraman@Sun.COM * Create per-callout table kstats. 15918048SMadhavan.Venkataraman@Sun.COM */ 15928048SMadhavan.Venkataraman@Sun.COM static void 15938048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(callout_table_t *ct) 15948048SMadhavan.Venkataraman@Sun.COM { 15958048SMadhavan.Venkataraman@Sun.COM callout_stat_type_t stat; 15968048SMadhavan.Venkataraman@Sun.COM kstat_t *ct_kstats; 15978048SMadhavan.Venkataraman@Sun.COM int ndx; 15988048SMadhavan.Venkataraman@Sun.COM 15998048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 16008048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_kstats == NULL); 16018048SMadhavan.Venkataraman@Sun.COM 16028048SMadhavan.Venkataraman@Sun.COM ndx = ct - callout_table; 16038048SMadhavan.Venkataraman@Sun.COM ct_kstats = kstat_create("unix", ndx, "callout", 16048048SMadhavan.Venkataraman@Sun.COM "misc", KSTAT_TYPE_NAMED, CALLOUT_NUM_STATS, KSTAT_FLAG_VIRTUAL); 16058048SMadhavan.Venkataraman@Sun.COM 16068048SMadhavan.Venkataraman@Sun.COM if (ct_kstats == NULL) { 16078048SMadhavan.Venkataraman@Sun.COM cmn_err(CE_WARN, "kstat_create for callout table %p failed", 16088048SMadhavan.Venkataraman@Sun.COM (void *)ct); 16098048SMadhavan.Venkataraman@Sun.COM } else { 16108048SMadhavan.Venkataraman@Sun.COM ct_kstats->ks_data = ct->ct_kstat_data; 16118048SMadhavan.Venkataraman@Sun.COM for (stat = 0; stat < CALLOUT_NUM_STATS; stat++) 16128048SMadhavan.Venkataraman@Sun.COM kstat_named_init(&ct->ct_kstat_data[stat], 16138048SMadhavan.Venkataraman@Sun.COM callout_kstat_names[stat], KSTAT_DATA_INT64); 16148048SMadhavan.Venkataraman@Sun.COM ct->ct_kstats = ct_kstats; 16158048SMadhavan.Venkataraman@Sun.COM kstat_install(ct_kstats); 16168048SMadhavan.Venkataraman@Sun.COM } 16178048SMadhavan.Venkataraman@Sun.COM } 16188048SMadhavan.Venkataraman@Sun.COM 16198048SMadhavan.Venkataraman@Sun.COM static void 16208048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(callout_table_t *ct) 16218048SMadhavan.Venkataraman@Sun.COM { 16228048SMadhavan.Venkataraman@Sun.COM cyc_handler_t hdlr; 16238048SMadhavan.Venkataraman@Sun.COM cyc_time_t when; 16248048SMadhavan.Venkataraman@Sun.COM processorid_t seqid; 16258048SMadhavan.Venkataraman@Sun.COM int t; 16268048SMadhavan.Venkataraman@Sun.COM 16278048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 16288048SMadhavan.Venkataraman@Sun.COM 16298048SMadhavan.Venkataraman@Sun.COM t = CALLOUT_TABLE_TYPE(ct); 16308048SMadhavan.Venkataraman@Sun.COM seqid = CALLOUT_TABLE_SEQID(ct); 16318048SMadhavan.Venkataraman@Sun.COM 16328048SMadhavan.Venkataraman@Sun.COM /* 16338048SMadhavan.Venkataraman@Sun.COM * Create the taskq thread if the table type is normal. 16348048SMadhavan.Venkataraman@Sun.COM * Realtime tables are handled at PIL1 by a softint 16358048SMadhavan.Venkataraman@Sun.COM * handler. 16368048SMadhavan.Venkataraman@Sun.COM */ 16378048SMadhavan.Venkataraman@Sun.COM if (t == CALLOUT_NORMAL) { 16388048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_taskq == NULL); 16398048SMadhavan.Venkataraman@Sun.COM /* 16408048SMadhavan.Venkataraman@Sun.COM * Each callout thread consumes exactly one 16418048SMadhavan.Venkataraman@Sun.COM * task structure while active. Therefore, 1642*9334SMadhavan.Venkataraman@Sun.COM * prepopulating with 2 * callout_threads tasks 16438048SMadhavan.Venkataraman@Sun.COM * ensures that there's at least one task per 16448048SMadhavan.Venkataraman@Sun.COM * thread that's either scheduled or on the 16458048SMadhavan.Venkataraman@Sun.COM * freelist. In turn, this guarantees that 16468048SMadhavan.Venkataraman@Sun.COM * taskq_dispatch() will always either succeed 16478048SMadhavan.Venkataraman@Sun.COM * (because there's a free task structure) or 16488048SMadhavan.Venkataraman@Sun.COM * be unnecessary (because "callout_excute(ct)" 16498048SMadhavan.Venkataraman@Sun.COM * has already scheduled). 16508048SMadhavan.Venkataraman@Sun.COM */ 16518048SMadhavan.Venkataraman@Sun.COM ct->ct_taskq = 16528048SMadhavan.Venkataraman@Sun.COM taskq_create_instance("callout_taskq", seqid, 1653*9334SMadhavan.Venkataraman@Sun.COM callout_threads, maxclsyspri, 1654*9334SMadhavan.Venkataraman@Sun.COM 2 * callout_threads, 2 * callout_threads, 16558048SMadhavan.Venkataraman@Sun.COM TASKQ_PREPOPULATE | TASKQ_CPR_SAFE); 16568048SMadhavan.Venkataraman@Sun.COM } 16578048SMadhavan.Venkataraman@Sun.COM 16588048SMadhavan.Venkataraman@Sun.COM /* 16598048SMadhavan.Venkataraman@Sun.COM * callouts can only be created in a table whose 16608048SMadhavan.Venkataraman@Sun.COM * cyclic has been initialized. 16618048SMadhavan.Venkataraman@Sun.COM */ 16628048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num == 0); 16638048SMadhavan.Venkataraman@Sun.COM 16648048SMadhavan.Venkataraman@Sun.COM /* 16658048SMadhavan.Venkataraman@Sun.COM * Create the callout table cyclics. 16669039SMadhavan.Venkataraman@Sun.COM * 16679039SMadhavan.Venkataraman@Sun.COM * The realtime cyclic handler executes at low PIL. The normal cyclic 16689039SMadhavan.Venkataraman@Sun.COM * handler executes at lock PIL. This is because there are cases 16699039SMadhavan.Venkataraman@Sun.COM * where code can block at PIL > 1 waiting for a normal callout handler 16709039SMadhavan.Venkataraman@Sun.COM * to unblock it directly or indirectly. If the normal cyclic were to 16719039SMadhavan.Venkataraman@Sun.COM * be executed at low PIL, it could get blocked out by the waiter 16729039SMadhavan.Venkataraman@Sun.COM * and cause a deadlock. 16738048SMadhavan.Venkataraman@Sun.COM */ 16748048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_cyclic == CYCLIC_NONE); 16758048SMadhavan.Venkataraman@Sun.COM 16768048SMadhavan.Venkataraman@Sun.COM hdlr.cyh_func = (cyc_func_t)CALLOUT_CYCLIC_HANDLER(t); 16779039SMadhavan.Venkataraman@Sun.COM if (ct->ct_type == CALLOUT_REALTIME) 16789039SMadhavan.Venkataraman@Sun.COM hdlr.cyh_level = callout_realtime_level; 16799039SMadhavan.Venkataraman@Sun.COM else 16809039SMadhavan.Venkataraman@Sun.COM hdlr.cyh_level = callout_normal_level; 16818048SMadhavan.Venkataraman@Sun.COM hdlr.cyh_arg = ct; 16828048SMadhavan.Venkataraman@Sun.COM when.cyt_when = CY_INFINITY; 16838048SMadhavan.Venkataraman@Sun.COM when.cyt_interval = CY_INFINITY; 16848048SMadhavan.Venkataraman@Sun.COM 16858048SMadhavan.Venkataraman@Sun.COM ct->ct_cyclic = cyclic_add(&hdlr, &when); 16868048SMadhavan.Venkataraman@Sun.COM } 16878048SMadhavan.Venkataraman@Sun.COM 16888048SMadhavan.Venkataraman@Sun.COM void 16898048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cpu_t *cp) 16908048SMadhavan.Venkataraman@Sun.COM { 16918048SMadhavan.Venkataraman@Sun.COM lgrp_handle_t hand; 16928048SMadhavan.Venkataraman@Sun.COM callout_cache_t *cache; 16938048SMadhavan.Venkataraman@Sun.COM char s[KMEM_CACHE_NAMELEN]; 16948048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 16958048SMadhavan.Venkataraman@Sun.COM processorid_t seqid; 16968048SMadhavan.Venkataraman@Sun.COM int t; 16978048SMadhavan.Venkataraman@Sun.COM 16988048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 16998048SMadhavan.Venkataraman@Sun.COM 17008048SMadhavan.Venkataraman@Sun.COM /* 17018048SMadhavan.Venkataraman@Sun.COM * Locate the cache corresponding to the onlined CPU's lgroup. 17028048SMadhavan.Venkataraman@Sun.COM * Note that access to callout_caches is protected by cpu_lock. 17038048SMadhavan.Venkataraman@Sun.COM */ 17048048SMadhavan.Venkataraman@Sun.COM hand = lgrp_plat_cpu_to_hand(cp->cpu_id); 17058048SMadhavan.Venkataraman@Sun.COM for (cache = callout_caches; cache != NULL; cache = cache->cc_next) { 17068048SMadhavan.Venkataraman@Sun.COM if (cache->cc_hand == hand) 17078048SMadhavan.Venkataraman@Sun.COM break; 17088048SMadhavan.Venkataraman@Sun.COM } 17098048SMadhavan.Venkataraman@Sun.COM 17108048SMadhavan.Venkataraman@Sun.COM /* 17118048SMadhavan.Venkataraman@Sun.COM * If not found, create one. The caches are never destroyed. 17128048SMadhavan.Venkataraman@Sun.COM */ 17138048SMadhavan.Venkataraman@Sun.COM if (cache == NULL) { 17148048SMadhavan.Venkataraman@Sun.COM cache = kmem_alloc(sizeof (callout_cache_t), KM_SLEEP); 17158048SMadhavan.Venkataraman@Sun.COM cache->cc_hand = hand; 17168048SMadhavan.Venkataraman@Sun.COM (void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_cache%lx", 17178048SMadhavan.Venkataraman@Sun.COM (long)hand); 17188048SMadhavan.Venkataraman@Sun.COM cache->cc_cache = kmem_cache_create(s, sizeof (callout_t), 17198048SMadhavan.Venkataraman@Sun.COM CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0); 17208048SMadhavan.Venkataraman@Sun.COM (void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_lcache%lx", 17218048SMadhavan.Venkataraman@Sun.COM (long)hand); 17228048SMadhavan.Venkataraman@Sun.COM cache->cc_lcache = kmem_cache_create(s, sizeof (callout_list_t), 17238048SMadhavan.Venkataraman@Sun.COM CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0); 17248048SMadhavan.Venkataraman@Sun.COM cache->cc_next = callout_caches; 17258048SMadhavan.Venkataraman@Sun.COM callout_caches = cache; 17268048SMadhavan.Venkataraman@Sun.COM } 17278048SMadhavan.Venkataraman@Sun.COM 17288048SMadhavan.Venkataraman@Sun.COM seqid = cp->cpu_seqid; 17298048SMadhavan.Venkataraman@Sun.COM 17308048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 17318048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, seqid)]; 17328048SMadhavan.Venkataraman@Sun.COM 17338048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 17348048SMadhavan.Venkataraman@Sun.COM /* 17358048SMadhavan.Venkataraman@Sun.COM * Store convinience pointers to the kmem caches 17368048SMadhavan.Venkataraman@Sun.COM * in the callout table. These assignments should always be 17378048SMadhavan.Venkataraman@Sun.COM * done as callout tables can map to different physical 17388048SMadhavan.Venkataraman@Sun.COM * CPUs each time. 17398048SMadhavan.Venkataraman@Sun.COM */ 17408048SMadhavan.Venkataraman@Sun.COM ct->ct_cache = cache->cc_cache; 17418048SMadhavan.Venkataraman@Sun.COM ct->ct_lcache = cache->cc_lcache; 17428048SMadhavan.Venkataraman@Sun.COM 17438048SMadhavan.Venkataraman@Sun.COM /* 17448048SMadhavan.Venkataraman@Sun.COM * We use the heap pointer to check if stuff has been 17458048SMadhavan.Venkataraman@Sun.COM * initialized for this callout table. 17468048SMadhavan.Venkataraman@Sun.COM */ 17478048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap == NULL) { 17488048SMadhavan.Venkataraman@Sun.COM callout_heap_init(ct); 17498048SMadhavan.Venkataraman@Sun.COM callout_hash_init(ct); 17508048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(ct); 17518048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(ct); 17528048SMadhavan.Venkataraman@Sun.COM } 17538048SMadhavan.Venkataraman@Sun.COM 17548048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 17558048SMadhavan.Venkataraman@Sun.COM 17568048SMadhavan.Venkataraman@Sun.COM /* 17578566SMadhavan.Venkataraman@Sun.COM * Move the cyclic to this CPU by doing a bind. 17588048SMadhavan.Venkataraman@Sun.COM */ 17598048SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_cyclic, cp, NULL); 17608566SMadhavan.Venkataraman@Sun.COM } 17618566SMadhavan.Venkataraman@Sun.COM } 17628566SMadhavan.Venkataraman@Sun.COM 17638566SMadhavan.Venkataraman@Sun.COM void 17648566SMadhavan.Venkataraman@Sun.COM callout_cpu_offline(cpu_t *cp) 17658566SMadhavan.Venkataraman@Sun.COM { 17668566SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 17678566SMadhavan.Venkataraman@Sun.COM processorid_t seqid; 17688566SMadhavan.Venkataraman@Sun.COM int t; 17698566SMadhavan.Venkataraman@Sun.COM 17708566SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 17718566SMadhavan.Venkataraman@Sun.COM 17728566SMadhavan.Venkataraman@Sun.COM seqid = cp->cpu_seqid; 17738566SMadhavan.Venkataraman@Sun.COM 17748566SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 17758566SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, seqid)]; 17768566SMadhavan.Venkataraman@Sun.COM 17778566SMadhavan.Venkataraman@Sun.COM /* 17788566SMadhavan.Venkataraman@Sun.COM * Unbind the cyclic. This will allow the cyclic subsystem 17798566SMadhavan.Venkataraman@Sun.COM * to juggle the cyclic during CPU offline. 17808566SMadhavan.Venkataraman@Sun.COM */ 17818048SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_cyclic, NULL, NULL); 17828048SMadhavan.Venkataraman@Sun.COM } 17838048SMadhavan.Venkataraman@Sun.COM } 17848048SMadhavan.Venkataraman@Sun.COM 17858048SMadhavan.Venkataraman@Sun.COM /* 17868048SMadhavan.Venkataraman@Sun.COM * This is called to perform per-CPU initialization for slave CPUs at 17878048SMadhavan.Venkataraman@Sun.COM * boot time. 17888048SMadhavan.Venkataraman@Sun.COM */ 17898048SMadhavan.Venkataraman@Sun.COM void 17908048SMadhavan.Venkataraman@Sun.COM callout_mp_init(void) 17918048SMadhavan.Venkataraman@Sun.COM { 17928048SMadhavan.Venkataraman@Sun.COM cpu_t *cp; 17938048SMadhavan.Venkataraman@Sun.COM 17948048SMadhavan.Venkataraman@Sun.COM mutex_enter(&cpu_lock); 17958048SMadhavan.Venkataraman@Sun.COM 17968048SMadhavan.Venkataraman@Sun.COM cp = cpu_active; 17978048SMadhavan.Venkataraman@Sun.COM do { 17988048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cp); 17998048SMadhavan.Venkataraman@Sun.COM } while ((cp = cp->cpu_next_onln) != cpu_active); 18008048SMadhavan.Venkataraman@Sun.COM 18018048SMadhavan.Venkataraman@Sun.COM mutex_exit(&cpu_lock); 18028048SMadhavan.Venkataraman@Sun.COM } 18038048SMadhavan.Venkataraman@Sun.COM 18048048SMadhavan.Venkataraman@Sun.COM /* 18050Sstevel@tonic-gate * Initialize all callout tables. Called at boot time just before clkstart(). 18060Sstevel@tonic-gate */ 18070Sstevel@tonic-gate void 18080Sstevel@tonic-gate callout_init(void) 18090Sstevel@tonic-gate { 18100Sstevel@tonic-gate int f, t; 18118048SMadhavan.Venkataraman@Sun.COM size_t size; 18120Sstevel@tonic-gate int table_id; 18130Sstevel@tonic-gate callout_table_t *ct; 18148048SMadhavan.Venkataraman@Sun.COM long bits, fanout; 18158048SMadhavan.Venkataraman@Sun.COM uintptr_t buf; 18160Sstevel@tonic-gate 18178048SMadhavan.Venkataraman@Sun.COM /* 18188048SMadhavan.Venkataraman@Sun.COM * Initialize callout globals. 18198048SMadhavan.Venkataraman@Sun.COM */ 18208048SMadhavan.Venkataraman@Sun.COM bits = 0; 18218048SMadhavan.Venkataraman@Sun.COM for (fanout = 1; (fanout < max_ncpus); fanout <<= 1) 18228048SMadhavan.Venkataraman@Sun.COM bits++; 18238048SMadhavan.Venkataraman@Sun.COM callout_table_bits = CALLOUT_TYPE_BITS + bits; 18248048SMadhavan.Venkataraman@Sun.COM callout_table_mask = (1 << callout_table_bits) - 1; 18258048SMadhavan.Venkataraman@Sun.COM callout_counter_low = 1 << CALLOUT_COUNTER_SHIFT; 18268048SMadhavan.Venkataraman@Sun.COM callout_longterm = TICK_TO_NSEC(CALLOUT_LONGTERM_TICKS); 18278566SMadhavan.Venkataraman@Sun.COM callout_max_ticks = CALLOUT_MAX_TICKS; 1828*9334SMadhavan.Venkataraman@Sun.COM if (callout_min_reap == 0) 1829*9334SMadhavan.Venkataraman@Sun.COM callout_min_reap = CALLOUT_MIN_REAP; 18300Sstevel@tonic-gate 1831*9334SMadhavan.Venkataraman@Sun.COM if (callout_tolerance <= 0) 1832*9334SMadhavan.Venkataraman@Sun.COM callout_tolerance = CALLOUT_TOLERANCE; 1833*9334SMadhavan.Venkataraman@Sun.COM if (callout_threads <= 0) 1834*9334SMadhavan.Venkataraman@Sun.COM callout_threads = CALLOUT_THREADS; 18358048SMadhavan.Venkataraman@Sun.COM 18368048SMadhavan.Venkataraman@Sun.COM /* 18378048SMadhavan.Venkataraman@Sun.COM * Allocate all the callout tables based on max_ncpus. We have chosen 18388048SMadhavan.Venkataraman@Sun.COM * to do boot-time allocation instead of dynamic allocation because: 18398048SMadhavan.Venkataraman@Sun.COM * 18408048SMadhavan.Venkataraman@Sun.COM * - the size of the callout tables is not too large. 18418048SMadhavan.Venkataraman@Sun.COM * - there are race conditions involved in making this dynamic. 18428048SMadhavan.Venkataraman@Sun.COM * - the hash tables that go with the callout tables consume 18438048SMadhavan.Venkataraman@Sun.COM * most of the memory and they are only allocated in 18448048SMadhavan.Venkataraman@Sun.COM * callout_cpu_online(). 18458048SMadhavan.Venkataraman@Sun.COM * 18468048SMadhavan.Venkataraman@Sun.COM * Each CPU has two tables that are consecutive in the array. The first 18478048SMadhavan.Venkataraman@Sun.COM * one is for realtime callouts and the second one is for normal ones. 18488048SMadhavan.Venkataraman@Sun.COM * 18498048SMadhavan.Venkataraman@Sun.COM * We do this alignment dance to make sure that callout table 18508048SMadhavan.Venkataraman@Sun.COM * structures will always be on a cache line boundary. 18518048SMadhavan.Venkataraman@Sun.COM */ 18528048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_table_t) * CALLOUT_NTYPES * max_ncpus; 18538048SMadhavan.Venkataraman@Sun.COM size += CALLOUT_ALIGN; 18548048SMadhavan.Venkataraman@Sun.COM buf = (uintptr_t)kmem_zalloc(size, KM_SLEEP); 18558048SMadhavan.Venkataraman@Sun.COM callout_table = (callout_table_t *)P2ROUNDUP(buf, CALLOUT_ALIGN); 18568048SMadhavan.Venkataraman@Sun.COM 18578048SMadhavan.Venkataraman@Sun.COM size = sizeof (kstat_named_t) * CALLOUT_NUM_STATS; 18588048SMadhavan.Venkataraman@Sun.COM /* 18598048SMadhavan.Venkataraman@Sun.COM * Now, initialize the tables for all the CPUs. 18608048SMadhavan.Venkataraman@Sun.COM */ 18618048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 18628048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 18630Sstevel@tonic-gate table_id = CALLOUT_TABLE(t, f); 18648048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[table_id]; 18658566SMadhavan.Venkataraman@Sun.COM ct->ct_type = t; 18668048SMadhavan.Venkataraman@Sun.COM mutex_init(&ct->ct_mutex, NULL, MUTEX_DEFAULT, NULL); 18676422Sqiao /* 18688048SMadhavan.Venkataraman@Sun.COM * Precompute the base IDs for long and short-term 18698048SMadhavan.Venkataraman@Sun.COM * legacy IDs. This makes ID generation during 18708048SMadhavan.Venkataraman@Sun.COM * timeout() fast. 18716422Sqiao */ 18728048SMadhavan.Venkataraman@Sun.COM ct->ct_short_id = CALLOUT_SHORT_ID(table_id); 18738048SMadhavan.Venkataraman@Sun.COM ct->ct_long_id = CALLOUT_LONG_ID(table_id); 18748048SMadhavan.Venkataraman@Sun.COM /* 18758048SMadhavan.Venkataraman@Sun.COM * Precompute the base ID for generation-based IDs. 18768048SMadhavan.Venkataraman@Sun.COM * Note that when the first ID gets allocated, the 18778048SMadhavan.Venkataraman@Sun.COM * ID will wrap. This will cause the generation 18788048SMadhavan.Venkataraman@Sun.COM * number to be incremented to 1. 18798048SMadhavan.Venkataraman@Sun.COM */ 18808048SMadhavan.Venkataraman@Sun.COM ct->ct_gen_id = CALLOUT_SHORT_ID(table_id); 18818048SMadhavan.Venkataraman@Sun.COM /* 18828048SMadhavan.Venkataraman@Sun.COM * Initialize the cyclic as NONE. This will get set 18838048SMadhavan.Venkataraman@Sun.COM * during CPU online. This is so that partially 18848048SMadhavan.Venkataraman@Sun.COM * populated systems will only have the required 18858048SMadhavan.Venkataraman@Sun.COM * number of cyclics, not more. 18868048SMadhavan.Venkataraman@Sun.COM */ 18878048SMadhavan.Venkataraman@Sun.COM ct->ct_cyclic = CYCLIC_NONE; 18888048SMadhavan.Venkataraman@Sun.COM ct->ct_kstat_data = kmem_zalloc(size, KM_SLEEP); 18890Sstevel@tonic-gate } 18900Sstevel@tonic-gate } 18918048SMadhavan.Venkataraman@Sun.COM 18928048SMadhavan.Venkataraman@Sun.COM /* 18938048SMadhavan.Venkataraman@Sun.COM * Add the callback for CPR. This is called during checkpoint 18948048SMadhavan.Venkataraman@Sun.COM * resume to suspend and resume callouts. 18958048SMadhavan.Venkataraman@Sun.COM */ 18968048SMadhavan.Venkataraman@Sun.COM (void) callb_add(callout_cpr_callb, 0, CB_CL_CPR_CALLOUT, 18978048SMadhavan.Venkataraman@Sun.COM "callout_cpr"); 18988048SMadhavan.Venkataraman@Sun.COM (void) callb_add(callout_debug_callb, 0, CB_CL_ENTER_DEBUGGER, 18998048SMadhavan.Venkataraman@Sun.COM "callout_debug"); 19008048SMadhavan.Venkataraman@Sun.COM 19018048SMadhavan.Venkataraman@Sun.COM /* 19028048SMadhavan.Venkataraman@Sun.COM * Call the per-CPU initialization function for the boot CPU. This 19038048SMadhavan.Venkataraman@Sun.COM * is done here because the function is not called automatically for 19048048SMadhavan.Venkataraman@Sun.COM * the boot CPU from the CPU online/offline hooks. Note that the 19058048SMadhavan.Venkataraman@Sun.COM * CPU lock is taken here because of convention. 19068048SMadhavan.Venkataraman@Sun.COM */ 19078048SMadhavan.Venkataraman@Sun.COM mutex_enter(&cpu_lock); 19088048SMadhavan.Venkataraman@Sun.COM callout_boot_ct = &callout_table[CALLOUT_TABLE(0, CPU->cpu_seqid)]; 19098048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(CPU); 19108048SMadhavan.Venkataraman@Sun.COM mutex_exit(&cpu_lock); 19110Sstevel@tonic-gate } 1912