17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*c210ded4Sesaxe * Common Development and Distribution License (the "License"). 6*c210ded4Sesaxe * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*c210ded4Sesaxe * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * The Cyclic Subsystem 307c478bd9Sstevel@tonic-gate * -------------------- 317c478bd9Sstevel@tonic-gate * 327c478bd9Sstevel@tonic-gate * Prehistory 337c478bd9Sstevel@tonic-gate * 347c478bd9Sstevel@tonic-gate * Historically, most computer architectures have specified interval-based 357c478bd9Sstevel@tonic-gate * timer parts (e.g. SPARCstation's counter/timer; Intel's i8254). While 367c478bd9Sstevel@tonic-gate * these parts deal in relative (i.e. not absolute) time values, they are 377c478bd9Sstevel@tonic-gate * typically used by the operating system to implement the abstraction of 387c478bd9Sstevel@tonic-gate * absolute time. As a result, these parts cannot typically be reprogrammed 397c478bd9Sstevel@tonic-gate * without introducing error in the system's notion of time. 407c478bd9Sstevel@tonic-gate * 417c478bd9Sstevel@tonic-gate * Starting in about 1994, chip architectures began specifying high resolution 427c478bd9Sstevel@tonic-gate * timestamp registers. As of this writing (1999), all major chip families 437c478bd9Sstevel@tonic-gate * (UltraSPARC, PentiumPro, MIPS, PowerPC, Alpha) have high resolution 447c478bd9Sstevel@tonic-gate * timestamp registers, and two (UltraSPARC and MIPS) have added the capacity 457c478bd9Sstevel@tonic-gate * to interrupt based on timestamp values. These timestamp-compare registers 467c478bd9Sstevel@tonic-gate * present a time-based interrupt source which can be reprogrammed arbitrarily 477c478bd9Sstevel@tonic-gate * often without introducing error. Given the low cost of implementing such a 487c478bd9Sstevel@tonic-gate * timestamp-compare register (and the tangible benefit of eliminating 497c478bd9Sstevel@tonic-gate * discrete timer parts), it is reasonable to expect that future chip 507c478bd9Sstevel@tonic-gate * architectures will adopt this feature. 517c478bd9Sstevel@tonic-gate * 527c478bd9Sstevel@tonic-gate * The cyclic subsystem has been designed to take advantage of chip 537c478bd9Sstevel@tonic-gate * architectures with the capacity to interrupt based on absolute, high 547c478bd9Sstevel@tonic-gate * resolution values of time. 557c478bd9Sstevel@tonic-gate * 567c478bd9Sstevel@tonic-gate * Subsystem Overview 577c478bd9Sstevel@tonic-gate * 587c478bd9Sstevel@tonic-gate * The cyclic subsystem is a low-level kernel subsystem designed to provide 597c478bd9Sstevel@tonic-gate * arbitrarily high resolution, per-CPU interval timers (to avoid colliding 607c478bd9Sstevel@tonic-gate * with existing terms, we dub such an interval timer a "cyclic"). Cyclics 617c478bd9Sstevel@tonic-gate * can be specified to fire at high, lock or low interrupt level, and may be 627c478bd9Sstevel@tonic-gate * optionally bound to a CPU or a CPU partition. A cyclic's CPU or CPU 637c478bd9Sstevel@tonic-gate * partition binding may be changed dynamically; the cyclic will be "juggled" 647c478bd9Sstevel@tonic-gate * to a CPU which satisfies the new binding. Alternatively, a cyclic may 657c478bd9Sstevel@tonic-gate * be specified to be "omnipresent", denoting firing on all online CPUs. 667c478bd9Sstevel@tonic-gate * 677c478bd9Sstevel@tonic-gate * Cyclic Subsystem Interface Overview 687c478bd9Sstevel@tonic-gate * ----------------------------------- 697c478bd9Sstevel@tonic-gate * 707c478bd9Sstevel@tonic-gate * The cyclic subsystem has interfaces with the kernel at-large, with other 717c478bd9Sstevel@tonic-gate * kernel subsystems (e.g. the processor management subsystem, the checkpoint 727c478bd9Sstevel@tonic-gate * resume subsystem) and with the platform (the cyclic backend). Each 737c478bd9Sstevel@tonic-gate * of these interfaces is given a brief synopsis here, and is described 747c478bd9Sstevel@tonic-gate * in full above the interface's implementation. 757c478bd9Sstevel@tonic-gate * 767c478bd9Sstevel@tonic-gate * The following diagram displays the cyclic subsystem's interfaces to 777c478bd9Sstevel@tonic-gate * other kernel components. The arrows denote a "calls" relationship, with 787c478bd9Sstevel@tonic-gate * the large arrow indicating the cyclic subsystem's consumer interface. 797c478bd9Sstevel@tonic-gate * Each arrow is labeled with the section in which the corresponding 807c478bd9Sstevel@tonic-gate * interface is described. 817c478bd9Sstevel@tonic-gate * 827c478bd9Sstevel@tonic-gate * Kernel at-large consumers 837c478bd9Sstevel@tonic-gate * -----------++------------ 847c478bd9Sstevel@tonic-gate * || 857c478bd9Sstevel@tonic-gate * || 867c478bd9Sstevel@tonic-gate * _||_ 877c478bd9Sstevel@tonic-gate * \ / 887c478bd9Sstevel@tonic-gate * \/ 897c478bd9Sstevel@tonic-gate * +---------------------+ 907c478bd9Sstevel@tonic-gate * | | 917c478bd9Sstevel@tonic-gate * | Cyclic subsystem |<----------- Other kernel subsystems 927c478bd9Sstevel@tonic-gate * | | 937c478bd9Sstevel@tonic-gate * +---------------------+ 947c478bd9Sstevel@tonic-gate * ^ | 957c478bd9Sstevel@tonic-gate * | | 967c478bd9Sstevel@tonic-gate * | | 977c478bd9Sstevel@tonic-gate * | v 987c478bd9Sstevel@tonic-gate * +---------------------+ 997c478bd9Sstevel@tonic-gate * | | 1007c478bd9Sstevel@tonic-gate * | Cyclic backend | 1017c478bd9Sstevel@tonic-gate * | (platform specific) | 1027c478bd9Sstevel@tonic-gate * | | 1037c478bd9Sstevel@tonic-gate * +---------------------+ 1047c478bd9Sstevel@tonic-gate * 1057c478bd9Sstevel@tonic-gate * 1067c478bd9Sstevel@tonic-gate * Kernel At-Large Interfaces 1077c478bd9Sstevel@tonic-gate * 1087c478bd9Sstevel@tonic-gate * cyclic_add() <-- Creates a cyclic 1097c478bd9Sstevel@tonic-gate * cyclic_add_omni() <-- Creates an omnipresent cyclic 1107c478bd9Sstevel@tonic-gate * cyclic_remove() <-- Removes a cyclic 1117c478bd9Sstevel@tonic-gate * cyclic_bind() <-- Change a cyclic's CPU or partition binding 1127c478bd9Sstevel@tonic-gate * 1137c478bd9Sstevel@tonic-gate * Inter-subsystem Interfaces 1147c478bd9Sstevel@tonic-gate * 1157c478bd9Sstevel@tonic-gate * cyclic_juggle() <-- Juggles cyclics away from a CPU 1167c478bd9Sstevel@tonic-gate * cyclic_offline() <-- Offlines cyclic operation on a CPU 1177c478bd9Sstevel@tonic-gate * cyclic_online() <-- Reenables operation on an offlined CPU 1187c478bd9Sstevel@tonic-gate * cyclic_move_in() <-- Notifies subsystem of change in CPU partition 1197c478bd9Sstevel@tonic-gate * cyclic_move_out() <-- Notifies subsystem of change in CPU partition 1207c478bd9Sstevel@tonic-gate * cyclic_suspend() <-- Suspends the cyclic subsystem on all CPUs 1217c478bd9Sstevel@tonic-gate * cyclic_resume() <-- Resumes the cyclic subsystem on all CPUs 1227c478bd9Sstevel@tonic-gate * 1237c478bd9Sstevel@tonic-gate * Backend Interfaces 1247c478bd9Sstevel@tonic-gate * 1257c478bd9Sstevel@tonic-gate * cyclic_init() <-- Initializes the cyclic subsystem 1267c478bd9Sstevel@tonic-gate * cyclic_fire() <-- CY_HIGH_LEVEL interrupt entry point 1277c478bd9Sstevel@tonic-gate * cyclic_softint() <-- CY_LOCK/LOW_LEVEL soft interrupt entry point 1287c478bd9Sstevel@tonic-gate * 1297c478bd9Sstevel@tonic-gate * The backend-supplied interfaces (through the cyc_backend structure) are 1307c478bd9Sstevel@tonic-gate * documented in detail in <sys/cyclic_impl.h> 1317c478bd9Sstevel@tonic-gate * 1327c478bd9Sstevel@tonic-gate * 1337c478bd9Sstevel@tonic-gate * Cyclic Subsystem Implementation Overview 1347c478bd9Sstevel@tonic-gate * ---------------------------------------- 1357c478bd9Sstevel@tonic-gate * 1367c478bd9Sstevel@tonic-gate * The cyclic subsystem is designed to minimize interference between cyclics 1377c478bd9Sstevel@tonic-gate * on different CPUs. Thus, all of the cyclic subsystem's data structures 1387c478bd9Sstevel@tonic-gate * hang off of a per-CPU structure, cyc_cpu. 1397c478bd9Sstevel@tonic-gate * 1407c478bd9Sstevel@tonic-gate * Each cyc_cpu has a power-of-two sized array of cyclic structures (the 1417c478bd9Sstevel@tonic-gate * cyp_cyclics member of the cyc_cpu structure). If cyclic_add() is called 1427c478bd9Sstevel@tonic-gate * and there does not exist a free slot in the cyp_cyclics array, the size of 1437c478bd9Sstevel@tonic-gate * the array will be doubled. The array will never shrink. Cyclics are 1447c478bd9Sstevel@tonic-gate * referred to by their index in the cyp_cyclics array, which is of type 1457c478bd9Sstevel@tonic-gate * cyc_index_t. 1467c478bd9Sstevel@tonic-gate * 1477c478bd9Sstevel@tonic-gate * The cyclics are kept sorted by expiration time in the cyc_cpu's heap. The 1487c478bd9Sstevel@tonic-gate * heap is keyed by cyclic expiration time, with parents expiring earlier 1497c478bd9Sstevel@tonic-gate * than their children. 1507c478bd9Sstevel@tonic-gate * 1517c478bd9Sstevel@tonic-gate * Heap Management 1527c478bd9Sstevel@tonic-gate * 1537c478bd9Sstevel@tonic-gate * The heap is managed primarily by cyclic_fire(). Upon entry, cyclic_fire() 1547c478bd9Sstevel@tonic-gate * compares the root cyclic's expiration time to the current time. If the 1557c478bd9Sstevel@tonic-gate * expiration time is in the past, cyclic_expire() is called on the root 1567c478bd9Sstevel@tonic-gate * cyclic. Upon return from cyclic_expire(), the cyclic's new expiration time 1577c478bd9Sstevel@tonic-gate * is derived by adding its interval to its old expiration time, and a 1587c478bd9Sstevel@tonic-gate * downheap operation is performed. After the downheap, cyclic_fire() 1597c478bd9Sstevel@tonic-gate * examines the (potentially changed) root cyclic, repeating the 1607c478bd9Sstevel@tonic-gate * cyclic_expire()/add interval/cyclic_downheap() sequence until the root 1617c478bd9Sstevel@tonic-gate * cyclic has an expiration time in the future. This expiration time 1627c478bd9Sstevel@tonic-gate * (guaranteed to be the earliest in the heap) is then communicated to the 1637c478bd9Sstevel@tonic-gate * backend via cyb_reprogram. Optimal backends will next call cyclic_fire() 1647c478bd9Sstevel@tonic-gate * shortly after the root cyclic's expiration time. 1657c478bd9Sstevel@tonic-gate * 1667c478bd9Sstevel@tonic-gate * To allow efficient, deterministic downheap operations, we implement the 1677c478bd9Sstevel@tonic-gate * heap as an array (the cyp_heap member of the cyc_cpu structure), with each 1687c478bd9Sstevel@tonic-gate * element containing an index into the CPU's cyp_cyclics array. 1697c478bd9Sstevel@tonic-gate * 1707c478bd9Sstevel@tonic-gate * The heap is laid out in the array according to the following: 1717c478bd9Sstevel@tonic-gate * 1727c478bd9Sstevel@tonic-gate * 1. The root of the heap is always in the 0th element of the heap array 1737c478bd9Sstevel@tonic-gate * 2. The left and right children of the nth element are element 1747c478bd9Sstevel@tonic-gate * (((n + 1) << 1) - 1) and element ((n + 1) << 1), respectively. 1757c478bd9Sstevel@tonic-gate * 1767c478bd9Sstevel@tonic-gate * This layout is standard (see, e.g., Cormen's "Algorithms"); the proof 1777c478bd9Sstevel@tonic-gate * that these constraints correctly lay out a heap (or indeed, any binary 1787c478bd9Sstevel@tonic-gate * tree) is trivial and left to the reader. 1797c478bd9Sstevel@tonic-gate * 1807c478bd9Sstevel@tonic-gate * To see the heap by example, assume our cyclics array has the following 1817c478bd9Sstevel@tonic-gate * members (at time t): 1827c478bd9Sstevel@tonic-gate * 1837c478bd9Sstevel@tonic-gate * cy_handler cy_level cy_expire 1847c478bd9Sstevel@tonic-gate * --------------------------------------------- 1857c478bd9Sstevel@tonic-gate * [ 0] clock() LOCK t+10000000 1867c478bd9Sstevel@tonic-gate * [ 1] deadman() HIGH t+1000000000 1877c478bd9Sstevel@tonic-gate * [ 2] clock_highres_fire() LOW t+100 1887c478bd9Sstevel@tonic-gate * [ 3] clock_highres_fire() LOW t+1000 1897c478bd9Sstevel@tonic-gate * [ 4] clock_highres_fire() LOW t+500 1907c478bd9Sstevel@tonic-gate * [ 5] (free) -- -- 1917c478bd9Sstevel@tonic-gate * [ 6] (free) -- -- 1927c478bd9Sstevel@tonic-gate * [ 7] (free) -- -- 1937c478bd9Sstevel@tonic-gate * 1947c478bd9Sstevel@tonic-gate * The heap array could be: 1957c478bd9Sstevel@tonic-gate * 1967c478bd9Sstevel@tonic-gate * [0] [1] [2] [3] [4] [5] [6] [7] 1977c478bd9Sstevel@tonic-gate * +-----+-----+-----+-----+-----+-----+-----+-----+ 1987c478bd9Sstevel@tonic-gate * | | | | | | | | | 1997c478bd9Sstevel@tonic-gate * | 2 | 3 | 4 | 0 | 1 | x | x | x | 2007c478bd9Sstevel@tonic-gate * | | | | | | | | | 2017c478bd9Sstevel@tonic-gate * +-----+-----+-----+-----+-----+-----+-----+-----+ 2027c478bd9Sstevel@tonic-gate * 2037c478bd9Sstevel@tonic-gate * Graphically, this array corresponds to the following (excuse the ASCII art): 2047c478bd9Sstevel@tonic-gate * 2057c478bd9Sstevel@tonic-gate * 2 2067c478bd9Sstevel@tonic-gate * | 2077c478bd9Sstevel@tonic-gate * +------------------+------------------+ 2087c478bd9Sstevel@tonic-gate * 3 4 2097c478bd9Sstevel@tonic-gate * | 2107c478bd9Sstevel@tonic-gate * +---------+--------+ 2117c478bd9Sstevel@tonic-gate * 0 1 2127c478bd9Sstevel@tonic-gate * 2137c478bd9Sstevel@tonic-gate * Note that the heap is laid out by layer: all nodes at a given depth are 2147c478bd9Sstevel@tonic-gate * stored in consecutive elements of the array. Moreover, layers of 2157c478bd9Sstevel@tonic-gate * consecutive depths are in adjacent element ranges. This property 2167c478bd9Sstevel@tonic-gate * guarantees high locality of reference during downheap operations. 2177c478bd9Sstevel@tonic-gate * Specifically, we are guaranteed that we can downheap to a depth of 2187c478bd9Sstevel@tonic-gate * 2197c478bd9Sstevel@tonic-gate * lg (cache_line_size / sizeof (cyc_index_t)) 2207c478bd9Sstevel@tonic-gate * 2217c478bd9Sstevel@tonic-gate * nodes with at most one cache miss. On UltraSPARC (64 byte e-cache line 2227c478bd9Sstevel@tonic-gate * size), this corresponds to a depth of four nodes. Thus, if there are 2237c478bd9Sstevel@tonic-gate * fewer than sixteen cyclics in the heap, downheaps on UltraSPARC miss at 2247c478bd9Sstevel@tonic-gate * most once in the e-cache. 2257c478bd9Sstevel@tonic-gate * 2267c478bd9Sstevel@tonic-gate * Downheaps are required to compare siblings as they proceed down the 2277c478bd9Sstevel@tonic-gate * heap. For downheaps proceeding beyond the one-cache-miss depth, every 2287c478bd9Sstevel@tonic-gate * access to a left child could potentially miss in the cache. However, 2297c478bd9Sstevel@tonic-gate * if we assume 2307c478bd9Sstevel@tonic-gate * 2317c478bd9Sstevel@tonic-gate * (cache_line_size / sizeof (cyc_index_t)) > 2, 2327c478bd9Sstevel@tonic-gate * 2337c478bd9Sstevel@tonic-gate * then all siblings are guaranteed to be on the same cache line. Thus, the 2347c478bd9Sstevel@tonic-gate * miss on the left child will guarantee a hit on the right child; downheaps 2357c478bd9Sstevel@tonic-gate * will incur at most one cache miss per layer beyond the one-cache-miss 2367c478bd9Sstevel@tonic-gate * depth. The total number of cache misses for heap management during a 2377c478bd9Sstevel@tonic-gate * downheap operation is thus bounded by 2387c478bd9Sstevel@tonic-gate * 2397c478bd9Sstevel@tonic-gate * lg (n) - lg (cache_line_size / sizeof (cyc_index_t)) 2407c478bd9Sstevel@tonic-gate * 2417c478bd9Sstevel@tonic-gate * Traditional pointer-based heaps are implemented without regard to 2427c478bd9Sstevel@tonic-gate * locality. Downheaps can thus incur two cache misses per layer (one for 2437c478bd9Sstevel@tonic-gate * each child), but at most one cache miss at the root. This yields a bound 2447c478bd9Sstevel@tonic-gate * of 2457c478bd9Sstevel@tonic-gate * 2467c478bd9Sstevel@tonic-gate * 2 * lg (n) - 1 2477c478bd9Sstevel@tonic-gate * 2487c478bd9Sstevel@tonic-gate * on the total cache misses. 2497c478bd9Sstevel@tonic-gate * 2507c478bd9Sstevel@tonic-gate * This difference may seem theoretically trivial (the difference is, after 2517c478bd9Sstevel@tonic-gate * all, constant), but can become substantial in practice -- especially for 2527c478bd9Sstevel@tonic-gate * caches with very large cache lines and high miss penalties (e.g. TLBs). 2537c478bd9Sstevel@tonic-gate * 2547c478bd9Sstevel@tonic-gate * Heaps must always be full, balanced trees. Heap management must therefore 2557c478bd9Sstevel@tonic-gate * track the next point-of-insertion into the heap. In pointer-based heaps, 2567c478bd9Sstevel@tonic-gate * recomputing this point takes O(lg (n)). Given the layout of the 2577c478bd9Sstevel@tonic-gate * array-based implementation, however, the next point-of-insertion is 2587c478bd9Sstevel@tonic-gate * always: 2597c478bd9Sstevel@tonic-gate * 2607c478bd9Sstevel@tonic-gate * heap[number_of_elements] 2617c478bd9Sstevel@tonic-gate * 2627c478bd9Sstevel@tonic-gate * We exploit this property by implementing the free-list in the usused 2637c478bd9Sstevel@tonic-gate * heap elements. Heap insertion, therefore, consists only of filling in 2647c478bd9Sstevel@tonic-gate * the cyclic at cyp_cyclics[cyp_heap[number_of_elements]], incrementing 2657c478bd9Sstevel@tonic-gate * the number of elements, and performing an upheap. Heap deletion consists 2667c478bd9Sstevel@tonic-gate * of decrementing the number of elements, swapping the to-be-deleted element 2677c478bd9Sstevel@tonic-gate * with the element at cyp_heap[number_of_elements], and downheaping. 2687c478bd9Sstevel@tonic-gate * 2697c478bd9Sstevel@tonic-gate * Filling in more details in our earlier example: 2707c478bd9Sstevel@tonic-gate * 2717c478bd9Sstevel@tonic-gate * +--- free list head 2727c478bd9Sstevel@tonic-gate * | 2737c478bd9Sstevel@tonic-gate * V 2747c478bd9Sstevel@tonic-gate * 2757c478bd9Sstevel@tonic-gate * [0] [1] [2] [3] [4] [5] [6] [7] 2767c478bd9Sstevel@tonic-gate * +-----+-----+-----+-----+-----+-----+-----+-----+ 2777c478bd9Sstevel@tonic-gate * | | | | | | | | | 2787c478bd9Sstevel@tonic-gate * | 2 | 3 | 4 | 0 | 1 | 5 | 6 | 7 | 2797c478bd9Sstevel@tonic-gate * | | | | | | | | | 2807c478bd9Sstevel@tonic-gate * +-----+-----+-----+-----+-----+-----+-----+-----+ 2817c478bd9Sstevel@tonic-gate * 2827c478bd9Sstevel@tonic-gate * To insert into this heap, we would just need to fill in the cyclic at 2837c478bd9Sstevel@tonic-gate * cyp_cyclics[5], bump the number of elements (from 5 to 6) and perform 2847c478bd9Sstevel@tonic-gate * an upheap. 2857c478bd9Sstevel@tonic-gate * 2867c478bd9Sstevel@tonic-gate * If we wanted to remove, say, cyp_cyclics[3], we would first scan for it 2877c478bd9Sstevel@tonic-gate * in the cyp_heap, and discover it at cyp_heap[1]. We would then decrement 2887c478bd9Sstevel@tonic-gate * the number of elements (from 5 to 4), swap cyp_heap[1] with cyp_heap[4], 2897c478bd9Sstevel@tonic-gate * and perform a downheap from cyp_heap[1]. The linear scan is required 2907c478bd9Sstevel@tonic-gate * because the cyclic does not keep a backpointer into the heap. This makes 2917c478bd9Sstevel@tonic-gate * heap manipulation (e.g. downheaps) faster at the expense of removal 2927c478bd9Sstevel@tonic-gate * operations. 2937c478bd9Sstevel@tonic-gate * 2947c478bd9Sstevel@tonic-gate * Expiry processing 2957c478bd9Sstevel@tonic-gate * 2967c478bd9Sstevel@tonic-gate * As alluded to above, cyclic_expire() is called by cyclic_fire() at 2977c478bd9Sstevel@tonic-gate * CY_HIGH_LEVEL to expire a cyclic. Cyclic subsystem consumers are 2987c478bd9Sstevel@tonic-gate * guaranteed that for an arbitrary time t in the future, their cyclic 2997c478bd9Sstevel@tonic-gate * handler will have been called (t - cyt_when) / cyt_interval times. Thus, 3007c478bd9Sstevel@tonic-gate * there must be a one-to-one mapping between a cyclic's expiration at 3017c478bd9Sstevel@tonic-gate * CY_HIGH_LEVEL and its execution at the desired level (either CY_HIGH_LEVEL, 3027c478bd9Sstevel@tonic-gate * CY_LOCK_LEVEL or CY_LOW_LEVEL). 3037c478bd9Sstevel@tonic-gate * 3047c478bd9Sstevel@tonic-gate * For CY_HIGH_LEVEL cyclics, this is trivial; cyclic_expire() simply needs 3057c478bd9Sstevel@tonic-gate * to call the handler. 3067c478bd9Sstevel@tonic-gate * 3077c478bd9Sstevel@tonic-gate * For CY_LOCK_LEVEL and CY_LOW_LEVEL cyclics, however, there exists a 3087c478bd9Sstevel@tonic-gate * potential disconnect: if the CPU is at an interrupt level less than 3097c478bd9Sstevel@tonic-gate * CY_HIGH_LEVEL but greater than the level of a cyclic for a period of 3107c478bd9Sstevel@tonic-gate * time longer than twice the cyclic's interval, the cyclic will be expired 3117c478bd9Sstevel@tonic-gate * twice before it can be handled. 3127c478bd9Sstevel@tonic-gate * 3137c478bd9Sstevel@tonic-gate * To maintain the one-to-one mapping, we track the difference between the 3147c478bd9Sstevel@tonic-gate * number of times a cyclic has been expired and the number of times it's 3157c478bd9Sstevel@tonic-gate * been handled in a "pending count" (the cy_pend field of the cyclic 3167c478bd9Sstevel@tonic-gate * structure). cyclic_expire() thus increments the cy_pend count for the 3177c478bd9Sstevel@tonic-gate * expired cyclic and posts a soft interrupt at the desired level. In the 3187c478bd9Sstevel@tonic-gate * cyclic subsystem's soft interrupt handler, cyclic_softint(), we repeatedly 3197c478bd9Sstevel@tonic-gate * call the cyclic handler and decrement cy_pend until we have decremented 3207c478bd9Sstevel@tonic-gate * cy_pend to zero. 3217c478bd9Sstevel@tonic-gate * 3227c478bd9Sstevel@tonic-gate * The Producer/Consumer Buffer 3237c478bd9Sstevel@tonic-gate * 3247c478bd9Sstevel@tonic-gate * If we wish to avoid a linear scan of the cyclics array at soft interrupt 3257c478bd9Sstevel@tonic-gate * level, cyclic_softint() must be able to quickly determine which cyclics 3267c478bd9Sstevel@tonic-gate * have a non-zero cy_pend count. We thus introduce a per-soft interrupt 3277c478bd9Sstevel@tonic-gate * level producer/consumer buffer shared with CY_HIGH_LEVEL. These buffers 3287c478bd9Sstevel@tonic-gate * are encapsulated in the cyc_pcbuffer structure, and, like cyp_heap, are 3297c478bd9Sstevel@tonic-gate * implemented as cyc_index_t arrays (the cypc_buf member of the cyc_pcbuffer 3307c478bd9Sstevel@tonic-gate * structure). 3317c478bd9Sstevel@tonic-gate * 3327c478bd9Sstevel@tonic-gate * The producer (cyclic_expire() running at CY_HIGH_LEVEL) enqueues a cyclic 3337c478bd9Sstevel@tonic-gate * by storing the cyclic's index to cypc_buf[cypc_prodndx] and incrementing 3347c478bd9Sstevel@tonic-gate * cypc_prodndx. The consumer (cyclic_softint() running at either 3357c478bd9Sstevel@tonic-gate * CY_LOCK_LEVEL or CY_LOW_LEVEL) dequeues a cyclic by loading from 3367c478bd9Sstevel@tonic-gate * cypc_buf[cypc_consndx] and bumping cypc_consndx. The buffer is empty when 3377c478bd9Sstevel@tonic-gate * cypc_prodndx == cypc_consndx. 3387c478bd9Sstevel@tonic-gate * 3397c478bd9Sstevel@tonic-gate * To bound the size of the producer/consumer buffer, cyclic_expire() only 3407c478bd9Sstevel@tonic-gate * enqueues a cyclic if its cy_pend was zero (if the cyclic's cy_pend is 3417c478bd9Sstevel@tonic-gate * non-zero, cyclic_expire() only bumps cy_pend). Symmetrically, 3427c478bd9Sstevel@tonic-gate * cyclic_softint() only consumes a cyclic after it has decremented the 3437c478bd9Sstevel@tonic-gate * cy_pend count to zero. 3447c478bd9Sstevel@tonic-gate * 3457c478bd9Sstevel@tonic-gate * Returning to our example, here is what the CY_LOW_LEVEL producer/consumer 3467c478bd9Sstevel@tonic-gate * buffer might look like: 3477c478bd9Sstevel@tonic-gate * 3487c478bd9Sstevel@tonic-gate * cypc_consndx ---+ +--- cypc_prodndx 3497c478bd9Sstevel@tonic-gate * | | 3507c478bd9Sstevel@tonic-gate * V V 3517c478bd9Sstevel@tonic-gate * 3527c478bd9Sstevel@tonic-gate * [0] [1] [2] [3] [4] [5] [6] [7] 3537c478bd9Sstevel@tonic-gate * +-----+-----+-----+-----+-----+-----+-----+-----+ 3547c478bd9Sstevel@tonic-gate * | | | | | | | | | 3557c478bd9Sstevel@tonic-gate * | x | x | 3 | 2 | 4 | x | x | x | <== cypc_buf 3567c478bd9Sstevel@tonic-gate * | | | . | . | . | | | | 3577c478bd9Sstevel@tonic-gate * +-----+-----+- | -+- | -+- | -+-----+-----+-----+ 3587c478bd9Sstevel@tonic-gate * | | | 3597c478bd9Sstevel@tonic-gate * | | | cy_pend cy_handler 3607c478bd9Sstevel@tonic-gate * | | | ------------------------- 3617c478bd9Sstevel@tonic-gate * | | | [ 0] 1 clock() 3627c478bd9Sstevel@tonic-gate * | | | [ 1] 0 deadman() 3637c478bd9Sstevel@tonic-gate * | +---- | -------> [ 2] 3 clock_highres_fire() 3647c478bd9Sstevel@tonic-gate * +---------- | -------> [ 3] 1 clock_highres_fire() 3657c478bd9Sstevel@tonic-gate * +--------> [ 4] 1 clock_highres_fire() 3667c478bd9Sstevel@tonic-gate * [ 5] - (free) 3677c478bd9Sstevel@tonic-gate * [ 6] - (free) 3687c478bd9Sstevel@tonic-gate * [ 7] - (free) 3697c478bd9Sstevel@tonic-gate * 3707c478bd9Sstevel@tonic-gate * In particular, note that clock()'s cy_pend is 1 but that it is _not_ in 3717c478bd9Sstevel@tonic-gate * this producer/consumer buffer; it would be enqueued in the CY_LOCK_LEVEL 3727c478bd9Sstevel@tonic-gate * producer/consumer buffer. 3737c478bd9Sstevel@tonic-gate * 3747c478bd9Sstevel@tonic-gate * Locking 3757c478bd9Sstevel@tonic-gate * 3767c478bd9Sstevel@tonic-gate * Traditionally, access to per-CPU data structures shared between 3777c478bd9Sstevel@tonic-gate * interrupt levels is serialized by manipulating programmable interrupt 3787c478bd9Sstevel@tonic-gate * level: readers and writers are required to raise their interrupt level 3797c478bd9Sstevel@tonic-gate * to that of the highest level writer. 3807c478bd9Sstevel@tonic-gate * 3817c478bd9Sstevel@tonic-gate * For the producer/consumer buffers (shared between cyclic_fire()/ 3827c478bd9Sstevel@tonic-gate * cyclic_expire() executing at CY_HIGH_LEVEL and cyclic_softint() executing 3837c478bd9Sstevel@tonic-gate * at one of CY_LOCK_LEVEL or CY_LOW_LEVEL), forcing cyclic_softint() to raise 3847c478bd9Sstevel@tonic-gate * programmable interrupt level is undesirable: aside from the additional 3857c478bd9Sstevel@tonic-gate * latency incurred by manipulating interrupt level in the hot cy_pend 3867c478bd9Sstevel@tonic-gate * processing path, this would create the potential for soft level cy_pend 3877c478bd9Sstevel@tonic-gate * processing to delay CY_HIGH_LEVEL firing and expiry processing. 3887c478bd9Sstevel@tonic-gate * CY_LOCK/LOW_LEVEL cyclics could thereby induce jitter in CY_HIGH_LEVEL 3897c478bd9Sstevel@tonic-gate * cyclics. 3907c478bd9Sstevel@tonic-gate * 3917c478bd9Sstevel@tonic-gate * To minimize jitter, then, we would like the cyclic_fire()/cyclic_expire() 3927c478bd9Sstevel@tonic-gate * and cyclic_softint() code paths to be lock-free. 3937c478bd9Sstevel@tonic-gate * 3947c478bd9Sstevel@tonic-gate * For cyclic_fire()/cyclic_expire(), lock-free execution is straightforward: 3957c478bd9Sstevel@tonic-gate * because these routines execute at a higher interrupt level than 3967c478bd9Sstevel@tonic-gate * cyclic_softint(), their actions on the producer/consumer buffer appear 3977c478bd9Sstevel@tonic-gate * atomic. In particular, the increment of cy_pend appears to occur 3987c478bd9Sstevel@tonic-gate * atomically with the increment of cypc_prodndx. 3997c478bd9Sstevel@tonic-gate * 4007c478bd9Sstevel@tonic-gate * For cyclic_softint(), however, lock-free execution requires more delicacy. 4017c478bd9Sstevel@tonic-gate * When cyclic_softint() discovers a cyclic in the producer/consumer buffer, 4027c478bd9Sstevel@tonic-gate * it calls the cyclic's handler and attempts to atomically decrement the 4037c478bd9Sstevel@tonic-gate * cy_pend count with a compare&swap operation. 4047c478bd9Sstevel@tonic-gate * 4057c478bd9Sstevel@tonic-gate * If the compare&swap operation succeeds, cyclic_softint() behaves 4067c478bd9Sstevel@tonic-gate * conditionally based on the value it atomically wrote to cy_pend: 4077c478bd9Sstevel@tonic-gate * 4087c478bd9Sstevel@tonic-gate * - If the cy_pend was decremented to 0, the cyclic has been consumed; 4097c478bd9Sstevel@tonic-gate * cyclic_softint() increments the cypc_consndx and checks for more 4107c478bd9Sstevel@tonic-gate * enqueued work. 4117c478bd9Sstevel@tonic-gate * 4127c478bd9Sstevel@tonic-gate * - If the count was decremented to a non-zero value, there is more work 4137c478bd9Sstevel@tonic-gate * to be done on the cyclic; cyclic_softint() calls the cyclic handler 4147c478bd9Sstevel@tonic-gate * and repeats the atomic decrement process. 4157c478bd9Sstevel@tonic-gate * 4167c478bd9Sstevel@tonic-gate * If the compare&swap operation fails, cyclic_softint() knows that 4177c478bd9Sstevel@tonic-gate * cyclic_expire() has intervened and bumped the cy_pend count (resizes 4187c478bd9Sstevel@tonic-gate * and removals complicate this, however -- see the sections on their 4197c478bd9Sstevel@tonic-gate * operation, below). cyclic_softint() thus reloads cy_pend, and re-attempts 4207c478bd9Sstevel@tonic-gate * the atomic decrement. 4217c478bd9Sstevel@tonic-gate * 4227c478bd9Sstevel@tonic-gate * Recall that we bound the size of the producer/consumer buffer by 4237c478bd9Sstevel@tonic-gate * having cyclic_expire() only enqueue the specified cyclic if its 4247c478bd9Sstevel@tonic-gate * cy_pend count is zero; this assures that each cyclic is enqueued at 4257c478bd9Sstevel@tonic-gate * most once. This leads to a critical constraint on cyclic_softint(), 4267c478bd9Sstevel@tonic-gate * however: after the compare&swap operation which successfully decrements 4277c478bd9Sstevel@tonic-gate * cy_pend to zero, cyclic_softint() must _not_ re-examine the consumed 4287c478bd9Sstevel@tonic-gate * cyclic. In part to obey this constraint, cyclic_softint() calls the 4297c478bd9Sstevel@tonic-gate * cyclic handler before decrementing cy_pend. 4307c478bd9Sstevel@tonic-gate * 4317c478bd9Sstevel@tonic-gate * Resizing 4327c478bd9Sstevel@tonic-gate * 4337c478bd9Sstevel@tonic-gate * All of the discussion thus far has assumed a static number of cyclics. 4347c478bd9Sstevel@tonic-gate * Obviously, static limitations are not practical; we need the capacity 4357c478bd9Sstevel@tonic-gate * to resize our data structures dynamically. 4367c478bd9Sstevel@tonic-gate * 4377c478bd9Sstevel@tonic-gate * We resize our data structures lazily, and only on a per-CPU basis. 4387c478bd9Sstevel@tonic-gate * The size of the data structures always doubles and never shrinks. We 4397c478bd9Sstevel@tonic-gate * serialize adds (and thus resizes) on cpu_lock; we never need to deal 4407c478bd9Sstevel@tonic-gate * with concurrent resizes. Resizes should be rare; they may induce jitter 4417c478bd9Sstevel@tonic-gate * on the CPU being resized, but should not affect cyclic operation on other 4427c478bd9Sstevel@tonic-gate * CPUs. Pending cyclics may not be dropped during a resize operation. 4437c478bd9Sstevel@tonic-gate * 4447c478bd9Sstevel@tonic-gate * Three key cyc_cpu data structures need to be resized: the cyclics array, 4457c478bd9Sstevel@tonic-gate * the heap array and the producer/consumer buffers. Resizing the first two 4467c478bd9Sstevel@tonic-gate * is relatively straightforward: 4477c478bd9Sstevel@tonic-gate * 4487c478bd9Sstevel@tonic-gate * 1. The new, larger arrays are allocated in cyclic_expand() (called 4497c478bd9Sstevel@tonic-gate * from cyclic_add()). 4507c478bd9Sstevel@tonic-gate * 2. cyclic_expand() cross calls cyclic_expand_xcall() on the CPU 4517c478bd9Sstevel@tonic-gate * undergoing the resize. 4527c478bd9Sstevel@tonic-gate * 3. cyclic_expand_xcall() raises interrupt level to CY_HIGH_LEVEL 4537c478bd9Sstevel@tonic-gate * 4. The contents of the old arrays are copied into the new arrays. 4547c478bd9Sstevel@tonic-gate * 5. The old cyclics array is bzero()'d 4557c478bd9Sstevel@tonic-gate * 6. The pointers are updated. 4567c478bd9Sstevel@tonic-gate * 4577c478bd9Sstevel@tonic-gate * The producer/consumer buffer is dicier: cyclic_expand_xcall() may have 4587c478bd9Sstevel@tonic-gate * interrupted cyclic_softint() in the middle of consumption. To resize the 4597c478bd9Sstevel@tonic-gate * producer/consumer buffer, we implement up to two buffers per soft interrupt 4607c478bd9Sstevel@tonic-gate * level: a hard buffer (the buffer being produced into by cyclic_expire()) 4617c478bd9Sstevel@tonic-gate * and a soft buffer (the buffer from which cyclic_softint() is consuming). 4627c478bd9Sstevel@tonic-gate * During normal operation, the hard buffer and soft buffer point to the 4637c478bd9Sstevel@tonic-gate * same underlying producer/consumer buffer. 4647c478bd9Sstevel@tonic-gate * 4657c478bd9Sstevel@tonic-gate * During a resize, however, cyclic_expand_xcall() changes the hard buffer 4667c478bd9Sstevel@tonic-gate * to point to the new, larger producer/consumer buffer; all future 4677c478bd9Sstevel@tonic-gate * cyclic_expire()'s will produce into the new buffer. cyclic_expand_xcall() 4687c478bd9Sstevel@tonic-gate * then posts a CY_LOCK_LEVEL soft interrupt, landing in cyclic_softint(). 4697c478bd9Sstevel@tonic-gate * 4707c478bd9Sstevel@tonic-gate * As under normal operation, cyclic_softint() will consume cyclics from 4717c478bd9Sstevel@tonic-gate * its soft buffer. After the soft buffer is drained, however, 4727c478bd9Sstevel@tonic-gate * cyclic_softint() will see that the hard buffer has changed. At that time, 4737c478bd9Sstevel@tonic-gate * cyclic_softint() will change its soft buffer to point to the hard buffer, 4747c478bd9Sstevel@tonic-gate * and repeat the producer/consumer buffer draining procedure. 4757c478bd9Sstevel@tonic-gate * 4767c478bd9Sstevel@tonic-gate * After the new buffer is drained, cyclic_softint() will determine if both 4777c478bd9Sstevel@tonic-gate * soft levels have seen their new producer/consumer buffer. If both have, 4787c478bd9Sstevel@tonic-gate * cyclic_softint() will post on the semaphore cyp_modify_wait. If not, a 4797c478bd9Sstevel@tonic-gate * soft interrupt will be generated for the remaining level. 4807c478bd9Sstevel@tonic-gate * 4817c478bd9Sstevel@tonic-gate * cyclic_expand() blocks on the cyp_modify_wait semaphore (a semaphore is 4827c478bd9Sstevel@tonic-gate * used instead of a condition variable because of the race between the 4837c478bd9Sstevel@tonic-gate * sema_p() in cyclic_expand() and the sema_v() in cyclic_softint()). This 4847c478bd9Sstevel@tonic-gate * allows cyclic_expand() to know when the resize operation is complete; 4857c478bd9Sstevel@tonic-gate * all of the old buffers (the heap, the cyclics array and the producer/ 4867c478bd9Sstevel@tonic-gate * consumer buffers) can be freed. 4877c478bd9Sstevel@tonic-gate * 4887c478bd9Sstevel@tonic-gate * A final caveat on resizing: we described step (5) in the 4897c478bd9Sstevel@tonic-gate * cyclic_expand_xcall() procedure without providing any motivation. This 4907c478bd9Sstevel@tonic-gate * step addresses the problem of a cyclic_softint() attempting to decrement 4917c478bd9Sstevel@tonic-gate * a cy_pend count while interrupted by a cyclic_expand_xcall(). Because 4927c478bd9Sstevel@tonic-gate * cyclic_softint() has already called the handler by the time cy_pend is 4937c478bd9Sstevel@tonic-gate * decremented, we want to assure that it doesn't decrement a cy_pend 4947c478bd9Sstevel@tonic-gate * count in the old cyclics array. By zeroing the old cyclics array in 4957c478bd9Sstevel@tonic-gate * cyclic_expand_xcall(), we are zeroing out every cy_pend count; when 4967c478bd9Sstevel@tonic-gate * cyclic_softint() attempts to compare&swap on the cy_pend count, it will 4977c478bd9Sstevel@tonic-gate * fail and recognize that the count has been zeroed. cyclic_softint() will 4987c478bd9Sstevel@tonic-gate * update its stale copy of the cyp_cyclics pointer, re-read the cy_pend 4997c478bd9Sstevel@tonic-gate * count from the new cyclics array, and re-attempt the compare&swap. 5007c478bd9Sstevel@tonic-gate * 5017c478bd9Sstevel@tonic-gate * Removals 5027c478bd9Sstevel@tonic-gate * 5037c478bd9Sstevel@tonic-gate * Cyclic removals should be rare. To simplify the implementation (and to 5047c478bd9Sstevel@tonic-gate * allow optimization for the cyclic_fire()/cyclic_expire()/cyclic_softint() 5057c478bd9Sstevel@tonic-gate * path), we force removals and adds to serialize on cpu_lock. 5067c478bd9Sstevel@tonic-gate * 5077c478bd9Sstevel@tonic-gate * Cyclic removal is complicated by a guarantee made to the consumer of 5087c478bd9Sstevel@tonic-gate * the cyclic subsystem: after cyclic_remove() returns, the cyclic handler 5097c478bd9Sstevel@tonic-gate * has returned and will never again be called. 5107c478bd9Sstevel@tonic-gate * 5117c478bd9Sstevel@tonic-gate * Here is the procedure for cyclic removal: 5127c478bd9Sstevel@tonic-gate * 5137c478bd9Sstevel@tonic-gate * 1. cyclic_remove() calls cyclic_remove_xcall() on the CPU undergoing 5147c478bd9Sstevel@tonic-gate * the removal. 5157c478bd9Sstevel@tonic-gate * 2. cyclic_remove_xcall() raises interrupt level to CY_HIGH_LEVEL 5167c478bd9Sstevel@tonic-gate * 3. The current expiration time for the removed cyclic is recorded. 5177c478bd9Sstevel@tonic-gate * 4. If the cy_pend count on the removed cyclic is non-zero, it 5187c478bd9Sstevel@tonic-gate * is copied into cyp_rpend and subsequently zeroed. 5197c478bd9Sstevel@tonic-gate * 5. The cyclic is removed from the heap 5207c478bd9Sstevel@tonic-gate * 6. If the root of the heap has changed, the backend is reprogrammed. 5217c478bd9Sstevel@tonic-gate * 7. If the cy_pend count was non-zero cyclic_remove() blocks on the 5227c478bd9Sstevel@tonic-gate * cyp_modify_wait semaphore. 5237c478bd9Sstevel@tonic-gate * 5247c478bd9Sstevel@tonic-gate * The motivation for step (3) is explained in "Juggling", below. 5257c478bd9Sstevel@tonic-gate * 5267c478bd9Sstevel@tonic-gate * The cy_pend count is decremented in cyclic_softint() after the cyclic 5277c478bd9Sstevel@tonic-gate * handler returns. Thus, if we find a cy_pend count of zero in step 5287c478bd9Sstevel@tonic-gate * (4), we know that cyclic_remove() doesn't need to block. 5297c478bd9Sstevel@tonic-gate * 5307c478bd9Sstevel@tonic-gate * If the cy_pend count is non-zero, however, we must block in cyclic_remove() 5317c478bd9Sstevel@tonic-gate * until cyclic_softint() has finished calling the cyclic handler. To let 5327c478bd9Sstevel@tonic-gate * cyclic_softint() know that this cyclic has been removed, we zero the 5337c478bd9Sstevel@tonic-gate * cy_pend count. This will cause cyclic_softint()'s compare&swap to fail. 5347c478bd9Sstevel@tonic-gate * When cyclic_softint() sees the zero cy_pend count, it knows that it's been 5357c478bd9Sstevel@tonic-gate * caught during a resize (see "Resizing", above) or that the cyclic has been 5367c478bd9Sstevel@tonic-gate * removed. In the latter case, it calls cyclic_remove_pend() to call the 5377c478bd9Sstevel@tonic-gate * cyclic handler cyp_rpend - 1 times, and posts on cyp_modify_wait. 5387c478bd9Sstevel@tonic-gate * 5397c478bd9Sstevel@tonic-gate * Juggling 5407c478bd9Sstevel@tonic-gate * 5417c478bd9Sstevel@tonic-gate * At first glance, cyclic juggling seems to be a difficult problem. The 5427c478bd9Sstevel@tonic-gate * subsystem must guarantee that a cyclic doesn't execute simultaneously on 5437c478bd9Sstevel@tonic-gate * different CPUs, while also assuring that a cyclic fires exactly once 5447c478bd9Sstevel@tonic-gate * per interval. We solve this problem by leveraging a property of the 5457c478bd9Sstevel@tonic-gate * platform: gethrtime() is required to increase in lock-step across 5467c478bd9Sstevel@tonic-gate * multiple CPUs. Therefore, to juggle a cyclic, we remove it from its 5477c478bd9Sstevel@tonic-gate * CPU, recording its expiration time in the remove cross call (step (3) 5487c478bd9Sstevel@tonic-gate * in "Removing", above). We then add the cyclic to the new CPU, explicitly 5497c478bd9Sstevel@tonic-gate * setting its expiration time to the time recorded in the removal. This 5507c478bd9Sstevel@tonic-gate * leverages the existing cyclic expiry processing, which will compensate 5517c478bd9Sstevel@tonic-gate * for any time lost while juggling. 5527c478bd9Sstevel@tonic-gate * 5537c478bd9Sstevel@tonic-gate */ 5547c478bd9Sstevel@tonic-gate #include <sys/cyclic_impl.h> 5557c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 5567c478bd9Sstevel@tonic-gate #include <sys/systm.h> 5577c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 5587c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 5597c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 5607c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 561*c210ded4Sesaxe #include <sys/sdt.h> 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate #ifdef CYCLIC_TRACE 5647c478bd9Sstevel@tonic-gate 5657c478bd9Sstevel@tonic-gate /* 5667c478bd9Sstevel@tonic-gate * cyc_trace_enabled is for the benefit of kernel debuggers. 5677c478bd9Sstevel@tonic-gate */ 5687c478bd9Sstevel@tonic-gate int cyc_trace_enabled = 1; 5697c478bd9Sstevel@tonic-gate static cyc_tracebuf_t cyc_ptrace; 5707c478bd9Sstevel@tonic-gate static cyc_coverage_t cyc_coverage[CY_NCOVERAGE]; 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate /* 5737c478bd9Sstevel@tonic-gate * Seen this anywhere? 5747c478bd9Sstevel@tonic-gate */ 5757c478bd9Sstevel@tonic-gate static uint_t 5767c478bd9Sstevel@tonic-gate cyclic_coverage_hash(char *p) 5777c478bd9Sstevel@tonic-gate { 5787c478bd9Sstevel@tonic-gate unsigned int g; 5797c478bd9Sstevel@tonic-gate uint_t hval; 5807c478bd9Sstevel@tonic-gate 5817c478bd9Sstevel@tonic-gate hval = 0; 5827c478bd9Sstevel@tonic-gate while (*p) { 5837c478bd9Sstevel@tonic-gate hval = (hval << 4) + *p++; 5847c478bd9Sstevel@tonic-gate if ((g = (hval & 0xf0000000)) != 0) 5857c478bd9Sstevel@tonic-gate hval ^= g >> 24; 5867c478bd9Sstevel@tonic-gate hval &= ~g; 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate return (hval); 5897c478bd9Sstevel@tonic-gate } 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate static void 5927c478bd9Sstevel@tonic-gate cyclic_coverage(char *why, int level, uint64_t arg0, uint64_t arg1) 5937c478bd9Sstevel@tonic-gate { 5947c478bd9Sstevel@tonic-gate uint_t ndx, orig; 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate for (ndx = orig = cyclic_coverage_hash(why) % CY_NCOVERAGE; ; ) { 5977c478bd9Sstevel@tonic-gate if (cyc_coverage[ndx].cyv_why == why) 5987c478bd9Sstevel@tonic-gate break; 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate if (cyc_coverage[ndx].cyv_why != NULL || 6017c478bd9Sstevel@tonic-gate casptr(&cyc_coverage[ndx].cyv_why, NULL, why) != NULL) { 6027c478bd9Sstevel@tonic-gate 6037c478bd9Sstevel@tonic-gate if (++ndx == CY_NCOVERAGE) 6047c478bd9Sstevel@tonic-gate ndx = 0; 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate if (ndx == orig) 6077c478bd9Sstevel@tonic-gate panic("too many cyclic coverage points"); 6087c478bd9Sstevel@tonic-gate continue; 6097c478bd9Sstevel@tonic-gate } 6107c478bd9Sstevel@tonic-gate 6117c478bd9Sstevel@tonic-gate /* 6127c478bd9Sstevel@tonic-gate * If we're here, we have successfully swung our guy into 6137c478bd9Sstevel@tonic-gate * the position at "ndx". 6147c478bd9Sstevel@tonic-gate */ 6157c478bd9Sstevel@tonic-gate break; 6167c478bd9Sstevel@tonic-gate } 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate if (level == CY_PASSIVE_LEVEL) 6197c478bd9Sstevel@tonic-gate cyc_coverage[ndx].cyv_passive_count++; 6207c478bd9Sstevel@tonic-gate else 6217c478bd9Sstevel@tonic-gate cyc_coverage[ndx].cyv_count[level]++; 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate cyc_coverage[ndx].cyv_arg0 = arg0; 6247c478bd9Sstevel@tonic-gate cyc_coverage[ndx].cyv_arg1 = arg1; 6257c478bd9Sstevel@tonic-gate } 6267c478bd9Sstevel@tonic-gate 6277c478bd9Sstevel@tonic-gate #define CYC_TRACE(cpu, level, why, arg0, arg1) \ 6287c478bd9Sstevel@tonic-gate CYC_TRACE_IMPL(&cpu->cyp_trace[level], level, why, arg0, arg1) 6297c478bd9Sstevel@tonic-gate 6307c478bd9Sstevel@tonic-gate #define CYC_PTRACE(why, arg0, arg1) \ 6317c478bd9Sstevel@tonic-gate CYC_TRACE_IMPL(&cyc_ptrace, CY_PASSIVE_LEVEL, why, arg0, arg1) 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate #define CYC_TRACE_IMPL(buf, level, why, a0, a1) { \ 6347c478bd9Sstevel@tonic-gate if (panicstr == NULL) { \ 6357c478bd9Sstevel@tonic-gate int _ndx = (buf)->cyt_ndx; \ 6367c478bd9Sstevel@tonic-gate cyc_tracerec_t *_rec = &(buf)->cyt_buf[_ndx]; \ 6377c478bd9Sstevel@tonic-gate (buf)->cyt_ndx = (++_ndx == CY_NTRACEREC) ? 0 : _ndx; \ 6387c478bd9Sstevel@tonic-gate _rec->cyt_tstamp = gethrtime_unscaled(); \ 6397c478bd9Sstevel@tonic-gate _rec->cyt_why = (why); \ 6407c478bd9Sstevel@tonic-gate _rec->cyt_arg0 = (uint64_t)(uintptr_t)(a0); \ 6417c478bd9Sstevel@tonic-gate _rec->cyt_arg1 = (uint64_t)(uintptr_t)(a1); \ 6427c478bd9Sstevel@tonic-gate cyclic_coverage(why, level, \ 6437c478bd9Sstevel@tonic-gate (uint64_t)(uintptr_t)(a0), (uint64_t)(uintptr_t)(a1)); \ 6447c478bd9Sstevel@tonic-gate } \ 6457c478bd9Sstevel@tonic-gate } 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate #else 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate static int cyc_trace_enabled = 0; 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate #define CYC_TRACE(cpu, level, why, arg0, arg1) 6527c478bd9Sstevel@tonic-gate #define CYC_PTRACE(why, arg0, arg1) 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate #endif 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate #define CYC_TRACE0(cpu, level, why) CYC_TRACE(cpu, level, why, 0, 0) 6577c478bd9Sstevel@tonic-gate #define CYC_TRACE1(cpu, level, why, arg0) CYC_TRACE(cpu, level, why, arg0, 0) 6587c478bd9Sstevel@tonic-gate 6597c478bd9Sstevel@tonic-gate #define CYC_PTRACE0(why) CYC_PTRACE(why, 0, 0) 6607c478bd9Sstevel@tonic-gate #define CYC_PTRACE1(why, arg0) CYC_PTRACE(why, arg0, 0) 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate static kmem_cache_t *cyclic_id_cache; 6637c478bd9Sstevel@tonic-gate static cyc_id_t *cyclic_id_head; 6647c478bd9Sstevel@tonic-gate static hrtime_t cyclic_resolution; 6657c478bd9Sstevel@tonic-gate static cyc_backend_t cyclic_backend; 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate /* 6687c478bd9Sstevel@tonic-gate * Returns 1 if the upheap propagated to the root, 0 if it did not. This 6697c478bd9Sstevel@tonic-gate * allows the caller to reprogram the backend only when the root has been 6707c478bd9Sstevel@tonic-gate * modified. 6717c478bd9Sstevel@tonic-gate */ 6727c478bd9Sstevel@tonic-gate static int 6737c478bd9Sstevel@tonic-gate cyclic_upheap(cyc_cpu_t *cpu, cyc_index_t ndx) 6747c478bd9Sstevel@tonic-gate { 6757c478bd9Sstevel@tonic-gate cyclic_t *cyclics; 6767c478bd9Sstevel@tonic-gate cyc_index_t *heap; 6777c478bd9Sstevel@tonic-gate cyc_index_t heap_parent, heap_current = ndx; 6787c478bd9Sstevel@tonic-gate cyc_index_t parent, current; 6797c478bd9Sstevel@tonic-gate 6807c478bd9Sstevel@tonic-gate if (heap_current == 0) 6817c478bd9Sstevel@tonic-gate return (1); 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate heap = cpu->cyp_heap; 6847c478bd9Sstevel@tonic-gate cyclics = cpu->cyp_cyclics; 6857c478bd9Sstevel@tonic-gate heap_parent = CYC_HEAP_PARENT(heap_current); 6867c478bd9Sstevel@tonic-gate 6877c478bd9Sstevel@tonic-gate for (;;) { 6887c478bd9Sstevel@tonic-gate current = heap[heap_current]; 6897c478bd9Sstevel@tonic-gate parent = heap[heap_parent]; 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate /* 6927c478bd9Sstevel@tonic-gate * We have an expiration time later than our parent; we're 6937c478bd9Sstevel@tonic-gate * done. 6947c478bd9Sstevel@tonic-gate */ 6957c478bd9Sstevel@tonic-gate if (cyclics[current].cy_expire >= cyclics[parent].cy_expire) 6967c478bd9Sstevel@tonic-gate return (0); 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate /* 6997c478bd9Sstevel@tonic-gate * We need to swap with our parent, and continue up the heap. 7007c478bd9Sstevel@tonic-gate */ 7017c478bd9Sstevel@tonic-gate heap[heap_parent] = current; 7027c478bd9Sstevel@tonic-gate heap[heap_current] = parent; 7037c478bd9Sstevel@tonic-gate 7047c478bd9Sstevel@tonic-gate /* 7057c478bd9Sstevel@tonic-gate * If we just reached the root, we're done. 7067c478bd9Sstevel@tonic-gate */ 7077c478bd9Sstevel@tonic-gate if (heap_parent == 0) 7087c478bd9Sstevel@tonic-gate return (1); 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate heap_current = heap_parent; 7117c478bd9Sstevel@tonic-gate heap_parent = CYC_HEAP_PARENT(heap_current); 7127c478bd9Sstevel@tonic-gate } 7137c478bd9Sstevel@tonic-gate } 7147c478bd9Sstevel@tonic-gate 7157c478bd9Sstevel@tonic-gate static void 7167c478bd9Sstevel@tonic-gate cyclic_downheap(cyc_cpu_t *cpu, cyc_index_t ndx) 7177c478bd9Sstevel@tonic-gate { 7187c478bd9Sstevel@tonic-gate cyclic_t *cyclics = cpu->cyp_cyclics; 7197c478bd9Sstevel@tonic-gate cyc_index_t *heap = cpu->cyp_heap; 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate cyc_index_t heap_left, heap_right, heap_me = ndx; 7227c478bd9Sstevel@tonic-gate cyc_index_t left, right, me; 7237c478bd9Sstevel@tonic-gate cyc_index_t nelems = cpu->cyp_nelems; 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate for (;;) { 7267c478bd9Sstevel@tonic-gate /* 7277c478bd9Sstevel@tonic-gate * If we don't have a left child (i.e., we're a leaf), we're 7287c478bd9Sstevel@tonic-gate * done. 7297c478bd9Sstevel@tonic-gate */ 7307c478bd9Sstevel@tonic-gate if ((heap_left = CYC_HEAP_LEFT(heap_me)) >= nelems) 7317c478bd9Sstevel@tonic-gate return; 7327c478bd9Sstevel@tonic-gate 7337c478bd9Sstevel@tonic-gate left = heap[heap_left]; 7347c478bd9Sstevel@tonic-gate me = heap[heap_me]; 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate heap_right = CYC_HEAP_RIGHT(heap_me); 7377c478bd9Sstevel@tonic-gate 7387c478bd9Sstevel@tonic-gate /* 7397c478bd9Sstevel@tonic-gate * Even if we don't have a right child, we still need to compare 7407c478bd9Sstevel@tonic-gate * our expiration time against that of our left child. 7417c478bd9Sstevel@tonic-gate */ 7427c478bd9Sstevel@tonic-gate if (heap_right >= nelems) 7437c478bd9Sstevel@tonic-gate goto comp_left; 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate right = heap[heap_right]; 7467c478bd9Sstevel@tonic-gate 7477c478bd9Sstevel@tonic-gate /* 7487c478bd9Sstevel@tonic-gate * We have both a left and a right child. We need to compare 7497c478bd9Sstevel@tonic-gate * the expiration times of the children to determine which 7507c478bd9Sstevel@tonic-gate * expires earlier. 7517c478bd9Sstevel@tonic-gate */ 7527c478bd9Sstevel@tonic-gate if (cyclics[right].cy_expire < cyclics[left].cy_expire) { 7537c478bd9Sstevel@tonic-gate /* 7547c478bd9Sstevel@tonic-gate * Our right child is the earlier of our children. 7557c478bd9Sstevel@tonic-gate * We'll now compare our expiration time to its; if 7567c478bd9Sstevel@tonic-gate * ours is the earlier, we're done. 7577c478bd9Sstevel@tonic-gate */ 7587c478bd9Sstevel@tonic-gate if (cyclics[me].cy_expire <= cyclics[right].cy_expire) 7597c478bd9Sstevel@tonic-gate return; 7607c478bd9Sstevel@tonic-gate 7617c478bd9Sstevel@tonic-gate /* 7627c478bd9Sstevel@tonic-gate * Our right child expires earlier than we do; swap 7637c478bd9Sstevel@tonic-gate * with our right child, and descend right. 7647c478bd9Sstevel@tonic-gate */ 7657c478bd9Sstevel@tonic-gate heap[heap_right] = me; 7667c478bd9Sstevel@tonic-gate heap[heap_me] = right; 7677c478bd9Sstevel@tonic-gate heap_me = heap_right; 7687c478bd9Sstevel@tonic-gate continue; 7697c478bd9Sstevel@tonic-gate } 7707c478bd9Sstevel@tonic-gate 7717c478bd9Sstevel@tonic-gate comp_left: 7727c478bd9Sstevel@tonic-gate /* 7737c478bd9Sstevel@tonic-gate * Our left child is the earlier of our children (or we have 7747c478bd9Sstevel@tonic-gate * no right child). We'll now compare our expiration time 7757c478bd9Sstevel@tonic-gate * to its; if ours is the earlier, we're done. 7767c478bd9Sstevel@tonic-gate */ 7777c478bd9Sstevel@tonic-gate if (cyclics[me].cy_expire <= cyclics[left].cy_expire) 7787c478bd9Sstevel@tonic-gate return; 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate /* 7817c478bd9Sstevel@tonic-gate * Our left child expires earlier than we do; swap with our 7827c478bd9Sstevel@tonic-gate * left child, and descend left. 7837c478bd9Sstevel@tonic-gate */ 7847c478bd9Sstevel@tonic-gate heap[heap_left] = me; 7857c478bd9Sstevel@tonic-gate heap[heap_me] = left; 7867c478bd9Sstevel@tonic-gate heap_me = heap_left; 7877c478bd9Sstevel@tonic-gate } 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate 7907c478bd9Sstevel@tonic-gate static void 7917c478bd9Sstevel@tonic-gate cyclic_expire(cyc_cpu_t *cpu, cyc_index_t ndx, cyclic_t *cyclic) 7927c478bd9Sstevel@tonic-gate { 7937c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 7947c478bd9Sstevel@tonic-gate cyc_level_t level = cyclic->cy_level; 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate /* 7977c478bd9Sstevel@tonic-gate * If this is a CY_HIGH_LEVEL cyclic, just call the handler; we don't 7987c478bd9Sstevel@tonic-gate * need to worry about the pend count for CY_HIGH_LEVEL cyclics. 7997c478bd9Sstevel@tonic-gate */ 8007c478bd9Sstevel@tonic-gate if (level == CY_HIGH_LEVEL) { 8017c478bd9Sstevel@tonic-gate cyc_func_t handler = cyclic->cy_handler; 8027c478bd9Sstevel@tonic-gate void *arg = cyclic->cy_arg; 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "handler-in", handler, arg); 805*c210ded4Sesaxe DTRACE_PROBE1(cyclic__start, cyclic_t *, cyclic); 806*c210ded4Sesaxe 8077c478bd9Sstevel@tonic-gate (*handler)(arg); 808*c210ded4Sesaxe 809*c210ded4Sesaxe DTRACE_PROBE1(cyclic__end, cyclic_t *, cyclic); 8107c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "handler-out", handler, arg); 8117c478bd9Sstevel@tonic-gate 8127c478bd9Sstevel@tonic-gate return; 8137c478bd9Sstevel@tonic-gate } 8147c478bd9Sstevel@tonic-gate 8157c478bd9Sstevel@tonic-gate /* 8167c478bd9Sstevel@tonic-gate * We're at CY_HIGH_LEVEL; this modification to cy_pend need not 8177c478bd9Sstevel@tonic-gate * be atomic (the high interrupt level assures that it will appear 8187c478bd9Sstevel@tonic-gate * atomic to any softint currently running). 8197c478bd9Sstevel@tonic-gate */ 8207c478bd9Sstevel@tonic-gate if (cyclic->cy_pend++ == 0) { 8217c478bd9Sstevel@tonic-gate cyc_softbuf_t *softbuf = &cpu->cyp_softbuf[level]; 8227c478bd9Sstevel@tonic-gate cyc_pcbuffer_t *pc = &softbuf->cys_buf[softbuf->cys_hard]; 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate /* 8257c478bd9Sstevel@tonic-gate * We need to enqueue this cyclic in the soft buffer. 8267c478bd9Sstevel@tonic-gate */ 8277c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "expire-enq", cyclic, 8287c478bd9Sstevel@tonic-gate pc->cypc_prodndx); 8297c478bd9Sstevel@tonic-gate pc->cypc_buf[pc->cypc_prodndx++ & pc->cypc_sizemask] = ndx; 8307c478bd9Sstevel@tonic-gate 8317c478bd9Sstevel@tonic-gate ASSERT(pc->cypc_prodndx != pc->cypc_consndx); 8327c478bd9Sstevel@tonic-gate } else { 8337c478bd9Sstevel@tonic-gate /* 8347c478bd9Sstevel@tonic-gate * If the pend count is zero after we incremented it, then 8357c478bd9Sstevel@tonic-gate * we've wrapped (i.e. we had a cy_pend count of over four 8367c478bd9Sstevel@tonic-gate * billion. In this case, we clamp the pend count at 8377c478bd9Sstevel@tonic-gate * UINT32_MAX. Yes, cyclics can be lost in this case. 8387c478bd9Sstevel@tonic-gate */ 8397c478bd9Sstevel@tonic-gate if (cyclic->cy_pend == 0) { 8407c478bd9Sstevel@tonic-gate CYC_TRACE1(cpu, CY_HIGH_LEVEL, "expire-wrap", cyclic); 8417c478bd9Sstevel@tonic-gate cyclic->cy_pend = UINT32_MAX; 8427c478bd9Sstevel@tonic-gate } 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "expire-bump", cyclic, 0); 8457c478bd9Sstevel@tonic-gate } 8467c478bd9Sstevel@tonic-gate 8477c478bd9Sstevel@tonic-gate be->cyb_softint(be->cyb_arg, cyclic->cy_level); 8487c478bd9Sstevel@tonic-gate } 8497c478bd9Sstevel@tonic-gate 8507c478bd9Sstevel@tonic-gate /* 8517c478bd9Sstevel@tonic-gate * cyclic_fire(cpu_t *) 8527c478bd9Sstevel@tonic-gate * 8537c478bd9Sstevel@tonic-gate * Overview 8547c478bd9Sstevel@tonic-gate * 8557c478bd9Sstevel@tonic-gate * cyclic_fire() is the cyclic subsystem's CY_HIGH_LEVEL interrupt handler. 8567c478bd9Sstevel@tonic-gate * Called by the cyclic backend. 8577c478bd9Sstevel@tonic-gate * 8587c478bd9Sstevel@tonic-gate * Arguments and notes 8597c478bd9Sstevel@tonic-gate * 8607c478bd9Sstevel@tonic-gate * The only argument is the CPU on which the interrupt is executing; 8617c478bd9Sstevel@tonic-gate * backends must call into cyclic_fire() on the specified CPU. 8627c478bd9Sstevel@tonic-gate * 8637c478bd9Sstevel@tonic-gate * cyclic_fire() may be called spuriously without ill effect. Optimal 8647c478bd9Sstevel@tonic-gate * backends will call into cyclic_fire() at or shortly after the time 8657c478bd9Sstevel@tonic-gate * requested via cyb_reprogram(). However, calling cyclic_fire() 8667c478bd9Sstevel@tonic-gate * arbitrarily late will only manifest latency bubbles; the correctness 8677c478bd9Sstevel@tonic-gate * of the cyclic subsystem does not rely on the timeliness of the backend. 8687c478bd9Sstevel@tonic-gate * 8697c478bd9Sstevel@tonic-gate * cyclic_fire() is wait-free; it will not block or spin. 8707c478bd9Sstevel@tonic-gate * 8717c478bd9Sstevel@tonic-gate * Return values 8727c478bd9Sstevel@tonic-gate * 8737c478bd9Sstevel@tonic-gate * None. 8747c478bd9Sstevel@tonic-gate * 8757c478bd9Sstevel@tonic-gate * Caller's context 8767c478bd9Sstevel@tonic-gate * 8777c478bd9Sstevel@tonic-gate * cyclic_fire() must be called from CY_HIGH_LEVEL interrupt context. 8787c478bd9Sstevel@tonic-gate */ 8797c478bd9Sstevel@tonic-gate void 8807c478bd9Sstevel@tonic-gate cyclic_fire(cpu_t *c) 8817c478bd9Sstevel@tonic-gate { 8827c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = c->cpu_cyclic; 8837c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 8847c478bd9Sstevel@tonic-gate cyc_index_t *heap = cpu->cyp_heap; 8857c478bd9Sstevel@tonic-gate cyclic_t *cyclic, *cyclics = cpu->cyp_cyclics; 8867c478bd9Sstevel@tonic-gate void *arg = be->cyb_arg; 8877c478bd9Sstevel@tonic-gate hrtime_t now = gethrtime(); 8887c478bd9Sstevel@tonic-gate hrtime_t exp; 8897c478bd9Sstevel@tonic-gate 8907c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "fire", now, 0); 8917c478bd9Sstevel@tonic-gate 8927c478bd9Sstevel@tonic-gate if (cpu->cyp_nelems == 0) { 8937c478bd9Sstevel@tonic-gate /* 8947c478bd9Sstevel@tonic-gate * This is a spurious fire. Count it as such, and blow 8957c478bd9Sstevel@tonic-gate * out of here. 8967c478bd9Sstevel@tonic-gate */ 8977c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "fire-spurious"); 8987c478bd9Sstevel@tonic-gate return; 8997c478bd9Sstevel@tonic-gate } 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate for (;;) { 9027c478bd9Sstevel@tonic-gate cyc_index_t ndx = heap[0]; 9037c478bd9Sstevel@tonic-gate 9047c478bd9Sstevel@tonic-gate cyclic = &cyclics[ndx]; 9057c478bd9Sstevel@tonic-gate 9067c478bd9Sstevel@tonic-gate ASSERT(!(cyclic->cy_flags & CYF_FREE)); 9077c478bd9Sstevel@tonic-gate 9087c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "fire-check", cyclic, 9097c478bd9Sstevel@tonic-gate cyclic->cy_expire); 9107c478bd9Sstevel@tonic-gate 9117c478bd9Sstevel@tonic-gate if ((exp = cyclic->cy_expire) > now) 9127c478bd9Sstevel@tonic-gate break; 9137c478bd9Sstevel@tonic-gate 9147c478bd9Sstevel@tonic-gate cyclic_expire(cpu, ndx, cyclic); 9157c478bd9Sstevel@tonic-gate 9167c478bd9Sstevel@tonic-gate /* 9177c478bd9Sstevel@tonic-gate * If this cyclic will be set to next expire in the distant 9187c478bd9Sstevel@tonic-gate * past, we have one of two situations: 9197c478bd9Sstevel@tonic-gate * 9207c478bd9Sstevel@tonic-gate * a) This is the first firing of a cyclic which had 9217c478bd9Sstevel@tonic-gate * cy_expire set to 0. 9227c478bd9Sstevel@tonic-gate * 9237c478bd9Sstevel@tonic-gate * b) We are tragically late for a cyclic -- most likely 9247c478bd9Sstevel@tonic-gate * due to being in the debugger. 9257c478bd9Sstevel@tonic-gate * 9267c478bd9Sstevel@tonic-gate * In either case, we set the new expiration time to be the 9277c478bd9Sstevel@tonic-gate * the next interval boundary. This assures that the 9287c478bd9Sstevel@tonic-gate * expiration time modulo the interval is invariant. 9297c478bd9Sstevel@tonic-gate * 9307c478bd9Sstevel@tonic-gate * We arbitrarily define "distant" to be one second (one second 9317c478bd9Sstevel@tonic-gate * is chosen because it's shorter than any foray to the 9327c478bd9Sstevel@tonic-gate * debugger while still being longer than any legitimate 9337c478bd9Sstevel@tonic-gate * stretch at CY_HIGH_LEVEL). 9347c478bd9Sstevel@tonic-gate */ 9357c478bd9Sstevel@tonic-gate exp += cyclic->cy_interval; 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate if (now - exp > NANOSEC) { 9387c478bd9Sstevel@tonic-gate hrtime_t interval = cyclic->cy_interval; 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, exp == interval ? 9417c478bd9Sstevel@tonic-gate "fire-first" : "fire-swing", now, exp); 9427c478bd9Sstevel@tonic-gate 9437c478bd9Sstevel@tonic-gate exp += ((now - exp) / interval + 1) * interval; 9447c478bd9Sstevel@tonic-gate } 9457c478bd9Sstevel@tonic-gate 9467c478bd9Sstevel@tonic-gate cyclic->cy_expire = exp; 9477c478bd9Sstevel@tonic-gate cyclic_downheap(cpu, 0); 9487c478bd9Sstevel@tonic-gate } 9497c478bd9Sstevel@tonic-gate 9507c478bd9Sstevel@tonic-gate /* 9517c478bd9Sstevel@tonic-gate * Now we have a cyclic in the root slot which isn't in the past; 9527c478bd9Sstevel@tonic-gate * reprogram the interrupt source. 9537c478bd9Sstevel@tonic-gate */ 9547c478bd9Sstevel@tonic-gate be->cyb_reprogram(arg, exp); 9557c478bd9Sstevel@tonic-gate } 9567c478bd9Sstevel@tonic-gate 9577c478bd9Sstevel@tonic-gate static void 9587c478bd9Sstevel@tonic-gate cyclic_remove_pend(cyc_cpu_t *cpu, cyc_level_t level, cyclic_t *cyclic) 9597c478bd9Sstevel@tonic-gate { 9607c478bd9Sstevel@tonic-gate cyc_func_t handler = cyclic->cy_handler; 9617c478bd9Sstevel@tonic-gate void *arg = cyclic->cy_arg; 9627c478bd9Sstevel@tonic-gate uint32_t i, rpend = cpu->cyp_rpend - 1; 9637c478bd9Sstevel@tonic-gate 9647c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_flags & CYF_FREE); 9657c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_pend == 0); 9667c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_REMOVING); 9677c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_rpend > 0); 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "remove-rpend", cyclic, cpu->cyp_rpend); 9707c478bd9Sstevel@tonic-gate 9717c478bd9Sstevel@tonic-gate /* 9727c478bd9Sstevel@tonic-gate * Note that we only call the handler cyp_rpend - 1 times; this is 9737c478bd9Sstevel@tonic-gate * to account for the handler call in cyclic_softint(). 9747c478bd9Sstevel@tonic-gate */ 9757c478bd9Sstevel@tonic-gate for (i = 0; i < rpend; i++) { 9767c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "rpend-in", handler, arg); 977*c210ded4Sesaxe DTRACE_PROBE1(cyclic__start, cyclic_t *, cyclic); 978*c210ded4Sesaxe 9797c478bd9Sstevel@tonic-gate (*handler)(arg); 980*c210ded4Sesaxe 981*c210ded4Sesaxe DTRACE_PROBE1(cyclic__end, cyclic_t *, cyclic); 9827c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "rpend-out", handler, arg); 9837c478bd9Sstevel@tonic-gate } 9847c478bd9Sstevel@tonic-gate 9857c478bd9Sstevel@tonic-gate /* 9867c478bd9Sstevel@tonic-gate * We can now let the remove operation complete. 9877c478bd9Sstevel@tonic-gate */ 9887c478bd9Sstevel@tonic-gate sema_v(&cpu->cyp_modify_wait); 9897c478bd9Sstevel@tonic-gate } 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate /* 9927c478bd9Sstevel@tonic-gate * cyclic_softint(cpu_t *cpu, cyc_level_t level) 9937c478bd9Sstevel@tonic-gate * 9947c478bd9Sstevel@tonic-gate * Overview 9957c478bd9Sstevel@tonic-gate * 9967c478bd9Sstevel@tonic-gate * cyclic_softint() is the cyclic subsystem's CY_LOCK_LEVEL and CY_LOW_LEVEL 9977c478bd9Sstevel@tonic-gate * soft interrupt handler. Called by the cyclic backend. 9987c478bd9Sstevel@tonic-gate * 9997c478bd9Sstevel@tonic-gate * Arguments and notes 10007c478bd9Sstevel@tonic-gate * 10017c478bd9Sstevel@tonic-gate * The first argument to cyclic_softint() is the CPU on which the interrupt 10027c478bd9Sstevel@tonic-gate * is executing; backends must call into cyclic_softint() on the specified 10037c478bd9Sstevel@tonic-gate * CPU. The second argument is the level of the soft interrupt; it must 10047c478bd9Sstevel@tonic-gate * be one of CY_LOCK_LEVEL or CY_LOW_LEVEL. 10057c478bd9Sstevel@tonic-gate * 10067c478bd9Sstevel@tonic-gate * cyclic_softint() will call the handlers for cyclics pending at the 10077c478bd9Sstevel@tonic-gate * specified level. cyclic_softint() will not return until all pending 10087c478bd9Sstevel@tonic-gate * cyclics at the specified level have been dealt with; intervening 10097c478bd9Sstevel@tonic-gate * CY_HIGH_LEVEL interrupts which enqueue cyclics at the specified level 10107c478bd9Sstevel@tonic-gate * may therefore prolong cyclic_softint(). 10117c478bd9Sstevel@tonic-gate * 10127c478bd9Sstevel@tonic-gate * cyclic_softint() never disables interrupts, and, if neither a 10137c478bd9Sstevel@tonic-gate * cyclic_add() nor a cyclic_remove() is pending on the specified CPU, is 10147c478bd9Sstevel@tonic-gate * lock-free. This assures that in the common case, cyclic_softint() 10157c478bd9Sstevel@tonic-gate * completes without blocking, and never starves cyclic_fire(). If either 10167c478bd9Sstevel@tonic-gate * cyclic_add() or cyclic_remove() is pending, cyclic_softint() may grab 10177c478bd9Sstevel@tonic-gate * a dispatcher lock. 10187c478bd9Sstevel@tonic-gate * 10197c478bd9Sstevel@tonic-gate * While cyclic_softint() is designed for bounded latency, it is obviously 10207c478bd9Sstevel@tonic-gate * at the mercy of its cyclic handlers. Because cyclic handlers may block 10217c478bd9Sstevel@tonic-gate * arbitrarily, callers of cyclic_softint() should not rely upon 10227c478bd9Sstevel@tonic-gate * deterministic completion. 10237c478bd9Sstevel@tonic-gate * 10247c478bd9Sstevel@tonic-gate * cyclic_softint() may be called spuriously without ill effect. 10257c478bd9Sstevel@tonic-gate * 10267c478bd9Sstevel@tonic-gate * Return value 10277c478bd9Sstevel@tonic-gate * 10287c478bd9Sstevel@tonic-gate * None. 10297c478bd9Sstevel@tonic-gate * 10307c478bd9Sstevel@tonic-gate * Caller's context 10317c478bd9Sstevel@tonic-gate * 10327c478bd9Sstevel@tonic-gate * The caller must be executing in soft interrupt context at either 10337c478bd9Sstevel@tonic-gate * CY_LOCK_LEVEL or CY_LOW_LEVEL. The level passed to cyclic_softint() 10347c478bd9Sstevel@tonic-gate * must match the level at which it is executing. On optimal backends, 10357c478bd9Sstevel@tonic-gate * the caller will hold no locks. In any case, the caller may not hold 10367c478bd9Sstevel@tonic-gate * cpu_lock or any lock acquired by any cyclic handler or held across 10377c478bd9Sstevel@tonic-gate * any of cyclic_add(), cyclic_remove(), cyclic_bind() or cyclic_juggle(). 10387c478bd9Sstevel@tonic-gate */ 10397c478bd9Sstevel@tonic-gate void 10407c478bd9Sstevel@tonic-gate cyclic_softint(cpu_t *c, cyc_level_t level) 10417c478bd9Sstevel@tonic-gate { 10427c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = c->cpu_cyclic; 10437c478bd9Sstevel@tonic-gate cyc_softbuf_t *softbuf; 10447c478bd9Sstevel@tonic-gate int soft, *buf, consndx, resized = 0, intr_resized = 0; 10457c478bd9Sstevel@tonic-gate cyc_pcbuffer_t *pc; 10467c478bd9Sstevel@tonic-gate cyclic_t *cyclics = cpu->cyp_cyclics; 10477c478bd9Sstevel@tonic-gate int sizemask; 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "softint", cyclics, 0); 10507c478bd9Sstevel@tonic-gate 10517c478bd9Sstevel@tonic-gate ASSERT(level < CY_LOW_LEVEL + CY_SOFT_LEVELS); 10527c478bd9Sstevel@tonic-gate 10537c478bd9Sstevel@tonic-gate softbuf = &cpu->cyp_softbuf[level]; 10547c478bd9Sstevel@tonic-gate top: 10557c478bd9Sstevel@tonic-gate soft = softbuf->cys_soft; 10567c478bd9Sstevel@tonic-gate ASSERT(soft == 0 || soft == 1); 10577c478bd9Sstevel@tonic-gate 10587c478bd9Sstevel@tonic-gate pc = &softbuf->cys_buf[soft]; 10597c478bd9Sstevel@tonic-gate buf = pc->cypc_buf; 10607c478bd9Sstevel@tonic-gate consndx = pc->cypc_consndx; 10617c478bd9Sstevel@tonic-gate sizemask = pc->cypc_sizemask; 10627c478bd9Sstevel@tonic-gate 10637c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "softint-top", cyclics, pc); 10647c478bd9Sstevel@tonic-gate 10657c478bd9Sstevel@tonic-gate while (consndx != pc->cypc_prodndx) { 10667c478bd9Sstevel@tonic-gate int pend, npend, opend; 10677c478bd9Sstevel@tonic-gate int consmasked = consndx & sizemask; 10687c478bd9Sstevel@tonic-gate cyclic_t *cyclic = &cyclics[buf[consmasked]]; 10697c478bd9Sstevel@tonic-gate cyc_func_t handler = cyclic->cy_handler; 10707c478bd9Sstevel@tonic-gate void *arg = cyclic->cy_arg; 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate ASSERT(buf[consmasked] < cpu->cyp_size); 10737c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "consuming", consndx, cyclic); 10747c478bd9Sstevel@tonic-gate 10757c478bd9Sstevel@tonic-gate /* 10767c478bd9Sstevel@tonic-gate * We have found this cyclic in the pcbuffer. We know that 10777c478bd9Sstevel@tonic-gate * one of the following is true: 10787c478bd9Sstevel@tonic-gate * 10797c478bd9Sstevel@tonic-gate * (a) The pend is non-zero. We need to execute the handler 10807c478bd9Sstevel@tonic-gate * at least once. 10817c478bd9Sstevel@tonic-gate * 10827c478bd9Sstevel@tonic-gate * (b) The pend _was_ non-zero, but it's now zero due to a 10837c478bd9Sstevel@tonic-gate * resize. We will call the handler once, see that we 10847c478bd9Sstevel@tonic-gate * are in this case, and read the new cyclics buffer 10857c478bd9Sstevel@tonic-gate * (and hence the old non-zero pend). 10867c478bd9Sstevel@tonic-gate * 10877c478bd9Sstevel@tonic-gate * (c) The pend _was_ non-zero, but it's now zero due to a 10887c478bd9Sstevel@tonic-gate * removal. We will call the handler once, see that we 10897c478bd9Sstevel@tonic-gate * are in this case, and call into cyclic_remove_pend() 10907c478bd9Sstevel@tonic-gate * to call the cyclic rpend times. We will take into 10917c478bd9Sstevel@tonic-gate * account that we have already called the handler once. 10927c478bd9Sstevel@tonic-gate * 10937c478bd9Sstevel@tonic-gate * Point is: it's safe to call the handler without first 10947c478bd9Sstevel@tonic-gate * checking the pend. 10957c478bd9Sstevel@tonic-gate */ 10967c478bd9Sstevel@tonic-gate do { 10977c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "handler-in", handler, arg); 1098*c210ded4Sesaxe DTRACE_PROBE1(cyclic__start, cyclic_t *, cyclic); 1099*c210ded4Sesaxe 11007c478bd9Sstevel@tonic-gate (*handler)(arg); 1101*c210ded4Sesaxe 1102*c210ded4Sesaxe DTRACE_PROBE1(cyclic__end, cyclic_t *, cyclic); 11037c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "handler-out", handler, arg); 11047c478bd9Sstevel@tonic-gate reread: 11057c478bd9Sstevel@tonic-gate pend = cyclic->cy_pend; 11067c478bd9Sstevel@tonic-gate npend = pend - 1; 11077c478bd9Sstevel@tonic-gate 11087c478bd9Sstevel@tonic-gate if (pend == 0) { 11097c478bd9Sstevel@tonic-gate if (cpu->cyp_state == CYS_REMOVING) { 11107c478bd9Sstevel@tonic-gate /* 11117c478bd9Sstevel@tonic-gate * This cyclic has been removed while 11127c478bd9Sstevel@tonic-gate * it had a non-zero pend count (we 11137c478bd9Sstevel@tonic-gate * know it was non-zero because we 11147c478bd9Sstevel@tonic-gate * found this cyclic in the pcbuffer). 11157c478bd9Sstevel@tonic-gate * There must be a non-zero rpend for 11167c478bd9Sstevel@tonic-gate * this CPU, and there must be a remove 11177c478bd9Sstevel@tonic-gate * operation blocking; we'll call into 11187c478bd9Sstevel@tonic-gate * cyclic_remove_pend() to clean this 11197c478bd9Sstevel@tonic-gate * up, and break out of the pend loop. 11207c478bd9Sstevel@tonic-gate */ 11217c478bd9Sstevel@tonic-gate cyclic_remove_pend(cpu, level, cyclic); 11227c478bd9Sstevel@tonic-gate break; 11237c478bd9Sstevel@tonic-gate } 11247c478bd9Sstevel@tonic-gate 11257c478bd9Sstevel@tonic-gate /* 11267c478bd9Sstevel@tonic-gate * We must have had a resize interrupt us. 11277c478bd9Sstevel@tonic-gate */ 11287c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "resize-int", cyclics, 0); 11297c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_EXPANDING); 11307c478bd9Sstevel@tonic-gate ASSERT(cyclics != cpu->cyp_cyclics); 11317c478bd9Sstevel@tonic-gate ASSERT(resized == 0); 11327c478bd9Sstevel@tonic-gate ASSERT(intr_resized == 0); 11337c478bd9Sstevel@tonic-gate intr_resized = 1; 11347c478bd9Sstevel@tonic-gate cyclics = cpu->cyp_cyclics; 11357c478bd9Sstevel@tonic-gate cyclic = &cyclics[buf[consmasked]]; 11367c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_handler == handler); 11377c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_arg == arg); 11387c478bd9Sstevel@tonic-gate goto reread; 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate 11417c478bd9Sstevel@tonic-gate if ((opend = 11427c478bd9Sstevel@tonic-gate cas32(&cyclic->cy_pend, pend, npend)) != pend) { 11437c478bd9Sstevel@tonic-gate /* 11447c478bd9Sstevel@tonic-gate * Our cas32 can fail for one of several 11457c478bd9Sstevel@tonic-gate * reasons: 11467c478bd9Sstevel@tonic-gate * 11477c478bd9Sstevel@tonic-gate * (a) An intervening high level bumped up the 11487c478bd9Sstevel@tonic-gate * pend count on this cyclic. In this 11497c478bd9Sstevel@tonic-gate * case, we will see a higher pend. 11507c478bd9Sstevel@tonic-gate * 11517c478bd9Sstevel@tonic-gate * (b) The cyclics array has been yanked out 11527c478bd9Sstevel@tonic-gate * from underneath us by a resize 11537c478bd9Sstevel@tonic-gate * operation. In this case, pend is 0 and 11547c478bd9Sstevel@tonic-gate * cyp_state is CYS_EXPANDING. 11557c478bd9Sstevel@tonic-gate * 11567c478bd9Sstevel@tonic-gate * (c) The cyclic has been removed by an 11577c478bd9Sstevel@tonic-gate * intervening remove-xcall. In this case, 11587c478bd9Sstevel@tonic-gate * pend will be 0, the cyp_state will be 11597c478bd9Sstevel@tonic-gate * CYS_REMOVING, and the cyclic will be 11607c478bd9Sstevel@tonic-gate * marked CYF_FREE. 11617c478bd9Sstevel@tonic-gate * 11627c478bd9Sstevel@tonic-gate * The assertion below checks that we are 11637c478bd9Sstevel@tonic-gate * in one of the above situations. The 11647c478bd9Sstevel@tonic-gate * action under all three is to return to 11657c478bd9Sstevel@tonic-gate * the top of the loop. 11667c478bd9Sstevel@tonic-gate */ 11677c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "cas-fail", opend, pend); 11687c478bd9Sstevel@tonic-gate ASSERT(opend > pend || (opend == 0 && 11697c478bd9Sstevel@tonic-gate ((cyclics != cpu->cyp_cyclics && 11707c478bd9Sstevel@tonic-gate cpu->cyp_state == CYS_EXPANDING) || 11717c478bd9Sstevel@tonic-gate (cpu->cyp_state == CYS_REMOVING && 11727c478bd9Sstevel@tonic-gate (cyclic->cy_flags & CYF_FREE))))); 11737c478bd9Sstevel@tonic-gate goto reread; 11747c478bd9Sstevel@tonic-gate } 11757c478bd9Sstevel@tonic-gate 11767c478bd9Sstevel@tonic-gate /* 11777c478bd9Sstevel@tonic-gate * Okay, so we've managed to successfully decrement 11787c478bd9Sstevel@tonic-gate * pend. If we just decremented the pend to 0, we're 11797c478bd9Sstevel@tonic-gate * done. 11807c478bd9Sstevel@tonic-gate */ 11817c478bd9Sstevel@tonic-gate } while (npend > 0); 11827c478bd9Sstevel@tonic-gate 11837c478bd9Sstevel@tonic-gate pc->cypc_consndx = ++consndx; 11847c478bd9Sstevel@tonic-gate } 11857c478bd9Sstevel@tonic-gate 11867c478bd9Sstevel@tonic-gate /* 11877c478bd9Sstevel@tonic-gate * If the high level handler is no longer writing to the same 11887c478bd9Sstevel@tonic-gate * buffer, then we've had a resize. We need to switch our soft 11897c478bd9Sstevel@tonic-gate * index, and goto top. 11907c478bd9Sstevel@tonic-gate */ 11917c478bd9Sstevel@tonic-gate if (soft != softbuf->cys_hard) { 11927c478bd9Sstevel@tonic-gate /* 11937c478bd9Sstevel@tonic-gate * We can assert that the other buffer has grown by exactly 11947c478bd9Sstevel@tonic-gate * one factor of two. 11957c478bd9Sstevel@tonic-gate */ 11967c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, level, "buffer-grow", 0, 0); 11977c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_EXPANDING); 11987c478bd9Sstevel@tonic-gate ASSERT(softbuf->cys_buf[softbuf->cys_hard].cypc_sizemask == 11997c478bd9Sstevel@tonic-gate (softbuf->cys_buf[soft].cypc_sizemask << 1) + 1 || 12007c478bd9Sstevel@tonic-gate softbuf->cys_buf[soft].cypc_sizemask == 0); 12017c478bd9Sstevel@tonic-gate ASSERT(softbuf->cys_hard == (softbuf->cys_soft ^ 1)); 12027c478bd9Sstevel@tonic-gate 12037c478bd9Sstevel@tonic-gate /* 12047c478bd9Sstevel@tonic-gate * If our cached cyclics pointer doesn't match cyp_cyclics, 12057c478bd9Sstevel@tonic-gate * then we took a resize between our last iteration of the 12067c478bd9Sstevel@tonic-gate * pend loop and the check against softbuf->cys_hard. 12077c478bd9Sstevel@tonic-gate */ 12087c478bd9Sstevel@tonic-gate if (cpu->cyp_cyclics != cyclics) { 12097c478bd9Sstevel@tonic-gate CYC_TRACE1(cpu, level, "resize-int-int", consndx); 12107c478bd9Sstevel@tonic-gate cyclics = cpu->cyp_cyclics; 12117c478bd9Sstevel@tonic-gate } 12127c478bd9Sstevel@tonic-gate 12137c478bd9Sstevel@tonic-gate softbuf->cys_soft = softbuf->cys_hard; 12147c478bd9Sstevel@tonic-gate 12157c478bd9Sstevel@tonic-gate ASSERT(resized == 0); 12167c478bd9Sstevel@tonic-gate resized = 1; 12177c478bd9Sstevel@tonic-gate goto top; 12187c478bd9Sstevel@tonic-gate } 12197c478bd9Sstevel@tonic-gate 12207c478bd9Sstevel@tonic-gate /* 12217c478bd9Sstevel@tonic-gate * If we were interrupted by a resize operation, then we must have 12227c478bd9Sstevel@tonic-gate * seen the hard index change. 12237c478bd9Sstevel@tonic-gate */ 12247c478bd9Sstevel@tonic-gate ASSERT(!(intr_resized == 1 && resized == 0)); 12257c478bd9Sstevel@tonic-gate 12267c478bd9Sstevel@tonic-gate if (resized) { 12277c478bd9Sstevel@tonic-gate uint32_t lev, nlev; 12287c478bd9Sstevel@tonic-gate 12297c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_EXPANDING); 12307c478bd9Sstevel@tonic-gate 12317c478bd9Sstevel@tonic-gate do { 12327c478bd9Sstevel@tonic-gate lev = cpu->cyp_modify_levels; 12337c478bd9Sstevel@tonic-gate nlev = lev + 1; 12347c478bd9Sstevel@tonic-gate } while (cas32(&cpu->cyp_modify_levels, lev, nlev) != lev); 12357c478bd9Sstevel@tonic-gate 12367c478bd9Sstevel@tonic-gate /* 12377c478bd9Sstevel@tonic-gate * If we are the last soft level to see the modification, 12387c478bd9Sstevel@tonic-gate * post on cyp_modify_wait. Otherwise, (if we're not 12397c478bd9Sstevel@tonic-gate * already at low level), post down to the next soft level. 12407c478bd9Sstevel@tonic-gate */ 12417c478bd9Sstevel@tonic-gate if (nlev == CY_SOFT_LEVELS) { 12427c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, level, "resize-kick"); 12437c478bd9Sstevel@tonic-gate sema_v(&cpu->cyp_modify_wait); 12447c478bd9Sstevel@tonic-gate } else { 12457c478bd9Sstevel@tonic-gate ASSERT(nlev < CY_SOFT_LEVELS); 12467c478bd9Sstevel@tonic-gate if (level != CY_LOW_LEVEL) { 12477c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 12487c478bd9Sstevel@tonic-gate 12497c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, level, "resize-post"); 12507c478bd9Sstevel@tonic-gate be->cyb_softint(be->cyb_arg, level - 1); 12517c478bd9Sstevel@tonic-gate } 12527c478bd9Sstevel@tonic-gate } 12537c478bd9Sstevel@tonic-gate } 12547c478bd9Sstevel@tonic-gate } 12557c478bd9Sstevel@tonic-gate 12567c478bd9Sstevel@tonic-gate static void 12577c478bd9Sstevel@tonic-gate cyclic_expand_xcall(cyc_xcallarg_t *arg) 12587c478bd9Sstevel@tonic-gate { 12597c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = arg->cyx_cpu; 12607c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 12617c478bd9Sstevel@tonic-gate cyb_arg_t bar = be->cyb_arg; 12627c478bd9Sstevel@tonic-gate cyc_cookie_t cookie; 12637c478bd9Sstevel@tonic-gate cyc_index_t new_size = arg->cyx_size, size = cpu->cyp_size, i; 12647c478bd9Sstevel@tonic-gate cyc_index_t *new_heap = arg->cyx_heap; 12657c478bd9Sstevel@tonic-gate cyclic_t *cyclics = cpu->cyp_cyclics, *new_cyclics = arg->cyx_cyclics; 12667c478bd9Sstevel@tonic-gate 12677c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_EXPANDING); 12687c478bd9Sstevel@tonic-gate 12697c478bd9Sstevel@tonic-gate /* 12707c478bd9Sstevel@tonic-gate * This is a little dicey. First, we'll raise our interrupt level 12717c478bd9Sstevel@tonic-gate * to CY_HIGH_LEVEL. This CPU already has a new heap, cyclic array, 12727c478bd9Sstevel@tonic-gate * etc.; we just need to bcopy them across. As for the softint 12737c478bd9Sstevel@tonic-gate * buffers, we'll switch the active buffers. The actual softints will 12747c478bd9Sstevel@tonic-gate * take care of consuming any pending cyclics in the old buffer. 12757c478bd9Sstevel@tonic-gate */ 12767c478bd9Sstevel@tonic-gate cookie = be->cyb_set_level(bar, CY_HIGH_LEVEL); 12777c478bd9Sstevel@tonic-gate 12787c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "expand", new_size, 0); 12797c478bd9Sstevel@tonic-gate 12807c478bd9Sstevel@tonic-gate /* 12817c478bd9Sstevel@tonic-gate * Assert that the new size is a power of 2. 12827c478bd9Sstevel@tonic-gate */ 12837c478bd9Sstevel@tonic-gate ASSERT((new_size & new_size - 1) == 0); 12847c478bd9Sstevel@tonic-gate ASSERT(new_size == (size << 1)); 12857c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_heap != NULL && cpu->cyp_cyclics != NULL); 12867c478bd9Sstevel@tonic-gate 12877c478bd9Sstevel@tonic-gate bcopy(cpu->cyp_heap, new_heap, sizeof (cyc_index_t) * size); 12887c478bd9Sstevel@tonic-gate bcopy(cyclics, new_cyclics, sizeof (cyclic_t) * size); 12897c478bd9Sstevel@tonic-gate 12907c478bd9Sstevel@tonic-gate /* 12917c478bd9Sstevel@tonic-gate * Now run through the old cyclics array, setting pend to 0. To 12927c478bd9Sstevel@tonic-gate * softints (which are executing at a lower priority level), the 12937c478bd9Sstevel@tonic-gate * pends dropping to 0 will appear atomic with the cyp_cyclics 12947c478bd9Sstevel@tonic-gate * pointer changing. 12957c478bd9Sstevel@tonic-gate */ 12967c478bd9Sstevel@tonic-gate for (i = 0; i < size; i++) 12977c478bd9Sstevel@tonic-gate cyclics[i].cy_pend = 0; 12987c478bd9Sstevel@tonic-gate 12997c478bd9Sstevel@tonic-gate /* 13007c478bd9Sstevel@tonic-gate * Set up the free list, and set all of the new cyclics to be CYF_FREE. 13017c478bd9Sstevel@tonic-gate */ 13027c478bd9Sstevel@tonic-gate for (i = size; i < new_size; i++) { 13037c478bd9Sstevel@tonic-gate new_heap[i] = i; 13047c478bd9Sstevel@tonic-gate new_cyclics[i].cy_flags = CYF_FREE; 13057c478bd9Sstevel@tonic-gate } 13067c478bd9Sstevel@tonic-gate 13077c478bd9Sstevel@tonic-gate /* 13087c478bd9Sstevel@tonic-gate * We can go ahead and plow the value of cyp_heap and cyp_cyclics; 13097c478bd9Sstevel@tonic-gate * cyclic_expand() has kept a copy. 13107c478bd9Sstevel@tonic-gate */ 13117c478bd9Sstevel@tonic-gate cpu->cyp_heap = new_heap; 13127c478bd9Sstevel@tonic-gate cpu->cyp_cyclics = new_cyclics; 13137c478bd9Sstevel@tonic-gate cpu->cyp_size = new_size; 13147c478bd9Sstevel@tonic-gate 13157c478bd9Sstevel@tonic-gate /* 13167c478bd9Sstevel@tonic-gate * We've switched over the heap and the cyclics array. Now we need 13177c478bd9Sstevel@tonic-gate * to switch over our active softint buffer pointers. 13187c478bd9Sstevel@tonic-gate */ 13197c478bd9Sstevel@tonic-gate for (i = CY_LOW_LEVEL; i < CY_LOW_LEVEL + CY_SOFT_LEVELS; i++) { 13207c478bd9Sstevel@tonic-gate cyc_softbuf_t *softbuf = &cpu->cyp_softbuf[i]; 13217c478bd9Sstevel@tonic-gate uchar_t hard = softbuf->cys_hard; 13227c478bd9Sstevel@tonic-gate 13237c478bd9Sstevel@tonic-gate /* 13247c478bd9Sstevel@tonic-gate * Assert that we're not in the middle of a resize operation. 13257c478bd9Sstevel@tonic-gate */ 13267c478bd9Sstevel@tonic-gate ASSERT(hard == softbuf->cys_soft); 13277c478bd9Sstevel@tonic-gate ASSERT(hard == 0 || hard == 1); 13287c478bd9Sstevel@tonic-gate ASSERT(softbuf->cys_buf[hard].cypc_buf != NULL); 13297c478bd9Sstevel@tonic-gate 13307c478bd9Sstevel@tonic-gate softbuf->cys_hard = hard ^ 1; 13317c478bd9Sstevel@tonic-gate 13327c478bd9Sstevel@tonic-gate /* 13337c478bd9Sstevel@tonic-gate * The caller (cyclic_expand()) is responsible for setting 13347c478bd9Sstevel@tonic-gate * up the new producer-consumer buffer; assert that it's 13357c478bd9Sstevel@tonic-gate * been done correctly. 13367c478bd9Sstevel@tonic-gate */ 13377c478bd9Sstevel@tonic-gate ASSERT(softbuf->cys_buf[hard ^ 1].cypc_buf != NULL); 13387c478bd9Sstevel@tonic-gate ASSERT(softbuf->cys_buf[hard ^ 1].cypc_prodndx == 0); 13397c478bd9Sstevel@tonic-gate ASSERT(softbuf->cys_buf[hard ^ 1].cypc_consndx == 0); 13407c478bd9Sstevel@tonic-gate } 13417c478bd9Sstevel@tonic-gate 13427c478bd9Sstevel@tonic-gate /* 13437c478bd9Sstevel@tonic-gate * That's all there is to it; now we just need to postdown to 13447c478bd9Sstevel@tonic-gate * get the softint chain going. 13457c478bd9Sstevel@tonic-gate */ 13467c478bd9Sstevel@tonic-gate be->cyb_softint(bar, CY_HIGH_LEVEL - 1); 13477c478bd9Sstevel@tonic-gate be->cyb_restore_level(bar, cookie); 13487c478bd9Sstevel@tonic-gate } 13497c478bd9Sstevel@tonic-gate 13507c478bd9Sstevel@tonic-gate /* 13517c478bd9Sstevel@tonic-gate * cyclic_expand() will cross call onto the CPU to perform the actual 13527c478bd9Sstevel@tonic-gate * expand operation. 13537c478bd9Sstevel@tonic-gate */ 13547c478bd9Sstevel@tonic-gate static void 13557c478bd9Sstevel@tonic-gate cyclic_expand(cyc_cpu_t *cpu) 13567c478bd9Sstevel@tonic-gate { 13577c478bd9Sstevel@tonic-gate cyc_index_t new_size, old_size; 13587c478bd9Sstevel@tonic-gate cyc_index_t *new_heap, *old_heap; 13597c478bd9Sstevel@tonic-gate cyclic_t *new_cyclics, *old_cyclics; 13607c478bd9Sstevel@tonic-gate cyc_xcallarg_t arg; 13617c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 13627c478bd9Sstevel@tonic-gate char old_hard; 13637c478bd9Sstevel@tonic-gate int i; 13647c478bd9Sstevel@tonic-gate 13657c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 13667c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 13677c478bd9Sstevel@tonic-gate 13687c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_EXPANDING; 13697c478bd9Sstevel@tonic-gate 13707c478bd9Sstevel@tonic-gate old_heap = cpu->cyp_heap; 13717c478bd9Sstevel@tonic-gate old_cyclics = cpu->cyp_cyclics; 13727c478bd9Sstevel@tonic-gate 13737c478bd9Sstevel@tonic-gate if ((new_size = ((old_size = cpu->cyp_size) << 1)) == 0) { 13747c478bd9Sstevel@tonic-gate new_size = CY_DEFAULT_PERCPU; 13757c478bd9Sstevel@tonic-gate ASSERT(old_heap == NULL && old_cyclics == NULL); 13767c478bd9Sstevel@tonic-gate } 13777c478bd9Sstevel@tonic-gate 13787c478bd9Sstevel@tonic-gate /* 13797c478bd9Sstevel@tonic-gate * Check that the new_size is a power of 2. 13807c478bd9Sstevel@tonic-gate */ 13817c478bd9Sstevel@tonic-gate ASSERT((new_size - 1 & new_size) == 0); 13827c478bd9Sstevel@tonic-gate 13837c478bd9Sstevel@tonic-gate new_heap = kmem_alloc(sizeof (cyc_index_t) * new_size, KM_SLEEP); 13847c478bd9Sstevel@tonic-gate new_cyclics = kmem_zalloc(sizeof (cyclic_t) * new_size, KM_SLEEP); 13857c478bd9Sstevel@tonic-gate 13867c478bd9Sstevel@tonic-gate /* 13877c478bd9Sstevel@tonic-gate * We know that no other expansions are in progress (they serialize 13887c478bd9Sstevel@tonic-gate * on cpu_lock), so we can safely read the softbuf metadata. 13897c478bd9Sstevel@tonic-gate */ 13907c478bd9Sstevel@tonic-gate old_hard = cpu->cyp_softbuf[0].cys_hard; 13917c478bd9Sstevel@tonic-gate 13927c478bd9Sstevel@tonic-gate for (i = CY_LOW_LEVEL; i < CY_LOW_LEVEL + CY_SOFT_LEVELS; i++) { 13937c478bd9Sstevel@tonic-gate cyc_softbuf_t *softbuf = &cpu->cyp_softbuf[i]; 13947c478bd9Sstevel@tonic-gate char hard = softbuf->cys_hard; 13957c478bd9Sstevel@tonic-gate cyc_pcbuffer_t *pc = &softbuf->cys_buf[hard ^ 1]; 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate ASSERT(hard == old_hard); 13987c478bd9Sstevel@tonic-gate ASSERT(hard == softbuf->cys_soft); 13997c478bd9Sstevel@tonic-gate ASSERT(pc->cypc_buf == NULL); 14007c478bd9Sstevel@tonic-gate 14017c478bd9Sstevel@tonic-gate pc->cypc_buf = 14027c478bd9Sstevel@tonic-gate kmem_alloc(sizeof (cyc_index_t) * new_size, KM_SLEEP); 14037c478bd9Sstevel@tonic-gate pc->cypc_prodndx = pc->cypc_consndx = 0; 14047c478bd9Sstevel@tonic-gate pc->cypc_sizemask = new_size - 1; 14057c478bd9Sstevel@tonic-gate } 14067c478bd9Sstevel@tonic-gate 14077c478bd9Sstevel@tonic-gate arg.cyx_cpu = cpu; 14087c478bd9Sstevel@tonic-gate arg.cyx_heap = new_heap; 14097c478bd9Sstevel@tonic-gate arg.cyx_cyclics = new_cyclics; 14107c478bd9Sstevel@tonic-gate arg.cyx_size = new_size; 14117c478bd9Sstevel@tonic-gate 14127c478bd9Sstevel@tonic-gate cpu->cyp_modify_levels = 0; 14137c478bd9Sstevel@tonic-gate 14147c478bd9Sstevel@tonic-gate be->cyb_xcall(be->cyb_arg, cpu->cyp_cpu, 14157c478bd9Sstevel@tonic-gate (cyc_func_t)cyclic_expand_xcall, &arg); 14167c478bd9Sstevel@tonic-gate 14177c478bd9Sstevel@tonic-gate /* 14187c478bd9Sstevel@tonic-gate * Now block, waiting for the resize operation to complete. 14197c478bd9Sstevel@tonic-gate */ 14207c478bd9Sstevel@tonic-gate sema_p(&cpu->cyp_modify_wait); 14217c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_modify_levels == CY_SOFT_LEVELS); 14227c478bd9Sstevel@tonic-gate 14237c478bd9Sstevel@tonic-gate /* 14247c478bd9Sstevel@tonic-gate * The operation is complete; we can now free the old buffers. 14257c478bd9Sstevel@tonic-gate */ 14267c478bd9Sstevel@tonic-gate for (i = CY_LOW_LEVEL; i < CY_LOW_LEVEL + CY_SOFT_LEVELS; i++) { 14277c478bd9Sstevel@tonic-gate cyc_softbuf_t *softbuf = &cpu->cyp_softbuf[i]; 14287c478bd9Sstevel@tonic-gate char hard = softbuf->cys_hard; 14297c478bd9Sstevel@tonic-gate cyc_pcbuffer_t *pc = &softbuf->cys_buf[hard ^ 1]; 14307c478bd9Sstevel@tonic-gate 14317c478bd9Sstevel@tonic-gate ASSERT(hard == (old_hard ^ 1)); 14327c478bd9Sstevel@tonic-gate ASSERT(hard == softbuf->cys_soft); 14337c478bd9Sstevel@tonic-gate 14347c478bd9Sstevel@tonic-gate if (pc->cypc_buf == NULL) 14357c478bd9Sstevel@tonic-gate continue; 14367c478bd9Sstevel@tonic-gate 14377c478bd9Sstevel@tonic-gate ASSERT(pc->cypc_sizemask == ((new_size - 1) >> 1)); 14387c478bd9Sstevel@tonic-gate 14397c478bd9Sstevel@tonic-gate kmem_free(pc->cypc_buf, 14407c478bd9Sstevel@tonic-gate sizeof (cyc_index_t) * (pc->cypc_sizemask + 1)); 14417c478bd9Sstevel@tonic-gate pc->cypc_buf = NULL; 14427c478bd9Sstevel@tonic-gate } 14437c478bd9Sstevel@tonic-gate 14447c478bd9Sstevel@tonic-gate if (old_cyclics != NULL) { 14457c478bd9Sstevel@tonic-gate ASSERT(old_heap != NULL); 14467c478bd9Sstevel@tonic-gate ASSERT(old_size != 0); 14477c478bd9Sstevel@tonic-gate kmem_free(old_cyclics, sizeof (cyclic_t) * old_size); 14487c478bd9Sstevel@tonic-gate kmem_free(old_heap, sizeof (cyc_index_t) * old_size); 14497c478bd9Sstevel@tonic-gate } 14507c478bd9Sstevel@tonic-gate 14517c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_EXPANDING); 14527c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_ONLINE; 14537c478bd9Sstevel@tonic-gate } 14547c478bd9Sstevel@tonic-gate 14557c478bd9Sstevel@tonic-gate /* 14567c478bd9Sstevel@tonic-gate * cyclic_pick_cpu will attempt to pick a CPU according to the constraints 14577c478bd9Sstevel@tonic-gate * specified by the partition, bound CPU, and flags. Additionally, 14587c478bd9Sstevel@tonic-gate * cyclic_pick_cpu() will not pick the avoid CPU; it will return NULL if 14597c478bd9Sstevel@tonic-gate * the avoid CPU is the only CPU which satisfies the constraints. 14607c478bd9Sstevel@tonic-gate * 14617c478bd9Sstevel@tonic-gate * If CYF_CPU_BOUND is set in flags, the specified CPU must be non-NULL. 14627c478bd9Sstevel@tonic-gate * If CYF_PART_BOUND is set in flags, the specified partition must be non-NULL. 14637c478bd9Sstevel@tonic-gate * If both CYF_CPU_BOUND and CYF_PART_BOUND are set, the specified CPU must 14647c478bd9Sstevel@tonic-gate * be in the specified partition. 14657c478bd9Sstevel@tonic-gate */ 14667c478bd9Sstevel@tonic-gate static cyc_cpu_t * 14677c478bd9Sstevel@tonic-gate cyclic_pick_cpu(cpupart_t *part, cpu_t *bound, cpu_t *avoid, uint16_t flags) 14687c478bd9Sstevel@tonic-gate { 14697c478bd9Sstevel@tonic-gate cpu_t *c, *start = (part != NULL) ? part->cp_cpulist : CPU; 14707c478bd9Sstevel@tonic-gate cpu_t *online = NULL; 14717c478bd9Sstevel@tonic-gate uintptr_t offset; 14727c478bd9Sstevel@tonic-gate 14737c478bd9Sstevel@tonic-gate CYC_PTRACE("pick-cpu", part, bound); 14747c478bd9Sstevel@tonic-gate 14757c478bd9Sstevel@tonic-gate ASSERT(!(flags & CYF_CPU_BOUND) || bound != NULL); 14767c478bd9Sstevel@tonic-gate ASSERT(!(flags & CYF_PART_BOUND) || part != NULL); 14777c478bd9Sstevel@tonic-gate 14787c478bd9Sstevel@tonic-gate /* 14797c478bd9Sstevel@tonic-gate * If we're bound to our CPU, there isn't much choice involved. We 14807c478bd9Sstevel@tonic-gate * need to check that the CPU passed as bound is in the cpupart, and 14817c478bd9Sstevel@tonic-gate * that the CPU that we're binding to has been configured. 14827c478bd9Sstevel@tonic-gate */ 14837c478bd9Sstevel@tonic-gate if (flags & CYF_CPU_BOUND) { 14847c478bd9Sstevel@tonic-gate CYC_PTRACE("pick-cpu-bound", bound, avoid); 14857c478bd9Sstevel@tonic-gate 14867c478bd9Sstevel@tonic-gate if ((flags & CYF_PART_BOUND) && bound->cpu_part != part) 14877c478bd9Sstevel@tonic-gate panic("cyclic_pick_cpu: " 14887c478bd9Sstevel@tonic-gate "CPU binding contradicts partition binding"); 14897c478bd9Sstevel@tonic-gate 14907c478bd9Sstevel@tonic-gate if (bound == avoid) 14917c478bd9Sstevel@tonic-gate return (NULL); 14927c478bd9Sstevel@tonic-gate 14937c478bd9Sstevel@tonic-gate if (bound->cpu_cyclic == NULL) 14947c478bd9Sstevel@tonic-gate panic("cyclic_pick_cpu: " 14957c478bd9Sstevel@tonic-gate "attempt to bind to non-configured CPU"); 14967c478bd9Sstevel@tonic-gate 14977c478bd9Sstevel@tonic-gate return (bound->cpu_cyclic); 14987c478bd9Sstevel@tonic-gate } 14997c478bd9Sstevel@tonic-gate 15007c478bd9Sstevel@tonic-gate if (flags & CYF_PART_BOUND) { 15017c478bd9Sstevel@tonic-gate CYC_PTRACE("pick-part-bound", bound, avoid); 15027c478bd9Sstevel@tonic-gate offset = offsetof(cpu_t, cpu_next_part); 15037c478bd9Sstevel@tonic-gate } else { 15047c478bd9Sstevel@tonic-gate offset = offsetof(cpu_t, cpu_next_onln); 15057c478bd9Sstevel@tonic-gate } 15067c478bd9Sstevel@tonic-gate 15077c478bd9Sstevel@tonic-gate c = start; 15087c478bd9Sstevel@tonic-gate do { 15097c478bd9Sstevel@tonic-gate if (c->cpu_cyclic == NULL) 15107c478bd9Sstevel@tonic-gate continue; 15117c478bd9Sstevel@tonic-gate 15127c478bd9Sstevel@tonic-gate if (c->cpu_cyclic->cyp_state == CYS_OFFLINE) 15137c478bd9Sstevel@tonic-gate continue; 15147c478bd9Sstevel@tonic-gate 15157c478bd9Sstevel@tonic-gate if (c == avoid) 15167c478bd9Sstevel@tonic-gate continue; 15177c478bd9Sstevel@tonic-gate 15187c478bd9Sstevel@tonic-gate if (c->cpu_flags & CPU_ENABLE) 15197c478bd9Sstevel@tonic-gate goto found; 15207c478bd9Sstevel@tonic-gate 15217c478bd9Sstevel@tonic-gate if (online == NULL) 15227c478bd9Sstevel@tonic-gate online = c; 15237c478bd9Sstevel@tonic-gate } while ((c = *(cpu_t **)((uintptr_t)c + offset)) != start); 15247c478bd9Sstevel@tonic-gate 15257c478bd9Sstevel@tonic-gate /* 15267c478bd9Sstevel@tonic-gate * If we're here, we're in one of two situations: 15277c478bd9Sstevel@tonic-gate * 15287c478bd9Sstevel@tonic-gate * (a) We have a partition-bound cyclic, and there is no CPU in 15297c478bd9Sstevel@tonic-gate * our partition which is CPU_ENABLE'd. If we saw another 15307c478bd9Sstevel@tonic-gate * non-CYS_OFFLINE CPU in our partition, we'll go with it. 15317c478bd9Sstevel@tonic-gate * If not, the avoid CPU must be the only non-CYS_OFFLINE 15327c478bd9Sstevel@tonic-gate * CPU in the partition; we're forced to return NULL. 15337c478bd9Sstevel@tonic-gate * 15347c478bd9Sstevel@tonic-gate * (b) We have a partition-unbound cyclic, in which case there 15357c478bd9Sstevel@tonic-gate * must only be one CPU CPU_ENABLE'd, and it must be the one 15367c478bd9Sstevel@tonic-gate * we're trying to avoid. If cyclic_juggle()/cyclic_offline() 15377c478bd9Sstevel@tonic-gate * are called appropriately, this generally shouldn't happen 15387c478bd9Sstevel@tonic-gate * (the offline should fail before getting to this code). 15397c478bd9Sstevel@tonic-gate * At any rate: we can't avoid the avoid CPU, so we return 15407c478bd9Sstevel@tonic-gate * NULL. 15417c478bd9Sstevel@tonic-gate */ 15427c478bd9Sstevel@tonic-gate if (!(flags & CYF_PART_BOUND)) { 15437c478bd9Sstevel@tonic-gate ASSERT(avoid->cpu_flags & CPU_ENABLE); 15447c478bd9Sstevel@tonic-gate return (NULL); 15457c478bd9Sstevel@tonic-gate } 15467c478bd9Sstevel@tonic-gate 15477c478bd9Sstevel@tonic-gate CYC_PTRACE("pick-no-intr", part, avoid); 15487c478bd9Sstevel@tonic-gate 15497c478bd9Sstevel@tonic-gate if ((c = online) != NULL) 15507c478bd9Sstevel@tonic-gate goto found; 15517c478bd9Sstevel@tonic-gate 15527c478bd9Sstevel@tonic-gate CYC_PTRACE("pick-fail", part, avoid); 15537c478bd9Sstevel@tonic-gate ASSERT(avoid->cpu_part == start->cpu_part); 15547c478bd9Sstevel@tonic-gate return (NULL); 15557c478bd9Sstevel@tonic-gate 15567c478bd9Sstevel@tonic-gate found: 15577c478bd9Sstevel@tonic-gate CYC_PTRACE("pick-cpu-found", c, avoid); 15587c478bd9Sstevel@tonic-gate ASSERT(c != avoid); 15597c478bd9Sstevel@tonic-gate ASSERT(c->cpu_cyclic != NULL); 15607c478bd9Sstevel@tonic-gate 15617c478bd9Sstevel@tonic-gate return (c->cpu_cyclic); 15627c478bd9Sstevel@tonic-gate } 15637c478bd9Sstevel@tonic-gate 15647c478bd9Sstevel@tonic-gate static void 15657c478bd9Sstevel@tonic-gate cyclic_add_xcall(cyc_xcallarg_t *arg) 15667c478bd9Sstevel@tonic-gate { 15677c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = arg->cyx_cpu; 15687c478bd9Sstevel@tonic-gate cyc_handler_t *hdlr = arg->cyx_hdlr; 15697c478bd9Sstevel@tonic-gate cyc_time_t *when = arg->cyx_when; 15707c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 15717c478bd9Sstevel@tonic-gate cyc_index_t ndx, nelems; 15727c478bd9Sstevel@tonic-gate cyc_cookie_t cookie; 15737c478bd9Sstevel@tonic-gate cyb_arg_t bar = be->cyb_arg; 15747c478bd9Sstevel@tonic-gate cyclic_t *cyclic; 15757c478bd9Sstevel@tonic-gate 15767c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_nelems < cpu->cyp_size); 15777c478bd9Sstevel@tonic-gate 15787c478bd9Sstevel@tonic-gate cookie = be->cyb_set_level(bar, CY_HIGH_LEVEL); 15797c478bd9Sstevel@tonic-gate 15807c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, 15817c478bd9Sstevel@tonic-gate "add-xcall", when->cyt_when, when->cyt_interval); 15827c478bd9Sstevel@tonic-gate 15837c478bd9Sstevel@tonic-gate nelems = cpu->cyp_nelems++; 15847c478bd9Sstevel@tonic-gate 15857c478bd9Sstevel@tonic-gate if (nelems == 0) { 15867c478bd9Sstevel@tonic-gate /* 15877c478bd9Sstevel@tonic-gate * If this is the first element, we need to enable the 15887c478bd9Sstevel@tonic-gate * backend on this CPU. 15897c478bd9Sstevel@tonic-gate */ 15907c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "enabled"); 15917c478bd9Sstevel@tonic-gate be->cyb_enable(bar); 15927c478bd9Sstevel@tonic-gate } 15937c478bd9Sstevel@tonic-gate 15947c478bd9Sstevel@tonic-gate ndx = cpu->cyp_heap[nelems]; 15957c478bd9Sstevel@tonic-gate cyclic = &cpu->cyp_cyclics[ndx]; 15967c478bd9Sstevel@tonic-gate 15977c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_flags == CYF_FREE); 15987c478bd9Sstevel@tonic-gate cyclic->cy_interval = when->cyt_interval; 15997c478bd9Sstevel@tonic-gate 16007c478bd9Sstevel@tonic-gate if (when->cyt_when == 0) { 16017c478bd9Sstevel@tonic-gate /* 16027c478bd9Sstevel@tonic-gate * If a start time hasn't been explicitly specified, we'll 16037c478bd9Sstevel@tonic-gate * start on the next interval boundary. 16047c478bd9Sstevel@tonic-gate */ 16057c478bd9Sstevel@tonic-gate cyclic->cy_expire = (gethrtime() / cyclic->cy_interval + 1) * 16067c478bd9Sstevel@tonic-gate cyclic->cy_interval; 16077c478bd9Sstevel@tonic-gate } else { 16087c478bd9Sstevel@tonic-gate cyclic->cy_expire = when->cyt_when; 16097c478bd9Sstevel@tonic-gate } 16107c478bd9Sstevel@tonic-gate 16117c478bd9Sstevel@tonic-gate cyclic->cy_handler = hdlr->cyh_func; 16127c478bd9Sstevel@tonic-gate cyclic->cy_arg = hdlr->cyh_arg; 16137c478bd9Sstevel@tonic-gate cyclic->cy_level = hdlr->cyh_level; 16147c478bd9Sstevel@tonic-gate cyclic->cy_flags = arg->cyx_flags; 16157c478bd9Sstevel@tonic-gate 16167c478bd9Sstevel@tonic-gate if (cyclic_upheap(cpu, nelems)) { 16177c478bd9Sstevel@tonic-gate hrtime_t exp = cyclic->cy_expire; 16187c478bd9Sstevel@tonic-gate 16197c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "add-reprog", cyclic, exp); 16207c478bd9Sstevel@tonic-gate 16217c478bd9Sstevel@tonic-gate /* 16227c478bd9Sstevel@tonic-gate * If our upheap propagated to the root, we need to 16237c478bd9Sstevel@tonic-gate * reprogram the interrupt source. 16247c478bd9Sstevel@tonic-gate */ 16257c478bd9Sstevel@tonic-gate be->cyb_reprogram(bar, exp); 16267c478bd9Sstevel@tonic-gate } 16277c478bd9Sstevel@tonic-gate be->cyb_restore_level(bar, cookie); 16287c478bd9Sstevel@tonic-gate 16297c478bd9Sstevel@tonic-gate arg->cyx_ndx = ndx; 16307c478bd9Sstevel@tonic-gate } 16317c478bd9Sstevel@tonic-gate 16327c478bd9Sstevel@tonic-gate static cyc_index_t 16337c478bd9Sstevel@tonic-gate cyclic_add_here(cyc_cpu_t *cpu, cyc_handler_t *hdlr, 16347c478bd9Sstevel@tonic-gate cyc_time_t *when, uint16_t flags) 16357c478bd9Sstevel@tonic-gate { 16367c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 16377c478bd9Sstevel@tonic-gate cyb_arg_t bar = be->cyb_arg; 16387c478bd9Sstevel@tonic-gate cyc_xcallarg_t arg; 16397c478bd9Sstevel@tonic-gate 16407c478bd9Sstevel@tonic-gate CYC_PTRACE("add-cpu", cpu, hdlr->cyh_func); 16417c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 16427c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 16437c478bd9Sstevel@tonic-gate ASSERT(!(cpu->cyp_cpu->cpu_flags & CPU_OFFLINE)); 16447c478bd9Sstevel@tonic-gate ASSERT(when->cyt_when >= 0 && when->cyt_interval > 0); 16457c478bd9Sstevel@tonic-gate 16467c478bd9Sstevel@tonic-gate if (cpu->cyp_nelems == cpu->cyp_size) { 16477c478bd9Sstevel@tonic-gate /* 16487c478bd9Sstevel@tonic-gate * This is expensive; it will cross call onto the other 16497c478bd9Sstevel@tonic-gate * CPU to perform the expansion. 16507c478bd9Sstevel@tonic-gate */ 16517c478bd9Sstevel@tonic-gate cyclic_expand(cpu); 16527c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_nelems < cpu->cyp_size); 16537c478bd9Sstevel@tonic-gate } 16547c478bd9Sstevel@tonic-gate 16557c478bd9Sstevel@tonic-gate /* 16567c478bd9Sstevel@tonic-gate * By now, we know that we're going to be able to successfully 16577c478bd9Sstevel@tonic-gate * perform the add. Now cross call over to the CPU of interest to 16587c478bd9Sstevel@tonic-gate * actually add our cyclic. 16597c478bd9Sstevel@tonic-gate */ 16607c478bd9Sstevel@tonic-gate arg.cyx_cpu = cpu; 16617c478bd9Sstevel@tonic-gate arg.cyx_hdlr = hdlr; 16627c478bd9Sstevel@tonic-gate arg.cyx_when = when; 16637c478bd9Sstevel@tonic-gate arg.cyx_flags = flags; 16647c478bd9Sstevel@tonic-gate 16657c478bd9Sstevel@tonic-gate be->cyb_xcall(bar, cpu->cyp_cpu, (cyc_func_t)cyclic_add_xcall, &arg); 16667c478bd9Sstevel@tonic-gate 16677c478bd9Sstevel@tonic-gate CYC_PTRACE("add-cpu-done", cpu, arg.cyx_ndx); 16687c478bd9Sstevel@tonic-gate 16697c478bd9Sstevel@tonic-gate return (arg.cyx_ndx); 16707c478bd9Sstevel@tonic-gate } 16717c478bd9Sstevel@tonic-gate 16727c478bd9Sstevel@tonic-gate static void 16737c478bd9Sstevel@tonic-gate cyclic_remove_xcall(cyc_xcallarg_t *arg) 16747c478bd9Sstevel@tonic-gate { 16757c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = arg->cyx_cpu; 16767c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 16777c478bd9Sstevel@tonic-gate cyb_arg_t bar = be->cyb_arg; 16787c478bd9Sstevel@tonic-gate cyc_cookie_t cookie; 16797c478bd9Sstevel@tonic-gate cyc_index_t ndx = arg->cyx_ndx, nelems = cpu->cyp_nelems, i; 16807c478bd9Sstevel@tonic-gate cyc_index_t *heap = cpu->cyp_heap, last; 16817c478bd9Sstevel@tonic-gate cyclic_t *cyclic; 16827c478bd9Sstevel@tonic-gate #ifdef DEBUG 16837c478bd9Sstevel@tonic-gate cyc_index_t root; 16847c478bd9Sstevel@tonic-gate #endif 16857c478bd9Sstevel@tonic-gate 16867c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_REMOVING); 16877c478bd9Sstevel@tonic-gate ASSERT(nelems > 0); 16887c478bd9Sstevel@tonic-gate 16897c478bd9Sstevel@tonic-gate cookie = be->cyb_set_level(bar, CY_HIGH_LEVEL); 16907c478bd9Sstevel@tonic-gate 16917c478bd9Sstevel@tonic-gate CYC_TRACE1(cpu, CY_HIGH_LEVEL, "remove-xcall", ndx); 16927c478bd9Sstevel@tonic-gate 16937c478bd9Sstevel@tonic-gate cyclic = &cpu->cyp_cyclics[ndx]; 16947c478bd9Sstevel@tonic-gate 16957c478bd9Sstevel@tonic-gate /* 16967c478bd9Sstevel@tonic-gate * Grab the current expiration time. If this cyclic is being 16977c478bd9Sstevel@tonic-gate * removed as part of a juggling operation, the expiration time 16987c478bd9Sstevel@tonic-gate * will be used when the cyclic is added to the new CPU. 16997c478bd9Sstevel@tonic-gate */ 17007c478bd9Sstevel@tonic-gate if (arg->cyx_when != NULL) { 17017c478bd9Sstevel@tonic-gate arg->cyx_when->cyt_when = cyclic->cy_expire; 17027c478bd9Sstevel@tonic-gate arg->cyx_when->cyt_interval = cyclic->cy_interval; 17037c478bd9Sstevel@tonic-gate } 17047c478bd9Sstevel@tonic-gate 17057c478bd9Sstevel@tonic-gate if (cyclic->cy_pend != 0) { 17067c478bd9Sstevel@tonic-gate /* 17077c478bd9Sstevel@tonic-gate * The pend is non-zero; this cyclic is currently being 17087c478bd9Sstevel@tonic-gate * executed (or will be executed shortly). If the caller 17097c478bd9Sstevel@tonic-gate * refuses to wait, we must return (doing nothing). Otherwise, 17107c478bd9Sstevel@tonic-gate * we will stash the pend value * in this CPU's rpend, and 17117c478bd9Sstevel@tonic-gate * then zero it out. The softint in the pend loop will see 17127c478bd9Sstevel@tonic-gate * that we have zeroed out pend, and will call the cyclic 17137c478bd9Sstevel@tonic-gate * handler rpend times. The caller will wait until the 17147c478bd9Sstevel@tonic-gate * softint has completed calling the cyclic handler. 17157c478bd9Sstevel@tonic-gate */ 17167c478bd9Sstevel@tonic-gate if (arg->cyx_wait == CY_NOWAIT) { 17177c478bd9Sstevel@tonic-gate arg->cyx_wait = CY_WAIT; 17187c478bd9Sstevel@tonic-gate goto out; 17197c478bd9Sstevel@tonic-gate } 17207c478bd9Sstevel@tonic-gate 17217c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_level != CY_HIGH_LEVEL); 17227c478bd9Sstevel@tonic-gate CYC_TRACE1(cpu, CY_HIGH_LEVEL, "remove-pend", cyclic->cy_pend); 17237c478bd9Sstevel@tonic-gate cpu->cyp_rpend = cyclic->cy_pend; 17247c478bd9Sstevel@tonic-gate cyclic->cy_pend = 0; 17257c478bd9Sstevel@tonic-gate } 17267c478bd9Sstevel@tonic-gate 17277c478bd9Sstevel@tonic-gate /* 17287c478bd9Sstevel@tonic-gate * Now set the flags to CYF_FREE. We don't need a membar_enter() 17297c478bd9Sstevel@tonic-gate * between zeroing pend and setting the flags because we're at 17307c478bd9Sstevel@tonic-gate * CY_HIGH_LEVEL (that is, the zeroing of pend and the setting 17317c478bd9Sstevel@tonic-gate * of cy_flags appear atomic to softints). 17327c478bd9Sstevel@tonic-gate */ 17337c478bd9Sstevel@tonic-gate cyclic->cy_flags = CYF_FREE; 17347c478bd9Sstevel@tonic-gate 17357c478bd9Sstevel@tonic-gate for (i = 0; i < nelems; i++) { 17367c478bd9Sstevel@tonic-gate if (heap[i] == ndx) 17377c478bd9Sstevel@tonic-gate break; 17387c478bd9Sstevel@tonic-gate } 17397c478bd9Sstevel@tonic-gate 17407c478bd9Sstevel@tonic-gate if (i == nelems) 17417c478bd9Sstevel@tonic-gate panic("attempt to remove non-existent cyclic"); 17427c478bd9Sstevel@tonic-gate 17437c478bd9Sstevel@tonic-gate cpu->cyp_nelems = --nelems; 17447c478bd9Sstevel@tonic-gate 17457c478bd9Sstevel@tonic-gate if (nelems == 0) { 17467c478bd9Sstevel@tonic-gate /* 17477c478bd9Sstevel@tonic-gate * If we just removed the last element, then we need to 17487c478bd9Sstevel@tonic-gate * disable the backend on this CPU. 17497c478bd9Sstevel@tonic-gate */ 17507c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "disabled"); 17517c478bd9Sstevel@tonic-gate be->cyb_disable(bar); 17527c478bd9Sstevel@tonic-gate } 17537c478bd9Sstevel@tonic-gate 17547c478bd9Sstevel@tonic-gate if (i == nelems) { 17557c478bd9Sstevel@tonic-gate /* 17567c478bd9Sstevel@tonic-gate * If we just removed the last element of the heap, then 17577c478bd9Sstevel@tonic-gate * we don't have to downheap. 17587c478bd9Sstevel@tonic-gate */ 17597c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "remove-bottom"); 17607c478bd9Sstevel@tonic-gate goto out; 17617c478bd9Sstevel@tonic-gate } 17627c478bd9Sstevel@tonic-gate 17637c478bd9Sstevel@tonic-gate #ifdef DEBUG 17647c478bd9Sstevel@tonic-gate root = heap[0]; 17657c478bd9Sstevel@tonic-gate #endif 17667c478bd9Sstevel@tonic-gate 17677c478bd9Sstevel@tonic-gate /* 17687c478bd9Sstevel@tonic-gate * Swap the last element of the heap with the one we want to 17697c478bd9Sstevel@tonic-gate * remove, and downheap (this has the implicit effect of putting 17707c478bd9Sstevel@tonic-gate * the newly freed element on the free list). 17717c478bd9Sstevel@tonic-gate */ 17727c478bd9Sstevel@tonic-gate heap[i] = (last = heap[nelems]); 17737c478bd9Sstevel@tonic-gate heap[nelems] = ndx; 17747c478bd9Sstevel@tonic-gate 17757c478bd9Sstevel@tonic-gate if (i == 0) { 17767c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "remove-root"); 17777c478bd9Sstevel@tonic-gate cyclic_downheap(cpu, 0); 17787c478bd9Sstevel@tonic-gate } else { 17797c478bd9Sstevel@tonic-gate if (cyclic_upheap(cpu, i) == 0) { 17807c478bd9Sstevel@tonic-gate /* 17817c478bd9Sstevel@tonic-gate * The upheap didn't propagate to the root; if it 17827c478bd9Sstevel@tonic-gate * didn't propagate at all, we need to downheap. 17837c478bd9Sstevel@tonic-gate */ 17847c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "remove-no-root"); 17857c478bd9Sstevel@tonic-gate if (heap[i] == last) { 17867c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "remove-no-up"); 17877c478bd9Sstevel@tonic-gate cyclic_downheap(cpu, i); 17887c478bd9Sstevel@tonic-gate } 17897c478bd9Sstevel@tonic-gate ASSERT(heap[0] == root); 17907c478bd9Sstevel@tonic-gate goto out; 17917c478bd9Sstevel@tonic-gate } 17927c478bd9Sstevel@tonic-gate } 17937c478bd9Sstevel@tonic-gate 17947c478bd9Sstevel@tonic-gate /* 17957c478bd9Sstevel@tonic-gate * We're here because we changed the root; we need to reprogram 17967c478bd9Sstevel@tonic-gate * the clock source. 17977c478bd9Sstevel@tonic-gate */ 17987c478bd9Sstevel@tonic-gate cyclic = &cpu->cyp_cyclics[heap[0]]; 17997c478bd9Sstevel@tonic-gate 18007c478bd9Sstevel@tonic-gate CYC_TRACE0(cpu, CY_HIGH_LEVEL, "remove-reprog"); 18017c478bd9Sstevel@tonic-gate 18027c478bd9Sstevel@tonic-gate ASSERT(nelems != 0); 18037c478bd9Sstevel@tonic-gate be->cyb_reprogram(bar, cyclic->cy_expire); 18047c478bd9Sstevel@tonic-gate out: 18057c478bd9Sstevel@tonic-gate be->cyb_restore_level(bar, cookie); 18067c478bd9Sstevel@tonic-gate } 18077c478bd9Sstevel@tonic-gate 18087c478bd9Sstevel@tonic-gate static int 18097c478bd9Sstevel@tonic-gate cyclic_remove_here(cyc_cpu_t *cpu, cyc_index_t ndx, cyc_time_t *when, int wait) 18107c478bd9Sstevel@tonic-gate { 18117c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 18127c478bd9Sstevel@tonic-gate cyc_xcallarg_t arg; 18137c478bd9Sstevel@tonic-gate cyclic_t *cyclic = &cpu->cyp_cyclics[ndx]; 18147c478bd9Sstevel@tonic-gate cyc_level_t level = cyclic->cy_level; 18157c478bd9Sstevel@tonic-gate 18167c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 18177c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_rpend == 0); 18187c478bd9Sstevel@tonic-gate ASSERT(wait == CY_WAIT || wait == CY_NOWAIT); 18197c478bd9Sstevel@tonic-gate 18207c478bd9Sstevel@tonic-gate arg.cyx_ndx = ndx; 18217c478bd9Sstevel@tonic-gate arg.cyx_cpu = cpu; 18227c478bd9Sstevel@tonic-gate arg.cyx_when = when; 18237c478bd9Sstevel@tonic-gate arg.cyx_wait = wait; 18247c478bd9Sstevel@tonic-gate 18257c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 18267c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_REMOVING; 18277c478bd9Sstevel@tonic-gate 18287c478bd9Sstevel@tonic-gate be->cyb_xcall(be->cyb_arg, cpu->cyp_cpu, 18297c478bd9Sstevel@tonic-gate (cyc_func_t)cyclic_remove_xcall, &arg); 18307c478bd9Sstevel@tonic-gate 18317c478bd9Sstevel@tonic-gate /* 18327c478bd9Sstevel@tonic-gate * If the cyclic we removed wasn't at CY_HIGH_LEVEL, then we need to 18337c478bd9Sstevel@tonic-gate * check the cyp_rpend. If it's non-zero, then we need to wait here 18347c478bd9Sstevel@tonic-gate * for all pending cyclic handlers to run. 18357c478bd9Sstevel@tonic-gate */ 18367c478bd9Sstevel@tonic-gate ASSERT(!(level == CY_HIGH_LEVEL && cpu->cyp_rpend != 0)); 18377c478bd9Sstevel@tonic-gate ASSERT(!(wait == CY_NOWAIT && cpu->cyp_rpend != 0)); 18387c478bd9Sstevel@tonic-gate ASSERT(!(arg.cyx_wait == CY_NOWAIT && cpu->cyp_rpend != 0)); 18397c478bd9Sstevel@tonic-gate 18407c478bd9Sstevel@tonic-gate if (wait != arg.cyx_wait) { 18417c478bd9Sstevel@tonic-gate /* 18427c478bd9Sstevel@tonic-gate * We are being told that we must wait if we want to 18437c478bd9Sstevel@tonic-gate * remove this cyclic; put the CPU back in the CYS_ONLINE 18447c478bd9Sstevel@tonic-gate * state and return failure. 18457c478bd9Sstevel@tonic-gate */ 18467c478bd9Sstevel@tonic-gate ASSERT(wait == CY_NOWAIT && arg.cyx_wait == CY_WAIT); 18477c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_REMOVING); 18487c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_ONLINE; 18497c478bd9Sstevel@tonic-gate 18507c478bd9Sstevel@tonic-gate return (0); 18517c478bd9Sstevel@tonic-gate } 18527c478bd9Sstevel@tonic-gate 18537c478bd9Sstevel@tonic-gate if (cpu->cyp_rpend != 0) 18547c478bd9Sstevel@tonic-gate sema_p(&cpu->cyp_modify_wait); 18557c478bd9Sstevel@tonic-gate 18567c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_REMOVING); 18577c478bd9Sstevel@tonic-gate 18587c478bd9Sstevel@tonic-gate cpu->cyp_rpend = 0; 18597c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_ONLINE; 18607c478bd9Sstevel@tonic-gate 18617c478bd9Sstevel@tonic-gate return (1); 18627c478bd9Sstevel@tonic-gate } 18637c478bd9Sstevel@tonic-gate 18647c478bd9Sstevel@tonic-gate /* 18657c478bd9Sstevel@tonic-gate * cyclic_juggle_one_to() should only be called when the source cyclic 18667c478bd9Sstevel@tonic-gate * can be juggled and the destination CPU is known to be able to accept 18677c478bd9Sstevel@tonic-gate * it. 18687c478bd9Sstevel@tonic-gate */ 18697c478bd9Sstevel@tonic-gate static void 18707c478bd9Sstevel@tonic-gate cyclic_juggle_one_to(cyc_id_t *idp, cyc_cpu_t *dest) 18717c478bd9Sstevel@tonic-gate { 18727c478bd9Sstevel@tonic-gate cyc_cpu_t *src = idp->cyi_cpu; 18737c478bd9Sstevel@tonic-gate cyc_index_t ndx = idp->cyi_ndx; 18747c478bd9Sstevel@tonic-gate cyc_time_t when; 18757c478bd9Sstevel@tonic-gate cyc_handler_t hdlr; 18767c478bd9Sstevel@tonic-gate cyclic_t *cyclic; 18777c478bd9Sstevel@tonic-gate uint16_t flags; 18787c478bd9Sstevel@tonic-gate hrtime_t delay; 18797c478bd9Sstevel@tonic-gate 18807c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 18817c478bd9Sstevel@tonic-gate ASSERT(src != NULL && idp->cyi_omni_list == NULL); 18827c478bd9Sstevel@tonic-gate ASSERT(!(dest->cyp_cpu->cpu_flags & (CPU_QUIESCED | CPU_OFFLINE))); 18837c478bd9Sstevel@tonic-gate CYC_PTRACE("juggle-one-to", idp, dest); 18847c478bd9Sstevel@tonic-gate 18857c478bd9Sstevel@tonic-gate cyclic = &src->cyp_cyclics[ndx]; 18867c478bd9Sstevel@tonic-gate 18877c478bd9Sstevel@tonic-gate flags = cyclic->cy_flags; 18887c478bd9Sstevel@tonic-gate ASSERT(!(flags & CYF_CPU_BOUND) && !(flags & CYF_FREE)); 18897c478bd9Sstevel@tonic-gate 18907c478bd9Sstevel@tonic-gate hdlr.cyh_func = cyclic->cy_handler; 18917c478bd9Sstevel@tonic-gate hdlr.cyh_level = cyclic->cy_level; 18927c478bd9Sstevel@tonic-gate hdlr.cyh_arg = cyclic->cy_arg; 18937c478bd9Sstevel@tonic-gate 18947c478bd9Sstevel@tonic-gate /* 18957c478bd9Sstevel@tonic-gate * Before we begin the juggling process, see if the destination 18967c478bd9Sstevel@tonic-gate * CPU requires an expansion. If it does, we'll perform the 18977c478bd9Sstevel@tonic-gate * expansion before removing the cyclic. This is to prevent us 18987c478bd9Sstevel@tonic-gate * from blocking while a system-critical cyclic (notably, the clock 18997c478bd9Sstevel@tonic-gate * cyclic) isn't on a CPU. 19007c478bd9Sstevel@tonic-gate */ 19017c478bd9Sstevel@tonic-gate if (dest->cyp_nelems == dest->cyp_size) { 19027c478bd9Sstevel@tonic-gate CYC_PTRACE("remove-expand", idp, dest); 19037c478bd9Sstevel@tonic-gate cyclic_expand(dest); 19047c478bd9Sstevel@tonic-gate ASSERT(dest->cyp_nelems < dest->cyp_size); 19057c478bd9Sstevel@tonic-gate } 19067c478bd9Sstevel@tonic-gate 19077c478bd9Sstevel@tonic-gate /* 19087c478bd9Sstevel@tonic-gate * Remove the cyclic from the source. As mentioned above, we cannot 19097c478bd9Sstevel@tonic-gate * block during this operation; if we cannot remove the cyclic 19107c478bd9Sstevel@tonic-gate * without waiting, we spin for a time shorter than the interval, and 19117c478bd9Sstevel@tonic-gate * reattempt the (non-blocking) removal. If we continue to fail, 19127c478bd9Sstevel@tonic-gate * we will exponentially back off (up to half of the interval). 19137c478bd9Sstevel@tonic-gate * Note that the removal will ultimately succeed -- even if the 19147c478bd9Sstevel@tonic-gate * cyclic handler is blocked on a resource held by a thread which we 19157c478bd9Sstevel@tonic-gate * have preempted, priority inheritance assures that the preempted 19167c478bd9Sstevel@tonic-gate * thread will preempt us and continue to progress. 19177c478bd9Sstevel@tonic-gate */ 19187c478bd9Sstevel@tonic-gate for (delay = NANOSEC / MICROSEC; ; delay <<= 1) { 19197c478bd9Sstevel@tonic-gate /* 19207c478bd9Sstevel@tonic-gate * Before we begin this operation, disable kernel preemption. 19217c478bd9Sstevel@tonic-gate */ 19227c478bd9Sstevel@tonic-gate kpreempt_disable(); 19237c478bd9Sstevel@tonic-gate if (cyclic_remove_here(src, ndx, &when, CY_NOWAIT)) 19247c478bd9Sstevel@tonic-gate break; 19257c478bd9Sstevel@tonic-gate 19267c478bd9Sstevel@tonic-gate /* 19277c478bd9Sstevel@tonic-gate * The operation failed; enable kernel preemption while 19287c478bd9Sstevel@tonic-gate * spinning. 19297c478bd9Sstevel@tonic-gate */ 19307c478bd9Sstevel@tonic-gate kpreempt_enable(); 19317c478bd9Sstevel@tonic-gate 19327c478bd9Sstevel@tonic-gate CYC_PTRACE("remove-retry", idp, src); 19337c478bd9Sstevel@tonic-gate 19347c478bd9Sstevel@tonic-gate if (delay > (cyclic->cy_interval >> 1)) 19357c478bd9Sstevel@tonic-gate delay = cyclic->cy_interval >> 1; 19367c478bd9Sstevel@tonic-gate 19377c478bd9Sstevel@tonic-gate drv_usecwait((clock_t)(delay / (NANOSEC / MICROSEC))); 19387c478bd9Sstevel@tonic-gate } 19397c478bd9Sstevel@tonic-gate 19407c478bd9Sstevel@tonic-gate /* 19417c478bd9Sstevel@tonic-gate * Now add the cyclic to the destination. This won't block; we 19427c478bd9Sstevel@tonic-gate * performed any necessary (blocking) expansion of the destination 19437c478bd9Sstevel@tonic-gate * CPU before removing the cyclic from the source CPU. 19447c478bd9Sstevel@tonic-gate */ 19457c478bd9Sstevel@tonic-gate idp->cyi_ndx = cyclic_add_here(dest, &hdlr, &when, flags); 19467c478bd9Sstevel@tonic-gate idp->cyi_cpu = dest; 19477c478bd9Sstevel@tonic-gate kpreempt_enable(); 19487c478bd9Sstevel@tonic-gate } 19497c478bd9Sstevel@tonic-gate 19507c478bd9Sstevel@tonic-gate static int 19517c478bd9Sstevel@tonic-gate cyclic_juggle_one(cyc_id_t *idp) 19527c478bd9Sstevel@tonic-gate { 19537c478bd9Sstevel@tonic-gate cyc_index_t ndx = idp->cyi_ndx; 19547c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = idp->cyi_cpu, *dest; 19557c478bd9Sstevel@tonic-gate cyclic_t *cyclic = &cpu->cyp_cyclics[ndx]; 19567c478bd9Sstevel@tonic-gate cpu_t *c = cpu->cyp_cpu; 19577c478bd9Sstevel@tonic-gate cpupart_t *part = c->cpu_part; 19587c478bd9Sstevel@tonic-gate 19597c478bd9Sstevel@tonic-gate CYC_PTRACE("juggle-one", idp, cpu); 19607c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 19617c478bd9Sstevel@tonic-gate ASSERT(!(c->cpu_flags & CPU_OFFLINE)); 19627c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 19637c478bd9Sstevel@tonic-gate ASSERT(!(cyclic->cy_flags & CYF_FREE)); 19647c478bd9Sstevel@tonic-gate 19657c478bd9Sstevel@tonic-gate if ((dest = cyclic_pick_cpu(part, c, c, cyclic->cy_flags)) == NULL) { 19667c478bd9Sstevel@tonic-gate /* 19677c478bd9Sstevel@tonic-gate * Bad news: this cyclic can't be juggled. 19687c478bd9Sstevel@tonic-gate */ 19697c478bd9Sstevel@tonic-gate CYC_PTRACE("juggle-fail", idp, cpu) 19707c478bd9Sstevel@tonic-gate return (0); 19717c478bd9Sstevel@tonic-gate } 19727c478bd9Sstevel@tonic-gate 19737c478bd9Sstevel@tonic-gate cyclic_juggle_one_to(idp, dest); 19747c478bd9Sstevel@tonic-gate 19757c478bd9Sstevel@tonic-gate return (1); 19767c478bd9Sstevel@tonic-gate } 19777c478bd9Sstevel@tonic-gate 19787c478bd9Sstevel@tonic-gate static void 19797c478bd9Sstevel@tonic-gate cyclic_unbind_cpu(cyclic_id_t id) 19807c478bd9Sstevel@tonic-gate { 19817c478bd9Sstevel@tonic-gate cyc_id_t *idp = (cyc_id_t *)id; 19827c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = idp->cyi_cpu; 19837c478bd9Sstevel@tonic-gate cpu_t *c = cpu->cyp_cpu; 19847c478bd9Sstevel@tonic-gate cyclic_t *cyclic = &cpu->cyp_cyclics[idp->cyi_ndx]; 19857c478bd9Sstevel@tonic-gate 19867c478bd9Sstevel@tonic-gate CYC_PTRACE("unbind-cpu", id, cpu); 19877c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 19887c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 19897c478bd9Sstevel@tonic-gate ASSERT(!(cyclic->cy_flags & CYF_FREE)); 19907c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_flags & CYF_CPU_BOUND); 19917c478bd9Sstevel@tonic-gate 19927c478bd9Sstevel@tonic-gate cyclic->cy_flags &= ~CYF_CPU_BOUND; 19937c478bd9Sstevel@tonic-gate 19947c478bd9Sstevel@tonic-gate /* 19957c478bd9Sstevel@tonic-gate * If we were bound to CPU which has interrupts disabled, we need 19967c478bd9Sstevel@tonic-gate * to juggle away. This can only fail if we are bound to a 19977c478bd9Sstevel@tonic-gate * processor set, and if every CPU in the processor set has 19987c478bd9Sstevel@tonic-gate * interrupts disabled. 19997c478bd9Sstevel@tonic-gate */ 20007c478bd9Sstevel@tonic-gate if (!(c->cpu_flags & CPU_ENABLE)) { 20017c478bd9Sstevel@tonic-gate int res = cyclic_juggle_one(idp); 20027c478bd9Sstevel@tonic-gate 20037c478bd9Sstevel@tonic-gate ASSERT((res && idp->cyi_cpu != cpu) || 20047c478bd9Sstevel@tonic-gate (!res && (cyclic->cy_flags & CYF_PART_BOUND))); 20057c478bd9Sstevel@tonic-gate } 20067c478bd9Sstevel@tonic-gate } 20077c478bd9Sstevel@tonic-gate 20087c478bd9Sstevel@tonic-gate static void 20097c478bd9Sstevel@tonic-gate cyclic_bind_cpu(cyclic_id_t id, cpu_t *d) 20107c478bd9Sstevel@tonic-gate { 20117c478bd9Sstevel@tonic-gate cyc_id_t *idp = (cyc_id_t *)id; 20127c478bd9Sstevel@tonic-gate cyc_cpu_t *dest = d->cpu_cyclic, *cpu = idp->cyi_cpu; 20137c478bd9Sstevel@tonic-gate cpu_t *c = cpu->cyp_cpu; 20147c478bd9Sstevel@tonic-gate cyclic_t *cyclic = &cpu->cyp_cyclics[idp->cyi_ndx]; 20157c478bd9Sstevel@tonic-gate cpupart_t *part = c->cpu_part; 20167c478bd9Sstevel@tonic-gate 20177c478bd9Sstevel@tonic-gate CYC_PTRACE("bind-cpu", id, dest); 20187c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 20197c478bd9Sstevel@tonic-gate ASSERT(!(d->cpu_flags & CPU_OFFLINE)); 20207c478bd9Sstevel@tonic-gate ASSERT(!(c->cpu_flags & CPU_OFFLINE)); 20217c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 20227c478bd9Sstevel@tonic-gate ASSERT(dest != NULL); 20237c478bd9Sstevel@tonic-gate ASSERT(dest->cyp_state == CYS_ONLINE); 20247c478bd9Sstevel@tonic-gate ASSERT(!(cyclic->cy_flags & CYF_FREE)); 20257c478bd9Sstevel@tonic-gate ASSERT(!(cyclic->cy_flags & CYF_CPU_BOUND)); 20267c478bd9Sstevel@tonic-gate 20277c478bd9Sstevel@tonic-gate dest = cyclic_pick_cpu(part, d, NULL, cyclic->cy_flags | CYF_CPU_BOUND); 20287c478bd9Sstevel@tonic-gate 20297c478bd9Sstevel@tonic-gate if (dest != cpu) { 20307c478bd9Sstevel@tonic-gate cyclic_juggle_one_to(idp, dest); 20317c478bd9Sstevel@tonic-gate cyclic = &dest->cyp_cyclics[idp->cyi_ndx]; 20327c478bd9Sstevel@tonic-gate } 20337c478bd9Sstevel@tonic-gate 20347c478bd9Sstevel@tonic-gate cyclic->cy_flags |= CYF_CPU_BOUND; 20357c478bd9Sstevel@tonic-gate } 20367c478bd9Sstevel@tonic-gate 20377c478bd9Sstevel@tonic-gate static void 20387c478bd9Sstevel@tonic-gate cyclic_unbind_cpupart(cyclic_id_t id) 20397c478bd9Sstevel@tonic-gate { 20407c478bd9Sstevel@tonic-gate cyc_id_t *idp = (cyc_id_t *)id; 20417c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = idp->cyi_cpu; 20427c478bd9Sstevel@tonic-gate cpu_t *c = cpu->cyp_cpu; 20437c478bd9Sstevel@tonic-gate cyclic_t *cyc = &cpu->cyp_cyclics[idp->cyi_ndx]; 20447c478bd9Sstevel@tonic-gate 20457c478bd9Sstevel@tonic-gate CYC_PTRACE("unbind-part", idp, c->cpu_part); 20467c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 20477c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 20487c478bd9Sstevel@tonic-gate ASSERT(!(cyc->cy_flags & CYF_FREE)); 20497c478bd9Sstevel@tonic-gate ASSERT(cyc->cy_flags & CYF_PART_BOUND); 20507c478bd9Sstevel@tonic-gate 20517c478bd9Sstevel@tonic-gate cyc->cy_flags &= ~CYF_PART_BOUND; 20527c478bd9Sstevel@tonic-gate 20537c478bd9Sstevel@tonic-gate /* 20547c478bd9Sstevel@tonic-gate * If we're on a CPU which has interrupts disabled (and if this cyclic 20557c478bd9Sstevel@tonic-gate * isn't bound to the CPU), we need to juggle away. 20567c478bd9Sstevel@tonic-gate */ 20577c478bd9Sstevel@tonic-gate if (!(c->cpu_flags & CPU_ENABLE) && !(cyc->cy_flags & CYF_CPU_BOUND)) { 20587c478bd9Sstevel@tonic-gate int res = cyclic_juggle_one(idp); 20597c478bd9Sstevel@tonic-gate 20607c478bd9Sstevel@tonic-gate ASSERT(res && idp->cyi_cpu != cpu); 20617c478bd9Sstevel@tonic-gate } 20627c478bd9Sstevel@tonic-gate } 20637c478bd9Sstevel@tonic-gate 20647c478bd9Sstevel@tonic-gate static void 20657c478bd9Sstevel@tonic-gate cyclic_bind_cpupart(cyclic_id_t id, cpupart_t *part) 20667c478bd9Sstevel@tonic-gate { 20677c478bd9Sstevel@tonic-gate cyc_id_t *idp = (cyc_id_t *)id; 20687c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = idp->cyi_cpu, *dest; 20697c478bd9Sstevel@tonic-gate cpu_t *c = cpu->cyp_cpu; 20707c478bd9Sstevel@tonic-gate cyclic_t *cyc = &cpu->cyp_cyclics[idp->cyi_ndx]; 20717c478bd9Sstevel@tonic-gate 20727c478bd9Sstevel@tonic-gate CYC_PTRACE("bind-part", idp, part); 20737c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 20747c478bd9Sstevel@tonic-gate ASSERT(!(c->cpu_flags & CPU_OFFLINE)); 20757c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 20767c478bd9Sstevel@tonic-gate ASSERT(!(cyc->cy_flags & CYF_FREE)); 20777c478bd9Sstevel@tonic-gate ASSERT(!(cyc->cy_flags & CYF_PART_BOUND)); 20787c478bd9Sstevel@tonic-gate ASSERT(part->cp_ncpus > 0); 20797c478bd9Sstevel@tonic-gate 20807c478bd9Sstevel@tonic-gate dest = cyclic_pick_cpu(part, c, NULL, cyc->cy_flags | CYF_PART_BOUND); 20817c478bd9Sstevel@tonic-gate 20827c478bd9Sstevel@tonic-gate if (dest != cpu) { 20837c478bd9Sstevel@tonic-gate cyclic_juggle_one_to(idp, dest); 20847c478bd9Sstevel@tonic-gate cyc = &dest->cyp_cyclics[idp->cyi_ndx]; 20857c478bd9Sstevel@tonic-gate } 20867c478bd9Sstevel@tonic-gate 20877c478bd9Sstevel@tonic-gate cyc->cy_flags |= CYF_PART_BOUND; 20887c478bd9Sstevel@tonic-gate } 20897c478bd9Sstevel@tonic-gate 20907c478bd9Sstevel@tonic-gate static void 20917c478bd9Sstevel@tonic-gate cyclic_configure(cpu_t *c) 20927c478bd9Sstevel@tonic-gate { 20937c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = kmem_zalloc(sizeof (cyc_cpu_t), KM_SLEEP); 20947c478bd9Sstevel@tonic-gate cyc_backend_t *nbe = kmem_zalloc(sizeof (cyc_backend_t), KM_SLEEP); 20957c478bd9Sstevel@tonic-gate int i; 20967c478bd9Sstevel@tonic-gate 20977c478bd9Sstevel@tonic-gate CYC_PTRACE1("configure", cpu); 20987c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 20997c478bd9Sstevel@tonic-gate 21007c478bd9Sstevel@tonic-gate if (cyclic_id_cache == NULL) 21017c478bd9Sstevel@tonic-gate cyclic_id_cache = kmem_cache_create("cyclic_id_cache", 21027c478bd9Sstevel@tonic-gate sizeof (cyc_id_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 21037c478bd9Sstevel@tonic-gate 21047c478bd9Sstevel@tonic-gate cpu->cyp_cpu = c; 21057c478bd9Sstevel@tonic-gate 21067c478bd9Sstevel@tonic-gate sema_init(&cpu->cyp_modify_wait, 0, NULL, SEMA_DEFAULT, NULL); 21077c478bd9Sstevel@tonic-gate 21087c478bd9Sstevel@tonic-gate cpu->cyp_size = 1; 21097c478bd9Sstevel@tonic-gate cpu->cyp_heap = kmem_zalloc(sizeof (cyc_index_t), KM_SLEEP); 21107c478bd9Sstevel@tonic-gate cpu->cyp_cyclics = kmem_zalloc(sizeof (cyclic_t), KM_SLEEP); 21117c478bd9Sstevel@tonic-gate cpu->cyp_cyclics->cy_flags = CYF_FREE; 21127c478bd9Sstevel@tonic-gate 21137c478bd9Sstevel@tonic-gate for (i = CY_LOW_LEVEL; i < CY_LOW_LEVEL + CY_SOFT_LEVELS; i++) { 21147c478bd9Sstevel@tonic-gate /* 21157c478bd9Sstevel@tonic-gate * We don't need to set the sizemask; it's already zero 21167c478bd9Sstevel@tonic-gate * (which is the appropriate sizemask for a size of 1). 21177c478bd9Sstevel@tonic-gate */ 21187c478bd9Sstevel@tonic-gate cpu->cyp_softbuf[i].cys_buf[0].cypc_buf = 21197c478bd9Sstevel@tonic-gate kmem_alloc(sizeof (cyc_index_t), KM_SLEEP); 21207c478bd9Sstevel@tonic-gate } 21217c478bd9Sstevel@tonic-gate 21227c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_OFFLINE; 21237c478bd9Sstevel@tonic-gate 21247c478bd9Sstevel@tonic-gate /* 21257c478bd9Sstevel@tonic-gate * Setup the backend for this CPU. 21267c478bd9Sstevel@tonic-gate */ 21277c478bd9Sstevel@tonic-gate bcopy(&cyclic_backend, nbe, sizeof (cyc_backend_t)); 21287c478bd9Sstevel@tonic-gate nbe->cyb_arg = nbe->cyb_configure(c); 21297c478bd9Sstevel@tonic-gate cpu->cyp_backend = nbe; 21307c478bd9Sstevel@tonic-gate 21317c478bd9Sstevel@tonic-gate /* 21327c478bd9Sstevel@tonic-gate * On platforms where stray interrupts may be taken during startup, 21337c478bd9Sstevel@tonic-gate * the CPU's cpu_cyclic pointer serves as an indicator that the 21347c478bd9Sstevel@tonic-gate * cyclic subsystem for this CPU is prepared to field interrupts. 21357c478bd9Sstevel@tonic-gate */ 21367c478bd9Sstevel@tonic-gate membar_producer(); 21377c478bd9Sstevel@tonic-gate 21387c478bd9Sstevel@tonic-gate c->cpu_cyclic = cpu; 21397c478bd9Sstevel@tonic-gate } 21407c478bd9Sstevel@tonic-gate 21417c478bd9Sstevel@tonic-gate static void 21427c478bd9Sstevel@tonic-gate cyclic_unconfigure(cpu_t *c) 21437c478bd9Sstevel@tonic-gate { 21447c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = c->cpu_cyclic; 21457c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 21467c478bd9Sstevel@tonic-gate cyb_arg_t bar = be->cyb_arg; 21477c478bd9Sstevel@tonic-gate int i; 21487c478bd9Sstevel@tonic-gate 21497c478bd9Sstevel@tonic-gate CYC_PTRACE1("unconfigure", cpu); 21507c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 21517c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_OFFLINE); 21527c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_nelems == 0); 21537c478bd9Sstevel@tonic-gate 21547c478bd9Sstevel@tonic-gate /* 21557c478bd9Sstevel@tonic-gate * Let the backend know that the CPU is being yanked, and free up 21567c478bd9Sstevel@tonic-gate * the backend structure. 21577c478bd9Sstevel@tonic-gate */ 21587c478bd9Sstevel@tonic-gate be->cyb_unconfigure(bar); 21597c478bd9Sstevel@tonic-gate kmem_free(be, sizeof (cyc_backend_t)); 21607c478bd9Sstevel@tonic-gate cpu->cyp_backend = NULL; 21617c478bd9Sstevel@tonic-gate 21627c478bd9Sstevel@tonic-gate /* 21637c478bd9Sstevel@tonic-gate * Free up the producer/consumer buffers at each of the soft levels. 21647c478bd9Sstevel@tonic-gate */ 21657c478bd9Sstevel@tonic-gate for (i = CY_LOW_LEVEL; i < CY_LOW_LEVEL + CY_SOFT_LEVELS; i++) { 21667c478bd9Sstevel@tonic-gate cyc_softbuf_t *softbuf = &cpu->cyp_softbuf[i]; 21677c478bd9Sstevel@tonic-gate uchar_t hard = softbuf->cys_hard; 21687c478bd9Sstevel@tonic-gate cyc_pcbuffer_t *pc = &softbuf->cys_buf[hard]; 21697c478bd9Sstevel@tonic-gate size_t bufsize = sizeof (cyc_index_t) * (pc->cypc_sizemask + 1); 21707c478bd9Sstevel@tonic-gate 21717c478bd9Sstevel@tonic-gate /* 21727c478bd9Sstevel@tonic-gate * Assert that we're not in the middle of a resize operation. 21737c478bd9Sstevel@tonic-gate */ 21747c478bd9Sstevel@tonic-gate ASSERT(hard == softbuf->cys_soft); 21757c478bd9Sstevel@tonic-gate ASSERT(hard == 0 || hard == 1); 21767c478bd9Sstevel@tonic-gate ASSERT(pc->cypc_buf != NULL); 21777c478bd9Sstevel@tonic-gate ASSERT(softbuf->cys_buf[hard ^ 1].cypc_buf == NULL); 21787c478bd9Sstevel@tonic-gate 21797c478bd9Sstevel@tonic-gate kmem_free(pc->cypc_buf, bufsize); 21807c478bd9Sstevel@tonic-gate pc->cypc_buf = NULL; 21817c478bd9Sstevel@tonic-gate } 21827c478bd9Sstevel@tonic-gate 21837c478bd9Sstevel@tonic-gate /* 21847c478bd9Sstevel@tonic-gate * Finally, clean up our remaining dynamic structures and NULL out 21857c478bd9Sstevel@tonic-gate * the cpu_cyclic pointer. 21867c478bd9Sstevel@tonic-gate */ 21877c478bd9Sstevel@tonic-gate kmem_free(cpu->cyp_cyclics, cpu->cyp_size * sizeof (cyclic_t)); 21887c478bd9Sstevel@tonic-gate kmem_free(cpu->cyp_heap, cpu->cyp_size * sizeof (cyc_index_t)); 21897c478bd9Sstevel@tonic-gate kmem_free(cpu, sizeof (cyc_cpu_t)); 21907c478bd9Sstevel@tonic-gate 21917c478bd9Sstevel@tonic-gate c->cpu_cyclic = NULL; 21927c478bd9Sstevel@tonic-gate } 21937c478bd9Sstevel@tonic-gate 21947c478bd9Sstevel@tonic-gate static int 21957c478bd9Sstevel@tonic-gate cyclic_cpu_setup(cpu_setup_t what, int id) 21967c478bd9Sstevel@tonic-gate { 21977c478bd9Sstevel@tonic-gate /* 21987c478bd9Sstevel@tonic-gate * We are guaranteed that there is still/already an entry in the 21997c478bd9Sstevel@tonic-gate * cpu array for this CPU. 22007c478bd9Sstevel@tonic-gate */ 22017c478bd9Sstevel@tonic-gate cpu_t *c = cpu[id]; 22027c478bd9Sstevel@tonic-gate cyc_cpu_t *cyp = c->cpu_cyclic; 22037c478bd9Sstevel@tonic-gate 22047c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 22057c478bd9Sstevel@tonic-gate 22067c478bd9Sstevel@tonic-gate switch (what) { 22077c478bd9Sstevel@tonic-gate case CPU_CONFIG: 22087c478bd9Sstevel@tonic-gate ASSERT(cyp == NULL); 22097c478bd9Sstevel@tonic-gate cyclic_configure(c); 22107c478bd9Sstevel@tonic-gate break; 22117c478bd9Sstevel@tonic-gate 22127c478bd9Sstevel@tonic-gate case CPU_UNCONFIG: 22137c478bd9Sstevel@tonic-gate ASSERT(cyp != NULL && cyp->cyp_state == CYS_OFFLINE); 22147c478bd9Sstevel@tonic-gate cyclic_unconfigure(c); 22157c478bd9Sstevel@tonic-gate break; 22167c478bd9Sstevel@tonic-gate 22177c478bd9Sstevel@tonic-gate default: 22187c478bd9Sstevel@tonic-gate break; 22197c478bd9Sstevel@tonic-gate } 22207c478bd9Sstevel@tonic-gate 22217c478bd9Sstevel@tonic-gate return (0); 22227c478bd9Sstevel@tonic-gate } 22237c478bd9Sstevel@tonic-gate 22247c478bd9Sstevel@tonic-gate static void 22257c478bd9Sstevel@tonic-gate cyclic_suspend_xcall(cyc_xcallarg_t *arg) 22267c478bd9Sstevel@tonic-gate { 22277c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = arg->cyx_cpu; 22287c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 22297c478bd9Sstevel@tonic-gate cyc_cookie_t cookie; 22307c478bd9Sstevel@tonic-gate cyb_arg_t bar = be->cyb_arg; 22317c478bd9Sstevel@tonic-gate 22327c478bd9Sstevel@tonic-gate cookie = be->cyb_set_level(bar, CY_HIGH_LEVEL); 22337c478bd9Sstevel@tonic-gate 22347c478bd9Sstevel@tonic-gate CYC_TRACE1(cpu, CY_HIGH_LEVEL, "suspend-xcall", cpu->cyp_nelems); 22357c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE || cpu->cyp_state == CYS_OFFLINE); 22367c478bd9Sstevel@tonic-gate 22377c478bd9Sstevel@tonic-gate /* 22387c478bd9Sstevel@tonic-gate * We won't disable this CPU unless it has a non-zero number of 22397c478bd9Sstevel@tonic-gate * elements (cpu_lock assures that no one else may be attempting 22407c478bd9Sstevel@tonic-gate * to disable this CPU). 22417c478bd9Sstevel@tonic-gate */ 22427c478bd9Sstevel@tonic-gate if (cpu->cyp_nelems > 0) { 22437c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 22447c478bd9Sstevel@tonic-gate be->cyb_disable(bar); 22457c478bd9Sstevel@tonic-gate } 22467c478bd9Sstevel@tonic-gate 22477c478bd9Sstevel@tonic-gate if (cpu->cyp_state == CYS_ONLINE) 22487c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_SUSPENDED; 22497c478bd9Sstevel@tonic-gate 22507c478bd9Sstevel@tonic-gate be->cyb_suspend(bar); 22517c478bd9Sstevel@tonic-gate be->cyb_restore_level(bar, cookie); 22527c478bd9Sstevel@tonic-gate } 22537c478bd9Sstevel@tonic-gate 22547c478bd9Sstevel@tonic-gate static void 22557c478bd9Sstevel@tonic-gate cyclic_resume_xcall(cyc_xcallarg_t *arg) 22567c478bd9Sstevel@tonic-gate { 22577c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = arg->cyx_cpu; 22587c478bd9Sstevel@tonic-gate cyc_backend_t *be = cpu->cyp_backend; 22597c478bd9Sstevel@tonic-gate cyc_cookie_t cookie; 22607c478bd9Sstevel@tonic-gate cyb_arg_t bar = be->cyb_arg; 22617c478bd9Sstevel@tonic-gate cyc_state_t state = cpu->cyp_state; 22627c478bd9Sstevel@tonic-gate 22637c478bd9Sstevel@tonic-gate cookie = be->cyb_set_level(bar, CY_HIGH_LEVEL); 22647c478bd9Sstevel@tonic-gate 22657c478bd9Sstevel@tonic-gate CYC_TRACE1(cpu, CY_HIGH_LEVEL, "resume-xcall", cpu->cyp_nelems); 22667c478bd9Sstevel@tonic-gate ASSERT(state == CYS_SUSPENDED || state == CYS_OFFLINE); 22677c478bd9Sstevel@tonic-gate 22687c478bd9Sstevel@tonic-gate be->cyb_resume(bar); 22697c478bd9Sstevel@tonic-gate 22707c478bd9Sstevel@tonic-gate /* 22717c478bd9Sstevel@tonic-gate * We won't enable this CPU unless it has a non-zero number of 22727c478bd9Sstevel@tonic-gate * elements. 22737c478bd9Sstevel@tonic-gate */ 22747c478bd9Sstevel@tonic-gate if (cpu->cyp_nelems > 0) { 22757c478bd9Sstevel@tonic-gate cyclic_t *cyclic = &cpu->cyp_cyclics[cpu->cyp_heap[0]]; 22767c478bd9Sstevel@tonic-gate hrtime_t exp = cyclic->cy_expire; 22777c478bd9Sstevel@tonic-gate 22787c478bd9Sstevel@tonic-gate CYC_TRACE(cpu, CY_HIGH_LEVEL, "resume-reprog", cyclic, exp); 22797c478bd9Sstevel@tonic-gate ASSERT(state == CYS_SUSPENDED); 22807c478bd9Sstevel@tonic-gate be->cyb_enable(bar); 22817c478bd9Sstevel@tonic-gate be->cyb_reprogram(bar, exp); 22827c478bd9Sstevel@tonic-gate } 22837c478bd9Sstevel@tonic-gate 22847c478bd9Sstevel@tonic-gate if (state == CYS_SUSPENDED) 22857c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_ONLINE; 22867c478bd9Sstevel@tonic-gate 22877c478bd9Sstevel@tonic-gate CYC_TRACE1(cpu, CY_HIGH_LEVEL, "resume-done", cpu->cyp_nelems); 22887c478bd9Sstevel@tonic-gate be->cyb_restore_level(bar, cookie); 22897c478bd9Sstevel@tonic-gate } 22907c478bd9Sstevel@tonic-gate 22917c478bd9Sstevel@tonic-gate static void 22927c478bd9Sstevel@tonic-gate cyclic_omni_start(cyc_id_t *idp, cyc_cpu_t *cpu) 22937c478bd9Sstevel@tonic-gate { 22947c478bd9Sstevel@tonic-gate cyc_omni_handler_t *omni = &idp->cyi_omni_hdlr; 22957c478bd9Sstevel@tonic-gate cyc_omni_cpu_t *ocpu = kmem_alloc(sizeof (cyc_omni_cpu_t), KM_SLEEP); 22967c478bd9Sstevel@tonic-gate cyc_handler_t hdlr; 22977c478bd9Sstevel@tonic-gate cyc_time_t when; 22987c478bd9Sstevel@tonic-gate 22997c478bd9Sstevel@tonic-gate CYC_PTRACE("omni-start", cpu, idp); 23007c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 23017c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 23027c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_cpu == NULL); 23037c478bd9Sstevel@tonic-gate 23047c478bd9Sstevel@tonic-gate hdlr.cyh_func = NULL; 23057c478bd9Sstevel@tonic-gate hdlr.cyh_arg = NULL; 23067c478bd9Sstevel@tonic-gate hdlr.cyh_level = CY_LEVELS; 23077c478bd9Sstevel@tonic-gate 23087c478bd9Sstevel@tonic-gate when.cyt_when = 0; 23097c478bd9Sstevel@tonic-gate when.cyt_interval = 0; 23107c478bd9Sstevel@tonic-gate 23117c478bd9Sstevel@tonic-gate omni->cyo_online(omni->cyo_arg, cpu->cyp_cpu, &hdlr, &when); 23127c478bd9Sstevel@tonic-gate 23137c478bd9Sstevel@tonic-gate ASSERT(hdlr.cyh_func != NULL); 23147c478bd9Sstevel@tonic-gate ASSERT(hdlr.cyh_level < CY_LEVELS); 23157c478bd9Sstevel@tonic-gate ASSERT(when.cyt_when >= 0 && when.cyt_interval > 0); 23167c478bd9Sstevel@tonic-gate 23177c478bd9Sstevel@tonic-gate ocpu->cyo_cpu = cpu; 23187c478bd9Sstevel@tonic-gate ocpu->cyo_arg = hdlr.cyh_arg; 23197c478bd9Sstevel@tonic-gate ocpu->cyo_ndx = cyclic_add_here(cpu, &hdlr, &when, 0); 23207c478bd9Sstevel@tonic-gate ocpu->cyo_next = idp->cyi_omni_list; 23217c478bd9Sstevel@tonic-gate idp->cyi_omni_list = ocpu; 23227c478bd9Sstevel@tonic-gate } 23237c478bd9Sstevel@tonic-gate 23247c478bd9Sstevel@tonic-gate static void 23257c478bd9Sstevel@tonic-gate cyclic_omni_stop(cyc_id_t *idp, cyc_cpu_t *cpu) 23267c478bd9Sstevel@tonic-gate { 23277c478bd9Sstevel@tonic-gate cyc_omni_handler_t *omni = &idp->cyi_omni_hdlr; 23287c478bd9Sstevel@tonic-gate cyc_omni_cpu_t *ocpu = idp->cyi_omni_list, *prev = NULL; 23297c478bd9Sstevel@tonic-gate 23307c478bd9Sstevel@tonic-gate CYC_PTRACE("omni-stop", cpu, idp); 23317c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 23327c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 23337c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_cpu == NULL); 23347c478bd9Sstevel@tonic-gate ASSERT(ocpu != NULL); 23357c478bd9Sstevel@tonic-gate 23367c478bd9Sstevel@tonic-gate while (ocpu != NULL && ocpu->cyo_cpu != cpu) { 23377c478bd9Sstevel@tonic-gate prev = ocpu; 23387c478bd9Sstevel@tonic-gate ocpu = ocpu->cyo_next; 23397c478bd9Sstevel@tonic-gate } 23407c478bd9Sstevel@tonic-gate 23417c478bd9Sstevel@tonic-gate /* 23427c478bd9Sstevel@tonic-gate * We _must_ have found an cyc_omni_cpu which corresponds to this 23437c478bd9Sstevel@tonic-gate * CPU -- the definition of an omnipresent cyclic is that it runs 23447c478bd9Sstevel@tonic-gate * on all online CPUs. 23457c478bd9Sstevel@tonic-gate */ 23467c478bd9Sstevel@tonic-gate ASSERT(ocpu != NULL); 23477c478bd9Sstevel@tonic-gate 23487c478bd9Sstevel@tonic-gate if (prev == NULL) { 23497c478bd9Sstevel@tonic-gate idp->cyi_omni_list = ocpu->cyo_next; 23507c478bd9Sstevel@tonic-gate } else { 23517c478bd9Sstevel@tonic-gate prev->cyo_next = ocpu->cyo_next; 23527c478bd9Sstevel@tonic-gate } 23537c478bd9Sstevel@tonic-gate 23547c478bd9Sstevel@tonic-gate (void) cyclic_remove_here(ocpu->cyo_cpu, ocpu->cyo_ndx, NULL, CY_WAIT); 23557c478bd9Sstevel@tonic-gate 23567c478bd9Sstevel@tonic-gate /* 23577c478bd9Sstevel@tonic-gate * The cyclic has been removed from this CPU; time to call the 23587c478bd9Sstevel@tonic-gate * omnipresent offline handler. 23597c478bd9Sstevel@tonic-gate */ 23607c478bd9Sstevel@tonic-gate if (omni->cyo_offline != NULL) 23617c478bd9Sstevel@tonic-gate omni->cyo_offline(omni->cyo_arg, cpu->cyp_cpu, ocpu->cyo_arg); 23627c478bd9Sstevel@tonic-gate 23637c478bd9Sstevel@tonic-gate kmem_free(ocpu, sizeof (cyc_omni_cpu_t)); 23647c478bd9Sstevel@tonic-gate } 23657c478bd9Sstevel@tonic-gate 23667c478bd9Sstevel@tonic-gate static cyc_id_t * 23677c478bd9Sstevel@tonic-gate cyclic_new_id() 23687c478bd9Sstevel@tonic-gate { 23697c478bd9Sstevel@tonic-gate cyc_id_t *idp; 23707c478bd9Sstevel@tonic-gate 23717c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 23727c478bd9Sstevel@tonic-gate 23737c478bd9Sstevel@tonic-gate idp = kmem_cache_alloc(cyclic_id_cache, KM_SLEEP); 23747c478bd9Sstevel@tonic-gate 23757c478bd9Sstevel@tonic-gate /* 23767c478bd9Sstevel@tonic-gate * The cyi_cpu field of the cyc_id_t structure tracks the CPU 23777c478bd9Sstevel@tonic-gate * associated with the cyclic. If and only if this field is NULL, the 23787c478bd9Sstevel@tonic-gate * cyc_id_t is an omnipresent cyclic. Note that cyi_omni_list may be 23797c478bd9Sstevel@tonic-gate * NULL for an omnipresent cyclic while the cyclic is being created 23807c478bd9Sstevel@tonic-gate * or destroyed. 23817c478bd9Sstevel@tonic-gate */ 23827c478bd9Sstevel@tonic-gate idp->cyi_cpu = NULL; 23837c478bd9Sstevel@tonic-gate idp->cyi_ndx = 0; 23847c478bd9Sstevel@tonic-gate 23857c478bd9Sstevel@tonic-gate idp->cyi_next = cyclic_id_head; 23867c478bd9Sstevel@tonic-gate idp->cyi_prev = NULL; 23877c478bd9Sstevel@tonic-gate idp->cyi_omni_list = NULL; 23887c478bd9Sstevel@tonic-gate 23897c478bd9Sstevel@tonic-gate if (cyclic_id_head != NULL) { 23907c478bd9Sstevel@tonic-gate ASSERT(cyclic_id_head->cyi_prev == NULL); 23917c478bd9Sstevel@tonic-gate cyclic_id_head->cyi_prev = idp; 23927c478bd9Sstevel@tonic-gate } 23937c478bd9Sstevel@tonic-gate 23947c478bd9Sstevel@tonic-gate cyclic_id_head = idp; 23957c478bd9Sstevel@tonic-gate 23967c478bd9Sstevel@tonic-gate return (idp); 23977c478bd9Sstevel@tonic-gate } 23987c478bd9Sstevel@tonic-gate 23997c478bd9Sstevel@tonic-gate /* 24007c478bd9Sstevel@tonic-gate * cyclic_id_t cyclic_add(cyc_handler_t *, cyc_time_t *) 24017c478bd9Sstevel@tonic-gate * 24027c478bd9Sstevel@tonic-gate * Overview 24037c478bd9Sstevel@tonic-gate * 24047c478bd9Sstevel@tonic-gate * cyclic_add() will create an unbound cyclic with the specified handler and 24057c478bd9Sstevel@tonic-gate * interval. The cyclic will run on a CPU which both has interrupts enabled 24067c478bd9Sstevel@tonic-gate * and is in the system CPU partition. 24077c478bd9Sstevel@tonic-gate * 24087c478bd9Sstevel@tonic-gate * Arguments and notes 24097c478bd9Sstevel@tonic-gate * 24107c478bd9Sstevel@tonic-gate * As its first argument, cyclic_add() takes a cyc_handler, which has the 24117c478bd9Sstevel@tonic-gate * following members: 24127c478bd9Sstevel@tonic-gate * 24137c478bd9Sstevel@tonic-gate * cyc_func_t cyh_func <-- Cyclic handler 24147c478bd9Sstevel@tonic-gate * void *cyh_arg <-- Argument to cyclic handler 24157c478bd9Sstevel@tonic-gate * cyc_level_t cyh_level <-- Level at which to fire; must be one of 24167c478bd9Sstevel@tonic-gate * CY_LOW_LEVEL, CY_LOCK_LEVEL or CY_HIGH_LEVEL 24177c478bd9Sstevel@tonic-gate * 24187c478bd9Sstevel@tonic-gate * Note that cyh_level is _not_ an ipl or spl; it must be one the 24197c478bd9Sstevel@tonic-gate * CY_*_LEVELs. This layer of abstraction allows the platform to define 24207c478bd9Sstevel@tonic-gate * the precise interrupt priority levels, within the following constraints: 24217c478bd9Sstevel@tonic-gate * 24227c478bd9Sstevel@tonic-gate * CY_LOCK_LEVEL must map to LOCK_LEVEL 24237c478bd9Sstevel@tonic-gate * CY_HIGH_LEVEL must map to an ipl greater than LOCK_LEVEL 24247c478bd9Sstevel@tonic-gate * CY_LOW_LEVEL must map to an ipl below LOCK_LEVEL 24257c478bd9Sstevel@tonic-gate * 24267c478bd9Sstevel@tonic-gate * In addition to a cyc_handler, cyclic_add() takes a cyc_time, which 24277c478bd9Sstevel@tonic-gate * has the following members: 24287c478bd9Sstevel@tonic-gate * 24297c478bd9Sstevel@tonic-gate * hrtime_t cyt_when <-- Absolute time, in nanoseconds since boot, at 24307c478bd9Sstevel@tonic-gate * which to start firing 24317c478bd9Sstevel@tonic-gate * hrtime_t cyt_interval <-- Length of interval, in nanoseconds 24327c478bd9Sstevel@tonic-gate * 24337c478bd9Sstevel@tonic-gate * gethrtime() is the time source for nanoseconds since boot. If cyt_when 24347c478bd9Sstevel@tonic-gate * is set to 0, the cyclic will start to fire when cyt_interval next 24357c478bd9Sstevel@tonic-gate * divides the number of nanoseconds since boot. 24367c478bd9Sstevel@tonic-gate * 24377c478bd9Sstevel@tonic-gate * The cyt_interval field _must_ be filled in by the caller; one-shots are 24387c478bd9Sstevel@tonic-gate * _not_ explicitly supported by the cyclic subsystem (cyclic_add() will 24397c478bd9Sstevel@tonic-gate * assert that cyt_interval is non-zero). The maximum value for either 24407c478bd9Sstevel@tonic-gate * field is INT64_MAX; the caller is responsible for assuring that 24417c478bd9Sstevel@tonic-gate * cyt_when + cyt_interval <= INT64_MAX. Neither field may be negative. 24427c478bd9Sstevel@tonic-gate * 24437c478bd9Sstevel@tonic-gate * For an arbitrary time t in the future, the cyclic handler is guaranteed 24447c478bd9Sstevel@tonic-gate * to have been called (t - cyt_when) / cyt_interval times. This will 24457c478bd9Sstevel@tonic-gate * be true even if interrupts have been disabled for periods greater than 24467c478bd9Sstevel@tonic-gate * cyt_interval nanoseconds. In order to compensate for such periods, 24477c478bd9Sstevel@tonic-gate * the cyclic handler may be called a finite number of times with an 24487c478bd9Sstevel@tonic-gate * arbitrarily small interval. 24497c478bd9Sstevel@tonic-gate * 24507c478bd9Sstevel@tonic-gate * The cyclic subsystem will not enforce any lower bound on the interval; 24517c478bd9Sstevel@tonic-gate * if the interval is less than the time required to process an interrupt, 24527c478bd9Sstevel@tonic-gate * the CPU will wedge. It's the responsibility of the caller to assure that 24537c478bd9Sstevel@tonic-gate * either the value of the interval is sane, or that its caller has 24547c478bd9Sstevel@tonic-gate * sufficient privilege to deny service (i.e. its caller is root). 24557c478bd9Sstevel@tonic-gate * 24567c478bd9Sstevel@tonic-gate * The cyclic handler is guaranteed to be single threaded, even while the 24577c478bd9Sstevel@tonic-gate * cyclic is being juggled between CPUs (see cyclic_juggle(), below). 24587c478bd9Sstevel@tonic-gate * That is, a given cyclic handler will never be executed simultaneously 24597c478bd9Sstevel@tonic-gate * on different CPUs. 24607c478bd9Sstevel@tonic-gate * 24617c478bd9Sstevel@tonic-gate * Return value 24627c478bd9Sstevel@tonic-gate * 24637c478bd9Sstevel@tonic-gate * cyclic_add() returns a cyclic_id_t, which is guaranteed to be a value 24647c478bd9Sstevel@tonic-gate * other than CYCLIC_NONE. cyclic_add() cannot fail. 24657c478bd9Sstevel@tonic-gate * 24667c478bd9Sstevel@tonic-gate * Caller's context 24677c478bd9Sstevel@tonic-gate * 24687c478bd9Sstevel@tonic-gate * cpu_lock must be held by the caller, and the caller must not be in 24697c478bd9Sstevel@tonic-gate * interrupt context. cyclic_add() will perform a KM_SLEEP kernel 24707c478bd9Sstevel@tonic-gate * memory allocation, so the usual rules (e.g. p_lock cannot be held) 24717c478bd9Sstevel@tonic-gate * apply. A cyclic may be added even in the presence of CPUs that have 24727c478bd9Sstevel@tonic-gate * not been configured with respect to the cyclic subsystem, but only 24737c478bd9Sstevel@tonic-gate * configured CPUs will be eligible to run the new cyclic. 24747c478bd9Sstevel@tonic-gate * 24757c478bd9Sstevel@tonic-gate * Cyclic handler's context 24767c478bd9Sstevel@tonic-gate * 24777c478bd9Sstevel@tonic-gate * Cyclic handlers will be executed in the interrupt context corresponding 24787c478bd9Sstevel@tonic-gate * to the specified level (i.e. either high, lock or low level). The 24797c478bd9Sstevel@tonic-gate * usual context rules apply. 24807c478bd9Sstevel@tonic-gate * 24817c478bd9Sstevel@tonic-gate * A cyclic handler may not grab ANY locks held by the caller of any of 24827c478bd9Sstevel@tonic-gate * cyclic_add(), cyclic_remove() or cyclic_bind(); the implementation of 24837c478bd9Sstevel@tonic-gate * these functions may require blocking on cyclic handler completion. 24847c478bd9Sstevel@tonic-gate * Moreover, cyclic handlers may not make any call back into the cyclic 24857c478bd9Sstevel@tonic-gate * subsystem. 24867c478bd9Sstevel@tonic-gate */ 24877c478bd9Sstevel@tonic-gate cyclic_id_t 24887c478bd9Sstevel@tonic-gate cyclic_add(cyc_handler_t *hdlr, cyc_time_t *when) 24897c478bd9Sstevel@tonic-gate { 24907c478bd9Sstevel@tonic-gate cyc_id_t *idp = cyclic_new_id(); 24917c478bd9Sstevel@tonic-gate 24927c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 24937c478bd9Sstevel@tonic-gate ASSERT(when->cyt_when >= 0 && when->cyt_interval > 0); 24947c478bd9Sstevel@tonic-gate 24957c478bd9Sstevel@tonic-gate idp->cyi_cpu = cyclic_pick_cpu(NULL, NULL, NULL, 0); 24967c478bd9Sstevel@tonic-gate idp->cyi_ndx = cyclic_add_here(idp->cyi_cpu, hdlr, when, 0); 24977c478bd9Sstevel@tonic-gate 24987c478bd9Sstevel@tonic-gate return ((uintptr_t)idp); 24997c478bd9Sstevel@tonic-gate } 25007c478bd9Sstevel@tonic-gate 25017c478bd9Sstevel@tonic-gate /* 25027c478bd9Sstevel@tonic-gate * cyclic_id_t cyclic_add_omni(cyc_omni_handler_t *) 25037c478bd9Sstevel@tonic-gate * 25047c478bd9Sstevel@tonic-gate * Overview 25057c478bd9Sstevel@tonic-gate * 25067c478bd9Sstevel@tonic-gate * cyclic_add_omni() will create an omnipresent cyclic with the specified 25077c478bd9Sstevel@tonic-gate * online and offline handlers. Omnipresent cyclics run on all online 25087c478bd9Sstevel@tonic-gate * CPUs, including CPUs which have unbound interrupts disabled. 25097c478bd9Sstevel@tonic-gate * 25107c478bd9Sstevel@tonic-gate * Arguments 25117c478bd9Sstevel@tonic-gate * 25127c478bd9Sstevel@tonic-gate * As its only argument, cyclic_add_omni() takes a cyc_omni_handler, which 25137c478bd9Sstevel@tonic-gate * has the following members: 25147c478bd9Sstevel@tonic-gate * 25157c478bd9Sstevel@tonic-gate * void (*cyo_online)() <-- Online handler 25167c478bd9Sstevel@tonic-gate * void (*cyo_offline)() <-- Offline handler 25177c478bd9Sstevel@tonic-gate * void *cyo_arg <-- Argument to be passed to on/offline handlers 25187c478bd9Sstevel@tonic-gate * 25197c478bd9Sstevel@tonic-gate * Online handler 25207c478bd9Sstevel@tonic-gate * 25217c478bd9Sstevel@tonic-gate * The cyo_online member is a pointer to a function which has the following 25227c478bd9Sstevel@tonic-gate * four arguments: 25237c478bd9Sstevel@tonic-gate * 25247c478bd9Sstevel@tonic-gate * void * <-- Argument (cyo_arg) 25257c478bd9Sstevel@tonic-gate * cpu_t * <-- Pointer to CPU about to be onlined 25267c478bd9Sstevel@tonic-gate * cyc_handler_t * <-- Pointer to cyc_handler_t; must be filled in 25277c478bd9Sstevel@tonic-gate * by omni online handler 25287c478bd9Sstevel@tonic-gate * cyc_time_t * <-- Pointer to cyc_time_t; must be filled in by 25297c478bd9Sstevel@tonic-gate * omni online handler 25307c478bd9Sstevel@tonic-gate * 25317c478bd9Sstevel@tonic-gate * The omni cyclic online handler is always called _before_ the omni 25327c478bd9Sstevel@tonic-gate * cyclic begins to fire on the specified CPU. As the above argument 25337c478bd9Sstevel@tonic-gate * description implies, the online handler must fill in the two structures 25347c478bd9Sstevel@tonic-gate * passed to it: the cyc_handler_t and the cyc_time_t. These are the 25357c478bd9Sstevel@tonic-gate * same two structures passed to cyclic_add(), outlined above. This 25367c478bd9Sstevel@tonic-gate * allows the omni cyclic to have maximum flexibility; different CPUs may 25377c478bd9Sstevel@tonic-gate * optionally 25387c478bd9Sstevel@tonic-gate * 25397c478bd9Sstevel@tonic-gate * (a) have different intervals 25407c478bd9Sstevel@tonic-gate * (b) be explicitly in or out of phase with one another 25417c478bd9Sstevel@tonic-gate * (c) have different handlers 25427c478bd9Sstevel@tonic-gate * (d) have different handler arguments 25437c478bd9Sstevel@tonic-gate * (e) fire at different levels 25447c478bd9Sstevel@tonic-gate * 25457c478bd9Sstevel@tonic-gate * Of these, (e) seems somewhat dubious, but is nonetheless allowed. 25467c478bd9Sstevel@tonic-gate * 25477c478bd9Sstevel@tonic-gate * The omni online handler is called in the same context as cyclic_add(), 25487c478bd9Sstevel@tonic-gate * and has the same liberties: omni online handlers may perform KM_SLEEP 25497c478bd9Sstevel@tonic-gate * kernel memory allocations, and may grab locks which are also acquired 25507c478bd9Sstevel@tonic-gate * by cyclic handlers. However, omni cyclic online handlers may _not_ 25517c478bd9Sstevel@tonic-gate * call back into the cyclic subsystem, and should be generally careful 25527c478bd9Sstevel@tonic-gate * about calling into arbitrary kernel subsystems. 25537c478bd9Sstevel@tonic-gate * 25547c478bd9Sstevel@tonic-gate * Offline handler 25557c478bd9Sstevel@tonic-gate * 25567c478bd9Sstevel@tonic-gate * The cyo_offline member is a pointer to a function which has the following 25577c478bd9Sstevel@tonic-gate * three arguments: 25587c478bd9Sstevel@tonic-gate * 25597c478bd9Sstevel@tonic-gate * void * <-- Argument (cyo_arg) 25607c478bd9Sstevel@tonic-gate * cpu_t * <-- Pointer to CPU about to be offlined 25617c478bd9Sstevel@tonic-gate * void * <-- CPU's cyclic argument (that is, value 25627c478bd9Sstevel@tonic-gate * to which cyh_arg member of the cyc_handler_t 25637c478bd9Sstevel@tonic-gate * was set in the omni online handler) 25647c478bd9Sstevel@tonic-gate * 25657c478bd9Sstevel@tonic-gate * The omni cyclic offline handler is always called _after_ the omni 25667c478bd9Sstevel@tonic-gate * cyclic has ceased firing on the specified CPU. Its purpose is to 25677c478bd9Sstevel@tonic-gate * allow cleanup of any resources dynamically allocated in the omni cyclic 25687c478bd9Sstevel@tonic-gate * online handler. The context of the offline handler is identical to 25697c478bd9Sstevel@tonic-gate * that of the online handler; the same constraints and liberties apply. 25707c478bd9Sstevel@tonic-gate * 25717c478bd9Sstevel@tonic-gate * The offline handler is optional; it may be NULL. 25727c478bd9Sstevel@tonic-gate * 25737c478bd9Sstevel@tonic-gate * Return value 25747c478bd9Sstevel@tonic-gate * 25757c478bd9Sstevel@tonic-gate * cyclic_add_omni() returns a cyclic_id_t, which is guaranteed to be a 25767c478bd9Sstevel@tonic-gate * value other than CYCLIC_NONE. cyclic_add_omni() cannot fail. 25777c478bd9Sstevel@tonic-gate * 25787c478bd9Sstevel@tonic-gate * Caller's context 25797c478bd9Sstevel@tonic-gate * 25807c478bd9Sstevel@tonic-gate * The caller's context is identical to that of cyclic_add(), specified 25817c478bd9Sstevel@tonic-gate * above. 25827c478bd9Sstevel@tonic-gate */ 25837c478bd9Sstevel@tonic-gate cyclic_id_t 25847c478bd9Sstevel@tonic-gate cyclic_add_omni(cyc_omni_handler_t *omni) 25857c478bd9Sstevel@tonic-gate { 25867c478bd9Sstevel@tonic-gate cyc_id_t *idp = cyclic_new_id(); 25877c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu; 25887c478bd9Sstevel@tonic-gate cpu_t *c; 25897c478bd9Sstevel@tonic-gate 25907c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 25917c478bd9Sstevel@tonic-gate ASSERT(omni != NULL && omni->cyo_online != NULL); 25927c478bd9Sstevel@tonic-gate 25937c478bd9Sstevel@tonic-gate idp->cyi_omni_hdlr = *omni; 25947c478bd9Sstevel@tonic-gate 25957c478bd9Sstevel@tonic-gate c = cpu_list; 25967c478bd9Sstevel@tonic-gate do { 25977c478bd9Sstevel@tonic-gate if ((cpu = c->cpu_cyclic) == NULL) 25987c478bd9Sstevel@tonic-gate continue; 25997c478bd9Sstevel@tonic-gate 26007c478bd9Sstevel@tonic-gate if (cpu->cyp_state != CYS_ONLINE) { 26017c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_OFFLINE); 26027c478bd9Sstevel@tonic-gate continue; 26037c478bd9Sstevel@tonic-gate } 26047c478bd9Sstevel@tonic-gate 26057c478bd9Sstevel@tonic-gate cyclic_omni_start(idp, cpu); 26067c478bd9Sstevel@tonic-gate } while ((c = c->cpu_next) != cpu_list); 26077c478bd9Sstevel@tonic-gate 26087c478bd9Sstevel@tonic-gate /* 26097c478bd9Sstevel@tonic-gate * We must have found at least one online CPU on which to run 26107c478bd9Sstevel@tonic-gate * this cyclic. 26117c478bd9Sstevel@tonic-gate */ 26127c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_omni_list != NULL); 26137c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_cpu == NULL); 26147c478bd9Sstevel@tonic-gate 26157c478bd9Sstevel@tonic-gate return ((uintptr_t)idp); 26167c478bd9Sstevel@tonic-gate } 26177c478bd9Sstevel@tonic-gate 26187c478bd9Sstevel@tonic-gate /* 26197c478bd9Sstevel@tonic-gate * void cyclic_remove(cyclic_id_t) 26207c478bd9Sstevel@tonic-gate * 26217c478bd9Sstevel@tonic-gate * Overview 26227c478bd9Sstevel@tonic-gate * 26237c478bd9Sstevel@tonic-gate * cyclic_remove() will remove the specified cyclic from the system. 26247c478bd9Sstevel@tonic-gate * 26257c478bd9Sstevel@tonic-gate * Arguments and notes 26267c478bd9Sstevel@tonic-gate * 26277c478bd9Sstevel@tonic-gate * The only argument is a cyclic_id returned from either cyclic_add() or 26287c478bd9Sstevel@tonic-gate * cyclic_add_omni(). 26297c478bd9Sstevel@tonic-gate * 26307c478bd9Sstevel@tonic-gate * By the time cyclic_remove() returns, the caller is guaranteed that the 26317c478bd9Sstevel@tonic-gate * removed cyclic handler has completed execution (this is the same 26327c478bd9Sstevel@tonic-gate * semantic that untimeout() provides). As a result, cyclic_remove() may 26337c478bd9Sstevel@tonic-gate * need to block, waiting for the removed cyclic to complete execution. 26347c478bd9Sstevel@tonic-gate * This leads to an important constraint on the caller: no lock may be 26357c478bd9Sstevel@tonic-gate * held across cyclic_remove() that also may be acquired by a cyclic 26367c478bd9Sstevel@tonic-gate * handler. 26377c478bd9Sstevel@tonic-gate * 26387c478bd9Sstevel@tonic-gate * Return value 26397c478bd9Sstevel@tonic-gate * 26407c478bd9Sstevel@tonic-gate * None; cyclic_remove() always succeeds. 26417c478bd9Sstevel@tonic-gate * 26427c478bd9Sstevel@tonic-gate * Caller's context 26437c478bd9Sstevel@tonic-gate * 26447c478bd9Sstevel@tonic-gate * cpu_lock must be held by the caller, and the caller must not be in 26457c478bd9Sstevel@tonic-gate * interrupt context. The caller may not hold any locks which are also 26467c478bd9Sstevel@tonic-gate * grabbed by any cyclic handler. See "Arguments and notes", above. 26477c478bd9Sstevel@tonic-gate */ 26487c478bd9Sstevel@tonic-gate void 26497c478bd9Sstevel@tonic-gate cyclic_remove(cyclic_id_t id) 26507c478bd9Sstevel@tonic-gate { 26517c478bd9Sstevel@tonic-gate cyc_id_t *idp = (cyc_id_t *)id; 26527c478bd9Sstevel@tonic-gate cyc_id_t *prev = idp->cyi_prev, *next = idp->cyi_next; 26537c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = idp->cyi_cpu; 26547c478bd9Sstevel@tonic-gate 26557c478bd9Sstevel@tonic-gate CYC_PTRACE("remove", idp, idp->cyi_cpu); 26567c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 26577c478bd9Sstevel@tonic-gate 26587c478bd9Sstevel@tonic-gate if (cpu != NULL) { 26597c478bd9Sstevel@tonic-gate (void) cyclic_remove_here(cpu, idp->cyi_ndx, NULL, CY_WAIT); 26607c478bd9Sstevel@tonic-gate } else { 26617c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_omni_list != NULL); 26627c478bd9Sstevel@tonic-gate while (idp->cyi_omni_list != NULL) 26637c478bd9Sstevel@tonic-gate cyclic_omni_stop(idp, idp->cyi_omni_list->cyo_cpu); 26647c478bd9Sstevel@tonic-gate } 26657c478bd9Sstevel@tonic-gate 26667c478bd9Sstevel@tonic-gate if (prev != NULL) { 26677c478bd9Sstevel@tonic-gate ASSERT(cyclic_id_head != idp); 26687c478bd9Sstevel@tonic-gate prev->cyi_next = next; 26697c478bd9Sstevel@tonic-gate } else { 26707c478bd9Sstevel@tonic-gate ASSERT(cyclic_id_head == idp); 26717c478bd9Sstevel@tonic-gate cyclic_id_head = next; 26727c478bd9Sstevel@tonic-gate } 26737c478bd9Sstevel@tonic-gate 26747c478bd9Sstevel@tonic-gate if (next != NULL) 26757c478bd9Sstevel@tonic-gate next->cyi_prev = prev; 26767c478bd9Sstevel@tonic-gate 26777c478bd9Sstevel@tonic-gate kmem_cache_free(cyclic_id_cache, idp); 26787c478bd9Sstevel@tonic-gate } 26797c478bd9Sstevel@tonic-gate 26807c478bd9Sstevel@tonic-gate /* 26817c478bd9Sstevel@tonic-gate * void cyclic_bind(cyclic_id_t, cpu_t *, cpupart_t *) 26827c478bd9Sstevel@tonic-gate * 26837c478bd9Sstevel@tonic-gate * Overview 26847c478bd9Sstevel@tonic-gate * 26857c478bd9Sstevel@tonic-gate * cyclic_bind() atomically changes the CPU and CPU partition bindings 26867c478bd9Sstevel@tonic-gate * of a cyclic. 26877c478bd9Sstevel@tonic-gate * 26887c478bd9Sstevel@tonic-gate * Arguments and notes 26897c478bd9Sstevel@tonic-gate * 26907c478bd9Sstevel@tonic-gate * The first argument is a cyclic_id retuned from cyclic_add(). 26917c478bd9Sstevel@tonic-gate * cyclic_bind() may _not_ be called on a cyclic_id returned from 26927c478bd9Sstevel@tonic-gate * cyclic_add_omni(). 26937c478bd9Sstevel@tonic-gate * 26947c478bd9Sstevel@tonic-gate * The second argument specifies the CPU to which to bind the specified 26957c478bd9Sstevel@tonic-gate * cyclic. If the specified cyclic is bound to a CPU other than the one 26967c478bd9Sstevel@tonic-gate * specified, it will be unbound from its bound CPU. Unbinding the cyclic 26977c478bd9Sstevel@tonic-gate * from its CPU may cause it to be juggled to another CPU. If the specified 26987c478bd9Sstevel@tonic-gate * CPU is non-NULL, the cyclic will be subsequently rebound to the specified 26997c478bd9Sstevel@tonic-gate * CPU. 27007c478bd9Sstevel@tonic-gate * 27017c478bd9Sstevel@tonic-gate * If a CPU with bound cyclics is transitioned into the P_NOINTR state, 27027c478bd9Sstevel@tonic-gate * only cyclics not bound to the CPU can be juggled away; CPU-bound cyclics 27037c478bd9Sstevel@tonic-gate * will continue to fire on the P_NOINTR CPU. A CPU with bound cyclics 27047c478bd9Sstevel@tonic-gate * cannot be offlined (attempts to offline the CPU will return EBUSY). 27057c478bd9Sstevel@tonic-gate * Likewise, cyclics may not be bound to an offline CPU; if the caller 27067c478bd9Sstevel@tonic-gate * attempts to bind a cyclic to an offline CPU, the cyclic subsystem will 27077c478bd9Sstevel@tonic-gate * panic. 27087c478bd9Sstevel@tonic-gate * 27097c478bd9Sstevel@tonic-gate * The third argument specifies the CPU partition to which to bind the 27107c478bd9Sstevel@tonic-gate * specified cyclic. If the specified cyclic is bound to a CPU partition 27117c478bd9Sstevel@tonic-gate * other than the one specified, it will be unbound from its bound 27127c478bd9Sstevel@tonic-gate * partition. Unbinding the cyclic from its CPU partition may cause it 27137c478bd9Sstevel@tonic-gate * to be juggled to another CPU. If the specified CPU partition is 27147c478bd9Sstevel@tonic-gate * non-NULL, the cyclic will be subsequently rebound to the specified CPU 27157c478bd9Sstevel@tonic-gate * partition. 27167c478bd9Sstevel@tonic-gate * 27177c478bd9Sstevel@tonic-gate * It is the caller's responsibility to assure that the specified CPU 27187c478bd9Sstevel@tonic-gate * partition contains a CPU. If it does not, the cyclic subsystem will 27197c478bd9Sstevel@tonic-gate * panic. A CPU partition with bound cyclics cannot be destroyed (attempts 27207c478bd9Sstevel@tonic-gate * to destroy the partition will return EBUSY). If a CPU with 27217c478bd9Sstevel@tonic-gate * partition-bound cyclics is transitioned into the P_NOINTR state, cyclics 27227c478bd9Sstevel@tonic-gate * bound to the CPU's partition (but not bound to the CPU) will be juggled 27237c478bd9Sstevel@tonic-gate * away only if there exists another CPU in the partition in the P_ONLINE 27247c478bd9Sstevel@tonic-gate * state. 27257c478bd9Sstevel@tonic-gate * 27267c478bd9Sstevel@tonic-gate * It is the caller's responsibility to assure that the specified CPU and 27277c478bd9Sstevel@tonic-gate * CPU partition are self-consistent. If both parameters are non-NULL, 27287c478bd9Sstevel@tonic-gate * and the specified CPU partition does not contain the specified CPU, the 27297c478bd9Sstevel@tonic-gate * cyclic subsystem will panic. 27307c478bd9Sstevel@tonic-gate * 27317c478bd9Sstevel@tonic-gate * It is the caller's responsibility to assure that the specified CPU has 27327c478bd9Sstevel@tonic-gate * been configured with respect to the cyclic subsystem. Generally, this 27337c478bd9Sstevel@tonic-gate * is always true for valid, on-line CPUs. The only periods of time during 27347c478bd9Sstevel@tonic-gate * which this may not be true are during MP boot (i.e. after cyclic_init() 27357c478bd9Sstevel@tonic-gate * is called but before cyclic_mp_init() is called) or during dynamic 27367c478bd9Sstevel@tonic-gate * reconfiguration; cyclic_bind() should only be called with great care 27377c478bd9Sstevel@tonic-gate * from these contexts. 27387c478bd9Sstevel@tonic-gate * 27397c478bd9Sstevel@tonic-gate * Return value 27407c478bd9Sstevel@tonic-gate * 27417c478bd9Sstevel@tonic-gate * None; cyclic_bind() always succeeds. 27427c478bd9Sstevel@tonic-gate * 27437c478bd9Sstevel@tonic-gate * Caller's context 27447c478bd9Sstevel@tonic-gate * 27457c478bd9Sstevel@tonic-gate * cpu_lock must be held by the caller, and the caller must not be in 27467c478bd9Sstevel@tonic-gate * interrupt context. The caller may not hold any locks which are also 27477c478bd9Sstevel@tonic-gate * grabbed by any cyclic handler. 27487c478bd9Sstevel@tonic-gate */ 27497c478bd9Sstevel@tonic-gate void 27507c478bd9Sstevel@tonic-gate cyclic_bind(cyclic_id_t id, cpu_t *d, cpupart_t *part) 27517c478bd9Sstevel@tonic-gate { 27527c478bd9Sstevel@tonic-gate cyc_id_t *idp = (cyc_id_t *)id; 27537c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = idp->cyi_cpu; 27547c478bd9Sstevel@tonic-gate cpu_t *c; 27557c478bd9Sstevel@tonic-gate uint16_t flags; 27567c478bd9Sstevel@tonic-gate 27577c478bd9Sstevel@tonic-gate CYC_PTRACE("bind", d, part); 27587c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 27597c478bd9Sstevel@tonic-gate ASSERT(part == NULL || d == NULL || d->cpu_part == part); 27607c478bd9Sstevel@tonic-gate 27617c478bd9Sstevel@tonic-gate if (cpu == NULL) { 27627c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_omni_list != NULL); 27637c478bd9Sstevel@tonic-gate panic("attempt to change binding of omnipresent cyclic"); 27647c478bd9Sstevel@tonic-gate } 27657c478bd9Sstevel@tonic-gate 27667c478bd9Sstevel@tonic-gate c = cpu->cyp_cpu; 27677c478bd9Sstevel@tonic-gate flags = cpu->cyp_cyclics[idp->cyi_ndx].cy_flags; 27687c478bd9Sstevel@tonic-gate 27697c478bd9Sstevel@tonic-gate if (c != d && (flags & CYF_CPU_BOUND)) 27707c478bd9Sstevel@tonic-gate cyclic_unbind_cpu(id); 27717c478bd9Sstevel@tonic-gate 27727c478bd9Sstevel@tonic-gate /* 27737c478bd9Sstevel@tonic-gate * Reload our cpu (we may have migrated). We don't have to reload 27747c478bd9Sstevel@tonic-gate * the flags field here; if we were CYF_PART_BOUND on entry, we are 27757c478bd9Sstevel@tonic-gate * CYF_PART_BOUND now. 27767c478bd9Sstevel@tonic-gate */ 27777c478bd9Sstevel@tonic-gate cpu = idp->cyi_cpu; 27787c478bd9Sstevel@tonic-gate c = cpu->cyp_cpu; 27797c478bd9Sstevel@tonic-gate 27807c478bd9Sstevel@tonic-gate if (part != c->cpu_part && (flags & CYF_PART_BOUND)) 27817c478bd9Sstevel@tonic-gate cyclic_unbind_cpupart(id); 27827c478bd9Sstevel@tonic-gate 27837c478bd9Sstevel@tonic-gate /* 27847c478bd9Sstevel@tonic-gate * Now reload the flags field, asserting that if we are CPU bound, 27857c478bd9Sstevel@tonic-gate * the CPU was specified (and likewise, if we are partition bound, 27867c478bd9Sstevel@tonic-gate * the partition was specified). 27877c478bd9Sstevel@tonic-gate */ 27887c478bd9Sstevel@tonic-gate cpu = idp->cyi_cpu; 27897c478bd9Sstevel@tonic-gate c = cpu->cyp_cpu; 27907c478bd9Sstevel@tonic-gate flags = cpu->cyp_cyclics[idp->cyi_ndx].cy_flags; 27917c478bd9Sstevel@tonic-gate ASSERT(!(flags & CYF_CPU_BOUND) || c == d); 27927c478bd9Sstevel@tonic-gate ASSERT(!(flags & CYF_PART_BOUND) || c->cpu_part == part); 27937c478bd9Sstevel@tonic-gate 27947c478bd9Sstevel@tonic-gate if (!(flags & CYF_CPU_BOUND) && d != NULL) 27957c478bd9Sstevel@tonic-gate cyclic_bind_cpu(id, d); 27967c478bd9Sstevel@tonic-gate 27977c478bd9Sstevel@tonic-gate if (!(flags & CYF_PART_BOUND) && part != NULL) 27987c478bd9Sstevel@tonic-gate cyclic_bind_cpupart(id, part); 27997c478bd9Sstevel@tonic-gate } 28007c478bd9Sstevel@tonic-gate 28017c478bd9Sstevel@tonic-gate hrtime_t 28027c478bd9Sstevel@tonic-gate cyclic_getres() 28037c478bd9Sstevel@tonic-gate { 28047c478bd9Sstevel@tonic-gate return (cyclic_resolution); 28057c478bd9Sstevel@tonic-gate } 28067c478bd9Sstevel@tonic-gate 28077c478bd9Sstevel@tonic-gate void 28087c478bd9Sstevel@tonic-gate cyclic_init(cyc_backend_t *be, hrtime_t resolution) 28097c478bd9Sstevel@tonic-gate { 28107c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 28117c478bd9Sstevel@tonic-gate 28127c478bd9Sstevel@tonic-gate CYC_PTRACE("init", be, resolution); 28137c478bd9Sstevel@tonic-gate cyclic_resolution = resolution; 28147c478bd9Sstevel@tonic-gate 28157c478bd9Sstevel@tonic-gate /* 28167c478bd9Sstevel@tonic-gate * Copy the passed cyc_backend into the backend template. This must 28177c478bd9Sstevel@tonic-gate * be done before the CPU can be configured. 28187c478bd9Sstevel@tonic-gate */ 28197c478bd9Sstevel@tonic-gate bcopy(be, &cyclic_backend, sizeof (cyc_backend_t)); 28207c478bd9Sstevel@tonic-gate 28217c478bd9Sstevel@tonic-gate /* 28227c478bd9Sstevel@tonic-gate * It's safe to look at the "CPU" pointer without disabling kernel 28237c478bd9Sstevel@tonic-gate * preemption; cyclic_init() is called only during startup by the 28247c478bd9Sstevel@tonic-gate * cyclic backend. 28257c478bd9Sstevel@tonic-gate */ 28267c478bd9Sstevel@tonic-gate cyclic_configure(CPU); 28277c478bd9Sstevel@tonic-gate cyclic_online(CPU); 28287c478bd9Sstevel@tonic-gate } 28297c478bd9Sstevel@tonic-gate 28307c478bd9Sstevel@tonic-gate /* 28317c478bd9Sstevel@tonic-gate * It is assumed that cyclic_mp_init() is called some time after cyclic 28327c478bd9Sstevel@tonic-gate * init (and therefore, after cpu0 has been initialized). We grab cpu_lock, 28337c478bd9Sstevel@tonic-gate * find the already initialized CPU, and initialize every other CPU with the 28347c478bd9Sstevel@tonic-gate * same backend. Finally, we register a cpu_setup function. 28357c478bd9Sstevel@tonic-gate */ 28367c478bd9Sstevel@tonic-gate void 28377c478bd9Sstevel@tonic-gate cyclic_mp_init() 28387c478bd9Sstevel@tonic-gate { 28397c478bd9Sstevel@tonic-gate cpu_t *c; 28407c478bd9Sstevel@tonic-gate 28417c478bd9Sstevel@tonic-gate mutex_enter(&cpu_lock); 28427c478bd9Sstevel@tonic-gate 28437c478bd9Sstevel@tonic-gate c = cpu_list; 28447c478bd9Sstevel@tonic-gate do { 28457c478bd9Sstevel@tonic-gate if (c->cpu_cyclic == NULL) { 28467c478bd9Sstevel@tonic-gate cyclic_configure(c); 28477c478bd9Sstevel@tonic-gate cyclic_online(c); 28487c478bd9Sstevel@tonic-gate } 28497c478bd9Sstevel@tonic-gate } while ((c = c->cpu_next) != cpu_list); 28507c478bd9Sstevel@tonic-gate 28517c478bd9Sstevel@tonic-gate register_cpu_setup_func((cpu_setup_func_t *)cyclic_cpu_setup, NULL); 28527c478bd9Sstevel@tonic-gate mutex_exit(&cpu_lock); 28537c478bd9Sstevel@tonic-gate } 28547c478bd9Sstevel@tonic-gate 28557c478bd9Sstevel@tonic-gate /* 28567c478bd9Sstevel@tonic-gate * int cyclic_juggle(cpu_t *) 28577c478bd9Sstevel@tonic-gate * 28587c478bd9Sstevel@tonic-gate * Overview 28597c478bd9Sstevel@tonic-gate * 28607c478bd9Sstevel@tonic-gate * cyclic_juggle() juggles as many cyclics as possible away from the 28617c478bd9Sstevel@tonic-gate * specified CPU; all remaining cyclics on the CPU will either be CPU- 28627c478bd9Sstevel@tonic-gate * or partition-bound. 28637c478bd9Sstevel@tonic-gate * 28647c478bd9Sstevel@tonic-gate * Arguments and notes 28657c478bd9Sstevel@tonic-gate * 28667c478bd9Sstevel@tonic-gate * The only argument to cyclic_juggle() is the CPU from which cyclics 28677c478bd9Sstevel@tonic-gate * should be juggled. CPU-bound cyclics are never juggled; partition-bound 28687c478bd9Sstevel@tonic-gate * cyclics are only juggled if the specified CPU is in the P_NOINTR state 28697c478bd9Sstevel@tonic-gate * and there exists a P_ONLINE CPU in the partition. The cyclic subsystem 28707c478bd9Sstevel@tonic-gate * assures that a cyclic will never fire late or spuriously, even while 28717c478bd9Sstevel@tonic-gate * being juggled. 28727c478bd9Sstevel@tonic-gate * 28737c478bd9Sstevel@tonic-gate * Return value 28747c478bd9Sstevel@tonic-gate * 28757c478bd9Sstevel@tonic-gate * cyclic_juggle() returns a non-zero value if all cyclics were able to 28767c478bd9Sstevel@tonic-gate * be juggled away from the CPU, and zero if one or more cyclics could 28777c478bd9Sstevel@tonic-gate * not be juggled away. 28787c478bd9Sstevel@tonic-gate * 28797c478bd9Sstevel@tonic-gate * Caller's context 28807c478bd9Sstevel@tonic-gate * 28817c478bd9Sstevel@tonic-gate * cpu_lock must be held by the caller, and the caller must not be in 28827c478bd9Sstevel@tonic-gate * interrupt context. The caller may not hold any locks which are also 28837c478bd9Sstevel@tonic-gate * grabbed by any cyclic handler. While cyclic_juggle() _may_ be called 28847c478bd9Sstevel@tonic-gate * in any context satisfying these constraints, it _must_ be called 28857c478bd9Sstevel@tonic-gate * immediately after clearing CPU_ENABLE (i.e. before dropping cpu_lock). 28867c478bd9Sstevel@tonic-gate * Failure to do so could result in an assertion failure in the cyclic 28877c478bd9Sstevel@tonic-gate * subsystem. 28887c478bd9Sstevel@tonic-gate */ 28897c478bd9Sstevel@tonic-gate int 28907c478bd9Sstevel@tonic-gate cyclic_juggle(cpu_t *c) 28917c478bd9Sstevel@tonic-gate { 28927c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = c->cpu_cyclic; 28937c478bd9Sstevel@tonic-gate cyc_id_t *idp; 28947c478bd9Sstevel@tonic-gate int all_juggled = 1; 28957c478bd9Sstevel@tonic-gate 28967c478bd9Sstevel@tonic-gate CYC_PTRACE1("juggle", c); 28977c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 28987c478bd9Sstevel@tonic-gate 28997c478bd9Sstevel@tonic-gate /* 29007c478bd9Sstevel@tonic-gate * We'll go through each cyclic on the CPU, attempting to juggle 29017c478bd9Sstevel@tonic-gate * each one elsewhere. 29027c478bd9Sstevel@tonic-gate */ 29037c478bd9Sstevel@tonic-gate for (idp = cyclic_id_head; idp != NULL; idp = idp->cyi_next) { 29047c478bd9Sstevel@tonic-gate if (idp->cyi_cpu != cpu) 29057c478bd9Sstevel@tonic-gate continue; 29067c478bd9Sstevel@tonic-gate 29077c478bd9Sstevel@tonic-gate if (cyclic_juggle_one(idp) == 0) { 29087c478bd9Sstevel@tonic-gate all_juggled = 0; 29097c478bd9Sstevel@tonic-gate continue; 29107c478bd9Sstevel@tonic-gate } 29117c478bd9Sstevel@tonic-gate 29127c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_cpu != cpu); 29137c478bd9Sstevel@tonic-gate } 29147c478bd9Sstevel@tonic-gate 29157c478bd9Sstevel@tonic-gate return (all_juggled); 29167c478bd9Sstevel@tonic-gate } 29177c478bd9Sstevel@tonic-gate 29187c478bd9Sstevel@tonic-gate /* 29197c478bd9Sstevel@tonic-gate * int cyclic_offline(cpu_t *) 29207c478bd9Sstevel@tonic-gate * 29217c478bd9Sstevel@tonic-gate * Overview 29227c478bd9Sstevel@tonic-gate * 29237c478bd9Sstevel@tonic-gate * cyclic_offline() offlines the cyclic subsystem on the specified CPU. 29247c478bd9Sstevel@tonic-gate * 29257c478bd9Sstevel@tonic-gate * Arguments and notes 29267c478bd9Sstevel@tonic-gate * 29277c478bd9Sstevel@tonic-gate * The only argument to cyclic_offline() is a CPU to offline. 29287c478bd9Sstevel@tonic-gate * cyclic_offline() will attempt to juggle cyclics away from the specified 29297c478bd9Sstevel@tonic-gate * CPU. 29307c478bd9Sstevel@tonic-gate * 29317c478bd9Sstevel@tonic-gate * Return value 29327c478bd9Sstevel@tonic-gate * 29337c478bd9Sstevel@tonic-gate * cyclic_offline() returns 1 if all cyclics on the CPU were juggled away 29347c478bd9Sstevel@tonic-gate * and the cyclic subsystem on the CPU was successfully offlines. 29357c478bd9Sstevel@tonic-gate * cyclic_offline returns 0 if some cyclics remain, blocking the cyclic 29367c478bd9Sstevel@tonic-gate * offline operation. All remaining cyclics on the CPU will either be 29377c478bd9Sstevel@tonic-gate * CPU- or partition-bound. 29387c478bd9Sstevel@tonic-gate * 29397c478bd9Sstevel@tonic-gate * See the "Arguments and notes" of cyclic_juggle(), below, for more detail 29407c478bd9Sstevel@tonic-gate * on cyclic juggling. 29417c478bd9Sstevel@tonic-gate * 29427c478bd9Sstevel@tonic-gate * Caller's context 29437c478bd9Sstevel@tonic-gate * 29447c478bd9Sstevel@tonic-gate * The only caller of cyclic_offline() should be the processor management 29457c478bd9Sstevel@tonic-gate * subsystem. It is expected that the caller of cyclic_offline() will 29467c478bd9Sstevel@tonic-gate * offline the CPU immediately after cyclic_offline() returns success (i.e. 29477c478bd9Sstevel@tonic-gate * before dropping cpu_lock). Moreover, it is expected that the caller will 29487c478bd9Sstevel@tonic-gate * fail the CPU offline operation if cyclic_offline() returns failure. 29497c478bd9Sstevel@tonic-gate */ 29507c478bd9Sstevel@tonic-gate int 29517c478bd9Sstevel@tonic-gate cyclic_offline(cpu_t *c) 29527c478bd9Sstevel@tonic-gate { 29537c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = c->cpu_cyclic; 29547c478bd9Sstevel@tonic-gate cyc_id_t *idp; 29557c478bd9Sstevel@tonic-gate 29567c478bd9Sstevel@tonic-gate CYC_PTRACE1("offline", cpu); 29577c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 29587c478bd9Sstevel@tonic-gate 29597c478bd9Sstevel@tonic-gate if (!cyclic_juggle(c)) 29607c478bd9Sstevel@tonic-gate return (0); 29617c478bd9Sstevel@tonic-gate 29627c478bd9Sstevel@tonic-gate /* 29637c478bd9Sstevel@tonic-gate * This CPU is headed offline; we need to now stop omnipresent 29647c478bd9Sstevel@tonic-gate * cyclic firing on this CPU. 29657c478bd9Sstevel@tonic-gate */ 29667c478bd9Sstevel@tonic-gate for (idp = cyclic_id_head; idp != NULL; idp = idp->cyi_next) { 29677c478bd9Sstevel@tonic-gate if (idp->cyi_cpu != NULL) 29687c478bd9Sstevel@tonic-gate continue; 29697c478bd9Sstevel@tonic-gate 29707c478bd9Sstevel@tonic-gate /* 29717c478bd9Sstevel@tonic-gate * We cannot possibly be offlining the last CPU; cyi_omni_list 29727c478bd9Sstevel@tonic-gate * must be non-NULL. 29737c478bd9Sstevel@tonic-gate */ 29747c478bd9Sstevel@tonic-gate ASSERT(idp->cyi_omni_list != NULL); 29757c478bd9Sstevel@tonic-gate cyclic_omni_stop(idp, cpu); 29767c478bd9Sstevel@tonic-gate } 29777c478bd9Sstevel@tonic-gate 29787c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_ONLINE); 29797c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_OFFLINE; 29807c478bd9Sstevel@tonic-gate 29817c478bd9Sstevel@tonic-gate return (1); 29827c478bd9Sstevel@tonic-gate } 29837c478bd9Sstevel@tonic-gate 29847c478bd9Sstevel@tonic-gate /* 29857c478bd9Sstevel@tonic-gate * void cyclic_online(cpu_t *) 29867c478bd9Sstevel@tonic-gate * 29877c478bd9Sstevel@tonic-gate * Overview 29887c478bd9Sstevel@tonic-gate * 29897c478bd9Sstevel@tonic-gate * cyclic_online() onlines a CPU previously offlined with cyclic_offline(). 29907c478bd9Sstevel@tonic-gate * 29917c478bd9Sstevel@tonic-gate * Arguments and notes 29927c478bd9Sstevel@tonic-gate * 29937c478bd9Sstevel@tonic-gate * cyclic_online()'s only argument is a CPU to online. The specified 29947c478bd9Sstevel@tonic-gate * CPU must have been previously offlined with cyclic_offline(). After 29957c478bd9Sstevel@tonic-gate * cyclic_online() returns, the specified CPU will be eligible to execute 29967c478bd9Sstevel@tonic-gate * cyclics. 29977c478bd9Sstevel@tonic-gate * 29987c478bd9Sstevel@tonic-gate * Return value 29997c478bd9Sstevel@tonic-gate * 30007c478bd9Sstevel@tonic-gate * None; cyclic_online() always succeeds. 30017c478bd9Sstevel@tonic-gate * 30027c478bd9Sstevel@tonic-gate * Caller's context 30037c478bd9Sstevel@tonic-gate * 30047c478bd9Sstevel@tonic-gate * cyclic_online() should only be called by the processor management 30057c478bd9Sstevel@tonic-gate * subsystem; cpu_lock must be held. 30067c478bd9Sstevel@tonic-gate */ 30077c478bd9Sstevel@tonic-gate void 30087c478bd9Sstevel@tonic-gate cyclic_online(cpu_t *c) 30097c478bd9Sstevel@tonic-gate { 30107c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = c->cpu_cyclic; 30117c478bd9Sstevel@tonic-gate cyc_id_t *idp; 30127c478bd9Sstevel@tonic-gate 30137c478bd9Sstevel@tonic-gate CYC_PTRACE1("online", cpu); 30147c478bd9Sstevel@tonic-gate ASSERT(c->cpu_flags & CPU_ENABLE); 30157c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 30167c478bd9Sstevel@tonic-gate ASSERT(cpu->cyp_state == CYS_OFFLINE); 30177c478bd9Sstevel@tonic-gate 30187c478bd9Sstevel@tonic-gate cpu->cyp_state = CYS_ONLINE; 30197c478bd9Sstevel@tonic-gate 30207c478bd9Sstevel@tonic-gate /* 30217c478bd9Sstevel@tonic-gate * Now that this CPU is open for business, we need to start firing 30227c478bd9Sstevel@tonic-gate * all omnipresent cyclics on it. 30237c478bd9Sstevel@tonic-gate */ 30247c478bd9Sstevel@tonic-gate for (idp = cyclic_id_head; idp != NULL; idp = idp->cyi_next) { 30257c478bd9Sstevel@tonic-gate if (idp->cyi_cpu != NULL) 30267c478bd9Sstevel@tonic-gate continue; 30277c478bd9Sstevel@tonic-gate 30287c478bd9Sstevel@tonic-gate cyclic_omni_start(idp, cpu); 30297c478bd9Sstevel@tonic-gate } 30307c478bd9Sstevel@tonic-gate } 30317c478bd9Sstevel@tonic-gate 30327c478bd9Sstevel@tonic-gate /* 30337c478bd9Sstevel@tonic-gate * void cyclic_move_in(cpu_t *) 30347c478bd9Sstevel@tonic-gate * 30357c478bd9Sstevel@tonic-gate * Overview 30367c478bd9Sstevel@tonic-gate * 30377c478bd9Sstevel@tonic-gate * cyclic_move_in() is called by the CPU partition code immediately after 30387c478bd9Sstevel@tonic-gate * the specified CPU has moved into a new partition. 30397c478bd9Sstevel@tonic-gate * 30407c478bd9Sstevel@tonic-gate * Arguments and notes 30417c478bd9Sstevel@tonic-gate * 30427c478bd9Sstevel@tonic-gate * The only argument to cyclic_move_in() is a CPU which has moved into a 30437c478bd9Sstevel@tonic-gate * new partition. If the specified CPU is P_ONLINE, and every other 30447c478bd9Sstevel@tonic-gate * CPU in the specified CPU's new partition is P_NOINTR, cyclic_move_in() 30457c478bd9Sstevel@tonic-gate * will juggle all partition-bound, CPU-unbound cyclics to the specified 30467c478bd9Sstevel@tonic-gate * CPU. 30477c478bd9Sstevel@tonic-gate * 30487c478bd9Sstevel@tonic-gate * Return value 30497c478bd9Sstevel@tonic-gate * 30507c478bd9Sstevel@tonic-gate * None; cyclic_move_in() always succeeds. 30517c478bd9Sstevel@tonic-gate * 30527c478bd9Sstevel@tonic-gate * Caller's context 30537c478bd9Sstevel@tonic-gate * 30547c478bd9Sstevel@tonic-gate * cyclic_move_in() should _only_ be called immediately after a CPU has 30557c478bd9Sstevel@tonic-gate * moved into a new partition, with cpu_lock held. As with other calls 30567c478bd9Sstevel@tonic-gate * into the cyclic subsystem, no lock may be held which is also grabbed 30577c478bd9Sstevel@tonic-gate * by any cyclic handler. 30587c478bd9Sstevel@tonic-gate */ 30597c478bd9Sstevel@tonic-gate void 30607c478bd9Sstevel@tonic-gate cyclic_move_in(cpu_t *d) 30617c478bd9Sstevel@tonic-gate { 30627c478bd9Sstevel@tonic-gate cyc_id_t *idp; 30637c478bd9Sstevel@tonic-gate cyc_cpu_t *dest = d->cpu_cyclic; 30647c478bd9Sstevel@tonic-gate cyclic_t *cyclic; 30657c478bd9Sstevel@tonic-gate cpupart_t *part = d->cpu_part; 30667c478bd9Sstevel@tonic-gate 30677c478bd9Sstevel@tonic-gate CYC_PTRACE("move-in", dest, part); 30687c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 30697c478bd9Sstevel@tonic-gate 30707c478bd9Sstevel@tonic-gate /* 30717c478bd9Sstevel@tonic-gate * Look for CYF_PART_BOUND cyclics in the new partition. If 30727c478bd9Sstevel@tonic-gate * we find one, check to see if it is currently on a CPU which has 30737c478bd9Sstevel@tonic-gate * interrupts disabled. If it is (and if this CPU currently has 30747c478bd9Sstevel@tonic-gate * interrupts enabled), we'll juggle those cyclics over here. 30757c478bd9Sstevel@tonic-gate */ 30767c478bd9Sstevel@tonic-gate if (!(d->cpu_flags & CPU_ENABLE)) { 30777c478bd9Sstevel@tonic-gate CYC_PTRACE1("move-in-none", dest); 30787c478bd9Sstevel@tonic-gate return; 30797c478bd9Sstevel@tonic-gate } 30807c478bd9Sstevel@tonic-gate 30817c478bd9Sstevel@tonic-gate for (idp = cyclic_id_head; idp != NULL; idp = idp->cyi_next) { 30827c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = idp->cyi_cpu; 30837c478bd9Sstevel@tonic-gate cpu_t *c; 30847c478bd9Sstevel@tonic-gate 30857c478bd9Sstevel@tonic-gate /* 30867c478bd9Sstevel@tonic-gate * Omnipresent cyclics are exempt from juggling. 30877c478bd9Sstevel@tonic-gate */ 30887c478bd9Sstevel@tonic-gate if (cpu == NULL) 30897c478bd9Sstevel@tonic-gate continue; 30907c478bd9Sstevel@tonic-gate 30917c478bd9Sstevel@tonic-gate c = cpu->cyp_cpu; 30927c478bd9Sstevel@tonic-gate 30937c478bd9Sstevel@tonic-gate if (c->cpu_part != part || (c->cpu_flags & CPU_ENABLE)) 30947c478bd9Sstevel@tonic-gate continue; 30957c478bd9Sstevel@tonic-gate 30967c478bd9Sstevel@tonic-gate cyclic = &cpu->cyp_cyclics[idp->cyi_ndx]; 30977c478bd9Sstevel@tonic-gate 30987c478bd9Sstevel@tonic-gate if (cyclic->cy_flags & CYF_CPU_BOUND) 30997c478bd9Sstevel@tonic-gate continue; 31007c478bd9Sstevel@tonic-gate 31017c478bd9Sstevel@tonic-gate /* 31027c478bd9Sstevel@tonic-gate * We know that this cyclic is bound to its processor set 31037c478bd9Sstevel@tonic-gate * (otherwise, it would not be on a CPU with interrupts 31047c478bd9Sstevel@tonic-gate * disabled); juggle it to our CPU. 31057c478bd9Sstevel@tonic-gate */ 31067c478bd9Sstevel@tonic-gate ASSERT(cyclic->cy_flags & CYF_PART_BOUND); 31077c478bd9Sstevel@tonic-gate cyclic_juggle_one_to(idp, dest); 31087c478bd9Sstevel@tonic-gate } 31097c478bd9Sstevel@tonic-gate 31107c478bd9Sstevel@tonic-gate CYC_PTRACE1("move-in-done", dest); 31117c478bd9Sstevel@tonic-gate } 31127c478bd9Sstevel@tonic-gate 31137c478bd9Sstevel@tonic-gate /* 31147c478bd9Sstevel@tonic-gate * int cyclic_move_out(cpu_t *) 31157c478bd9Sstevel@tonic-gate * 31167c478bd9Sstevel@tonic-gate * Overview 31177c478bd9Sstevel@tonic-gate * 31187c478bd9Sstevel@tonic-gate * cyclic_move_out() is called by the CPU partition code immediately before 31197c478bd9Sstevel@tonic-gate * the specified CPU is to move out of its partition. 31207c478bd9Sstevel@tonic-gate * 31217c478bd9Sstevel@tonic-gate * Arguments and notes 31227c478bd9Sstevel@tonic-gate * 31237c478bd9Sstevel@tonic-gate * The only argument to cyclic_move_out() is a CPU which is to move out of 31247c478bd9Sstevel@tonic-gate * its partition. 31257c478bd9Sstevel@tonic-gate * 31267c478bd9Sstevel@tonic-gate * cyclic_move_out() will attempt to juggle away all partition-bound 31277c478bd9Sstevel@tonic-gate * cyclics. If the specified CPU is the last CPU in a partition with 31287c478bd9Sstevel@tonic-gate * partition-bound cyclics, cyclic_move_out() will fail. If there exists 31297c478bd9Sstevel@tonic-gate * a partition-bound cyclic which is CPU-bound to the specified CPU, 31307c478bd9Sstevel@tonic-gate * cyclic_move_out() will fail. 31317c478bd9Sstevel@tonic-gate * 31327c478bd9Sstevel@tonic-gate * Note that cyclic_move_out() will _only_ attempt to juggle away 31337c478bd9Sstevel@tonic-gate * partition-bound cyclics; CPU-bound cyclics which are not partition-bound 31347c478bd9Sstevel@tonic-gate * and unbound cyclics are not affected by changing the partition 31357c478bd9Sstevel@tonic-gate * affiliation of the CPU. 31367c478bd9Sstevel@tonic-gate * 31377c478bd9Sstevel@tonic-gate * Return value 31387c478bd9Sstevel@tonic-gate * 31397c478bd9Sstevel@tonic-gate * cyclic_move_out() returns 1 if all partition-bound cyclics on the CPU 31407c478bd9Sstevel@tonic-gate * were juggled away; 0 if some cyclics remain. 31417c478bd9Sstevel@tonic-gate * 31427c478bd9Sstevel@tonic-gate * Caller's context 31437c478bd9Sstevel@tonic-gate * 31447c478bd9Sstevel@tonic-gate * cyclic_move_out() should _only_ be called immediately before a CPU has 31457c478bd9Sstevel@tonic-gate * moved out of its partition, with cpu_lock held. It is expected that 31467c478bd9Sstevel@tonic-gate * the caller of cyclic_move_out() will change the processor set affiliation 31477c478bd9Sstevel@tonic-gate * of the specified CPU immediately after cyclic_move_out() returns 31487c478bd9Sstevel@tonic-gate * success (i.e. before dropping cpu_lock). Moreover, it is expected that 31497c478bd9Sstevel@tonic-gate * the caller will fail the CPU repartitioning operation if cyclic_move_out() 31507c478bd9Sstevel@tonic-gate * returns failure. As with other calls into the cyclic subsystem, no lock 31517c478bd9Sstevel@tonic-gate * may be held which is also grabbed by any cyclic handler. 31527c478bd9Sstevel@tonic-gate */ 31537c478bd9Sstevel@tonic-gate int 31547c478bd9Sstevel@tonic-gate cyclic_move_out(cpu_t *c) 31557c478bd9Sstevel@tonic-gate { 31567c478bd9Sstevel@tonic-gate cyc_id_t *idp; 31577c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu = c->cpu_cyclic, *dest; 31587c478bd9Sstevel@tonic-gate cyclic_t *cyclic, *cyclics = cpu->cyp_cyclics; 31597c478bd9Sstevel@tonic-gate cpupart_t *part = c->cpu_part; 31607c478bd9Sstevel@tonic-gate 31617c478bd9Sstevel@tonic-gate CYC_PTRACE1("move-out", cpu); 31627c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 31637c478bd9Sstevel@tonic-gate 31647c478bd9Sstevel@tonic-gate /* 31657c478bd9Sstevel@tonic-gate * If there are any CYF_PART_BOUND cyclics on this CPU, we need 31667c478bd9Sstevel@tonic-gate * to try to juggle them away. 31677c478bd9Sstevel@tonic-gate */ 31687c478bd9Sstevel@tonic-gate for (idp = cyclic_id_head; idp != NULL; idp = idp->cyi_next) { 31697c478bd9Sstevel@tonic-gate 31707c478bd9Sstevel@tonic-gate if (idp->cyi_cpu != cpu) 31717c478bd9Sstevel@tonic-gate continue; 31727c478bd9Sstevel@tonic-gate 31737c478bd9Sstevel@tonic-gate cyclic = &cyclics[idp->cyi_ndx]; 31747c478bd9Sstevel@tonic-gate 31757c478bd9Sstevel@tonic-gate if (!(cyclic->cy_flags & CYF_PART_BOUND)) 31767c478bd9Sstevel@tonic-gate continue; 31777c478bd9Sstevel@tonic-gate 31787c478bd9Sstevel@tonic-gate dest = cyclic_pick_cpu(part, c, c, cyclic->cy_flags); 31797c478bd9Sstevel@tonic-gate 31807c478bd9Sstevel@tonic-gate if (dest == NULL) { 31817c478bd9Sstevel@tonic-gate /* 31827c478bd9Sstevel@tonic-gate * We can't juggle this cyclic; we need to return 31837c478bd9Sstevel@tonic-gate * failure (we won't bother trying to juggle away 31847c478bd9Sstevel@tonic-gate * other cyclics). 31857c478bd9Sstevel@tonic-gate */ 31867c478bd9Sstevel@tonic-gate CYC_PTRACE("move-out-fail", cpu, idp); 31877c478bd9Sstevel@tonic-gate return (0); 31887c478bd9Sstevel@tonic-gate } 31897c478bd9Sstevel@tonic-gate cyclic_juggle_one_to(idp, dest); 31907c478bd9Sstevel@tonic-gate } 31917c478bd9Sstevel@tonic-gate 31927c478bd9Sstevel@tonic-gate CYC_PTRACE1("move-out-done", cpu); 31937c478bd9Sstevel@tonic-gate return (1); 31947c478bd9Sstevel@tonic-gate } 31957c478bd9Sstevel@tonic-gate 31967c478bd9Sstevel@tonic-gate /* 31977c478bd9Sstevel@tonic-gate * void cyclic_suspend() 31987c478bd9Sstevel@tonic-gate * 31997c478bd9Sstevel@tonic-gate * Overview 32007c478bd9Sstevel@tonic-gate * 32017c478bd9Sstevel@tonic-gate * cyclic_suspend() suspends all cyclic activity throughout the cyclic 32027c478bd9Sstevel@tonic-gate * subsystem. It should be called only by subsystems which are attempting 32037c478bd9Sstevel@tonic-gate * to suspend the entire system (e.g. checkpoint/resume, dynamic 32047c478bd9Sstevel@tonic-gate * reconfiguration). 32057c478bd9Sstevel@tonic-gate * 32067c478bd9Sstevel@tonic-gate * Arguments and notes 32077c478bd9Sstevel@tonic-gate * 32087c478bd9Sstevel@tonic-gate * cyclic_suspend() takes no arguments. Each CPU with an active cyclic 32097c478bd9Sstevel@tonic-gate * disables its backend (offline CPUs disable their backends as part of 32107c478bd9Sstevel@tonic-gate * the cyclic_offline() operation), thereby disabling future CY_HIGH_LEVEL 32117c478bd9Sstevel@tonic-gate * interrupts. 32127c478bd9Sstevel@tonic-gate * 32137c478bd9Sstevel@tonic-gate * Note that disabling CY_HIGH_LEVEL interrupts does not completely preclude 32147c478bd9Sstevel@tonic-gate * cyclic handlers from being called after cyclic_suspend() returns: if a 32157c478bd9Sstevel@tonic-gate * CY_LOCK_LEVEL or CY_LOW_LEVEL interrupt thread was blocked at the time 32167c478bd9Sstevel@tonic-gate * of cyclic_suspend(), cyclic handlers at its level may continue to be 32177c478bd9Sstevel@tonic-gate * called after the interrupt thread becomes unblocked. The 32187c478bd9Sstevel@tonic-gate * post-cyclic_suspend() activity is bounded by the pend count on all 32197c478bd9Sstevel@tonic-gate * cyclics at the time of cyclic_suspend(). Callers concerned with more 32207c478bd9Sstevel@tonic-gate * than simply disabling future CY_HIGH_LEVEL interrupts must check for 32217c478bd9Sstevel@tonic-gate * this condition. 32227c478bd9Sstevel@tonic-gate * 32237c478bd9Sstevel@tonic-gate * On most platforms, timestamps from gethrtime() and gethrestime() are not 32247c478bd9Sstevel@tonic-gate * guaranteed to monotonically increase between cyclic_suspend() and 32257c478bd9Sstevel@tonic-gate * cyclic_resume(). However, timestamps are guaranteed to monotonically 32267c478bd9Sstevel@tonic-gate * increase across the entire cyclic_suspend()/cyclic_resume() operation. 32277c478bd9Sstevel@tonic-gate * That is, every timestamp obtained before cyclic_suspend() will be less 32287c478bd9Sstevel@tonic-gate * than every timestamp obtained after cyclic_resume(). 32297c478bd9Sstevel@tonic-gate * 32307c478bd9Sstevel@tonic-gate * Return value 32317c478bd9Sstevel@tonic-gate * 32327c478bd9Sstevel@tonic-gate * None; cyclic_suspend() always succeeds. 32337c478bd9Sstevel@tonic-gate * 32347c478bd9Sstevel@tonic-gate * Caller's context 32357c478bd9Sstevel@tonic-gate * 32367c478bd9Sstevel@tonic-gate * The cyclic subsystem must be configured on every valid CPU; 32377c478bd9Sstevel@tonic-gate * cyclic_suspend() may not be called during boot or during dynamic 32387c478bd9Sstevel@tonic-gate * reconfiguration. Additionally, cpu_lock must be held, and the caller 32397c478bd9Sstevel@tonic-gate * cannot be in high-level interrupt context. However, unlike most other 32407c478bd9Sstevel@tonic-gate * cyclic entry points, cyclic_suspend() may be called with locks held 32417c478bd9Sstevel@tonic-gate * which are also acquired by CY_LOCK_LEVEL or CY_LOW_LEVEL cyclic 32427c478bd9Sstevel@tonic-gate * handlers. 32437c478bd9Sstevel@tonic-gate */ 32447c478bd9Sstevel@tonic-gate void 32457c478bd9Sstevel@tonic-gate cyclic_suspend() 32467c478bd9Sstevel@tonic-gate { 32477c478bd9Sstevel@tonic-gate cpu_t *c; 32487c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu; 32497c478bd9Sstevel@tonic-gate cyc_xcallarg_t arg; 32507c478bd9Sstevel@tonic-gate cyc_backend_t *be; 32517c478bd9Sstevel@tonic-gate 32527c478bd9Sstevel@tonic-gate CYC_PTRACE0("suspend"); 32537c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 32547c478bd9Sstevel@tonic-gate c = cpu_list; 32557c478bd9Sstevel@tonic-gate 32567c478bd9Sstevel@tonic-gate do { 32577c478bd9Sstevel@tonic-gate cpu = c->cpu_cyclic; 32587c478bd9Sstevel@tonic-gate be = cpu->cyp_backend; 32597c478bd9Sstevel@tonic-gate arg.cyx_cpu = cpu; 32607c478bd9Sstevel@tonic-gate 32617c478bd9Sstevel@tonic-gate be->cyb_xcall(be->cyb_arg, c, 32627c478bd9Sstevel@tonic-gate (cyc_func_t)cyclic_suspend_xcall, &arg); 32637c478bd9Sstevel@tonic-gate } while ((c = c->cpu_next) != cpu_list); 32647c478bd9Sstevel@tonic-gate } 32657c478bd9Sstevel@tonic-gate 32667c478bd9Sstevel@tonic-gate /* 32677c478bd9Sstevel@tonic-gate * void cyclic_resume() 32687c478bd9Sstevel@tonic-gate * 32697c478bd9Sstevel@tonic-gate * cyclic_resume() resumes all cyclic activity throughout the cyclic 32707c478bd9Sstevel@tonic-gate * subsystem. It should be called only by system-suspending subsystems. 32717c478bd9Sstevel@tonic-gate * 32727c478bd9Sstevel@tonic-gate * Arguments and notes 32737c478bd9Sstevel@tonic-gate * 32747c478bd9Sstevel@tonic-gate * cyclic_resume() takes no arguments. Each CPU with an active cyclic 32757c478bd9Sstevel@tonic-gate * reenables and reprograms its backend (offline CPUs are not reenabled). 32767c478bd9Sstevel@tonic-gate * On most platforms, timestamps from gethrtime() and gethrestime() are not 32777c478bd9Sstevel@tonic-gate * guaranteed to monotonically increase between cyclic_suspend() and 32787c478bd9Sstevel@tonic-gate * cyclic_resume(). However, timestamps are guaranteed to monotonically 32797c478bd9Sstevel@tonic-gate * increase across the entire cyclic_suspend()/cyclic_resume() operation. 32807c478bd9Sstevel@tonic-gate * That is, every timestamp obtained before cyclic_suspend() will be less 32817c478bd9Sstevel@tonic-gate * than every timestamp obtained after cyclic_resume(). 32827c478bd9Sstevel@tonic-gate * 32837c478bd9Sstevel@tonic-gate * Return value 32847c478bd9Sstevel@tonic-gate * 32857c478bd9Sstevel@tonic-gate * None; cyclic_resume() always succeeds. 32867c478bd9Sstevel@tonic-gate * 32877c478bd9Sstevel@tonic-gate * Caller's context 32887c478bd9Sstevel@tonic-gate * 32897c478bd9Sstevel@tonic-gate * The cyclic subsystem must be configured on every valid CPU; 32907c478bd9Sstevel@tonic-gate * cyclic_resume() may not be called during boot or during dynamic 32917c478bd9Sstevel@tonic-gate * reconfiguration. Additionally, cpu_lock must be held, and the caller 32927c478bd9Sstevel@tonic-gate * cannot be in high-level interrupt context. However, unlike most other 32937c478bd9Sstevel@tonic-gate * cyclic entry points, cyclic_resume() may be called with locks held which 32947c478bd9Sstevel@tonic-gate * are also acquired by CY_LOCK_LEVEL or CY_LOW_LEVEL cyclic handlers. 32957c478bd9Sstevel@tonic-gate */ 32967c478bd9Sstevel@tonic-gate void 32977c478bd9Sstevel@tonic-gate cyclic_resume() 32987c478bd9Sstevel@tonic-gate { 32997c478bd9Sstevel@tonic-gate cpu_t *c; 33007c478bd9Sstevel@tonic-gate cyc_cpu_t *cpu; 33017c478bd9Sstevel@tonic-gate cyc_xcallarg_t arg; 33027c478bd9Sstevel@tonic-gate cyc_backend_t *be; 33037c478bd9Sstevel@tonic-gate 33047c478bd9Sstevel@tonic-gate CYC_PTRACE0("resume"); 33057c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 33067c478bd9Sstevel@tonic-gate 33077c478bd9Sstevel@tonic-gate c = cpu_list; 33087c478bd9Sstevel@tonic-gate 33097c478bd9Sstevel@tonic-gate do { 33107c478bd9Sstevel@tonic-gate cpu = c->cpu_cyclic; 33117c478bd9Sstevel@tonic-gate be = cpu->cyp_backend; 33127c478bd9Sstevel@tonic-gate arg.cyx_cpu = cpu; 33137c478bd9Sstevel@tonic-gate 33147c478bd9Sstevel@tonic-gate be->cyb_xcall(be->cyb_arg, c, 33157c478bd9Sstevel@tonic-gate (cyc_func_t)cyclic_resume_xcall, &arg); 33167c478bd9Sstevel@tonic-gate } while ((c = c->cpu_next) != cpu_list); 33177c478bd9Sstevel@tonic-gate } 3318