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