10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 56422Sqiao * Common Development and Distribution License (the "License"). 66422Sqiao * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*8566SMadhavan.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 */ 46*8566SMadhavan.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 558048SMadhavan.Venkataraman@Sun.COM static char *callout_kstat_names[] = { 568048SMadhavan.Venkataraman@Sun.COM "callout_timeouts", 578048SMadhavan.Venkataraman@Sun.COM "callout_timeouts_pending", 588048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_unexpired", 598048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_executing", 608048SMadhavan.Venkataraman@Sun.COM "callout_untimeouts_expired", 618048SMadhavan.Venkataraman@Sun.COM "callout_expirations", 628048SMadhavan.Venkataraman@Sun.COM "callout_allocations", 638048SMadhavan.Venkataraman@Sun.COM }; 648048SMadhavan.Venkataraman@Sun.COM 658048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_INSERT(hash, cp, cnext, cprev) \ 660Sstevel@tonic-gate { \ 678048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \ 688048SMadhavan.Venkataraman@Sun.COM \ 690Sstevel@tonic-gate cp->cprev = NULL; \ 708048SMadhavan.Venkataraman@Sun.COM cp->cnext = hashp->ch_head; \ 718048SMadhavan.Venkataraman@Sun.COM if (hashp->ch_head == NULL) \ 728048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp; \ 738048SMadhavan.Venkataraman@Sun.COM else \ 748048SMadhavan.Venkataraman@Sun.COM cp->cnext->cprev = cp; \ 758048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp; \ 768048SMadhavan.Venkataraman@Sun.COM } 778048SMadhavan.Venkataraman@Sun.COM 788048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_APPEND(hash, cp, cnext, cprev) \ 798048SMadhavan.Venkataraman@Sun.COM { \ 808048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \ 818048SMadhavan.Venkataraman@Sun.COM \ 828048SMadhavan.Venkataraman@Sun.COM cp->cnext = NULL; \ 838048SMadhavan.Venkataraman@Sun.COM cp->cprev = hashp->ch_tail; \ 848048SMadhavan.Venkataraman@Sun.COM if (hashp->ch_tail == NULL) \ 858048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp; \ 868048SMadhavan.Venkataraman@Sun.COM else \ 878048SMadhavan.Venkataraman@Sun.COM cp->cprev->cnext = cp; \ 888048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp; \ 890Sstevel@tonic-gate } 900Sstevel@tonic-gate 918048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_HASH_DELETE(hash, cp, cnext, cprev) \ 920Sstevel@tonic-gate { \ 938048SMadhavan.Venkataraman@Sun.COM callout_hash_t *hashp = &(hash); \ 948048SMadhavan.Venkataraman@Sun.COM \ 958048SMadhavan.Venkataraman@Sun.COM if (cp->cnext == NULL) \ 968048SMadhavan.Venkataraman@Sun.COM hashp->ch_tail = cp->cprev; \ 970Sstevel@tonic-gate else \ 988048SMadhavan.Venkataraman@Sun.COM cp->cnext->cprev = cp->cprev; \ 998048SMadhavan.Venkataraman@Sun.COM if (cp->cprev == NULL) \ 1008048SMadhavan.Venkataraman@Sun.COM hashp->ch_head = cp->cnext; \ 1018048SMadhavan.Venkataraman@Sun.COM else \ 1028048SMadhavan.Venkataraman@Sun.COM cp->cprev->cnext = cp->cnext; \ 1030Sstevel@tonic-gate } 1040Sstevel@tonic-gate 1058048SMadhavan.Venkataraman@Sun.COM /* 1068048SMadhavan.Venkataraman@Sun.COM * These definitions help us queue callouts and callout lists. Here is 1078048SMadhavan.Venkataraman@Sun.COM * the queueing rationale: 1088048SMadhavan.Venkataraman@Sun.COM * 1098048SMadhavan.Venkataraman@Sun.COM * - callouts are queued in a FIFO manner in the ID hash table. 1108048SMadhavan.Venkataraman@Sun.COM * TCP timers are typically cancelled in the same order that they 1118048SMadhavan.Venkataraman@Sun.COM * were issued. The FIFO queueing shortens the search for a callout 1128048SMadhavan.Venkataraman@Sun.COM * during untimeout(). 1138048SMadhavan.Venkataraman@Sun.COM * 1148048SMadhavan.Venkataraman@Sun.COM * - callouts are queued in a FIFO manner in their callout lists. 1158048SMadhavan.Venkataraman@Sun.COM * This ensures that the callouts are executed in the same order that 1168048SMadhavan.Venkataraman@Sun.COM * they were queued. This is fair. Plus, it helps to make each 1178048SMadhavan.Venkataraman@Sun.COM * callout expiration timely. It also favors cancellations. 1188048SMadhavan.Venkataraman@Sun.COM * 1198048SMadhavan.Venkataraman@Sun.COM * - callout lists are queued in a LIFO manner in the callout list hash 1208048SMadhavan.Venkataraman@Sun.COM * table. This ensures that long term timers stay at the rear of the 1218048SMadhavan.Venkataraman@Sun.COM * hash lists. 1228048SMadhavan.Venkataraman@Sun.COM * 1238048SMadhavan.Venkataraman@Sun.COM * - callout lists are queued in a FIFO manner in the expired callouts 1248048SMadhavan.Venkataraman@Sun.COM * list. This ensures that callout lists are executed in the order 1258048SMadhavan.Venkataraman@Sun.COM * of expiration. 1268048SMadhavan.Venkataraman@Sun.COM */ 1278048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_APPEND(ct, cp) \ 1288048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)], \ 1298048SMadhavan.Venkataraman@Sun.COM cp, c_idnext, c_idprev); \ 1308048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(cp->c_list->cl_callouts, cp, c_clnext, c_clprev) 1318048SMadhavan.Venkataraman@Sun.COM 1328048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_DELETE(ct, cp) \ 1338048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(ct->ct_idhash[CALLOUT_IDHASH(cp->c_xid)], \ 1348048SMadhavan.Venkataraman@Sun.COM cp, c_idnext, c_idprev); \ 1358048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(cp->c_list->cl_callouts, cp, c_clnext, c_clprev) 1368048SMadhavan.Venkataraman@Sun.COM 1378048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_INSERT(hash, cl) \ 1388048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_INSERT(hash, cl, cl_next, cl_prev) 1398048SMadhavan.Venkataraman@Sun.COM 1408048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_APPEND(hash, cl) \ 1418048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(hash, cl, cl_next, cl_prev) 1428048SMadhavan.Venkataraman@Sun.COM 1438048SMadhavan.Venkataraman@Sun.COM #define CALLOUT_LIST_DELETE(hash, cl) \ 1448048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(hash, cl, cl_next, cl_prev) 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate /* 1470Sstevel@tonic-gate * Allocate a callout structure. We try quite hard because we 1480Sstevel@tonic-gate * can't sleep, and if we can't do the allocation, we're toast. 1498048SMadhavan.Venkataraman@Sun.COM * Failing all, we try a KM_PANIC allocation. Note that we never 1508048SMadhavan.Venkataraman@Sun.COM * deallocate a callout. See untimeout() for the reasoning. 1510Sstevel@tonic-gate */ 1520Sstevel@tonic-gate static callout_t * 1530Sstevel@tonic-gate callout_alloc(callout_table_t *ct) 1540Sstevel@tonic-gate { 1558048SMadhavan.Venkataraman@Sun.COM size_t size; 1568048SMadhavan.Venkataraman@Sun.COM callout_t *cp; 1578048SMadhavan.Venkataraman@Sun.COM 1588048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 1598048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 1600Sstevel@tonic-gate 1618048SMadhavan.Venkataraman@Sun.COM cp = kmem_cache_alloc(ct->ct_cache, KM_NOSLEEP); 1628048SMadhavan.Venkataraman@Sun.COM if (cp == NULL) { 1638048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_t); 1648048SMadhavan.Venkataraman@Sun.COM cp = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC); 1658048SMadhavan.Venkataraman@Sun.COM } 1668048SMadhavan.Venkataraman@Sun.COM cp->c_xid = 0; 1678048SMadhavan.Venkataraman@Sun.COM 1688048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 1698048SMadhavan.Venkataraman@Sun.COM ct->ct_allocations++; 1700Sstevel@tonic-gate return (cp); 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1748048SMadhavan.Venkataraman@Sun.COM * Allocate a callout list structure. We try quite hard because we 1758048SMadhavan.Venkataraman@Sun.COM * can't sleep, and if we can't do the allocation, we're toast. 1768048SMadhavan.Venkataraman@Sun.COM * Failing all, we try a KM_PANIC allocation. Note that we never 1778048SMadhavan.Venkataraman@Sun.COM * deallocate a callout list. 1788048SMadhavan.Venkataraman@Sun.COM */ 1798048SMadhavan.Venkataraman@Sun.COM static void 1808048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(callout_table_t *ct) 1818048SMadhavan.Venkataraman@Sun.COM { 1828048SMadhavan.Venkataraman@Sun.COM size_t size; 1838048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 1848048SMadhavan.Venkataraman@Sun.COM 1858048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 1868048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 1878048SMadhavan.Venkataraman@Sun.COM 1888048SMadhavan.Venkataraman@Sun.COM cl = kmem_cache_alloc(ct->ct_lcache, KM_NOSLEEP); 1898048SMadhavan.Venkataraman@Sun.COM if (cl == NULL) { 1908048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_list_t); 1918048SMadhavan.Venkataraman@Sun.COM cl = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC); 1928048SMadhavan.Venkataraman@Sun.COM } 1938048SMadhavan.Venkataraman@Sun.COM bzero(cl, sizeof (callout_list_t)); 1948048SMadhavan.Venkataraman@Sun.COM 1958048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 1968048SMadhavan.Venkataraman@Sun.COM cl->cl_next = ct->ct_lfree; 1978048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl; 1988048SMadhavan.Venkataraman@Sun.COM } 1998048SMadhavan.Venkataraman@Sun.COM 2008048SMadhavan.Venkataraman@Sun.COM /* 2018048SMadhavan.Venkataraman@Sun.COM * Find the callout list that corresponds to an expiration. There can 2028048SMadhavan.Venkataraman@Sun.COM * be only one. 2038048SMadhavan.Venkataraman@Sun.COM */ 2048048SMadhavan.Venkataraman@Sun.COM static callout_list_t * 2058048SMadhavan.Venkataraman@Sun.COM callout_list_get(callout_table_t *ct, hrtime_t expiration, int hash) 2068048SMadhavan.Venkataraman@Sun.COM { 2078048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 2088048SMadhavan.Venkataraman@Sun.COM 2098048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 2108048SMadhavan.Venkataraman@Sun.COM 2118048SMadhavan.Venkataraman@Sun.COM for (cl = ct->ct_clhash[hash].ch_head; (cl != NULL); cl = cl->cl_next) { 2128048SMadhavan.Venkataraman@Sun.COM if (cl->cl_expiration == expiration) 2138048SMadhavan.Venkataraman@Sun.COM return (cl); 2148048SMadhavan.Venkataraman@Sun.COM } 2158048SMadhavan.Venkataraman@Sun.COM 2168048SMadhavan.Venkataraman@Sun.COM return (NULL); 2178048SMadhavan.Venkataraman@Sun.COM } 2188048SMadhavan.Venkataraman@Sun.COM 2198048SMadhavan.Venkataraman@Sun.COM /* 2208048SMadhavan.Venkataraman@Sun.COM * Find the callout list that corresponds to an expiration. There can 2218048SMadhavan.Venkataraman@Sun.COM * be only one. If the callout list is null, free it. Else, return it. 2228048SMadhavan.Venkataraman@Sun.COM */ 2238048SMadhavan.Venkataraman@Sun.COM static callout_list_t * 2248048SMadhavan.Venkataraman@Sun.COM callout_list_check(callout_table_t *ct, hrtime_t expiration, int hash) 2258048SMadhavan.Venkataraman@Sun.COM { 2268048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 2278048SMadhavan.Venkataraman@Sun.COM 2288048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 2298048SMadhavan.Venkataraman@Sun.COM 2308048SMadhavan.Venkataraman@Sun.COM cl = callout_list_get(ct, expiration, hash); 2318048SMadhavan.Venkataraman@Sun.COM if (cl != NULL) { 2328048SMadhavan.Venkataraman@Sun.COM if (cl->cl_callouts.ch_head != NULL) { 2338048SMadhavan.Venkataraman@Sun.COM /* 2348048SMadhavan.Venkataraman@Sun.COM * There is exactly one callout list for every 2358048SMadhavan.Venkataraman@Sun.COM * unique expiration. So, we are done. 2368048SMadhavan.Venkataraman@Sun.COM */ 2378048SMadhavan.Venkataraman@Sun.COM return (cl); 2388048SMadhavan.Venkataraman@Sun.COM } 2398048SMadhavan.Venkataraman@Sun.COM 2408048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 2418048SMadhavan.Venkataraman@Sun.COM cl->cl_next = ct->ct_lfree; 2428048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl; 2438048SMadhavan.Venkataraman@Sun.COM } 2448048SMadhavan.Venkataraman@Sun.COM 2458048SMadhavan.Venkataraman@Sun.COM return (NULL); 2468048SMadhavan.Venkataraman@Sun.COM } 2478048SMadhavan.Venkataraman@Sun.COM 2488048SMadhavan.Venkataraman@Sun.COM /* 2498048SMadhavan.Venkataraman@Sun.COM * Initialize a callout table's heap, if necessary. Preallocate some free 2508048SMadhavan.Venkataraman@Sun.COM * entries so we don't have to check for NULL elsewhere. 2518048SMadhavan.Venkataraman@Sun.COM */ 2528048SMadhavan.Venkataraman@Sun.COM static void 2538048SMadhavan.Venkataraman@Sun.COM callout_heap_init(callout_table_t *ct) 2548048SMadhavan.Venkataraman@Sun.COM { 2558048SMadhavan.Venkataraman@Sun.COM size_t size; 2568048SMadhavan.Venkataraman@Sun.COM 2578048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 2588048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap == NULL); 2598048SMadhavan.Venkataraman@Sun.COM 2608048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num = 0; 2618048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_max = CALLOUT_CHUNK; 2628048SMadhavan.Venkataraman@Sun.COM size = sizeof (hrtime_t) * CALLOUT_CHUNK; 2638048SMadhavan.Venkataraman@Sun.COM ct->ct_heap = kmem_alloc(size, KM_SLEEP); 2648048SMadhavan.Venkataraman@Sun.COM } 2658048SMadhavan.Venkataraman@Sun.COM 2668048SMadhavan.Venkataraman@Sun.COM /* 2678048SMadhavan.Venkataraman@Sun.COM * Reallocate the heap. We try quite hard because we can't sleep, and if 2688048SMadhavan.Venkataraman@Sun.COM * we can't do the allocation, we're toast. Failing all, we try a KM_PANIC 2698048SMadhavan.Venkataraman@Sun.COM * allocation. Note that the heap only expands, it never contracts. 2708048SMadhavan.Venkataraman@Sun.COM */ 2718048SMadhavan.Venkataraman@Sun.COM static void 2728048SMadhavan.Venkataraman@Sun.COM callout_heap_expand(callout_table_t *ct) 2738048SMadhavan.Venkataraman@Sun.COM { 2748048SMadhavan.Venkataraman@Sun.COM size_t max, size, osize; 2758048SMadhavan.Venkataraman@Sun.COM hrtime_t *heap; 2768048SMadhavan.Venkataraman@Sun.COM 2778048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 2788048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num <= ct->ct_heap_max); 2798048SMadhavan.Venkataraman@Sun.COM 2808048SMadhavan.Venkataraman@Sun.COM while (ct->ct_heap_num == ct->ct_heap_max) { 2818048SMadhavan.Venkataraman@Sun.COM max = ct->ct_heap_max; 2828048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 2838048SMadhavan.Venkataraman@Sun.COM 2848048SMadhavan.Venkataraman@Sun.COM osize = sizeof (hrtime_t) * max; 2858048SMadhavan.Venkataraman@Sun.COM size = sizeof (hrtime_t) * (max + CALLOUT_CHUNK); 2868048SMadhavan.Venkataraman@Sun.COM heap = kmem_alloc_tryhard(size, &size, KM_NOSLEEP | KM_PANIC); 2878048SMadhavan.Venkataraman@Sun.COM 2888048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 2898048SMadhavan.Venkataraman@Sun.COM if (max < ct->ct_heap_max) { 2908048SMadhavan.Venkataraman@Sun.COM /* 2918048SMadhavan.Venkataraman@Sun.COM * Someone beat us to the allocation. Free what we 2928048SMadhavan.Venkataraman@Sun.COM * just allocated and proceed. 2938048SMadhavan.Venkataraman@Sun.COM */ 2948048SMadhavan.Venkataraman@Sun.COM kmem_free(heap, size); 2958048SMadhavan.Venkataraman@Sun.COM continue; 2968048SMadhavan.Venkataraman@Sun.COM } 2978048SMadhavan.Venkataraman@Sun.COM 2988048SMadhavan.Venkataraman@Sun.COM bcopy(ct->ct_heap, heap, osize); 2998048SMadhavan.Venkataraman@Sun.COM kmem_free(ct->ct_heap, osize); 3008048SMadhavan.Venkataraman@Sun.COM ct->ct_heap = heap; 3018048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_max = size / sizeof (hrtime_t); 3028048SMadhavan.Venkataraman@Sun.COM } 3038048SMadhavan.Venkataraman@Sun.COM } 3048048SMadhavan.Venkataraman@Sun.COM 3058048SMadhavan.Venkataraman@Sun.COM /* 3068048SMadhavan.Venkataraman@Sun.COM * Move an expiration from the bottom of the heap to its correct place 3078048SMadhavan.Venkataraman@Sun.COM * in the heap. If we reached the root doing this, return 1. Else, 3088048SMadhavan.Venkataraman@Sun.COM * return 0. 3090Sstevel@tonic-gate */ 3108048SMadhavan.Venkataraman@Sun.COM static int 3118048SMadhavan.Venkataraman@Sun.COM callout_upheap(callout_table_t *ct) 3128048SMadhavan.Venkataraman@Sun.COM { 3138048SMadhavan.Venkataraman@Sun.COM int current, parent; 3148048SMadhavan.Venkataraman@Sun.COM hrtime_t *heap, current_expiration, parent_expiration; 3158048SMadhavan.Venkataraman@Sun.COM 3168048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 3178048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num >= 1); 3188048SMadhavan.Venkataraman@Sun.COM 3198048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 1) { 3208048SMadhavan.Venkataraman@Sun.COM return (1); 3218048SMadhavan.Venkataraman@Sun.COM } 3228048SMadhavan.Venkataraman@Sun.COM 3238048SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap; 3248048SMadhavan.Venkataraman@Sun.COM current = ct->ct_heap_num - 1; 3258048SMadhavan.Venkataraman@Sun.COM 3268048SMadhavan.Venkataraman@Sun.COM for (;;) { 3278048SMadhavan.Venkataraman@Sun.COM parent = CALLOUT_HEAP_PARENT(current); 3288048SMadhavan.Venkataraman@Sun.COM current_expiration = heap[current]; 3298048SMadhavan.Venkataraman@Sun.COM parent_expiration = heap[parent]; 3308048SMadhavan.Venkataraman@Sun.COM 3318048SMadhavan.Venkataraman@Sun.COM /* 3328048SMadhavan.Venkataraman@Sun.COM * We have an expiration later than our parent; we're done. 3338048SMadhavan.Venkataraman@Sun.COM */ 3348048SMadhavan.Venkataraman@Sun.COM if (current_expiration >= parent_expiration) { 3358048SMadhavan.Venkataraman@Sun.COM return (0); 3368048SMadhavan.Venkataraman@Sun.COM } 3378048SMadhavan.Venkataraman@Sun.COM 3388048SMadhavan.Venkataraman@Sun.COM /* 3398048SMadhavan.Venkataraman@Sun.COM * We need to swap with our parent, and continue up the heap. 3408048SMadhavan.Venkataraman@Sun.COM */ 3418048SMadhavan.Venkataraman@Sun.COM heap[parent] = current_expiration; 3428048SMadhavan.Venkataraman@Sun.COM heap[current] = parent_expiration; 3438048SMadhavan.Venkataraman@Sun.COM 3448048SMadhavan.Venkataraman@Sun.COM /* 3458048SMadhavan.Venkataraman@Sun.COM * If we just reached the root, we're done. 3468048SMadhavan.Venkataraman@Sun.COM */ 3478048SMadhavan.Venkataraman@Sun.COM if (parent == 0) { 3488048SMadhavan.Venkataraman@Sun.COM return (1); 3498048SMadhavan.Venkataraman@Sun.COM } 3508048SMadhavan.Venkataraman@Sun.COM 3518048SMadhavan.Venkataraman@Sun.COM current = parent; 3528048SMadhavan.Venkataraman@Sun.COM } 3538048SMadhavan.Venkataraman@Sun.COM /*NOTREACHED*/ 3548048SMadhavan.Venkataraman@Sun.COM } 3558048SMadhavan.Venkataraman@Sun.COM 3568048SMadhavan.Venkataraman@Sun.COM /* 3578048SMadhavan.Venkataraman@Sun.COM * Insert a new, unique expiration into a callout table's heap. 3588048SMadhavan.Venkataraman@Sun.COM */ 3598048SMadhavan.Venkataraman@Sun.COM static void 3608048SMadhavan.Venkataraman@Sun.COM callout_heap_insert(callout_table_t *ct, hrtime_t expiration) 3618048SMadhavan.Venkataraman@Sun.COM { 3628048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 3638048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num < ct->ct_heap_max); 3648048SMadhavan.Venkataraman@Sun.COM 3658048SMadhavan.Venkataraman@Sun.COM /* 3668048SMadhavan.Venkataraman@Sun.COM * First, copy the expiration to the bottom of the heap. 3678048SMadhavan.Venkataraman@Sun.COM */ 3688048SMadhavan.Venkataraman@Sun.COM ct->ct_heap[ct->ct_heap_num] = expiration; 3698048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num++; 3708048SMadhavan.Venkataraman@Sun.COM 3718048SMadhavan.Venkataraman@Sun.COM /* 3728048SMadhavan.Venkataraman@Sun.COM * Now, perform an upheap operation. If we reached the root, then 3738048SMadhavan.Venkataraman@Sun.COM * the cyclic needs to be reprogrammed as we have an earlier 3748048SMadhavan.Venkataraman@Sun.COM * expiration. 3758048SMadhavan.Venkataraman@Sun.COM * 3768048SMadhavan.Venkataraman@Sun.COM * Also, during the CPR suspend phase, do not reprogram the cyclic. 3778048SMadhavan.Venkataraman@Sun.COM * We don't want any callout activity. When the CPR resume phase is 3788048SMadhavan.Venkataraman@Sun.COM * entered, the cyclic will be programmed for the earliest expiration 3798048SMadhavan.Venkataraman@Sun.COM * in the heap. 3808048SMadhavan.Venkataraman@Sun.COM */ 381*8566SMadhavan.Venkataraman@Sun.COM if (callout_upheap(ct) && (ct->ct_suspend == 0)) 3828048SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, expiration); 3838048SMadhavan.Venkataraman@Sun.COM } 3848048SMadhavan.Venkataraman@Sun.COM 3858048SMadhavan.Venkataraman@Sun.COM /* 3868048SMadhavan.Venkataraman@Sun.COM * Move an expiration from the top of the heap to its correct place 3878048SMadhavan.Venkataraman@Sun.COM * in the heap. 3888048SMadhavan.Venkataraman@Sun.COM */ 3898048SMadhavan.Venkataraman@Sun.COM static void 3908048SMadhavan.Venkataraman@Sun.COM callout_downheap(callout_table_t *ct) 3910Sstevel@tonic-gate { 3928048SMadhavan.Venkataraman@Sun.COM int left, right, current, nelems; 3938048SMadhavan.Venkataraman@Sun.COM hrtime_t *heap, left_expiration, right_expiration, current_expiration; 3948048SMadhavan.Venkataraman@Sun.COM 3958048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 3968048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num >= 1); 3978048SMadhavan.Venkataraman@Sun.COM 3988048SMadhavan.Venkataraman@Sun.COM heap = ct->ct_heap; 3998048SMadhavan.Venkataraman@Sun.COM current = 0; 4008048SMadhavan.Venkataraman@Sun.COM nelems = ct->ct_heap_num; 4018048SMadhavan.Venkataraman@Sun.COM 4028048SMadhavan.Venkataraman@Sun.COM for (;;) { 4038048SMadhavan.Venkataraman@Sun.COM /* 4048048SMadhavan.Venkataraman@Sun.COM * If we don't have a left child (i.e., we're a leaf), we're 4058048SMadhavan.Venkataraman@Sun.COM * done. 4068048SMadhavan.Venkataraman@Sun.COM */ 4078048SMadhavan.Venkataraman@Sun.COM if ((left = CALLOUT_HEAP_LEFT(current)) >= nelems) 4088048SMadhavan.Venkataraman@Sun.COM return; 4098048SMadhavan.Venkataraman@Sun.COM 4108048SMadhavan.Venkataraman@Sun.COM left_expiration = heap[left]; 4118048SMadhavan.Venkataraman@Sun.COM current_expiration = heap[current]; 4128048SMadhavan.Venkataraman@Sun.COM 4138048SMadhavan.Venkataraman@Sun.COM right = CALLOUT_HEAP_RIGHT(current); 4148048SMadhavan.Venkataraman@Sun.COM 4158048SMadhavan.Venkataraman@Sun.COM /* 4168048SMadhavan.Venkataraman@Sun.COM * Even if we don't have a right child, we still need to compare 4178048SMadhavan.Venkataraman@Sun.COM * our expiration against that of our left child. 4188048SMadhavan.Venkataraman@Sun.COM */ 4198048SMadhavan.Venkataraman@Sun.COM if (right >= nelems) 4208048SMadhavan.Venkataraman@Sun.COM goto comp_left; 4218048SMadhavan.Venkataraman@Sun.COM 4228048SMadhavan.Venkataraman@Sun.COM right_expiration = heap[right]; 4238048SMadhavan.Venkataraman@Sun.COM 4248048SMadhavan.Venkataraman@Sun.COM /* 4258048SMadhavan.Venkataraman@Sun.COM * We have both a left and a right child. We need to compare 4268048SMadhavan.Venkataraman@Sun.COM * the expiration of the children to determine which 4278048SMadhavan.Venkataraman@Sun.COM * expires earlier. 4288048SMadhavan.Venkataraman@Sun.COM */ 4298048SMadhavan.Venkataraman@Sun.COM if (right_expiration < left_expiration) { 4308048SMadhavan.Venkataraman@Sun.COM /* 4318048SMadhavan.Venkataraman@Sun.COM * Our right child is the earlier of our children. 4328048SMadhavan.Venkataraman@Sun.COM * We'll now compare our expiration to its expiration. 4338048SMadhavan.Venkataraman@Sun.COM * If ours is the earlier one, we're done. 4348048SMadhavan.Venkataraman@Sun.COM */ 4358048SMadhavan.Venkataraman@Sun.COM if (current_expiration <= right_expiration) 4368048SMadhavan.Venkataraman@Sun.COM return; 4378048SMadhavan.Venkataraman@Sun.COM 4388048SMadhavan.Venkataraman@Sun.COM /* 4398048SMadhavan.Venkataraman@Sun.COM * Our right child expires earlier than we do; swap 4408048SMadhavan.Venkataraman@Sun.COM * with our right child, and descend right. 4418048SMadhavan.Venkataraman@Sun.COM */ 4428048SMadhavan.Venkataraman@Sun.COM heap[right] = current_expiration; 4438048SMadhavan.Venkataraman@Sun.COM heap[current] = right_expiration; 4448048SMadhavan.Venkataraman@Sun.COM current = right; 4458048SMadhavan.Venkataraman@Sun.COM continue; 4468048SMadhavan.Venkataraman@Sun.COM } 4478048SMadhavan.Venkataraman@Sun.COM 4488048SMadhavan.Venkataraman@Sun.COM comp_left: 4498048SMadhavan.Venkataraman@Sun.COM /* 4508048SMadhavan.Venkataraman@Sun.COM * Our left child is the earlier of our children (or we have 4518048SMadhavan.Venkataraman@Sun.COM * no right child). We'll now compare our expiration 4528048SMadhavan.Venkataraman@Sun.COM * to its expiration. If ours is the earlier one, we're done. 4538048SMadhavan.Venkataraman@Sun.COM */ 4548048SMadhavan.Venkataraman@Sun.COM if (current_expiration <= left_expiration) 4558048SMadhavan.Venkataraman@Sun.COM return; 4568048SMadhavan.Venkataraman@Sun.COM 4578048SMadhavan.Venkataraman@Sun.COM /* 4588048SMadhavan.Venkataraman@Sun.COM * Our left child expires earlier than we do; swap with our 4598048SMadhavan.Venkataraman@Sun.COM * left child, and descend left. 4608048SMadhavan.Venkataraman@Sun.COM */ 4618048SMadhavan.Venkataraman@Sun.COM heap[left] = current_expiration; 4628048SMadhavan.Venkataraman@Sun.COM heap[current] = left_expiration; 4638048SMadhavan.Venkataraman@Sun.COM current = left; 4648048SMadhavan.Venkataraman@Sun.COM } 4658048SMadhavan.Venkataraman@Sun.COM } 4668048SMadhavan.Venkataraman@Sun.COM 4678048SMadhavan.Venkataraman@Sun.COM /* 4688048SMadhavan.Venkataraman@Sun.COM * Delete and handle all past expirations in a callout table's heap. 4698048SMadhavan.Venkataraman@Sun.COM */ 4708048SMadhavan.Venkataraman@Sun.COM static void 4718048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(callout_table_t *ct) 4728048SMadhavan.Venkataraman@Sun.COM { 4738048SMadhavan.Venkataraman@Sun.COM hrtime_t now, expiration; 4748048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 4758048SMadhavan.Venkataraman@Sun.COM int hash; 4768048SMadhavan.Venkataraman@Sun.COM 4778048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 4788048SMadhavan.Venkataraman@Sun.COM 4798048SMadhavan.Venkataraman@Sun.COM now = gethrtime(); 4808048SMadhavan.Venkataraman@Sun.COM 4818048SMadhavan.Venkataraman@Sun.COM while (ct->ct_heap_num > 0) { 4828048SMadhavan.Venkataraman@Sun.COM expiration = ct->ct_heap[0]; 4838048SMadhavan.Venkataraman@Sun.COM /* 4848048SMadhavan.Venkataraman@Sun.COM * Find the callout list that corresponds to the expiration. 4858048SMadhavan.Venkataraman@Sun.COM * If the callout list is empty, callout_list_check() 4868048SMadhavan.Venkataraman@Sun.COM * will free the callout list and return NULL. 4878048SMadhavan.Venkataraman@Sun.COM */ 4888048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(expiration); 4898048SMadhavan.Venkataraman@Sun.COM cl = callout_list_check(ct, expiration, hash); 4908048SMadhavan.Venkataraman@Sun.COM if (cl != NULL) { 4918048SMadhavan.Venkataraman@Sun.COM /* 4928048SMadhavan.Venkataraman@Sun.COM * If the root of the heap expires in the future, we are 4938048SMadhavan.Venkataraman@Sun.COM * done. We are doing this check here instead of at the 4948048SMadhavan.Venkataraman@Sun.COM * beginning because we want to first free all the 4958048SMadhavan.Venkataraman@Sun.COM * empty callout lists at the top of the heap. 4968048SMadhavan.Venkataraman@Sun.COM */ 4978048SMadhavan.Venkataraman@Sun.COM if (expiration > now) 4988048SMadhavan.Venkataraman@Sun.COM break; 4998048SMadhavan.Venkataraman@Sun.COM 5008048SMadhavan.Venkataraman@Sun.COM /* 5018048SMadhavan.Venkataraman@Sun.COM * Move the callout list for this expiration to the 5028048SMadhavan.Venkataraman@Sun.COM * list of expired callout lists. It will be processed 5038048SMadhavan.Venkataraman@Sun.COM * by the callout executor. 5048048SMadhavan.Venkataraman@Sun.COM */ 5058048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 5068048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, cl); 5078048SMadhavan.Venkataraman@Sun.COM } 5088048SMadhavan.Venkataraman@Sun.COM 5098048SMadhavan.Venkataraman@Sun.COM /* 5108048SMadhavan.Venkataraman@Sun.COM * Now delete the root. This is done by swapping the root with 5118048SMadhavan.Venkataraman@Sun.COM * the last item in the heap and downheaping the item. 5128048SMadhavan.Venkataraman@Sun.COM */ 5138048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num--; 5148048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num > 0) { 5158048SMadhavan.Venkataraman@Sun.COM ct->ct_heap[0] = ct->ct_heap[ct->ct_heap_num]; 5168048SMadhavan.Venkataraman@Sun.COM callout_downheap(ct); 5178048SMadhavan.Venkataraman@Sun.COM } 5188048SMadhavan.Venkataraman@Sun.COM } 5198048SMadhavan.Venkataraman@Sun.COM 5208048SMadhavan.Venkataraman@Sun.COM /* 5218048SMadhavan.Venkataraman@Sun.COM * If this callout table is empty or callouts have been suspended 5228048SMadhavan.Venkataraman@Sun.COM * by CPR, just return. The cyclic has already been programmed to 5238048SMadhavan.Venkataraman@Sun.COM * infinity by the cyclic subsystem. 5248048SMadhavan.Venkataraman@Sun.COM */ 525*8566SMadhavan.Venkataraman@Sun.COM if ((ct->ct_heap_num == 0) || (ct->ct_suspend > 0)) 5268048SMadhavan.Venkataraman@Sun.COM return; 5278048SMadhavan.Venkataraman@Sun.COM 5288048SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, expiration); 5298048SMadhavan.Venkataraman@Sun.COM } 5308048SMadhavan.Venkataraman@Sun.COM 531*8566SMadhavan.Venkataraman@Sun.COM /* 532*8566SMadhavan.Venkataraman@Sun.COM * Common function used to create normal and realtime callouts. 533*8566SMadhavan.Venkataraman@Sun.COM * 534*8566SMadhavan.Venkataraman@Sun.COM * Realtime callouts are handled at CY_LOW_PIL by a cyclic handler. So, 535*8566SMadhavan.Venkataraman@Sun.COM * there is one restriction on a realtime callout handler - it should not 536*8566SMadhavan.Venkataraman@Sun.COM * directly or indirectly acquire cpu_lock. CPU offline waits for pending 537*8566SMadhavan.Venkataraman@Sun.COM * cyclic handlers to complete while holding cpu_lock. So, if a realtime 538*8566SMadhavan.Venkataraman@Sun.COM * callout handler were to try to get cpu_lock, there would be a deadlock 539*8566SMadhavan.Venkataraman@Sun.COM * during CPU offline. 540*8566SMadhavan.Venkataraman@Sun.COM */ 5418048SMadhavan.Venkataraman@Sun.COM callout_id_t 5428048SMadhavan.Venkataraman@Sun.COM timeout_generic(int type, void (*func)(void *), void *arg, 5438048SMadhavan.Venkataraman@Sun.COM hrtime_t expiration, hrtime_t resolution, int flags) 5448048SMadhavan.Venkataraman@Sun.COM { 5458048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 5464123Sdm120769 callout_t *cp; 5474123Sdm120769 callout_id_t id; 5488048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 5498048SMadhavan.Venkataraman@Sun.COM hrtime_t now, interval; 5508048SMadhavan.Venkataraman@Sun.COM int hash; 5518048SMadhavan.Venkataraman@Sun.COM 5528048SMadhavan.Venkataraman@Sun.COM ASSERT(resolution > 0); 5538048SMadhavan.Venkataraman@Sun.COM ASSERT(func != NULL); 5548048SMadhavan.Venkataraman@Sun.COM 5558048SMadhavan.Venkataraman@Sun.COM /* 5568048SMadhavan.Venkataraman@Sun.COM * Please see comment about minimum resolution in callout_init(). 5578048SMadhavan.Venkataraman@Sun.COM */ 5588048SMadhavan.Venkataraman@Sun.COM if (resolution < callout_min_resolution) 5598048SMadhavan.Venkataraman@Sun.COM resolution = callout_min_resolution; 5606422Sqiao 5618048SMadhavan.Venkataraman@Sun.COM /* 5628048SMadhavan.Venkataraman@Sun.COM * We disable kernel preemption so that we remain on the same CPU 5638048SMadhavan.Venkataraman@Sun.COM * throughout. If we needed to reprogram the callout table's cyclic, 5648048SMadhavan.Venkataraman@Sun.COM * we can avoid X-calls if we are on the same CPU. 5658048SMadhavan.Venkataraman@Sun.COM * 5668048SMadhavan.Venkataraman@Sun.COM * Note that callout_alloc() releases and reacquires the callout 5678048SMadhavan.Venkataraman@Sun.COM * table mutex. While reacquiring the mutex, it is possible for us 5688048SMadhavan.Venkataraman@Sun.COM * to go to sleep and later migrate to another CPU. This should be 5698048SMadhavan.Venkataraman@Sun.COM * pretty rare, though. 5708048SMadhavan.Venkataraman@Sun.COM */ 5718048SMadhavan.Venkataraman@Sun.COM kpreempt_disable(); 5720Sstevel@tonic-gate 5738048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(type, CPU->cpu_seqid)]; 5748048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 5750Sstevel@tonic-gate 5768048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) { 5778048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 5788048SMadhavan.Venkataraman@Sun.COM /* 5798048SMadhavan.Venkataraman@Sun.COM * The callout table has not yet been initialized fully. 5808048SMadhavan.Venkataraman@Sun.COM * So, put this one on the boot callout table which is 5818048SMadhavan.Venkataraman@Sun.COM * always initialized. 5828048SMadhavan.Venkataraman@Sun.COM */ 5838048SMadhavan.Venkataraman@Sun.COM ct = &callout_boot_ct[type]; 5848048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 5858048SMadhavan.Venkataraman@Sun.COM } 5868048SMadhavan.Venkataraman@Sun.COM 5878048SMadhavan.Venkataraman@Sun.COM if ((cp = ct->ct_free) == NULL) 5880Sstevel@tonic-gate cp = callout_alloc(ct); 5890Sstevel@tonic-gate else 5908048SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp->c_idnext; 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate cp->c_func = func; 5930Sstevel@tonic-gate cp->c_arg = arg; 5940Sstevel@tonic-gate 5950Sstevel@tonic-gate /* 5968048SMadhavan.Venkataraman@Sun.COM * Compute the expiration hrtime. 5978048SMadhavan.Venkataraman@Sun.COM */ 5988048SMadhavan.Venkataraman@Sun.COM now = gethrtime(); 5998048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ABSOLUTE) { 6008048SMadhavan.Venkataraman@Sun.COM ASSERT(expiration > 0); 6018048SMadhavan.Venkataraman@Sun.COM interval = expiration - now; 6028048SMadhavan.Venkataraman@Sun.COM } else { 6038048SMadhavan.Venkataraman@Sun.COM interval = expiration; 6048048SMadhavan.Venkataraman@Sun.COM expiration += now; 6058048SMadhavan.Venkataraman@Sun.COM ASSERT(expiration > 0); 6068048SMadhavan.Venkataraman@Sun.COM } 6078048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_ROUNDUP) 6088048SMadhavan.Venkataraman@Sun.COM expiration += resolution - 1; 6098048SMadhavan.Venkataraman@Sun.COM expiration = (expiration / resolution) * resolution; 610*8566SMadhavan.Venkataraman@Sun.COM if (expiration <= 0) { 611*8566SMadhavan.Venkataraman@Sun.COM /* 612*8566SMadhavan.Venkataraman@Sun.COM * expiration hrtime overflow has occurred. Just set the 613*8566SMadhavan.Venkataraman@Sun.COM * expiration to infinity. 614*8566SMadhavan.Venkataraman@Sun.COM */ 615*8566SMadhavan.Venkataraman@Sun.COM expiration = CY_INFINITY; 616*8566SMadhavan.Venkataraman@Sun.COM } 6178048SMadhavan.Venkataraman@Sun.COM 6188048SMadhavan.Venkataraman@Sun.COM /* 6198048SMadhavan.Venkataraman@Sun.COM * Assign an ID to this callout 6208048SMadhavan.Venkataraman@Sun.COM */ 6218048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_32BIT) { 6228048SMadhavan.Venkataraman@Sun.COM if (interval > callout_longterm) { 6238048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_long_id - callout_counter_low); 6248048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH; 6258048SMadhavan.Venkataraman@Sun.COM ct->ct_long_id = id; 6268048SMadhavan.Venkataraman@Sun.COM } else { 6278048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_short_id - callout_counter_low); 6288048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH; 6298048SMadhavan.Venkataraman@Sun.COM ct->ct_short_id = id; 6308048SMadhavan.Venkataraman@Sun.COM } 6318048SMadhavan.Venkataraman@Sun.COM } else { 6328048SMadhavan.Venkataraman@Sun.COM id = (ct->ct_gen_id - callout_counter_low); 6338048SMadhavan.Venkataraman@Sun.COM if ((id & CALLOUT_COUNTER_HIGH) == 0) { 6348048SMadhavan.Venkataraman@Sun.COM id |= CALLOUT_COUNTER_HIGH; 6358048SMadhavan.Venkataraman@Sun.COM id += CALLOUT_GENERATION_LOW; 6368048SMadhavan.Venkataraman@Sun.COM } 6378048SMadhavan.Venkataraman@Sun.COM ct->ct_gen_id = id; 6388048SMadhavan.Venkataraman@Sun.COM } 6398048SMadhavan.Venkataraman@Sun.COM 6408048SMadhavan.Venkataraman@Sun.COM cp->c_xid = id; 6418048SMadhavan.Venkataraman@Sun.COM if (flags & CALLOUT_FLAG_HRESTIME) 6428048SMadhavan.Venkataraman@Sun.COM cp->c_xid |= CALLOUT_HRESTIME; 6438048SMadhavan.Venkataraman@Sun.COM 6448048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_CLHASH(expiration); 6458048SMadhavan.Venkataraman@Sun.COM 6468048SMadhavan.Venkataraman@Sun.COM again: 6478048SMadhavan.Venkataraman@Sun.COM /* 6488048SMadhavan.Venkataraman@Sun.COM * Try to see if a callout list already exists for this expiration. 6498048SMadhavan.Venkataraman@Sun.COM * Most of the time, this will be the case. 6508048SMadhavan.Venkataraman@Sun.COM */ 6518048SMadhavan.Venkataraman@Sun.COM cl = callout_list_get(ct, expiration, hash); 6528048SMadhavan.Venkataraman@Sun.COM if (cl == NULL) { 6538048SMadhavan.Venkataraman@Sun.COM /* 6548048SMadhavan.Venkataraman@Sun.COM * Check if we have enough space in the heap to insert one 6558048SMadhavan.Venkataraman@Sun.COM * expiration. If not, expand the heap. 6568048SMadhavan.Venkataraman@Sun.COM */ 6578048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == ct->ct_heap_max) { 6588048SMadhavan.Venkataraman@Sun.COM callout_heap_expand(ct); 6598048SMadhavan.Venkataraman@Sun.COM /* 6608048SMadhavan.Venkataraman@Sun.COM * In the above call, we drop the lock, allocate and 6618048SMadhavan.Venkataraman@Sun.COM * reacquire the lock. So, we could have been away 6628048SMadhavan.Venkataraman@Sun.COM * for a while. In the meantime, someone could have 6638048SMadhavan.Venkataraman@Sun.COM * inserted a callout list with the same expiration. 6648048SMadhavan.Venkataraman@Sun.COM * So, the best course is to repeat the steps. This 6658048SMadhavan.Venkataraman@Sun.COM * should be an infrequent event. 6668048SMadhavan.Venkataraman@Sun.COM */ 6678048SMadhavan.Venkataraman@Sun.COM goto again; 6688048SMadhavan.Venkataraman@Sun.COM } 6698048SMadhavan.Venkataraman@Sun.COM 6708048SMadhavan.Venkataraman@Sun.COM /* 6718048SMadhavan.Venkataraman@Sun.COM * Check the free list. If we don't find one, we have to 6728048SMadhavan.Venkataraman@Sun.COM * take the slow path and allocate from kmem. 6738048SMadhavan.Venkataraman@Sun.COM */ 6748048SMadhavan.Venkataraman@Sun.COM if ((cl = ct->ct_lfree) == NULL) { 6758048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(ct); 6768048SMadhavan.Venkataraman@Sun.COM /* 6778048SMadhavan.Venkataraman@Sun.COM * In the above call, we drop the lock, allocate and 6788048SMadhavan.Venkataraman@Sun.COM * reacquire the lock. So, we could have been away 6798048SMadhavan.Venkataraman@Sun.COM * for a while. In the meantime, someone could have 6808048SMadhavan.Venkataraman@Sun.COM * inserted a callout list with the same expiration. 6818048SMadhavan.Venkataraman@Sun.COM * Plus, the heap could have become full. So, the best 6828048SMadhavan.Venkataraman@Sun.COM * course is to repeat the steps. This should be an 6838048SMadhavan.Venkataraman@Sun.COM * infrequent event. 6848048SMadhavan.Venkataraman@Sun.COM */ 6858048SMadhavan.Venkataraman@Sun.COM goto again; 6868048SMadhavan.Venkataraman@Sun.COM } 6878048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl->cl_next; 6888048SMadhavan.Venkataraman@Sun.COM cl->cl_expiration = expiration; 6898048SMadhavan.Venkataraman@Sun.COM 6908048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_INSERT(ct->ct_clhash[hash], cl); 6918048SMadhavan.Venkataraman@Sun.COM 6928048SMadhavan.Venkataraman@Sun.COM /* 6938048SMadhavan.Venkataraman@Sun.COM * This is a new expiration. So, insert it into the heap. 6948048SMadhavan.Venkataraman@Sun.COM * This will also reprogram the cyclic, if the expiration 6958048SMadhavan.Venkataraman@Sun.COM * propagated to the root of the heap. 6968048SMadhavan.Venkataraman@Sun.COM */ 6978048SMadhavan.Venkataraman@Sun.COM callout_heap_insert(ct, expiration); 6988048SMadhavan.Venkataraman@Sun.COM } 6998048SMadhavan.Venkataraman@Sun.COM cp->c_list = cl; 7008048SMadhavan.Venkataraman@Sun.COM CALLOUT_APPEND(ct, cp); 7018048SMadhavan.Venkataraman@Sun.COM 7028048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts++; 7038048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending++; 7048048SMadhavan.Venkataraman@Sun.COM 7058048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 7068048SMadhavan.Venkataraman@Sun.COM 7078048SMadhavan.Venkataraman@Sun.COM kpreempt_enable(); 7088048SMadhavan.Venkataraman@Sun.COM 7098048SMadhavan.Venkataraman@Sun.COM TRACE_4(TR_FAC_CALLOUT, TR_TIMEOUT, 7108048SMadhavan.Venkataraman@Sun.COM "timeout:%K(%p) in %llx expiration, cp %p", func, arg, expiration, 7118048SMadhavan.Venkataraman@Sun.COM cp); 7128048SMadhavan.Venkataraman@Sun.COM 7138048SMadhavan.Venkataraman@Sun.COM return (id); 7148048SMadhavan.Venkataraman@Sun.COM } 7158048SMadhavan.Venkataraman@Sun.COM 7168048SMadhavan.Venkataraman@Sun.COM timeout_id_t 7178048SMadhavan.Venkataraman@Sun.COM timeout(void (*func)(void *), void *arg, clock_t delta) 7188048SMadhavan.Venkataraman@Sun.COM { 7198048SMadhavan.Venkataraman@Sun.COM ulong_t id; 7208048SMadhavan.Venkataraman@Sun.COM 7218048SMadhavan.Venkataraman@Sun.COM /* 7220Sstevel@tonic-gate * Make sure the callout runs at least 1 tick in the future. 7230Sstevel@tonic-gate */ 7240Sstevel@tonic-gate if (delta <= 0) 7250Sstevel@tonic-gate delta = 1; 726*8566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 727*8566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 7280Sstevel@tonic-gate 7298048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)timeout_generic(CALLOUT_NORMAL, func, arg, 7308048SMadhavan.Venkataraman@Sun.COM TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY); 7310Sstevel@tonic-gate 7320Sstevel@tonic-gate return ((timeout_id_t)id); 7330Sstevel@tonic-gate } 7340Sstevel@tonic-gate 7358048SMadhavan.Venkataraman@Sun.COM /* 7368048SMadhavan.Venkataraman@Sun.COM * Convenience function that creates a normal callout with default parameters 7378048SMadhavan.Venkataraman@Sun.COM * and returns a full ID. 7388048SMadhavan.Venkataraman@Sun.COM */ 7398048SMadhavan.Venkataraman@Sun.COM callout_id_t 7408048SMadhavan.Venkataraman@Sun.COM timeout_default(void (*func)(void *), void *arg, clock_t delta) 7410Sstevel@tonic-gate { 7428048SMadhavan.Venkataraman@Sun.COM callout_id_t id; 7430Sstevel@tonic-gate 7448048SMadhavan.Venkataraman@Sun.COM /* 7458048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future. 7468048SMadhavan.Venkataraman@Sun.COM */ 7478048SMadhavan.Venkataraman@Sun.COM if (delta <= 0) 7488048SMadhavan.Venkataraman@Sun.COM delta = 1; 749*8566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 750*8566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 7518048SMadhavan.Venkataraman@Sun.COM 7528048SMadhavan.Venkataraman@Sun.COM id = timeout_generic(CALLOUT_NORMAL, func, arg, TICK_TO_NSEC(delta), 7538048SMadhavan.Venkataraman@Sun.COM nsec_per_tick, 0); 7548048SMadhavan.Venkataraman@Sun.COM 7558048SMadhavan.Venkataraman@Sun.COM return (id); 7560Sstevel@tonic-gate } 7570Sstevel@tonic-gate 7580Sstevel@tonic-gate timeout_id_t 7590Sstevel@tonic-gate realtime_timeout(void (*func)(void *), void *arg, clock_t delta) 7600Sstevel@tonic-gate { 7618048SMadhavan.Venkataraman@Sun.COM ulong_t id; 7628048SMadhavan.Venkataraman@Sun.COM 7638048SMadhavan.Venkataraman@Sun.COM /* 7648048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future. 7658048SMadhavan.Venkataraman@Sun.COM */ 7668048SMadhavan.Venkataraman@Sun.COM if (delta <= 0) 7678048SMadhavan.Venkataraman@Sun.COM delta = 1; 768*8566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 769*8566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 7708048SMadhavan.Venkataraman@Sun.COM 7718048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)timeout_generic(CALLOUT_REALTIME, func, arg, 7728048SMadhavan.Venkataraman@Sun.COM TICK_TO_NSEC(delta), nsec_per_tick, CALLOUT_LEGACY); 7738048SMadhavan.Venkataraman@Sun.COM 7748048SMadhavan.Venkataraman@Sun.COM return ((timeout_id_t)id); 7750Sstevel@tonic-gate } 7760Sstevel@tonic-gate 7778048SMadhavan.Venkataraman@Sun.COM /* 7788048SMadhavan.Venkataraman@Sun.COM * Convenience function that creates a realtime callout with default parameters 7798048SMadhavan.Venkataraman@Sun.COM * and returns a full ID. 7808048SMadhavan.Venkataraman@Sun.COM */ 7818048SMadhavan.Venkataraman@Sun.COM callout_id_t 7828048SMadhavan.Venkataraman@Sun.COM realtime_timeout_default(void (*func)(void *), void *arg, clock_t delta) 7830Sstevel@tonic-gate { 7848048SMadhavan.Venkataraman@Sun.COM callout_id_t id; 7858048SMadhavan.Venkataraman@Sun.COM 7868048SMadhavan.Venkataraman@Sun.COM /* 7878048SMadhavan.Venkataraman@Sun.COM * Make sure the callout runs at least 1 tick in the future. 7888048SMadhavan.Venkataraman@Sun.COM */ 7898048SMadhavan.Venkataraman@Sun.COM if (delta <= 0) 7908048SMadhavan.Venkataraman@Sun.COM delta = 1; 791*8566SMadhavan.Venkataraman@Sun.COM else if (delta > callout_max_ticks) 792*8566SMadhavan.Venkataraman@Sun.COM delta = callout_max_ticks; 7938048SMadhavan.Venkataraman@Sun.COM 7948048SMadhavan.Venkataraman@Sun.COM id = timeout_generic(CALLOUT_REALTIME, func, arg, TICK_TO_NSEC(delta), 7958048SMadhavan.Venkataraman@Sun.COM nsec_per_tick, 0); 7968048SMadhavan.Venkataraman@Sun.COM 7978048SMadhavan.Venkataraman@Sun.COM return (id); 7988048SMadhavan.Venkataraman@Sun.COM } 7998048SMadhavan.Venkataraman@Sun.COM 8008048SMadhavan.Venkataraman@Sun.COM hrtime_t 8018048SMadhavan.Venkataraman@Sun.COM untimeout_generic(callout_id_t id, int nowait) 8028048SMadhavan.Venkataraman@Sun.COM { 8030Sstevel@tonic-gate callout_table_t *ct; 8040Sstevel@tonic-gate callout_t *cp; 8050Sstevel@tonic-gate callout_id_t xid; 8068048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 8078048SMadhavan.Venkataraman@Sun.COM int hash; 8088048SMadhavan.Venkataraman@Sun.COM callout_id_t bogus; 8090Sstevel@tonic-gate 8108048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_ID_TO_TABLE(id)]; 8118048SMadhavan.Venkataraman@Sun.COM hash = CALLOUT_IDHASH(id); 8128048SMadhavan.Venkataraman@Sun.COM 8138048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 8140Sstevel@tonic-gate 8158048SMadhavan.Venkataraman@Sun.COM /* 8168048SMadhavan.Venkataraman@Sun.COM * Search the ID hash table for the callout. 8178048SMadhavan.Venkataraman@Sun.COM */ 8188048SMadhavan.Venkataraman@Sun.COM for (cp = ct->ct_idhash[hash].ch_head; cp; cp = cp->c_idnext) { 8190Sstevel@tonic-gate 8208048SMadhavan.Venkataraman@Sun.COM xid = cp->c_xid; 8218048SMadhavan.Venkataraman@Sun.COM 8228048SMadhavan.Venkataraman@Sun.COM /* 8238048SMadhavan.Venkataraman@Sun.COM * Match the ID and generation number. 8248048SMadhavan.Venkataraman@Sun.COM */ 8258048SMadhavan.Venkataraman@Sun.COM if ((xid & CALLOUT_ID_MASK) != id) 8268048SMadhavan.Venkataraman@Sun.COM continue; 8270Sstevel@tonic-gate 8288048SMadhavan.Venkataraman@Sun.COM cl = cp->c_list; 8298048SMadhavan.Venkataraman@Sun.COM if ((xid & CALLOUT_EXECUTING) == 0) { 8308048SMadhavan.Venkataraman@Sun.COM hrtime_t expiration; 8310Sstevel@tonic-gate 8328048SMadhavan.Venkataraman@Sun.COM /* 8338048SMadhavan.Venkataraman@Sun.COM * Delete the callout. If the callout list becomes 8348048SMadhavan.Venkataraman@Sun.COM * NULL, we don't remove it from the table. This is 8358048SMadhavan.Venkataraman@Sun.COM * so it can be reused. If the empty callout list 8368048SMadhavan.Venkataraman@Sun.COM * corresponds to the top of the the callout heap, we 8378048SMadhavan.Venkataraman@Sun.COM * don't reprogram the table cyclic here. This is in 8388048SMadhavan.Venkataraman@Sun.COM * order to avoid lots of X-calls to the CPU associated 8398048SMadhavan.Venkataraman@Sun.COM * with the callout table. 8408048SMadhavan.Venkataraman@Sun.COM */ 8418048SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration; 8428048SMadhavan.Venkataraman@Sun.COM CALLOUT_DELETE(ct, cp); 8438048SMadhavan.Venkataraman@Sun.COM cp->c_idnext = ct->ct_free; 8448048SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp; 8458048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_unexpired++; 8468048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending--; 8478048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 8486422Sqiao 8498048SMadhavan.Venkataraman@Sun.COM expiration -= gethrtime(); 8500Sstevel@tonic-gate TRACE_2(TR_FAC_CALLOUT, TR_UNTIMEOUT, 8518048SMadhavan.Venkataraman@Sun.COM "untimeout:ID %lx hrtime left %llx", id, 8528048SMadhavan.Venkataraman@Sun.COM expiration); 8538048SMadhavan.Venkataraman@Sun.COM return (expiration < 0 ? 0 : expiration); 8540Sstevel@tonic-gate } 8550Sstevel@tonic-gate 8568048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_executing++; 8570Sstevel@tonic-gate /* 8580Sstevel@tonic-gate * The callout we want to delete is currently executing. 8590Sstevel@tonic-gate * The DDI states that we must wait until the callout 8608048SMadhavan.Venkataraman@Sun.COM * completes before returning, so we block on cl_done until the 8618048SMadhavan.Venkataraman@Sun.COM * callout ID changes (to the old ID if it's on the freelist, 8620Sstevel@tonic-gate * or to a new callout ID if it's in use). This implicitly 8630Sstevel@tonic-gate * assumes that callout structures are persistent (they are). 8640Sstevel@tonic-gate */ 8658048SMadhavan.Venkataraman@Sun.COM if (cl->cl_executor == curthread) { 8660Sstevel@tonic-gate /* 8670Sstevel@tonic-gate * The timeout handler called untimeout() on itself. 8680Sstevel@tonic-gate * Stupid, but legal. We can't wait for the timeout 8690Sstevel@tonic-gate * to complete without deadlocking, so we just return. 8700Sstevel@tonic-gate */ 8718048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 8720Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_SELF, 8730Sstevel@tonic-gate "untimeout_self:ID %x", id); 8740Sstevel@tonic-gate return (-1); 8750Sstevel@tonic-gate } 8768048SMadhavan.Venkataraman@Sun.COM if (nowait == 0) { 8778048SMadhavan.Venkataraman@Sun.COM /* 8788048SMadhavan.Venkataraman@Sun.COM * We need to wait. Indicate that we are waiting by 8798048SMadhavan.Venkataraman@Sun.COM * incrementing cl_waiting. This prevents the executor 8808048SMadhavan.Venkataraman@Sun.COM * from doing a wakeup on cl_done if there are no 8818048SMadhavan.Venkataraman@Sun.COM * waiters. 8828048SMadhavan.Venkataraman@Sun.COM */ 8838048SMadhavan.Venkataraman@Sun.COM while (cp->c_xid == xid) { 8848048SMadhavan.Venkataraman@Sun.COM cl->cl_waiting = 1; 8858048SMadhavan.Venkataraman@Sun.COM cv_wait(&cl->cl_done, &ct->ct_mutex); 8868048SMadhavan.Venkataraman@Sun.COM } 8878048SMadhavan.Venkataraman@Sun.COM } 8888048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 8890Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_EXECUTING, 8900Sstevel@tonic-gate "untimeout_executing:ID %lx", id); 8910Sstevel@tonic-gate return (-1); 8920Sstevel@tonic-gate } 8938048SMadhavan.Venkataraman@Sun.COM ct->ct_untimeouts_expired++; 8940Sstevel@tonic-gate 8958048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 8960Sstevel@tonic-gate TRACE_1(TR_FAC_CALLOUT, TR_UNTIMEOUT_BOGUS_ID, 8970Sstevel@tonic-gate "untimeout_bogus_id:ID %lx", id); 8980Sstevel@tonic-gate 8990Sstevel@tonic-gate /* 9000Sstevel@tonic-gate * We didn't find the specified callout ID. This means either 9010Sstevel@tonic-gate * (1) the callout already fired, or (2) the caller passed us 9020Sstevel@tonic-gate * a bogus value. Perform a sanity check to detect case (2). 9030Sstevel@tonic-gate */ 9048048SMadhavan.Venkataraman@Sun.COM bogus = (CALLOUT_EXECUTING | CALLOUT_HRESTIME | CALLOUT_COUNTER_HIGH); 9058048SMadhavan.Venkataraman@Sun.COM if (((id & bogus) != CALLOUT_COUNTER_HIGH) && (id != 0)) 9068048SMadhavan.Venkataraman@Sun.COM panic("untimeout: impossible timeout id %llx", 9078048SMadhavan.Venkataraman@Sun.COM (unsigned long long)id); 9080Sstevel@tonic-gate 9090Sstevel@tonic-gate return (-1); 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate 9128048SMadhavan.Venkataraman@Sun.COM clock_t 9138048SMadhavan.Venkataraman@Sun.COM untimeout(timeout_id_t id_arg) 9148048SMadhavan.Venkataraman@Sun.COM { 9158048SMadhavan.Venkataraman@Sun.COM hrtime_t hleft; 9168048SMadhavan.Venkataraman@Sun.COM clock_t tleft; 9178048SMadhavan.Venkataraman@Sun.COM callout_id_t id; 9188048SMadhavan.Venkataraman@Sun.COM 9198048SMadhavan.Venkataraman@Sun.COM id = (ulong_t)id_arg; 9208048SMadhavan.Venkataraman@Sun.COM hleft = untimeout_generic(id, 0); 9218048SMadhavan.Venkataraman@Sun.COM if (hleft < 0) 9228048SMadhavan.Venkataraman@Sun.COM tleft = -1; 9238048SMadhavan.Venkataraman@Sun.COM else if (hleft == 0) 9248048SMadhavan.Venkataraman@Sun.COM tleft = 0; 9258048SMadhavan.Venkataraman@Sun.COM else 9268048SMadhavan.Venkataraman@Sun.COM tleft = NSEC_TO_TICK(hleft); 9278048SMadhavan.Venkataraman@Sun.COM 9288048SMadhavan.Venkataraman@Sun.COM return (tleft); 9298048SMadhavan.Venkataraman@Sun.COM } 9308048SMadhavan.Venkataraman@Sun.COM 9310Sstevel@tonic-gate /* 9328048SMadhavan.Venkataraman@Sun.COM * Convenience function to untimeout a timeout with a full ID with default 9338048SMadhavan.Venkataraman@Sun.COM * parameters. 9348048SMadhavan.Venkataraman@Sun.COM */ 9358048SMadhavan.Venkataraman@Sun.COM clock_t 9368048SMadhavan.Venkataraman@Sun.COM untimeout_default(callout_id_t id, int nowait) 9378048SMadhavan.Venkataraman@Sun.COM { 9388048SMadhavan.Venkataraman@Sun.COM hrtime_t hleft; 9398048SMadhavan.Venkataraman@Sun.COM clock_t tleft; 9408048SMadhavan.Venkataraman@Sun.COM 9418048SMadhavan.Venkataraman@Sun.COM hleft = untimeout_generic(id, nowait); 9428048SMadhavan.Venkataraman@Sun.COM if (hleft < 0) 9438048SMadhavan.Venkataraman@Sun.COM tleft = -1; 9448048SMadhavan.Venkataraman@Sun.COM else if (hleft == 0) 9458048SMadhavan.Venkataraman@Sun.COM tleft = 0; 9468048SMadhavan.Venkataraman@Sun.COM else 9478048SMadhavan.Venkataraman@Sun.COM tleft = NSEC_TO_TICK(hleft); 9488048SMadhavan.Venkataraman@Sun.COM 9498048SMadhavan.Venkataraman@Sun.COM return (tleft); 9508048SMadhavan.Venkataraman@Sun.COM } 9518048SMadhavan.Venkataraman@Sun.COM 9528048SMadhavan.Venkataraman@Sun.COM /* 9538048SMadhavan.Venkataraman@Sun.COM * Expire all the callouts queued in the specified callout list. 9540Sstevel@tonic-gate */ 9550Sstevel@tonic-gate static void 9568048SMadhavan.Venkataraman@Sun.COM callout_list_expire(callout_table_t *ct, callout_list_t *cl) 9570Sstevel@tonic-gate { 9584123Sdm120769 callout_t *cp; 9598048SMadhavan.Venkataraman@Sun.COM 9608048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 9618048SMadhavan.Venkataraman@Sun.COM ASSERT(cl != NULL); 9628048SMadhavan.Venkataraman@Sun.COM 9638048SMadhavan.Venkataraman@Sun.COM cl->cl_executor = curthread; 9648048SMadhavan.Venkataraman@Sun.COM 9658048SMadhavan.Venkataraman@Sun.COM while ((cp = cl->cl_callouts.ch_head) != NULL) { 9668048SMadhavan.Venkataraman@Sun.COM /* 9678048SMadhavan.Venkataraman@Sun.COM * Indicate to untimeout() that a callout is 9688048SMadhavan.Venkataraman@Sun.COM * being expired by the executor. 9698048SMadhavan.Venkataraman@Sun.COM */ 9708048SMadhavan.Venkataraman@Sun.COM cp->c_xid |= CALLOUT_EXECUTING; 9718048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 9728048SMadhavan.Venkataraman@Sun.COM 9738048SMadhavan.Venkataraman@Sun.COM DTRACE_PROBE1(callout__start, callout_t *, cp); 9748048SMadhavan.Venkataraman@Sun.COM (*cp->c_func)(cp->c_arg); 9758048SMadhavan.Venkataraman@Sun.COM DTRACE_PROBE1(callout__end, callout_t *, cp); 9760Sstevel@tonic-gate 9778048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 9788048SMadhavan.Venkataraman@Sun.COM 9798048SMadhavan.Venkataraman@Sun.COM ct->ct_expirations++; 9808048SMadhavan.Venkataraman@Sun.COM ct->ct_timeouts_pending--; 9818048SMadhavan.Venkataraman@Sun.COM /* 9828048SMadhavan.Venkataraman@Sun.COM * Indicate completion for cl_done. 9838048SMadhavan.Venkataraman@Sun.COM */ 9848048SMadhavan.Venkataraman@Sun.COM cp->c_xid &= ~CALLOUT_EXECUTING; 9858048SMadhavan.Venkataraman@Sun.COM 9868048SMadhavan.Venkataraman@Sun.COM /* 9878048SMadhavan.Venkataraman@Sun.COM * Delete callout from ID hash table and the callout 9888048SMadhavan.Venkataraman@Sun.COM * list, return to freelist, and tell any untimeout() that 9898048SMadhavan.Venkataraman@Sun.COM * cares that we're done. 9908048SMadhavan.Venkataraman@Sun.COM */ 9918048SMadhavan.Venkataraman@Sun.COM CALLOUT_DELETE(ct, cp); 9928048SMadhavan.Venkataraman@Sun.COM cp->c_idnext = ct->ct_free; 9938048SMadhavan.Venkataraman@Sun.COM ct->ct_free = cp; 9940Sstevel@tonic-gate 9958048SMadhavan.Venkataraman@Sun.COM if (cl->cl_waiting) { 9968048SMadhavan.Venkataraman@Sun.COM cl->cl_waiting = 0; 9978048SMadhavan.Venkataraman@Sun.COM cv_broadcast(&cl->cl_done); 9988048SMadhavan.Venkataraman@Sun.COM } 9998048SMadhavan.Venkataraman@Sun.COM } 10008048SMadhavan.Venkataraman@Sun.COM 10018048SMadhavan.Venkataraman@Sun.COM cl->cl_executor = NULL; 10028048SMadhavan.Venkataraman@Sun.COM } 10038048SMadhavan.Venkataraman@Sun.COM 10048048SMadhavan.Venkataraman@Sun.COM /* 10058048SMadhavan.Venkataraman@Sun.COM * Execute all expired callout lists for a callout table. 10068048SMadhavan.Venkataraman@Sun.COM */ 10078048SMadhavan.Venkataraman@Sun.COM static void 10088048SMadhavan.Venkataraman@Sun.COM callout_expire(callout_table_t *ct) 10098048SMadhavan.Venkataraman@Sun.COM { 10108048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl, *clnext; 10118048SMadhavan.Venkataraman@Sun.COM 10128048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 10130Sstevel@tonic-gate 10148048SMadhavan.Venkataraman@Sun.COM for (cl = ct->ct_expired.ch_head; (cl != NULL); cl = clnext) { 10158048SMadhavan.Venkataraman@Sun.COM /* 10168048SMadhavan.Venkataraman@Sun.COM * Multiple executor threads could be running at the same 10178048SMadhavan.Venkataraman@Sun.COM * time. Each callout list is processed by only one thread. 10188048SMadhavan.Venkataraman@Sun.COM * If this callout list is already being processed by another 10198048SMadhavan.Venkataraman@Sun.COM * executor, go on to the next one. 10208048SMadhavan.Venkataraman@Sun.COM */ 10218048SMadhavan.Venkataraman@Sun.COM if (cl->cl_executor != NULL) { 10228048SMadhavan.Venkataraman@Sun.COM clnext = cl->cl_next; 10238048SMadhavan.Venkataraman@Sun.COM continue; 10248048SMadhavan.Venkataraman@Sun.COM } 10256422Sqiao 10268048SMadhavan.Venkataraman@Sun.COM /* 10278048SMadhavan.Venkataraman@Sun.COM * Expire all the callouts in this callout list. 10288048SMadhavan.Venkataraman@Sun.COM */ 10298048SMadhavan.Venkataraman@Sun.COM callout_list_expire(ct, cl); 10308048SMadhavan.Venkataraman@Sun.COM 10310Sstevel@tonic-gate /* 10328048SMadhavan.Venkataraman@Sun.COM * Free the callout list. 10330Sstevel@tonic-gate */ 10348048SMadhavan.Venkataraman@Sun.COM clnext = cl->cl_next; 10358048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_expired, cl); 10368048SMadhavan.Venkataraman@Sun.COM cl->cl_next = ct->ct_lfree; 10378048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = cl; 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate } 10400Sstevel@tonic-gate 10410Sstevel@tonic-gate /* 10428048SMadhavan.Venkataraman@Sun.COM * The cyclic handlers below process callouts in two steps: 10438048SMadhavan.Venkataraman@Sun.COM * 10448048SMadhavan.Venkataraman@Sun.COM * 1. Find all expired callout lists and queue them in a separate 10458048SMadhavan.Venkataraman@Sun.COM * list of expired callouts. 10468048SMadhavan.Venkataraman@Sun.COM * 2. Execute the expired callout lists. 10478048SMadhavan.Venkataraman@Sun.COM * 10488048SMadhavan.Venkataraman@Sun.COM * This is done for two reasons: 10498048SMadhavan.Venkataraman@Sun.COM * 10508048SMadhavan.Venkataraman@Sun.COM * 1. We want to quickly find the next earliest expiration to program 10518048SMadhavan.Venkataraman@Sun.COM * the cyclic to and reprogram it. We can do this right at the end 10528048SMadhavan.Venkataraman@Sun.COM * of step 1. 10538048SMadhavan.Venkataraman@Sun.COM * 2. The realtime cyclic handler expires callouts in place. However, 10548048SMadhavan.Venkataraman@Sun.COM * for normal callouts, callouts are expired by a taskq thread. 10558048SMadhavan.Venkataraman@Sun.COM * So, it is simpler and more robust to have the taskq thread just 10568048SMadhavan.Venkataraman@Sun.COM * do step 2. 10570Sstevel@tonic-gate */ 10586422Sqiao 10598048SMadhavan.Venkataraman@Sun.COM /* 10608048SMadhavan.Venkataraman@Sun.COM * Realtime callout cyclic handler. 10618048SMadhavan.Venkataraman@Sun.COM */ 10628048SMadhavan.Venkataraman@Sun.COM void 10638048SMadhavan.Venkataraman@Sun.COM callout_realtime(callout_table_t *ct) 10648048SMadhavan.Venkataraman@Sun.COM { 10658048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 10668048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(ct); 10678048SMadhavan.Venkataraman@Sun.COM callout_expire(ct); 10688048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 10698048SMadhavan.Venkataraman@Sun.COM } 10708048SMadhavan.Venkataraman@Sun.COM 10718048SMadhavan.Venkataraman@Sun.COM void 10728048SMadhavan.Venkataraman@Sun.COM callout_execute(callout_table_t *ct) 10738048SMadhavan.Venkataraman@Sun.COM { 10748048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 10758048SMadhavan.Venkataraman@Sun.COM callout_expire(ct); 10768048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 10778048SMadhavan.Venkataraman@Sun.COM } 10788048SMadhavan.Venkataraman@Sun.COM 10798048SMadhavan.Venkataraman@Sun.COM /* 10808048SMadhavan.Venkataraman@Sun.COM * Normal callout cyclic handler. 10818048SMadhavan.Venkataraman@Sun.COM */ 10828048SMadhavan.Venkataraman@Sun.COM void 10838048SMadhavan.Venkataraman@Sun.COM callout_normal(callout_table_t *ct) 10848048SMadhavan.Venkataraman@Sun.COM { 10858048SMadhavan.Venkataraman@Sun.COM int exec; 10868048SMadhavan.Venkataraman@Sun.COM 10878048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 10888048SMadhavan.Venkataraman@Sun.COM callout_heap_delete(ct); 10898048SMadhavan.Venkataraman@Sun.COM exec = (ct->ct_expired.ch_head != NULL); 10908048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 10918048SMadhavan.Venkataraman@Sun.COM 10928048SMadhavan.Venkataraman@Sun.COM if (exec) { 10938048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_taskq != NULL); 10948048SMadhavan.Venkataraman@Sun.COM (void) taskq_dispatch(ct->ct_taskq, 10958048SMadhavan.Venkataraman@Sun.COM (task_func_t *)callout_execute, ct, TQ_NOSLEEP); 10960Sstevel@tonic-gate } 10970Sstevel@tonic-gate } 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate /* 11008048SMadhavan.Venkataraman@Sun.COM * Suspend callout processing. 11010Sstevel@tonic-gate */ 11028048SMadhavan.Venkataraman@Sun.COM static void 11038048SMadhavan.Venkataraman@Sun.COM callout_suspend(void) 11040Sstevel@tonic-gate { 11058048SMadhavan.Venkataraman@Sun.COM int t, f; 11068048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 11078048SMadhavan.Venkataraman@Sun.COM 11088048SMadhavan.Venkataraman@Sun.COM /* 11098048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and suspend callout 11108048SMadhavan.Venkataraman@Sun.COM * processing. 11118048SMadhavan.Venkataraman@Sun.COM * 11128048SMadhavan.Venkataraman@Sun.COM * We need to suspend all the tables (including the inactive ones) 11138048SMadhavan.Venkataraman@Sun.COM * so that if a table is made active while the suspend is still on, 11148048SMadhavan.Venkataraman@Sun.COM * the table remains suspended. 11158048SMadhavan.Venkataraman@Sun.COM */ 11168048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 11178048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 11188048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)]; 11198048SMadhavan.Venkataraman@Sun.COM 11208048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 1121*8566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend++; 11228048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) { 11238048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 11248048SMadhavan.Venkataraman@Sun.COM continue; 11258048SMadhavan.Venkataraman@Sun.COM } 1126*8566SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 1) 1127*8566SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, 1128*8566SMadhavan.Venkataraman@Sun.COM CY_INFINITY); 11298048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 11308048SMadhavan.Venkataraman@Sun.COM } 11318048SMadhavan.Venkataraman@Sun.COM } 11328048SMadhavan.Venkataraman@Sun.COM } 11338048SMadhavan.Venkataraman@Sun.COM 11348048SMadhavan.Venkataraman@Sun.COM static void 11358048SMadhavan.Venkataraman@Sun.COM callout_adjust(callout_table_t *ct, hrtime_t delta) 11368048SMadhavan.Venkataraman@Sun.COM { 11378048SMadhavan.Venkataraman@Sun.COM int hash, newhash; 11388048SMadhavan.Venkataraman@Sun.COM hrtime_t expiration; 11398048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl; 11408048SMadhavan.Venkataraman@Sun.COM callout_hash_t list; 11418048SMadhavan.Venkataraman@Sun.COM 11428048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 11438048SMadhavan.Venkataraman@Sun.COM 11448048SMadhavan.Venkataraman@Sun.COM /* 11458048SMadhavan.Venkataraman@Sun.COM * In order to adjust the expirations, we null out the heap. Then, 11468048SMadhavan.Venkataraman@Sun.COM * we reinsert adjusted expirations in the heap. Keeps it simple. 11478048SMadhavan.Venkataraman@Sun.COM * Note that since the CALLOUT_TABLE_SUSPENDED flag is set by the 11488048SMadhavan.Venkataraman@Sun.COM * caller, the heap insert does not result in cyclic reprogramming. 11498048SMadhavan.Venkataraman@Sun.COM */ 11508048SMadhavan.Venkataraman@Sun.COM ct->ct_heap_num = 0; 11518048SMadhavan.Venkataraman@Sun.COM 11528048SMadhavan.Venkataraman@Sun.COM /* 11538048SMadhavan.Venkataraman@Sun.COM * First, remove all the callout lists from the table and string them 11548048SMadhavan.Venkataraman@Sun.COM * in a list. 11558048SMadhavan.Venkataraman@Sun.COM */ 11568048SMadhavan.Venkataraman@Sun.COM list.ch_head = list.ch_tail = NULL; 11578048SMadhavan.Venkataraman@Sun.COM for (hash = 0; hash < CALLOUT_BUCKETS; hash++) { 11588048SMadhavan.Venkataraman@Sun.COM while ((cl = ct->ct_clhash[hash].ch_head) != NULL) { 11598048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(ct->ct_clhash[hash], cl); 11608048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(list, cl); 11618048SMadhavan.Venkataraman@Sun.COM } 11628048SMadhavan.Venkataraman@Sun.COM } 11630Sstevel@tonic-gate 11648048SMadhavan.Venkataraman@Sun.COM /* 11658048SMadhavan.Venkataraman@Sun.COM * Now, traverse the callout lists and adjust their expirations. 11668048SMadhavan.Venkataraman@Sun.COM */ 11678048SMadhavan.Venkataraman@Sun.COM while ((cl = list.ch_head) != NULL) { 11688048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_DELETE(list, cl); 11698048SMadhavan.Venkataraman@Sun.COM /* 11708048SMadhavan.Venkataraman@Sun.COM * Set the new expiration and reinsert in the right 11718048SMadhavan.Venkataraman@Sun.COM * hash bucket. 11728048SMadhavan.Venkataraman@Sun.COM */ 11738048SMadhavan.Venkataraman@Sun.COM expiration = cl->cl_expiration; 11748048SMadhavan.Venkataraman@Sun.COM expiration += delta; 11758048SMadhavan.Venkataraman@Sun.COM cl->cl_expiration = expiration; 11768048SMadhavan.Venkataraman@Sun.COM newhash = CALLOUT_CLHASH(expiration); 11778048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_INSERT(ct->ct_clhash[newhash], cl); 11788048SMadhavan.Venkataraman@Sun.COM callout_heap_insert(ct, expiration); 11798048SMadhavan.Venkataraman@Sun.COM } 11808048SMadhavan.Venkataraman@Sun.COM } 11818048SMadhavan.Venkataraman@Sun.COM 11828048SMadhavan.Venkataraman@Sun.COM /* 11838048SMadhavan.Venkataraman@Sun.COM * Resume callout processing. 11848048SMadhavan.Venkataraman@Sun.COM */ 11858048SMadhavan.Venkataraman@Sun.COM static void 11868048SMadhavan.Venkataraman@Sun.COM callout_resume(hrtime_t delta) 11878048SMadhavan.Venkataraman@Sun.COM { 11888048SMadhavan.Venkataraman@Sun.COM hrtime_t exp; 11898048SMadhavan.Venkataraman@Sun.COM int t, f; 11908048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 11910Sstevel@tonic-gate 11928048SMadhavan.Venkataraman@Sun.COM /* 11938048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and resume callout 11948048SMadhavan.Venkataraman@Sun.COM * processing. For active tables, perform any hrtime adjustments 11958048SMadhavan.Venkataraman@Sun.COM * necessary. 11968048SMadhavan.Venkataraman@Sun.COM */ 11978048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 11988048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 11998048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)]; 12008048SMadhavan.Venkataraman@Sun.COM 12018048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 12028048SMadhavan.Venkataraman@Sun.COM if (ct->ct_cyclic == CYCLIC_NONE) { 1203*8566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend--; 12048048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 12058048SMadhavan.Venkataraman@Sun.COM continue; 12068048SMadhavan.Venkataraman@Sun.COM } 12078048SMadhavan.Venkataraman@Sun.COM 12088048SMadhavan.Venkataraman@Sun.COM if (delta) 12098048SMadhavan.Venkataraman@Sun.COM callout_adjust(ct, delta); 12108048SMadhavan.Venkataraman@Sun.COM 1211*8566SMadhavan.Venkataraman@Sun.COM ct->ct_suspend--; 1212*8566SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 0) { 1213*8566SMadhavan.Venkataraman@Sun.COM /* 1214*8566SMadhavan.Venkataraman@Sun.COM * If the expired list is non-empty, then have 1215*8566SMadhavan.Venkataraman@Sun.COM * the cyclic expire immediately. Else, program 1216*8566SMadhavan.Venkataraman@Sun.COM * the cyclic based on the heap. 1217*8566SMadhavan.Venkataraman@Sun.COM */ 1218*8566SMadhavan.Venkataraman@Sun.COM if (ct->ct_expired.ch_head != NULL) 1219*8566SMadhavan.Venkataraman@Sun.COM exp = gethrtime(); 1220*8566SMadhavan.Venkataraman@Sun.COM else if (ct->ct_heap_num > 0) 1221*8566SMadhavan.Venkataraman@Sun.COM exp = ct->ct_heap[0]; 1222*8566SMadhavan.Venkataraman@Sun.COM else 1223*8566SMadhavan.Venkataraman@Sun.COM exp = 0; 1224*8566SMadhavan.Venkataraman@Sun.COM if (exp != 0) 1225*8566SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, 1226*8566SMadhavan.Venkataraman@Sun.COM exp); 1227*8566SMadhavan.Venkataraman@Sun.COM } 12288048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 12298048SMadhavan.Venkataraman@Sun.COM } 12308048SMadhavan.Venkataraman@Sun.COM } 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate 12330Sstevel@tonic-gate /* 12340Sstevel@tonic-gate * Callback handler used by CPR to stop and resume callouts. 12350Sstevel@tonic-gate */ 12360Sstevel@tonic-gate /*ARGSUSED*/ 12370Sstevel@tonic-gate static boolean_t 12380Sstevel@tonic-gate callout_cpr_callb(void *arg, int code) 12390Sstevel@tonic-gate { 12408048SMadhavan.Venkataraman@Sun.COM if (code == CB_CODE_CPR_CHKPT) 12418048SMadhavan.Venkataraman@Sun.COM callout_suspend(); 12428048SMadhavan.Venkataraman@Sun.COM else 12438048SMadhavan.Venkataraman@Sun.COM callout_resume(0); 12448048SMadhavan.Venkataraman@Sun.COM 12458048SMadhavan.Venkataraman@Sun.COM return (B_TRUE); 12468048SMadhavan.Venkataraman@Sun.COM } 12478048SMadhavan.Venkataraman@Sun.COM 12488048SMadhavan.Venkataraman@Sun.COM /* 12498048SMadhavan.Venkataraman@Sun.COM * Callback handler invoked when the debugger is entered or exited. 12508048SMadhavan.Venkataraman@Sun.COM */ 12518048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/ 12528048SMadhavan.Venkataraman@Sun.COM static boolean_t 12538048SMadhavan.Venkataraman@Sun.COM callout_debug_callb(void *arg, int code) 12548048SMadhavan.Venkataraman@Sun.COM { 12558048SMadhavan.Venkataraman@Sun.COM hrtime_t delta; 12568048SMadhavan.Venkataraman@Sun.COM 12578048SMadhavan.Venkataraman@Sun.COM /* 12588048SMadhavan.Venkataraman@Sun.COM * When the system enters the debugger. make a note of the hrtime. 12598048SMadhavan.Venkataraman@Sun.COM * When it is resumed, compute how long the system was in the 12608048SMadhavan.Venkataraman@Sun.COM * debugger. This interval should not be counted for callouts. 12618048SMadhavan.Venkataraman@Sun.COM */ 12628048SMadhavan.Venkataraman@Sun.COM if (code == 0) { 12638048SMadhavan.Venkataraman@Sun.COM callout_suspend(); 12648048SMadhavan.Venkataraman@Sun.COM callout_debug_hrtime = gethrtime(); 12658048SMadhavan.Venkataraman@Sun.COM } else { 12668048SMadhavan.Venkataraman@Sun.COM delta = gethrtime() - callout_debug_hrtime; 12678048SMadhavan.Venkataraman@Sun.COM callout_resume(delta); 12688048SMadhavan.Venkataraman@Sun.COM } 12698048SMadhavan.Venkataraman@Sun.COM 12700Sstevel@tonic-gate return (B_TRUE); 12710Sstevel@tonic-gate } 12720Sstevel@tonic-gate 12730Sstevel@tonic-gate /* 12748048SMadhavan.Venkataraman@Sun.COM * Move the hrestime callouts to the expired list. Then program the table's 12758048SMadhavan.Venkataraman@Sun.COM * cyclic to expire immediately so that the callouts can be executed 12768048SMadhavan.Venkataraman@Sun.COM * immediately. 12778048SMadhavan.Venkataraman@Sun.COM */ 12788048SMadhavan.Venkataraman@Sun.COM static void 12798048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(callout_table_t *ct) 12808048SMadhavan.Venkataraman@Sun.COM { 12818048SMadhavan.Venkataraman@Sun.COM callout_list_t *cl, *ecl; 12828048SMadhavan.Venkataraman@Sun.COM callout_t *cp; 12838048SMadhavan.Venkataraman@Sun.COM int hash; 12848048SMadhavan.Venkataraman@Sun.COM 12858048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 12868048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap_num == 0) { 12878048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 12888048SMadhavan.Venkataraman@Sun.COM return; 12898048SMadhavan.Venkataraman@Sun.COM } 12908048SMadhavan.Venkataraman@Sun.COM 12918048SMadhavan.Venkataraman@Sun.COM if (ct->ct_lfree == NULL) 12928048SMadhavan.Venkataraman@Sun.COM callout_list_alloc(ct); 12938048SMadhavan.Venkataraman@Sun.COM ecl = ct->ct_lfree; 12948048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = ecl->cl_next; 12958048SMadhavan.Venkataraman@Sun.COM 12968048SMadhavan.Venkataraman@Sun.COM for (hash = 0; hash < CALLOUT_BUCKETS; hash++) { 12978048SMadhavan.Venkataraman@Sun.COM for (cl = ct->ct_clhash[hash].ch_head; cl; cl = cl->cl_next) { 12988048SMadhavan.Venkataraman@Sun.COM for (cp = cl->cl_callouts.ch_head; cp; 12998048SMadhavan.Venkataraman@Sun.COM cp = cp->c_clnext) { 13008048SMadhavan.Venkataraman@Sun.COM if ((cp->c_xid & CALLOUT_HRESTIME) == 0) 13018048SMadhavan.Venkataraman@Sun.COM continue; 13028048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_DELETE(cl->cl_callouts, cp, 13038048SMadhavan.Venkataraman@Sun.COM c_clnext, c_clprev); 13048048SMadhavan.Venkataraman@Sun.COM cp->c_list = ecl; 13058048SMadhavan.Venkataraman@Sun.COM CALLOUT_HASH_APPEND(ecl->cl_callouts, cp, 13068048SMadhavan.Venkataraman@Sun.COM c_clnext, c_clprev); 13078048SMadhavan.Venkataraman@Sun.COM } 13088048SMadhavan.Venkataraman@Sun.COM } 13098048SMadhavan.Venkataraman@Sun.COM } 13108048SMadhavan.Venkataraman@Sun.COM 13118048SMadhavan.Venkataraman@Sun.COM if (ecl->cl_callouts.ch_head != NULL) { 13128048SMadhavan.Venkataraman@Sun.COM CALLOUT_LIST_APPEND(ct->ct_expired, ecl); 1313*8566SMadhavan.Venkataraman@Sun.COM if (ct->ct_suspend == 0) 13148048SMadhavan.Venkataraman@Sun.COM (void) cyclic_reprogram(ct->ct_cyclic, gethrtime()); 13158048SMadhavan.Venkataraman@Sun.COM } else { 13168048SMadhavan.Venkataraman@Sun.COM ecl->cl_next = ct->ct_lfree; 13178048SMadhavan.Venkataraman@Sun.COM ct->ct_lfree = ecl; 13188048SMadhavan.Venkataraman@Sun.COM } 13198048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 13208048SMadhavan.Venkataraman@Sun.COM } 13218048SMadhavan.Venkataraman@Sun.COM 13228048SMadhavan.Venkataraman@Sun.COM /* 13238048SMadhavan.Venkataraman@Sun.COM * This function is called whenever system time (hrestime) is changed 13248048SMadhavan.Venkataraman@Sun.COM * explicitly. All the HRESTIME callouts must be expired at once. 13258048SMadhavan.Venkataraman@Sun.COM */ 13268048SMadhavan.Venkataraman@Sun.COM /*ARGSUSED*/ 13278048SMadhavan.Venkataraman@Sun.COM void 13288048SMadhavan.Venkataraman@Sun.COM callout_hrestime(void) 13298048SMadhavan.Venkataraman@Sun.COM { 13308048SMadhavan.Venkataraman@Sun.COM int t, f; 13318048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 13328048SMadhavan.Venkataraman@Sun.COM 13338048SMadhavan.Venkataraman@Sun.COM /* 13348048SMadhavan.Venkataraman@Sun.COM * Traverse every callout table in the system and process the hrestime 13358048SMadhavan.Venkataraman@Sun.COM * callouts therein. 13368048SMadhavan.Venkataraman@Sun.COM * 13378048SMadhavan.Venkataraman@Sun.COM * We look at all the tables because we don't know which ones were 13388048SMadhavan.Venkataraman@Sun.COM * onlined and offlined in the past. The offlined tables may still 13398048SMadhavan.Venkataraman@Sun.COM * have active cyclics processing timers somewhere. 13408048SMadhavan.Venkataraman@Sun.COM */ 13418048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 13428048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 13438048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, f)]; 13448048SMadhavan.Venkataraman@Sun.COM callout_hrestime_one(ct); 13458048SMadhavan.Venkataraman@Sun.COM } 13468048SMadhavan.Venkataraman@Sun.COM } 13478048SMadhavan.Venkataraman@Sun.COM } 13488048SMadhavan.Venkataraman@Sun.COM 13498048SMadhavan.Venkataraman@Sun.COM /* 13508048SMadhavan.Venkataraman@Sun.COM * Create the hash tables for this callout table. 13518048SMadhavan.Venkataraman@Sun.COM */ 13528048SMadhavan.Venkataraman@Sun.COM static void 13538048SMadhavan.Venkataraman@Sun.COM callout_hash_init(callout_table_t *ct) 13548048SMadhavan.Venkataraman@Sun.COM { 13558048SMadhavan.Venkataraman@Sun.COM size_t size; 13568048SMadhavan.Venkataraman@Sun.COM 13578048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 13588048SMadhavan.Venkataraman@Sun.COM ASSERT((ct->ct_idhash == NULL) && (ct->ct_clhash == NULL)); 13598048SMadhavan.Venkataraman@Sun.COM 13608048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_hash_t) * CALLOUT_BUCKETS; 13618048SMadhavan.Venkataraman@Sun.COM ct->ct_idhash = kmem_zalloc(size, KM_SLEEP); 13628048SMadhavan.Venkataraman@Sun.COM ct->ct_clhash = kmem_zalloc(size, KM_SLEEP); 13638048SMadhavan.Venkataraman@Sun.COM } 13648048SMadhavan.Venkataraman@Sun.COM 13658048SMadhavan.Venkataraman@Sun.COM /* 13668048SMadhavan.Venkataraman@Sun.COM * Create per-callout table kstats. 13678048SMadhavan.Venkataraman@Sun.COM */ 13688048SMadhavan.Venkataraman@Sun.COM static void 13698048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(callout_table_t *ct) 13708048SMadhavan.Venkataraman@Sun.COM { 13718048SMadhavan.Venkataraman@Sun.COM callout_stat_type_t stat; 13728048SMadhavan.Venkataraman@Sun.COM kstat_t *ct_kstats; 13738048SMadhavan.Venkataraman@Sun.COM int ndx; 13748048SMadhavan.Venkataraman@Sun.COM 13758048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 13768048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_kstats == NULL); 13778048SMadhavan.Venkataraman@Sun.COM 13788048SMadhavan.Venkataraman@Sun.COM ndx = ct - callout_table; 13798048SMadhavan.Venkataraman@Sun.COM ct_kstats = kstat_create("unix", ndx, "callout", 13808048SMadhavan.Venkataraman@Sun.COM "misc", KSTAT_TYPE_NAMED, CALLOUT_NUM_STATS, KSTAT_FLAG_VIRTUAL); 13818048SMadhavan.Venkataraman@Sun.COM 13828048SMadhavan.Venkataraman@Sun.COM if (ct_kstats == NULL) { 13838048SMadhavan.Venkataraman@Sun.COM cmn_err(CE_WARN, "kstat_create for callout table %p failed", 13848048SMadhavan.Venkataraman@Sun.COM (void *)ct); 13858048SMadhavan.Venkataraman@Sun.COM } else { 13868048SMadhavan.Venkataraman@Sun.COM ct_kstats->ks_data = ct->ct_kstat_data; 13878048SMadhavan.Venkataraman@Sun.COM for (stat = 0; stat < CALLOUT_NUM_STATS; stat++) 13888048SMadhavan.Venkataraman@Sun.COM kstat_named_init(&ct->ct_kstat_data[stat], 13898048SMadhavan.Venkataraman@Sun.COM callout_kstat_names[stat], KSTAT_DATA_INT64); 13908048SMadhavan.Venkataraman@Sun.COM ct->ct_kstats = ct_kstats; 13918048SMadhavan.Venkataraman@Sun.COM kstat_install(ct_kstats); 13928048SMadhavan.Venkataraman@Sun.COM } 13938048SMadhavan.Venkataraman@Sun.COM } 13948048SMadhavan.Venkataraman@Sun.COM 13958048SMadhavan.Venkataraman@Sun.COM static void 13968048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(callout_table_t *ct) 13978048SMadhavan.Venkataraman@Sun.COM { 13988048SMadhavan.Venkataraman@Sun.COM cyc_handler_t hdlr; 13998048SMadhavan.Venkataraman@Sun.COM cyc_time_t when; 14008048SMadhavan.Venkataraman@Sun.COM processorid_t seqid; 14018048SMadhavan.Venkataraman@Sun.COM int t; 14028048SMadhavan.Venkataraman@Sun.COM 14038048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&ct->ct_mutex)); 14048048SMadhavan.Venkataraman@Sun.COM 14058048SMadhavan.Venkataraman@Sun.COM t = CALLOUT_TABLE_TYPE(ct); 14068048SMadhavan.Venkataraman@Sun.COM seqid = CALLOUT_TABLE_SEQID(ct); 14078048SMadhavan.Venkataraman@Sun.COM 14088048SMadhavan.Venkataraman@Sun.COM /* 14098048SMadhavan.Venkataraman@Sun.COM * Create the taskq thread if the table type is normal. 14108048SMadhavan.Venkataraman@Sun.COM * Realtime tables are handled at PIL1 by a softint 14118048SMadhavan.Venkataraman@Sun.COM * handler. 14128048SMadhavan.Venkataraman@Sun.COM */ 14138048SMadhavan.Venkataraman@Sun.COM if (t == CALLOUT_NORMAL) { 14148048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_taskq == NULL); 14158048SMadhavan.Venkataraman@Sun.COM /* 14168048SMadhavan.Venkataraman@Sun.COM * Each callout thread consumes exactly one 14178048SMadhavan.Venkataraman@Sun.COM * task structure while active. Therefore, 14188048SMadhavan.Venkataraman@Sun.COM * prepopulating with 2 * CALLOUT_THREADS tasks 14198048SMadhavan.Venkataraman@Sun.COM * ensures that there's at least one task per 14208048SMadhavan.Venkataraman@Sun.COM * thread that's either scheduled or on the 14218048SMadhavan.Venkataraman@Sun.COM * freelist. In turn, this guarantees that 14228048SMadhavan.Venkataraman@Sun.COM * taskq_dispatch() will always either succeed 14238048SMadhavan.Venkataraman@Sun.COM * (because there's a free task structure) or 14248048SMadhavan.Venkataraman@Sun.COM * be unnecessary (because "callout_excute(ct)" 14258048SMadhavan.Venkataraman@Sun.COM * has already scheduled). 14268048SMadhavan.Venkataraman@Sun.COM */ 14278048SMadhavan.Venkataraman@Sun.COM ct->ct_taskq = 14288048SMadhavan.Venkataraman@Sun.COM taskq_create_instance("callout_taskq", seqid, 14298048SMadhavan.Venkataraman@Sun.COM CALLOUT_THREADS, maxclsyspri, 14308048SMadhavan.Venkataraman@Sun.COM 2 * CALLOUT_THREADS, 2 * CALLOUT_THREADS, 14318048SMadhavan.Venkataraman@Sun.COM TASKQ_PREPOPULATE | TASKQ_CPR_SAFE); 14328048SMadhavan.Venkataraman@Sun.COM } 14338048SMadhavan.Venkataraman@Sun.COM 14348048SMadhavan.Venkataraman@Sun.COM /* 14358048SMadhavan.Venkataraman@Sun.COM * callouts can only be created in a table whose 14368048SMadhavan.Venkataraman@Sun.COM * cyclic has been initialized. 14378048SMadhavan.Venkataraman@Sun.COM */ 14388048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_heap_num == 0); 14398048SMadhavan.Venkataraman@Sun.COM 14408048SMadhavan.Venkataraman@Sun.COM /* 14418048SMadhavan.Venkataraman@Sun.COM * Create the callout table cyclics. 14428048SMadhavan.Venkataraman@Sun.COM */ 14438048SMadhavan.Venkataraman@Sun.COM ASSERT(ct->ct_cyclic == CYCLIC_NONE); 14448048SMadhavan.Venkataraman@Sun.COM 14458048SMadhavan.Venkataraman@Sun.COM hdlr.cyh_func = (cyc_func_t)CALLOUT_CYCLIC_HANDLER(t); 1446*8566SMadhavan.Venkataraman@Sun.COM hdlr.cyh_level = CY_LOW_LEVEL; 14478048SMadhavan.Venkataraman@Sun.COM hdlr.cyh_arg = ct; 14488048SMadhavan.Venkataraman@Sun.COM when.cyt_when = CY_INFINITY; 14498048SMadhavan.Venkataraman@Sun.COM when.cyt_interval = CY_INFINITY; 14508048SMadhavan.Venkataraman@Sun.COM 14518048SMadhavan.Venkataraman@Sun.COM ct->ct_cyclic = cyclic_add(&hdlr, &when); 14528048SMadhavan.Venkataraman@Sun.COM } 14538048SMadhavan.Venkataraman@Sun.COM 14548048SMadhavan.Venkataraman@Sun.COM void 14558048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cpu_t *cp) 14568048SMadhavan.Venkataraman@Sun.COM { 14578048SMadhavan.Venkataraman@Sun.COM lgrp_handle_t hand; 14588048SMadhavan.Venkataraman@Sun.COM callout_cache_t *cache; 14598048SMadhavan.Venkataraman@Sun.COM char s[KMEM_CACHE_NAMELEN]; 14608048SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 14618048SMadhavan.Venkataraman@Sun.COM processorid_t seqid; 14628048SMadhavan.Venkataraman@Sun.COM int t; 14638048SMadhavan.Venkataraman@Sun.COM 14648048SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 14658048SMadhavan.Venkataraman@Sun.COM 14668048SMadhavan.Venkataraman@Sun.COM /* 14678048SMadhavan.Venkataraman@Sun.COM * Locate the cache corresponding to the onlined CPU's lgroup. 14688048SMadhavan.Venkataraman@Sun.COM * Note that access to callout_caches is protected by cpu_lock. 14698048SMadhavan.Venkataraman@Sun.COM */ 14708048SMadhavan.Venkataraman@Sun.COM hand = lgrp_plat_cpu_to_hand(cp->cpu_id); 14718048SMadhavan.Venkataraman@Sun.COM for (cache = callout_caches; cache != NULL; cache = cache->cc_next) { 14728048SMadhavan.Venkataraman@Sun.COM if (cache->cc_hand == hand) 14738048SMadhavan.Venkataraman@Sun.COM break; 14748048SMadhavan.Venkataraman@Sun.COM } 14758048SMadhavan.Venkataraman@Sun.COM 14768048SMadhavan.Venkataraman@Sun.COM /* 14778048SMadhavan.Venkataraman@Sun.COM * If not found, create one. The caches are never destroyed. 14788048SMadhavan.Venkataraman@Sun.COM */ 14798048SMadhavan.Venkataraman@Sun.COM if (cache == NULL) { 14808048SMadhavan.Venkataraman@Sun.COM cache = kmem_alloc(sizeof (callout_cache_t), KM_SLEEP); 14818048SMadhavan.Venkataraman@Sun.COM cache->cc_hand = hand; 14828048SMadhavan.Venkataraman@Sun.COM (void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_cache%lx", 14838048SMadhavan.Venkataraman@Sun.COM (long)hand); 14848048SMadhavan.Venkataraman@Sun.COM cache->cc_cache = kmem_cache_create(s, sizeof (callout_t), 14858048SMadhavan.Venkataraman@Sun.COM CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0); 14868048SMadhavan.Venkataraman@Sun.COM (void) snprintf(s, KMEM_CACHE_NAMELEN, "callout_lcache%lx", 14878048SMadhavan.Venkataraman@Sun.COM (long)hand); 14888048SMadhavan.Venkataraman@Sun.COM cache->cc_lcache = kmem_cache_create(s, sizeof (callout_list_t), 14898048SMadhavan.Venkataraman@Sun.COM CALLOUT_ALIGN, NULL, NULL, NULL, NULL, NULL, 0); 14908048SMadhavan.Venkataraman@Sun.COM cache->cc_next = callout_caches; 14918048SMadhavan.Venkataraman@Sun.COM callout_caches = cache; 14928048SMadhavan.Venkataraman@Sun.COM } 14938048SMadhavan.Venkataraman@Sun.COM 14948048SMadhavan.Venkataraman@Sun.COM seqid = cp->cpu_seqid; 14958048SMadhavan.Venkataraman@Sun.COM 14968048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 14978048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, seqid)]; 14988048SMadhavan.Venkataraman@Sun.COM 14998048SMadhavan.Venkataraman@Sun.COM mutex_enter(&ct->ct_mutex); 15008048SMadhavan.Venkataraman@Sun.COM /* 15018048SMadhavan.Venkataraman@Sun.COM * Store convinience pointers to the kmem caches 15028048SMadhavan.Venkataraman@Sun.COM * in the callout table. These assignments should always be 15038048SMadhavan.Venkataraman@Sun.COM * done as callout tables can map to different physical 15048048SMadhavan.Venkataraman@Sun.COM * CPUs each time. 15058048SMadhavan.Venkataraman@Sun.COM */ 15068048SMadhavan.Venkataraman@Sun.COM ct->ct_cache = cache->cc_cache; 15078048SMadhavan.Venkataraman@Sun.COM ct->ct_lcache = cache->cc_lcache; 15088048SMadhavan.Venkataraman@Sun.COM 15098048SMadhavan.Venkataraman@Sun.COM /* 15108048SMadhavan.Venkataraman@Sun.COM * We use the heap pointer to check if stuff has been 15118048SMadhavan.Venkataraman@Sun.COM * initialized for this callout table. 15128048SMadhavan.Venkataraman@Sun.COM */ 15138048SMadhavan.Venkataraman@Sun.COM if (ct->ct_heap == NULL) { 15148048SMadhavan.Venkataraman@Sun.COM callout_heap_init(ct); 15158048SMadhavan.Venkataraman@Sun.COM callout_hash_init(ct); 15168048SMadhavan.Venkataraman@Sun.COM callout_kstat_init(ct); 15178048SMadhavan.Venkataraman@Sun.COM callout_cyclic_init(ct); 15188048SMadhavan.Venkataraman@Sun.COM } 15198048SMadhavan.Venkataraman@Sun.COM 15208048SMadhavan.Venkataraman@Sun.COM mutex_exit(&ct->ct_mutex); 15218048SMadhavan.Venkataraman@Sun.COM 15228048SMadhavan.Venkataraman@Sun.COM /* 1523*8566SMadhavan.Venkataraman@Sun.COM * Move the cyclic to this CPU by doing a bind. 15248048SMadhavan.Venkataraman@Sun.COM */ 15258048SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_cyclic, cp, NULL); 1526*8566SMadhavan.Venkataraman@Sun.COM } 1527*8566SMadhavan.Venkataraman@Sun.COM } 1528*8566SMadhavan.Venkataraman@Sun.COM 1529*8566SMadhavan.Venkataraman@Sun.COM void 1530*8566SMadhavan.Venkataraman@Sun.COM callout_cpu_offline(cpu_t *cp) 1531*8566SMadhavan.Venkataraman@Sun.COM { 1532*8566SMadhavan.Venkataraman@Sun.COM callout_table_t *ct; 1533*8566SMadhavan.Venkataraman@Sun.COM processorid_t seqid; 1534*8566SMadhavan.Venkataraman@Sun.COM int t; 1535*8566SMadhavan.Venkataraman@Sun.COM 1536*8566SMadhavan.Venkataraman@Sun.COM ASSERT(MUTEX_HELD(&cpu_lock)); 1537*8566SMadhavan.Venkataraman@Sun.COM 1538*8566SMadhavan.Venkataraman@Sun.COM seqid = cp->cpu_seqid; 1539*8566SMadhavan.Venkataraman@Sun.COM 1540*8566SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 1541*8566SMadhavan.Venkataraman@Sun.COM ct = &callout_table[CALLOUT_TABLE(t, seqid)]; 1542*8566SMadhavan.Venkataraman@Sun.COM 1543*8566SMadhavan.Venkataraman@Sun.COM /* 1544*8566SMadhavan.Venkataraman@Sun.COM * Unbind the cyclic. This will allow the cyclic subsystem 1545*8566SMadhavan.Venkataraman@Sun.COM * to juggle the cyclic during CPU offline. 1546*8566SMadhavan.Venkataraman@Sun.COM */ 15478048SMadhavan.Venkataraman@Sun.COM cyclic_bind(ct->ct_cyclic, NULL, NULL); 15488048SMadhavan.Venkataraman@Sun.COM } 15498048SMadhavan.Venkataraman@Sun.COM } 15508048SMadhavan.Venkataraman@Sun.COM 15518048SMadhavan.Venkataraman@Sun.COM /* 15528048SMadhavan.Venkataraman@Sun.COM * This is called to perform per-CPU initialization for slave CPUs at 15538048SMadhavan.Venkataraman@Sun.COM * boot time. 15548048SMadhavan.Venkataraman@Sun.COM */ 15558048SMadhavan.Venkataraman@Sun.COM void 15568048SMadhavan.Venkataraman@Sun.COM callout_mp_init(void) 15578048SMadhavan.Venkataraman@Sun.COM { 15588048SMadhavan.Venkataraman@Sun.COM cpu_t *cp; 15598048SMadhavan.Venkataraman@Sun.COM 15608048SMadhavan.Venkataraman@Sun.COM mutex_enter(&cpu_lock); 15618048SMadhavan.Venkataraman@Sun.COM 15628048SMadhavan.Venkataraman@Sun.COM cp = cpu_active; 15638048SMadhavan.Venkataraman@Sun.COM do { 15648048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(cp); 15658048SMadhavan.Venkataraman@Sun.COM } while ((cp = cp->cpu_next_onln) != cpu_active); 15668048SMadhavan.Venkataraman@Sun.COM 15678048SMadhavan.Venkataraman@Sun.COM mutex_exit(&cpu_lock); 15688048SMadhavan.Venkataraman@Sun.COM } 15698048SMadhavan.Venkataraman@Sun.COM 15708048SMadhavan.Venkataraman@Sun.COM /* 15710Sstevel@tonic-gate * Initialize all callout tables. Called at boot time just before clkstart(). 15720Sstevel@tonic-gate */ 15730Sstevel@tonic-gate void 15740Sstevel@tonic-gate callout_init(void) 15750Sstevel@tonic-gate { 15760Sstevel@tonic-gate int f, t; 15778048SMadhavan.Venkataraman@Sun.COM size_t size; 15780Sstevel@tonic-gate int table_id; 15790Sstevel@tonic-gate callout_table_t *ct; 15808048SMadhavan.Venkataraman@Sun.COM long bits, fanout; 15818048SMadhavan.Venkataraman@Sun.COM uintptr_t buf; 15820Sstevel@tonic-gate 15838048SMadhavan.Venkataraman@Sun.COM /* 15848048SMadhavan.Venkataraman@Sun.COM * Initialize callout globals. 15858048SMadhavan.Venkataraman@Sun.COM */ 15868048SMadhavan.Venkataraman@Sun.COM bits = 0; 15878048SMadhavan.Venkataraman@Sun.COM for (fanout = 1; (fanout < max_ncpus); fanout <<= 1) 15888048SMadhavan.Venkataraman@Sun.COM bits++; 15898048SMadhavan.Venkataraman@Sun.COM callout_table_bits = CALLOUT_TYPE_BITS + bits; 15908048SMadhavan.Venkataraman@Sun.COM callout_table_mask = (1 << callout_table_bits) - 1; 15918048SMadhavan.Venkataraman@Sun.COM callout_counter_low = 1 << CALLOUT_COUNTER_SHIFT; 15928048SMadhavan.Venkataraman@Sun.COM callout_longterm = TICK_TO_NSEC(CALLOUT_LONGTERM_TICKS); 1593*8566SMadhavan.Venkataraman@Sun.COM callout_max_ticks = CALLOUT_MAX_TICKS; 15940Sstevel@tonic-gate 15958048SMadhavan.Venkataraman@Sun.COM /* 15968048SMadhavan.Venkataraman@Sun.COM * Because of the variability in timing behavior across systems with 15978048SMadhavan.Venkataraman@Sun.COM * different architectures, we cannot allow arbitrarily low 15988048SMadhavan.Venkataraman@Sun.COM * resolutions. The minimum resolution has to be determined in a 15998048SMadhavan.Venkataraman@Sun.COM * platform-specific way. Until then, we define a blanket minimum 16008048SMadhavan.Venkataraman@Sun.COM * resolution for callouts of CALLOUT_MIN_RESOLUTION. 16018048SMadhavan.Venkataraman@Sun.COM * 16028048SMadhavan.Venkataraman@Sun.COM * If, in the future, someone requires lower resolution timers, they 16038048SMadhavan.Venkataraman@Sun.COM * can do one of two things: 16048048SMadhavan.Venkataraman@Sun.COM * 16058048SMadhavan.Venkataraman@Sun.COM * - Define a lower value for callout_min_resolution. This would 16068048SMadhavan.Venkataraman@Sun.COM * affect all clients of the callout subsystem. If this done 16078048SMadhavan.Venkataraman@Sun.COM * via /etc/system, then no code changes are required and it 16088048SMadhavan.Venkataraman@Sun.COM * would affect only that customer. 16098048SMadhavan.Venkataraman@Sun.COM * 16108048SMadhavan.Venkataraman@Sun.COM * - Define a flag to be passed to timeout creation that allows 16118048SMadhavan.Venkataraman@Sun.COM * the lower resolution. This involves code changes. But it 16128048SMadhavan.Venkataraman@Sun.COM * would affect only the calling module. It is the developer's 16138048SMadhavan.Venkataraman@Sun.COM * responsibility to test on all systems and make sure that 16148048SMadhavan.Venkataraman@Sun.COM * everything works. 16158048SMadhavan.Venkataraman@Sun.COM */ 16168048SMadhavan.Venkataraman@Sun.COM if (callout_min_resolution <= 0) 16178048SMadhavan.Venkataraman@Sun.COM callout_min_resolution = CALLOUT_MIN_RESOLUTION; 16188048SMadhavan.Venkataraman@Sun.COM 16198048SMadhavan.Venkataraman@Sun.COM /* 16208048SMadhavan.Venkataraman@Sun.COM * Allocate all the callout tables based on max_ncpus. We have chosen 16218048SMadhavan.Venkataraman@Sun.COM * to do boot-time allocation instead of dynamic allocation because: 16228048SMadhavan.Venkataraman@Sun.COM * 16238048SMadhavan.Venkataraman@Sun.COM * - the size of the callout tables is not too large. 16248048SMadhavan.Venkataraman@Sun.COM * - there are race conditions involved in making this dynamic. 16258048SMadhavan.Venkataraman@Sun.COM * - the hash tables that go with the callout tables consume 16268048SMadhavan.Venkataraman@Sun.COM * most of the memory and they are only allocated in 16278048SMadhavan.Venkataraman@Sun.COM * callout_cpu_online(). 16288048SMadhavan.Venkataraman@Sun.COM * 16298048SMadhavan.Venkataraman@Sun.COM * Each CPU has two tables that are consecutive in the array. The first 16308048SMadhavan.Venkataraman@Sun.COM * one is for realtime callouts and the second one is for normal ones. 16318048SMadhavan.Venkataraman@Sun.COM * 16328048SMadhavan.Venkataraman@Sun.COM * We do this alignment dance to make sure that callout table 16338048SMadhavan.Venkataraman@Sun.COM * structures will always be on a cache line boundary. 16348048SMadhavan.Venkataraman@Sun.COM */ 16358048SMadhavan.Venkataraman@Sun.COM size = sizeof (callout_table_t) * CALLOUT_NTYPES * max_ncpus; 16368048SMadhavan.Venkataraman@Sun.COM size += CALLOUT_ALIGN; 16378048SMadhavan.Venkataraman@Sun.COM buf = (uintptr_t)kmem_zalloc(size, KM_SLEEP); 16388048SMadhavan.Venkataraman@Sun.COM callout_table = (callout_table_t *)P2ROUNDUP(buf, CALLOUT_ALIGN); 16398048SMadhavan.Venkataraman@Sun.COM 16408048SMadhavan.Venkataraman@Sun.COM size = sizeof (kstat_named_t) * CALLOUT_NUM_STATS; 16418048SMadhavan.Venkataraman@Sun.COM /* 16428048SMadhavan.Venkataraman@Sun.COM * Now, initialize the tables for all the CPUs. 16438048SMadhavan.Venkataraman@Sun.COM */ 16448048SMadhavan.Venkataraman@Sun.COM for (f = 0; f < max_ncpus; f++) { 16458048SMadhavan.Venkataraman@Sun.COM for (t = 0; t < CALLOUT_NTYPES; t++) { 16460Sstevel@tonic-gate table_id = CALLOUT_TABLE(t, f); 16478048SMadhavan.Venkataraman@Sun.COM ct = &callout_table[table_id]; 1648*8566SMadhavan.Venkataraman@Sun.COM ct->ct_type = t; 16498048SMadhavan.Venkataraman@Sun.COM mutex_init(&ct->ct_mutex, NULL, MUTEX_DEFAULT, NULL); 16506422Sqiao /* 16518048SMadhavan.Venkataraman@Sun.COM * Precompute the base IDs for long and short-term 16528048SMadhavan.Venkataraman@Sun.COM * legacy IDs. This makes ID generation during 16538048SMadhavan.Venkataraman@Sun.COM * timeout() fast. 16546422Sqiao */ 16558048SMadhavan.Venkataraman@Sun.COM ct->ct_short_id = CALLOUT_SHORT_ID(table_id); 16568048SMadhavan.Venkataraman@Sun.COM ct->ct_long_id = CALLOUT_LONG_ID(table_id); 16578048SMadhavan.Venkataraman@Sun.COM /* 16588048SMadhavan.Venkataraman@Sun.COM * Precompute the base ID for generation-based IDs. 16598048SMadhavan.Venkataraman@Sun.COM * Note that when the first ID gets allocated, the 16608048SMadhavan.Venkataraman@Sun.COM * ID will wrap. This will cause the generation 16618048SMadhavan.Venkataraman@Sun.COM * number to be incremented to 1. 16628048SMadhavan.Venkataraman@Sun.COM */ 16638048SMadhavan.Venkataraman@Sun.COM ct->ct_gen_id = CALLOUT_SHORT_ID(table_id); 16648048SMadhavan.Venkataraman@Sun.COM /* 16658048SMadhavan.Venkataraman@Sun.COM * Initialize the cyclic as NONE. This will get set 16668048SMadhavan.Venkataraman@Sun.COM * during CPU online. This is so that partially 16678048SMadhavan.Venkataraman@Sun.COM * populated systems will only have the required 16688048SMadhavan.Venkataraman@Sun.COM * number of cyclics, not more. 16698048SMadhavan.Venkataraman@Sun.COM */ 16708048SMadhavan.Venkataraman@Sun.COM ct->ct_cyclic = CYCLIC_NONE; 16718048SMadhavan.Venkataraman@Sun.COM ct->ct_kstat_data = kmem_zalloc(size, KM_SLEEP); 16720Sstevel@tonic-gate } 16730Sstevel@tonic-gate } 16748048SMadhavan.Venkataraman@Sun.COM 16758048SMadhavan.Venkataraman@Sun.COM /* 16768048SMadhavan.Venkataraman@Sun.COM * Add the callback for CPR. This is called during checkpoint 16778048SMadhavan.Venkataraman@Sun.COM * resume to suspend and resume callouts. 16788048SMadhavan.Venkataraman@Sun.COM */ 16798048SMadhavan.Venkataraman@Sun.COM (void) callb_add(callout_cpr_callb, 0, CB_CL_CPR_CALLOUT, 16808048SMadhavan.Venkataraman@Sun.COM "callout_cpr"); 16818048SMadhavan.Venkataraman@Sun.COM (void) callb_add(callout_debug_callb, 0, CB_CL_ENTER_DEBUGGER, 16828048SMadhavan.Venkataraman@Sun.COM "callout_debug"); 16838048SMadhavan.Venkataraman@Sun.COM 16848048SMadhavan.Venkataraman@Sun.COM /* 16858048SMadhavan.Venkataraman@Sun.COM * Call the per-CPU initialization function for the boot CPU. This 16868048SMadhavan.Venkataraman@Sun.COM * is done here because the function is not called automatically for 16878048SMadhavan.Venkataraman@Sun.COM * the boot CPU from the CPU online/offline hooks. Note that the 16888048SMadhavan.Venkataraman@Sun.COM * CPU lock is taken here because of convention. 16898048SMadhavan.Venkataraman@Sun.COM */ 16908048SMadhavan.Venkataraman@Sun.COM mutex_enter(&cpu_lock); 16918048SMadhavan.Venkataraman@Sun.COM callout_boot_ct = &callout_table[CALLOUT_TABLE(0, CPU->cpu_seqid)]; 16928048SMadhavan.Venkataraman@Sun.COM callout_cpu_online(CPU); 16938048SMadhavan.Venkataraman@Sun.COM mutex_exit(&cpu_lock); 16940Sstevel@tonic-gate } 1695