xref: /onnv-gate/usr/src/uts/common/os/cpu_event.c (revision 12004:93f274d4a367)
19637SRandy.Fishel@Sun.COM /*
29637SRandy.Fishel@Sun.COM  * CDDL HEADER START
39637SRandy.Fishel@Sun.COM  *
49637SRandy.Fishel@Sun.COM  * The contents of this file are subject to the terms of the
59637SRandy.Fishel@Sun.COM  * Common Development and Distribution License (the "License").
69637SRandy.Fishel@Sun.COM  * You may not use this file except in compliance with the License.
79637SRandy.Fishel@Sun.COM  *
89637SRandy.Fishel@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99637SRandy.Fishel@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109637SRandy.Fishel@Sun.COM  * See the License for the specific language governing permissions
119637SRandy.Fishel@Sun.COM  * and limitations under the License.
129637SRandy.Fishel@Sun.COM  *
139637SRandy.Fishel@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149637SRandy.Fishel@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159637SRandy.Fishel@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169637SRandy.Fishel@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179637SRandy.Fishel@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189637SRandy.Fishel@Sun.COM  *
199637SRandy.Fishel@Sun.COM  * CDDL HEADER END
209637SRandy.Fishel@Sun.COM  */
219637SRandy.Fishel@Sun.COM /*
22*12004Sjiang.liu@intel.com  * Copyright (c) 2009-2010, Intel Corporation.
239637SRandy.Fishel@Sun.COM  * All rights reserved.
249637SRandy.Fishel@Sun.COM  */
259637SRandy.Fishel@Sun.COM 
269637SRandy.Fishel@Sun.COM /*
279637SRandy.Fishel@Sun.COM  * Introduction
289637SRandy.Fishel@Sun.COM  * This file implements a CPU event notification mechanism to signal clients
299637SRandy.Fishel@Sun.COM  * which are interested in CPU related events.
309637SRandy.Fishel@Sun.COM  * Currently it only supports CPU idle state change events which will be
319637SRandy.Fishel@Sun.COM  * triggered just before CPU entering hardware idle state and just after CPU
329637SRandy.Fishel@Sun.COM  * wakes up from hardware idle state.
339637SRandy.Fishel@Sun.COM  * Please refer to PSARC/2009/115 for detail information.
349637SRandy.Fishel@Sun.COM  *
359637SRandy.Fishel@Sun.COM  * Lock Strategy
369637SRandy.Fishel@Sun.COM  * 1) cpu_idle_prop_busy/free are protected by cpu_idle_prop_lock.
379637SRandy.Fishel@Sun.COM  * 2) No protection for cpu_idle_cb_state because it's per-CPU data.
389637SRandy.Fishel@Sun.COM  * 3) cpu_idle_cb_busy is protected by cpu_idle_cb_lock.
399637SRandy.Fishel@Sun.COM  * 4) cpu_idle_cb_array is protected by pause_cpus/start_cpus logic.
409637SRandy.Fishel@Sun.COM  * 5) cpu_idle_cb_max/curr are protected by both cpu_idle_cb_lock and
419637SRandy.Fishel@Sun.COM  *    pause_cpus/start_cpus logic.
429637SRandy.Fishel@Sun.COM  * We have optimized the algorithm for hot path on read side access.
439637SRandy.Fishel@Sun.COM  * In the current algorithm, it's lock free on read side access.
449637SRandy.Fishel@Sun.COM  * On write side, we use pause_cpus() to keep other CPUs in the pause thread,
459637SRandy.Fishel@Sun.COM  * which will guarantee that no other threads will access
469637SRandy.Fishel@Sun.COM  * cpu_idle_cb_max/curr/array data structure.
479637SRandy.Fishel@Sun.COM  */
489637SRandy.Fishel@Sun.COM 
499637SRandy.Fishel@Sun.COM #include <sys/types.h>
509637SRandy.Fishel@Sun.COM #include <sys/cmn_err.h>
519637SRandy.Fishel@Sun.COM #include <sys/cpuvar.h>
529637SRandy.Fishel@Sun.COM #include <sys/cpu.h>
539637SRandy.Fishel@Sun.COM #include <sys/kmem.h>
549637SRandy.Fishel@Sun.COM #include <sys/machcpuvar.h>
559637SRandy.Fishel@Sun.COM #include <sys/sdt.h>
569637SRandy.Fishel@Sun.COM #include <sys/sysmacros.h>
579637SRandy.Fishel@Sun.COM #include <sys/synch.h>
589637SRandy.Fishel@Sun.COM #include <sys/systm.h>
599637SRandy.Fishel@Sun.COM #include <sys/sunddi.h>
609637SRandy.Fishel@Sun.COM #if defined(__sparc)
619637SRandy.Fishel@Sun.COM #include <sys/machsystm.h>
629637SRandy.Fishel@Sun.COM #elif defined(__x86)
639637SRandy.Fishel@Sun.COM #include <sys/archsystm.h>
649637SRandy.Fishel@Sun.COM #endif
659637SRandy.Fishel@Sun.COM #include <sys/cpu_event.h>
669637SRandy.Fishel@Sun.COM 
679637SRandy.Fishel@Sun.COM /* Define normal state for CPU on different platforms. */
689637SRandy.Fishel@Sun.COM #if defined(__x86)
699637SRandy.Fishel@Sun.COM #define	CPU_IDLE_STATE_NORMAL		IDLE_STATE_C0
709637SRandy.Fishel@Sun.COM #elif defined(__sparc)
719637SRandy.Fishel@Sun.COM /*
729637SRandy.Fishel@Sun.COM  * At the time of this implementation IDLE_STATE_NORMAL is defined
739637SRandy.Fishel@Sun.COM  * in mach_startup.c, and not in a header file.  So if we find it is
749637SRandy.Fishel@Sun.COM  * undefined, then we set it to the value as defined in mach_startup.c
759637SRandy.Fishel@Sun.COM  * Should it eventually be defined, we will pick it up.
769637SRandy.Fishel@Sun.COM  */
779637SRandy.Fishel@Sun.COM #ifndef	IDLE_STATE_NORMAL
789637SRandy.Fishel@Sun.COM #define	IDLE_STATE_NORMAL	0
799637SRandy.Fishel@Sun.COM #endif
809637SRandy.Fishel@Sun.COM #define	CPU_IDLE_STATE_NORMAL	IDLE_STATE_NORMAL
819637SRandy.Fishel@Sun.COM #endif
829637SRandy.Fishel@Sun.COM 
839637SRandy.Fishel@Sun.COM /*
849637SRandy.Fishel@Sun.COM  * To improve cache efficiency and avoid cache false sharing, CPU idle
859637SRandy.Fishel@Sun.COM  * properties are grouped into cache lines as below:
869637SRandy.Fishel@Sun.COM  * |     CPU0      |     CPU1      |.........|     CPUn      |
879637SRandy.Fishel@Sun.COM  * | cache line 0  | cache line 1  |.........| cache line n  |
889637SRandy.Fishel@Sun.COM  * | v0 | ... | vm | v0 | ... | vm |.........| v0 | ... | vm |
899637SRandy.Fishel@Sun.COM  * To access value of property m for CPU n, using following value as index:
909637SRandy.Fishel@Sun.COM  *    index = seq_id_of_CPUn * CPU_IDLE_VALUE_GROUP_SIZE + m.
919637SRandy.Fishel@Sun.COM  */
929637SRandy.Fishel@Sun.COM #define	CPU_IDLE_VALUE_GROUP_SIZE	\
939637SRandy.Fishel@Sun.COM 	(CPU_CACHE_COHERENCE_SIZE / sizeof (cpu_idle_prop_value_t))
949637SRandy.Fishel@Sun.COM 
959637SRandy.Fishel@Sun.COM /* Get callback context handle for current CPU. */
969637SRandy.Fishel@Sun.COM #define	CPU_IDLE_GET_CTX(cp)		\
979637SRandy.Fishel@Sun.COM 	((cpu_idle_callback_context_t)(intptr_t)((cp)->cpu_seqid))
989637SRandy.Fishel@Sun.COM 
999637SRandy.Fishel@Sun.COM /* Get CPU sequential id from ctx. */
1009637SRandy.Fishel@Sun.COM #define	CPU_IDLE_CTX2CPUID(ctx)		((processorid_t)(intptr_t)(ctx))
1019637SRandy.Fishel@Sun.COM 
1029637SRandy.Fishel@Sun.COM /* Compute index from callback context handle. */
1039637SRandy.Fishel@Sun.COM #define	CPU_IDLE_CTX2IDX(ctx)		\
1049637SRandy.Fishel@Sun.COM 	(((int)(intptr_t)(ctx)) * CPU_IDLE_VALUE_GROUP_SIZE)
1059637SRandy.Fishel@Sun.COM 
1069637SRandy.Fishel@Sun.COM #define	CPU_IDLE_HDL2VALP(hdl, idx)	\
1079637SRandy.Fishel@Sun.COM 	(&((cpu_idle_prop_impl_t *)(hdl))->value[(idx)])
1089637SRandy.Fishel@Sun.COM 
1099637SRandy.Fishel@Sun.COM /*
1109637SRandy.Fishel@Sun.COM  * When cpu_idle_cb_array is NULL or full, increase CPU_IDLE_ARRAY_CAPACITY_INC
1119637SRandy.Fishel@Sun.COM  * entries every time. Here we prefer linear growth instead of exponential.
1129637SRandy.Fishel@Sun.COM  */
1139637SRandy.Fishel@Sun.COM #define	CPU_IDLE_ARRAY_CAPACITY_INC	0x10
1149637SRandy.Fishel@Sun.COM 
1159637SRandy.Fishel@Sun.COM typedef struct cpu_idle_prop_impl {
1169637SRandy.Fishel@Sun.COM 	cpu_idle_prop_value_t		*value;
1179637SRandy.Fishel@Sun.COM 	struct cpu_idle_prop_impl	*next;
1189637SRandy.Fishel@Sun.COM 	char				*name;
1199637SRandy.Fishel@Sun.COM 	cpu_idle_prop_update_t		update;
1209637SRandy.Fishel@Sun.COM 	void				*private;
1219637SRandy.Fishel@Sun.COM 	cpu_idle_prop_type_t		type;
1229637SRandy.Fishel@Sun.COM 	uint32_t			refcnt;
1239637SRandy.Fishel@Sun.COM } cpu_idle_prop_impl_t;
1249637SRandy.Fishel@Sun.COM 
1259637SRandy.Fishel@Sun.COM typedef struct cpu_idle_prop_item {
1269637SRandy.Fishel@Sun.COM 	cpu_idle_prop_type_t		type;
1279637SRandy.Fishel@Sun.COM 	char				*name;
1289637SRandy.Fishel@Sun.COM 	cpu_idle_prop_update_t		update;
1299637SRandy.Fishel@Sun.COM 	void				*arg;
1309637SRandy.Fishel@Sun.COM 	cpu_idle_prop_handle_t		handle;
1319637SRandy.Fishel@Sun.COM } cpu_idle_prop_item_t;
1329637SRandy.Fishel@Sun.COM 
1339637SRandy.Fishel@Sun.COM /* Structure to maintain registered callbacks in list. */
1349637SRandy.Fishel@Sun.COM typedef struct cpu_idle_cb_impl {
1359637SRandy.Fishel@Sun.COM 	struct cpu_idle_cb_impl		*next;
1369637SRandy.Fishel@Sun.COM 	cpu_idle_callback_t		*callback;
1379637SRandy.Fishel@Sun.COM 	void				*argument;
1389637SRandy.Fishel@Sun.COM 	int				priority;
1399637SRandy.Fishel@Sun.COM } cpu_idle_cb_impl_t;
1409637SRandy.Fishel@Sun.COM 
1419637SRandy.Fishel@Sun.COM /*
1429637SRandy.Fishel@Sun.COM  * Structure to maintain registered callbacks in priority order and also
1439637SRandy.Fishel@Sun.COM  * optimized for cache efficiency for reading access.
1449637SRandy.Fishel@Sun.COM  */
1459637SRandy.Fishel@Sun.COM typedef struct cpu_idle_cb_item {
1469637SRandy.Fishel@Sun.COM 	cpu_idle_enter_cbfn_t		enter;
1479637SRandy.Fishel@Sun.COM 	cpu_idle_exit_cbfn_t		exit;
1489637SRandy.Fishel@Sun.COM 	void				*arg;
1499637SRandy.Fishel@Sun.COM 	cpu_idle_cb_impl_t		*impl;
1509637SRandy.Fishel@Sun.COM } cpu_idle_cb_item_t;
1519637SRandy.Fishel@Sun.COM 
1529637SRandy.Fishel@Sun.COM /* Per-CPU state aligned to CPU_CACHE_COHERENCE_SIZE to avoid false sharing. */
1539637SRandy.Fishel@Sun.COM typedef union cpu_idle_cb_state {
1549637SRandy.Fishel@Sun.COM 	struct {
155*12004Sjiang.liu@intel.com 		/* Index of already invoked callbacks. */
1569637SRandy.Fishel@Sun.COM 		int			index;
157*12004Sjiang.liu@intel.com 		/* Invoke registered callbacks if true. */
158*12004Sjiang.liu@intel.com 		boolean_t		enabled;
159*12004Sjiang.liu@intel.com 		/* Property values are valid if true. */
1609637SRandy.Fishel@Sun.COM 		boolean_t		ready;
161*12004Sjiang.liu@intel.com 		/* Pointers to per-CPU properties. */
1629637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*idle_state;
1639637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*enter_ts;
1649637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*exit_ts;
1659637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*last_idle;
1669637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*last_busy;
1679637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*total_idle;
1689637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*total_busy;
1699637SRandy.Fishel@Sun.COM 		cpu_idle_prop_value_t	*intr_cnt;
1709637SRandy.Fishel@Sun.COM 	} v;
1719637SRandy.Fishel@Sun.COM #ifdef _LP64
1729637SRandy.Fishel@Sun.COM 	char				align[2 * CPU_CACHE_COHERENCE_SIZE];
1739637SRandy.Fishel@Sun.COM #else
1749637SRandy.Fishel@Sun.COM 	char				align[CPU_CACHE_COHERENCE_SIZE];
1759637SRandy.Fishel@Sun.COM #endif
1769637SRandy.Fishel@Sun.COM } cpu_idle_cb_state_t;
1779637SRandy.Fishel@Sun.COM 
1789637SRandy.Fishel@Sun.COM static kmutex_t				cpu_idle_prop_lock;
1799637SRandy.Fishel@Sun.COM static cpu_idle_prop_impl_t		*cpu_idle_prop_busy = NULL;
1809637SRandy.Fishel@Sun.COM static cpu_idle_prop_impl_t		*cpu_idle_prop_free = NULL;
1819637SRandy.Fishel@Sun.COM 
1829637SRandy.Fishel@Sun.COM static kmutex_t				cpu_idle_cb_lock;
1839637SRandy.Fishel@Sun.COM static cpu_idle_cb_impl_t		*cpu_idle_cb_busy = NULL;
1849637SRandy.Fishel@Sun.COM static cpu_idle_cb_item_t		*cpu_idle_cb_array = NULL;
1859637SRandy.Fishel@Sun.COM static int				cpu_idle_cb_curr = 0;
1869637SRandy.Fishel@Sun.COM static int				cpu_idle_cb_max = 0;
1879637SRandy.Fishel@Sun.COM 
1889637SRandy.Fishel@Sun.COM static cpu_idle_cb_state_t		*cpu_idle_cb_state;
1899637SRandy.Fishel@Sun.COM 
190*12004Sjiang.liu@intel.com #ifdef	__x86
191*12004Sjiang.liu@intel.com /*
192*12004Sjiang.liu@intel.com  * cpuset used to intercept CPUs before powering them off.
193*12004Sjiang.liu@intel.com  * The control CPU sets the bit corresponding to the target CPU and waits
194*12004Sjiang.liu@intel.com  * until the bit is cleared.
195*12004Sjiang.liu@intel.com  * The target CPU disables interrupts before clearing corresponding bit and
196*12004Sjiang.liu@intel.com  * then loops for ever.
197*12004Sjiang.liu@intel.com  */
198*12004Sjiang.liu@intel.com static cpuset_t				cpu_idle_intercept_set;
199*12004Sjiang.liu@intel.com #endif
200*12004Sjiang.liu@intel.com 
2019637SRandy.Fishel@Sun.COM static int cpu_idle_prop_update_intr_cnt(void *arg, uint64_t seqnum,
2029637SRandy.Fishel@Sun.COM     cpu_idle_prop_value_t *valp);
2039637SRandy.Fishel@Sun.COM 
2049637SRandy.Fishel@Sun.COM static cpu_idle_prop_item_t cpu_idle_prop_array[] = {
2059637SRandy.Fishel@Sun.COM 	{
2069637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_INTPTR, CPU_IDLE_PROP_IDLE_STATE,
2079637SRandy.Fishel@Sun.COM 	    NULL, NULL, NULL
2089637SRandy.Fishel@Sun.COM 	},
2099637SRandy.Fishel@Sun.COM 	{
2109637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_HRTIME, CPU_IDLE_PROP_ENTER_TIMESTAMP,
2119637SRandy.Fishel@Sun.COM 	    NULL, NULL, NULL
2129637SRandy.Fishel@Sun.COM 	},
2139637SRandy.Fishel@Sun.COM 	{
2149637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_HRTIME, CPU_IDLE_PROP_EXIT_TIMESTAMP,
2159637SRandy.Fishel@Sun.COM 	    NULL, NULL, NULL
2169637SRandy.Fishel@Sun.COM 	},
2179637SRandy.Fishel@Sun.COM 	{
2189637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_HRTIME, CPU_IDLE_PROP_LAST_IDLE_TIME,
2199637SRandy.Fishel@Sun.COM 	    NULL, NULL, NULL
2209637SRandy.Fishel@Sun.COM 	},
2219637SRandy.Fishel@Sun.COM 	{
2229637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_HRTIME, CPU_IDLE_PROP_LAST_BUSY_TIME,
2239637SRandy.Fishel@Sun.COM 	    NULL, NULL, NULL
2249637SRandy.Fishel@Sun.COM 	},
2259637SRandy.Fishel@Sun.COM 	{
2269637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_HRTIME, CPU_IDLE_PROP_TOTAL_IDLE_TIME,
2279637SRandy.Fishel@Sun.COM 	    NULL, NULL, NULL
2289637SRandy.Fishel@Sun.COM 	},
2299637SRandy.Fishel@Sun.COM 	{
2309637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_HRTIME, CPU_IDLE_PROP_TOTAL_BUSY_TIME,
2319637SRandy.Fishel@Sun.COM 	    NULL, NULL, NULL
2329637SRandy.Fishel@Sun.COM 	},
2339637SRandy.Fishel@Sun.COM 	{
2349637SRandy.Fishel@Sun.COM 	    CPU_IDLE_PROP_TYPE_UINT64, CPU_IDLE_PROP_INTERRUPT_COUNT,
2359637SRandy.Fishel@Sun.COM 	    cpu_idle_prop_update_intr_cnt, NULL, NULL
2369637SRandy.Fishel@Sun.COM 	},
2379637SRandy.Fishel@Sun.COM };
2389637SRandy.Fishel@Sun.COM 
2399637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_IDLE_STATE	0
2409637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_ENTER_TS	1
2419637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_EXIT_TS	2
2429637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_LAST_IDLE	3
2439637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_LAST_BUSY	4
2449637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_TOTAL_IDLE	5
2459637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_TOTAL_BUSY	6
2469637SRandy.Fishel@Sun.COM #define	CPU_IDLE_PROP_IDX_INTR_CNT	7
2479637SRandy.Fishel@Sun.COM 
2489637SRandy.Fishel@Sun.COM /*ARGSUSED*/
2499637SRandy.Fishel@Sun.COM static void
cpu_idle_dtrace_enter(void * arg,cpu_idle_callback_context_t ctx,cpu_idle_check_wakeup_t check_func,void * check_arg)2509637SRandy.Fishel@Sun.COM cpu_idle_dtrace_enter(void *arg, cpu_idle_callback_context_t ctx,
2519637SRandy.Fishel@Sun.COM     cpu_idle_check_wakeup_t check_func, void *check_arg)
2529637SRandy.Fishel@Sun.COM {
2539637SRandy.Fishel@Sun.COM 	int state;
2549637SRandy.Fishel@Sun.COM 
2559637SRandy.Fishel@Sun.COM 	state = cpu_idle_prop_get_intptr(
2569637SRandy.Fishel@Sun.COM 	    cpu_idle_prop_array[CPU_IDLE_PROP_IDX_IDLE_STATE].handle, ctx);
2579637SRandy.Fishel@Sun.COM 	DTRACE_PROBE1(idle__state__transition, uint_t, state);
2589637SRandy.Fishel@Sun.COM }
2599637SRandy.Fishel@Sun.COM 
2609637SRandy.Fishel@Sun.COM /*ARGSUSED*/
2619637SRandy.Fishel@Sun.COM static void
cpu_idle_dtrace_exit(void * arg,cpu_idle_callback_context_t ctx,int flag)2629637SRandy.Fishel@Sun.COM cpu_idle_dtrace_exit(void *arg, cpu_idle_callback_context_t ctx, int flag)
2639637SRandy.Fishel@Sun.COM {
2649637SRandy.Fishel@Sun.COM 	DTRACE_PROBE1(idle__state__transition, uint_t, CPU_IDLE_STATE_NORMAL);
2659637SRandy.Fishel@Sun.COM }
2669637SRandy.Fishel@Sun.COM 
2679637SRandy.Fishel@Sun.COM static cpu_idle_callback_handle_t cpu_idle_cb_handle_dtrace;
2689637SRandy.Fishel@Sun.COM static cpu_idle_callback_t cpu_idle_callback_dtrace = {
2699637SRandy.Fishel@Sun.COM 	CPU_IDLE_CALLBACK_VERS,
2709637SRandy.Fishel@Sun.COM 	cpu_idle_dtrace_enter,
2719637SRandy.Fishel@Sun.COM 	cpu_idle_dtrace_exit,
2729637SRandy.Fishel@Sun.COM };
2739637SRandy.Fishel@Sun.COM 
2749637SRandy.Fishel@Sun.COM #if defined(__x86) && !defined(__xpv)
2759637SRandy.Fishel@Sun.COM extern void tlb_going_idle(void);
2769637SRandy.Fishel@Sun.COM extern void tlb_service(void);
2779637SRandy.Fishel@Sun.COM 
2789637SRandy.Fishel@Sun.COM static cpu_idle_callback_handle_t cpu_idle_cb_handle_tlb;
2799637SRandy.Fishel@Sun.COM static cpu_idle_callback_t cpu_idle_callback_tlb = {
2809637SRandy.Fishel@Sun.COM 	CPU_IDLE_CALLBACK_VERS,
2819637SRandy.Fishel@Sun.COM 	(cpu_idle_enter_cbfn_t)tlb_going_idle,
2829637SRandy.Fishel@Sun.COM 	(cpu_idle_exit_cbfn_t)tlb_service,
2839637SRandy.Fishel@Sun.COM };
2849637SRandy.Fishel@Sun.COM #endif
2859637SRandy.Fishel@Sun.COM 
2869637SRandy.Fishel@Sun.COM void
cpu_event_init(void)2879637SRandy.Fishel@Sun.COM cpu_event_init(void)
2889637SRandy.Fishel@Sun.COM {
2899637SRandy.Fishel@Sun.COM 	int i, idx;
2909637SRandy.Fishel@Sun.COM 	size_t sz;
2919637SRandy.Fishel@Sun.COM 	intptr_t buf;
2929637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state_t *sp;
2939637SRandy.Fishel@Sun.COM 	cpu_idle_prop_item_t *ip;
2949637SRandy.Fishel@Sun.COM 
2959637SRandy.Fishel@Sun.COM 	mutex_init(&cpu_idle_cb_lock, NULL, MUTEX_DRIVER, NULL);
2969637SRandy.Fishel@Sun.COM 	mutex_init(&cpu_idle_prop_lock, NULL, MUTEX_DRIVER, NULL);
2979637SRandy.Fishel@Sun.COM 
2989637SRandy.Fishel@Sun.COM 	/* Create internal properties. */
2999637SRandy.Fishel@Sun.COM 	for (i = 0, ip = cpu_idle_prop_array;
3009637SRandy.Fishel@Sun.COM 	    i < sizeof (cpu_idle_prop_array) / sizeof (cpu_idle_prop_array[0]);
3019637SRandy.Fishel@Sun.COM 	    i++, ip++) {
3029637SRandy.Fishel@Sun.COM 		(void) cpu_idle_prop_create_property(ip->name, ip->type,
3039637SRandy.Fishel@Sun.COM 		    ip->update, ip->arg, &ip->handle);
3049637SRandy.Fishel@Sun.COM 		ASSERT(ip->handle != NULL);
3059637SRandy.Fishel@Sun.COM 	}
3069637SRandy.Fishel@Sun.COM 
3079637SRandy.Fishel@Sun.COM 	/* Allocate buffer and align to CPU_CACHE_COHERENCE_SIZE. */
3089637SRandy.Fishel@Sun.COM 	sz = sizeof (cpu_idle_cb_state_t) * max_ncpus;
3099637SRandy.Fishel@Sun.COM 	sz += CPU_CACHE_COHERENCE_SIZE;
3109637SRandy.Fishel@Sun.COM 	buf = (intptr_t)kmem_zalloc(sz, KM_SLEEP);
3119637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state = (cpu_idle_cb_state_t *)P2ROUNDUP(buf,
3129637SRandy.Fishel@Sun.COM 	    CPU_CACHE_COHERENCE_SIZE);
3139637SRandy.Fishel@Sun.COM 
3149637SRandy.Fishel@Sun.COM 	/* Cache frequently used property value pointers. */
3159637SRandy.Fishel@Sun.COM 	for (sp = cpu_idle_cb_state, i = 0; i < max_ncpus; i++, sp++) {
3169637SRandy.Fishel@Sun.COM 		idx = CPU_IDLE_CTX2IDX(i);
3179637SRandy.Fishel@Sun.COM #define	___INIT_P(f, i)	\
3189637SRandy.Fishel@Sun.COM 	sp->v.f = CPU_IDLE_HDL2VALP(cpu_idle_prop_array[(i)].handle, idx)
3199637SRandy.Fishel@Sun.COM 		___INIT_P(idle_state, CPU_IDLE_PROP_IDX_IDLE_STATE);
3209637SRandy.Fishel@Sun.COM 		___INIT_P(enter_ts, CPU_IDLE_PROP_IDX_ENTER_TS);
3219637SRandy.Fishel@Sun.COM 		___INIT_P(exit_ts, CPU_IDLE_PROP_IDX_EXIT_TS);
3229637SRandy.Fishel@Sun.COM 		___INIT_P(last_idle, CPU_IDLE_PROP_IDX_LAST_IDLE);
3239637SRandy.Fishel@Sun.COM 		___INIT_P(last_busy, CPU_IDLE_PROP_IDX_LAST_BUSY);
3249637SRandy.Fishel@Sun.COM 		___INIT_P(total_idle, CPU_IDLE_PROP_IDX_TOTAL_IDLE);
3259637SRandy.Fishel@Sun.COM 		___INIT_P(total_busy, CPU_IDLE_PROP_IDX_TOTAL_BUSY);
3269637SRandy.Fishel@Sun.COM 		___INIT_P(last_idle, CPU_IDLE_PROP_IDX_INTR_CNT);
3279637SRandy.Fishel@Sun.COM #undef	___INIT_P
3289637SRandy.Fishel@Sun.COM 	}
3299637SRandy.Fishel@Sun.COM 
3309637SRandy.Fishel@Sun.COM 	/* Register built-in callbacks. */
3319637SRandy.Fishel@Sun.COM 	if (cpu_idle_register_callback(CPU_IDLE_CB_PRIO_DTRACE,
3329637SRandy.Fishel@Sun.COM 	    &cpu_idle_callback_dtrace, NULL, &cpu_idle_cb_handle_dtrace) != 0) {
3339637SRandy.Fishel@Sun.COM 		cmn_err(CE_PANIC,
3349637SRandy.Fishel@Sun.COM 		    "cpu_idle: failed to register callback for dtrace.");
3359637SRandy.Fishel@Sun.COM 	}
3369637SRandy.Fishel@Sun.COM #if defined(__x86) && !defined(__xpv)
3379637SRandy.Fishel@Sun.COM 	if (cpu_idle_register_callback(CPU_IDLE_CB_PRIO_TLB,
3389637SRandy.Fishel@Sun.COM 	    &cpu_idle_callback_tlb, NULL, &cpu_idle_cb_handle_tlb) != 0) {
3399637SRandy.Fishel@Sun.COM 		cmn_err(CE_PANIC,
3409637SRandy.Fishel@Sun.COM 		    "cpu_idle: failed to register callback for tlb_flush.");
3419637SRandy.Fishel@Sun.COM 	}
3429637SRandy.Fishel@Sun.COM #endif
3439637SRandy.Fishel@Sun.COM }
3449637SRandy.Fishel@Sun.COM 
345*12004Sjiang.liu@intel.com /*
346*12004Sjiang.liu@intel.com  * This function is called to initialize per CPU state when starting CPUs.
347*12004Sjiang.liu@intel.com  */
3489637SRandy.Fishel@Sun.COM void
cpu_event_init_cpu(cpu_t * cp)3499637SRandy.Fishel@Sun.COM cpu_event_init_cpu(cpu_t *cp)
3509637SRandy.Fishel@Sun.COM {
3519637SRandy.Fishel@Sun.COM 	ASSERT(cp->cpu_seqid < max_ncpus);
352*12004Sjiang.liu@intel.com 	cpu_idle_cb_state[cp->cpu_seqid].v.index = 0;
3539637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state[cp->cpu_seqid].v.ready = B_FALSE;
354*12004Sjiang.liu@intel.com 	cpu_idle_cb_state[cp->cpu_seqid].v.enabled = B_TRUE;
3559637SRandy.Fishel@Sun.COM }
3569637SRandy.Fishel@Sun.COM 
357*12004Sjiang.liu@intel.com /*
358*12004Sjiang.liu@intel.com  * This function is called to clean up per CPU state when stopping CPUs.
359*12004Sjiang.liu@intel.com  */
3609637SRandy.Fishel@Sun.COM void
cpu_event_fini_cpu(cpu_t * cp)3619637SRandy.Fishel@Sun.COM cpu_event_fini_cpu(cpu_t *cp)
3629637SRandy.Fishel@Sun.COM {
3639637SRandy.Fishel@Sun.COM 	ASSERT(cp->cpu_seqid < max_ncpus);
364*12004Sjiang.liu@intel.com 	cpu_idle_cb_state[cp->cpu_seqid].v.enabled = B_FALSE;
3659637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state[cp->cpu_seqid].v.ready = B_FALSE;
3669637SRandy.Fishel@Sun.COM }
3679637SRandy.Fishel@Sun.COM 
3689637SRandy.Fishel@Sun.COM static void
cpu_idle_insert_callback(cpu_idle_cb_impl_t * cip)3699637SRandy.Fishel@Sun.COM cpu_idle_insert_callback(cpu_idle_cb_impl_t *cip)
3709637SRandy.Fishel@Sun.COM {
3719637SRandy.Fishel@Sun.COM 	int unlock = 0, unpause = 0;
3729637SRandy.Fishel@Sun.COM 	int i, cnt_new = 0, cnt_old = 0;
3739637SRandy.Fishel@Sun.COM 	char *buf_new = NULL, *buf_old = NULL;
3749637SRandy.Fishel@Sun.COM 
3759637SRandy.Fishel@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_idle_cb_lock));
3769637SRandy.Fishel@Sun.COM 
3779637SRandy.Fishel@Sun.COM 	/*
3789637SRandy.Fishel@Sun.COM 	 * Expand array if it's full.
3799637SRandy.Fishel@Sun.COM 	 * Memory must be allocated out of pause/start_cpus() scope because
3809637SRandy.Fishel@Sun.COM 	 * kmem_zalloc() can't be called with KM_SLEEP flag within that scope.
3819637SRandy.Fishel@Sun.COM 	 */
3829637SRandy.Fishel@Sun.COM 	if (cpu_idle_cb_curr == cpu_idle_cb_max) {
3839637SRandy.Fishel@Sun.COM 		cnt_new = cpu_idle_cb_max + CPU_IDLE_ARRAY_CAPACITY_INC;
3849637SRandy.Fishel@Sun.COM 		buf_new = (char *)kmem_zalloc(cnt_new *
3859637SRandy.Fishel@Sun.COM 		    sizeof (cpu_idle_cb_item_t), KM_SLEEP);
3869637SRandy.Fishel@Sun.COM 	}
3879637SRandy.Fishel@Sun.COM 
3889637SRandy.Fishel@Sun.COM 	/* Try to acquire cpu_lock if not held yet. */
3899637SRandy.Fishel@Sun.COM 	if (!MUTEX_HELD(&cpu_lock)) {
3909637SRandy.Fishel@Sun.COM 		mutex_enter(&cpu_lock);
3919637SRandy.Fishel@Sun.COM 		unlock = 1;
3929637SRandy.Fishel@Sun.COM 	}
3939637SRandy.Fishel@Sun.COM 	/*
3949637SRandy.Fishel@Sun.COM 	 * Pause all other CPUs (and let them run pause thread).
3959637SRandy.Fishel@Sun.COM 	 * It's guaranteed that no other threads will access cpu_idle_cb_array
3969637SRandy.Fishel@Sun.COM 	 * after pause_cpus().
3979637SRandy.Fishel@Sun.COM 	 */
3989637SRandy.Fishel@Sun.COM 	if (!cpus_paused()) {
3999637SRandy.Fishel@Sun.COM 		pause_cpus(NULL);
4009637SRandy.Fishel@Sun.COM 		unpause = 1;
4019637SRandy.Fishel@Sun.COM 	}
4029637SRandy.Fishel@Sun.COM 
4039637SRandy.Fishel@Sun.COM 	/* Copy content to new buffer if needed. */
4049637SRandy.Fishel@Sun.COM 	if (buf_new != NULL) {
4059637SRandy.Fishel@Sun.COM 		buf_old = (char *)cpu_idle_cb_array;
4069637SRandy.Fishel@Sun.COM 		cnt_old = cpu_idle_cb_max;
4079637SRandy.Fishel@Sun.COM 		if (buf_old != NULL) {
4089637SRandy.Fishel@Sun.COM 			ASSERT(cnt_old != 0);
4099637SRandy.Fishel@Sun.COM 			bcopy(cpu_idle_cb_array, buf_new,
4109637SRandy.Fishel@Sun.COM 			    sizeof (cpu_idle_cb_item_t) * cnt_old);
4119637SRandy.Fishel@Sun.COM 		}
4129637SRandy.Fishel@Sun.COM 		cpu_idle_cb_array = (cpu_idle_cb_item_t *)buf_new;
4139637SRandy.Fishel@Sun.COM 		cpu_idle_cb_max = cnt_new;
4149637SRandy.Fishel@Sun.COM 	}
4159637SRandy.Fishel@Sun.COM 
4169637SRandy.Fishel@Sun.COM 	/* Insert into array according to priority. */
4179637SRandy.Fishel@Sun.COM 	ASSERT(cpu_idle_cb_curr < cpu_idle_cb_max);
4189637SRandy.Fishel@Sun.COM 	for (i = cpu_idle_cb_curr; i > 0; i--) {
4199637SRandy.Fishel@Sun.COM 		if (cpu_idle_cb_array[i - 1].impl->priority >= cip->priority) {
4209637SRandy.Fishel@Sun.COM 			break;
4219637SRandy.Fishel@Sun.COM 		}
4229637SRandy.Fishel@Sun.COM 		cpu_idle_cb_array[i] = cpu_idle_cb_array[i - 1];
4239637SRandy.Fishel@Sun.COM 	}
4249637SRandy.Fishel@Sun.COM 	cpu_idle_cb_array[i].arg = cip->argument;
4259637SRandy.Fishel@Sun.COM 	cpu_idle_cb_array[i].enter = cip->callback->idle_enter;
4269637SRandy.Fishel@Sun.COM 	cpu_idle_cb_array[i].exit = cip->callback->idle_exit;
4279637SRandy.Fishel@Sun.COM 	cpu_idle_cb_array[i].impl = cip;
4289637SRandy.Fishel@Sun.COM 	cpu_idle_cb_curr++;
4299637SRandy.Fishel@Sun.COM 
4309637SRandy.Fishel@Sun.COM 	/* Resume other CPUs from paused state if needed. */
4319637SRandy.Fishel@Sun.COM 	if (unpause) {
4329637SRandy.Fishel@Sun.COM 		start_cpus();
4339637SRandy.Fishel@Sun.COM 	}
4349637SRandy.Fishel@Sun.COM 	if (unlock) {
4359637SRandy.Fishel@Sun.COM 		mutex_exit(&cpu_lock);
4369637SRandy.Fishel@Sun.COM 	}
4379637SRandy.Fishel@Sun.COM 
4389637SRandy.Fishel@Sun.COM 	/* Free old resource if needed. */
4399637SRandy.Fishel@Sun.COM 	if (buf_old != NULL) {
4409637SRandy.Fishel@Sun.COM 		ASSERT(cnt_old != 0);
4419637SRandy.Fishel@Sun.COM 		kmem_free(buf_old, cnt_old * sizeof (cpu_idle_cb_item_t));
4429637SRandy.Fishel@Sun.COM 	}
4439637SRandy.Fishel@Sun.COM }
4449637SRandy.Fishel@Sun.COM 
4459637SRandy.Fishel@Sun.COM static void
cpu_idle_remove_callback(cpu_idle_cb_impl_t * cip)4469637SRandy.Fishel@Sun.COM cpu_idle_remove_callback(cpu_idle_cb_impl_t *cip)
4479637SRandy.Fishel@Sun.COM {
4489637SRandy.Fishel@Sun.COM 	int i, found = 0;
4499637SRandy.Fishel@Sun.COM 	int unlock = 0, unpause = 0;
4509637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state_t *sp;
4519637SRandy.Fishel@Sun.COM 
4529637SRandy.Fishel@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_idle_cb_lock));
4539637SRandy.Fishel@Sun.COM 
4549637SRandy.Fishel@Sun.COM 	/* Try to acquire cpu_lock if not held yet. */
4559637SRandy.Fishel@Sun.COM 	if (!MUTEX_HELD(&cpu_lock)) {
4569637SRandy.Fishel@Sun.COM 		mutex_enter(&cpu_lock);
4579637SRandy.Fishel@Sun.COM 		unlock = 1;
4589637SRandy.Fishel@Sun.COM 	}
4599637SRandy.Fishel@Sun.COM 	/*
4609637SRandy.Fishel@Sun.COM 	 * Pause all other CPUs.
4619637SRandy.Fishel@Sun.COM 	 * It's guaranteed that no other threads will access cpu_idle_cb_array
4629637SRandy.Fishel@Sun.COM 	 * after pause_cpus().
4639637SRandy.Fishel@Sun.COM 	 */
4649637SRandy.Fishel@Sun.COM 	if (!cpus_paused()) {
4659637SRandy.Fishel@Sun.COM 		pause_cpus(NULL);
4669637SRandy.Fishel@Sun.COM 		unpause = 1;
4679637SRandy.Fishel@Sun.COM 	}
4689637SRandy.Fishel@Sun.COM 
4699637SRandy.Fishel@Sun.COM 	/* Remove cip from array. */
4709637SRandy.Fishel@Sun.COM 	for (i = 0; i < cpu_idle_cb_curr; i++) {
4719637SRandy.Fishel@Sun.COM 		if (found == 0) {
4729637SRandy.Fishel@Sun.COM 			if (cpu_idle_cb_array[i].impl == cip) {
4739637SRandy.Fishel@Sun.COM 				found = 1;
4749637SRandy.Fishel@Sun.COM 			}
4759637SRandy.Fishel@Sun.COM 		} else {
4769637SRandy.Fishel@Sun.COM 			cpu_idle_cb_array[i - 1] = cpu_idle_cb_array[i];
4779637SRandy.Fishel@Sun.COM 		}
4789637SRandy.Fishel@Sun.COM 	}
4799637SRandy.Fishel@Sun.COM 	ASSERT(found != 0);
4809637SRandy.Fishel@Sun.COM 	cpu_idle_cb_curr--;
4819637SRandy.Fishel@Sun.COM 
4829637SRandy.Fishel@Sun.COM 	/*
4839637SRandy.Fishel@Sun.COM 	 * Reset property ready flag for all CPUs if no registered callback
4849637SRandy.Fishel@Sun.COM 	 * left because cpu_idle_enter/exit will stop updating property if
4859637SRandy.Fishel@Sun.COM 	 * there's no callback registered.
4869637SRandy.Fishel@Sun.COM 	 */
4879637SRandy.Fishel@Sun.COM 	if (cpu_idle_cb_curr == 0) {
4889637SRandy.Fishel@Sun.COM 		for (sp = cpu_idle_cb_state, i = 0; i < max_ncpus; i++, sp++) {
4899637SRandy.Fishel@Sun.COM 			sp->v.ready = B_FALSE;
4909637SRandy.Fishel@Sun.COM 		}
4919637SRandy.Fishel@Sun.COM 	}
4929637SRandy.Fishel@Sun.COM 
4939637SRandy.Fishel@Sun.COM 	/* Resume other CPUs from paused state if needed. */
4949637SRandy.Fishel@Sun.COM 	if (unpause) {
4959637SRandy.Fishel@Sun.COM 		start_cpus();
4969637SRandy.Fishel@Sun.COM 	}
4979637SRandy.Fishel@Sun.COM 	if (unlock) {
4989637SRandy.Fishel@Sun.COM 		mutex_exit(&cpu_lock);
4999637SRandy.Fishel@Sun.COM 	}
5009637SRandy.Fishel@Sun.COM }
5019637SRandy.Fishel@Sun.COM 
5029637SRandy.Fishel@Sun.COM int
cpu_idle_register_callback(uint_t prio,cpu_idle_callback_t * cbp,void * arg,cpu_idle_callback_handle_t * hdlp)5039637SRandy.Fishel@Sun.COM cpu_idle_register_callback(uint_t prio, cpu_idle_callback_t *cbp,
5049637SRandy.Fishel@Sun.COM     void *arg, cpu_idle_callback_handle_t *hdlp)
5059637SRandy.Fishel@Sun.COM {
5069637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state_t *sp;
5079637SRandy.Fishel@Sun.COM 	cpu_idle_cb_impl_t *cip = NULL;
5089637SRandy.Fishel@Sun.COM 
5099637SRandy.Fishel@Sun.COM 	/* First validate parameters. */
5109637SRandy.Fishel@Sun.COM 	ASSERT(!CPU_ON_INTR(CPU));
5119637SRandy.Fishel@Sun.COM 	ASSERT(CPU->cpu_seqid < max_ncpus);
5129637SRandy.Fishel@Sun.COM 	sp = &cpu_idle_cb_state[CPU->cpu_seqid];
5139637SRandy.Fishel@Sun.COM 	if (sp->v.index != 0) {
5149637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE,
5159637SRandy.Fishel@Sun.COM 		    "!cpu_event: register_callback called from callback.");
5169637SRandy.Fishel@Sun.COM 		return (EBUSY);
5179637SRandy.Fishel@Sun.COM 	} else if (cbp == NULL || hdlp == NULL) {
5189637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE,
5199637SRandy.Fishel@Sun.COM 		    "!cpu_event: NULL parameters in register_callback.");
5209637SRandy.Fishel@Sun.COM 		return (EINVAL);
5219637SRandy.Fishel@Sun.COM 	} else if (prio < CPU_IDLE_CB_PRIO_LOW_BASE ||
5229637SRandy.Fishel@Sun.COM 	    prio >= CPU_IDLE_CB_PRIO_RESV_BASE) {
5239637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE,
5249637SRandy.Fishel@Sun.COM 		    "!cpu_event: priority 0x%x out of range.", prio);
5259637SRandy.Fishel@Sun.COM 		return (EINVAL);
5269637SRandy.Fishel@Sun.COM 	} else if (cbp->version != CPU_IDLE_CALLBACK_VERS) {
5279637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE,
5289637SRandy.Fishel@Sun.COM 		    "!cpu_event: callback version %d is not supported.",
5299637SRandy.Fishel@Sun.COM 		    cbp->version);
5309637SRandy.Fishel@Sun.COM 		return (EINVAL);
5319637SRandy.Fishel@Sun.COM 	}
5329637SRandy.Fishel@Sun.COM 
5339637SRandy.Fishel@Sun.COM 	mutex_enter(&cpu_idle_cb_lock);
5349637SRandy.Fishel@Sun.COM 	/* Check whether callback with priority exists if not dynamic. */
5359637SRandy.Fishel@Sun.COM 	if (prio != CPU_IDLE_CB_PRIO_DYNAMIC) {
5369637SRandy.Fishel@Sun.COM 		for (cip = cpu_idle_cb_busy; cip != NULL;
5379637SRandy.Fishel@Sun.COM 		    cip = cip->next) {
5389637SRandy.Fishel@Sun.COM 			if (cip->priority == prio) {
5399637SRandy.Fishel@Sun.COM 				mutex_exit(&cpu_idle_cb_lock);
5409637SRandy.Fishel@Sun.COM 				cmn_err(CE_NOTE, "!cpu_event: callback with "
5419637SRandy.Fishel@Sun.COM 				    "priority 0x%x already exists.", prio);
5429637SRandy.Fishel@Sun.COM 				return (EEXIST);
5439637SRandy.Fishel@Sun.COM 			}
5449637SRandy.Fishel@Sun.COM 		}
5459637SRandy.Fishel@Sun.COM 	}
5469637SRandy.Fishel@Sun.COM 
5479637SRandy.Fishel@Sun.COM 	cip = kmem_zalloc(sizeof (*cip), KM_SLEEP);
5489637SRandy.Fishel@Sun.COM 	cip->callback = cbp;
5499637SRandy.Fishel@Sun.COM 	cip->argument = arg;
5509637SRandy.Fishel@Sun.COM 	cip->priority = prio;
5519637SRandy.Fishel@Sun.COM 	cip->next = cpu_idle_cb_busy;
5529637SRandy.Fishel@Sun.COM 	cpu_idle_cb_busy = cip;
5539637SRandy.Fishel@Sun.COM 	cpu_idle_insert_callback(cip);
5549637SRandy.Fishel@Sun.COM 	mutex_exit(&cpu_idle_cb_lock);
5559637SRandy.Fishel@Sun.COM 
5569637SRandy.Fishel@Sun.COM 	*hdlp = (cpu_idle_callback_handle_t)cip;
5579637SRandy.Fishel@Sun.COM 
5589637SRandy.Fishel@Sun.COM 	return (0);
5599637SRandy.Fishel@Sun.COM }
5609637SRandy.Fishel@Sun.COM 
5619637SRandy.Fishel@Sun.COM int
cpu_idle_unregister_callback(cpu_idle_callback_handle_t hdl)5629637SRandy.Fishel@Sun.COM cpu_idle_unregister_callback(cpu_idle_callback_handle_t hdl)
5639637SRandy.Fishel@Sun.COM {
5649637SRandy.Fishel@Sun.COM 	int rc = ENODEV;
5659637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state_t *sp;
5669637SRandy.Fishel@Sun.COM 	cpu_idle_cb_impl_t *ip, **ipp;
5679637SRandy.Fishel@Sun.COM 
5689637SRandy.Fishel@Sun.COM 	ASSERT(!CPU_ON_INTR(CPU));
5699637SRandy.Fishel@Sun.COM 	ASSERT(CPU->cpu_seqid < max_ncpus);
5709637SRandy.Fishel@Sun.COM 	sp = &cpu_idle_cb_state[CPU->cpu_seqid];
5719637SRandy.Fishel@Sun.COM 	if (sp->v.index != 0) {
5729637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE,
5739637SRandy.Fishel@Sun.COM 		    "!cpu_event: unregister_callback called from callback.");
5749637SRandy.Fishel@Sun.COM 		return (EBUSY);
5759637SRandy.Fishel@Sun.COM 	} else if (hdl == NULL) {
5769637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE,
5779637SRandy.Fishel@Sun.COM 		    "!cpu_event: hdl is NULL in unregister_callback.");
5789637SRandy.Fishel@Sun.COM 		return (EINVAL);
5799637SRandy.Fishel@Sun.COM 	}
5809637SRandy.Fishel@Sun.COM 
5819637SRandy.Fishel@Sun.COM 	ip = (cpu_idle_cb_impl_t *)hdl;
5829637SRandy.Fishel@Sun.COM 	mutex_enter(&cpu_idle_cb_lock);
5839637SRandy.Fishel@Sun.COM 	for (ipp = &cpu_idle_cb_busy; *ipp != NULL; ipp = &(*ipp)->next) {
5849637SRandy.Fishel@Sun.COM 		if (*ipp == ip) {
5859637SRandy.Fishel@Sun.COM 			*ipp = ip->next;
5869637SRandy.Fishel@Sun.COM 			cpu_idle_remove_callback(ip);
5879637SRandy.Fishel@Sun.COM 			rc = 0;
5889637SRandy.Fishel@Sun.COM 			break;
5899637SRandy.Fishel@Sun.COM 		}
5909637SRandy.Fishel@Sun.COM 	}
5919637SRandy.Fishel@Sun.COM 	mutex_exit(&cpu_idle_cb_lock);
5929637SRandy.Fishel@Sun.COM 
5939637SRandy.Fishel@Sun.COM 	if (rc == 0) {
5949637SRandy.Fishel@Sun.COM 		kmem_free(ip, sizeof (*ip));
5959637SRandy.Fishel@Sun.COM 	} else {
5969637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE,
5979637SRandy.Fishel@Sun.COM 		    "!cpu_event: callback handle %p not found.", (void *)hdl);
5989637SRandy.Fishel@Sun.COM 	}
5999637SRandy.Fishel@Sun.COM 
6009637SRandy.Fishel@Sun.COM 	return (rc);
6019637SRandy.Fishel@Sun.COM }
6029637SRandy.Fishel@Sun.COM 
6039637SRandy.Fishel@Sun.COM static int
cpu_idle_enter_state(cpu_idle_cb_state_t * sp,intptr_t state)6049637SRandy.Fishel@Sun.COM cpu_idle_enter_state(cpu_idle_cb_state_t *sp, intptr_t state)
6059637SRandy.Fishel@Sun.COM {
6069637SRandy.Fishel@Sun.COM 	sp->v.idle_state->cipv_intptr = state;
6079637SRandy.Fishel@Sun.COM 	sp->v.enter_ts->cipv_hrtime = gethrtime_unscaled();
6089637SRandy.Fishel@Sun.COM 	sp->v.last_busy->cipv_hrtime = sp->v.enter_ts->cipv_hrtime -
6099637SRandy.Fishel@Sun.COM 	    sp->v.exit_ts->cipv_hrtime;
6109637SRandy.Fishel@Sun.COM 	sp->v.total_busy->cipv_hrtime += sp->v.last_busy->cipv_hrtime;
6119637SRandy.Fishel@Sun.COM 	if (sp->v.ready == B_FALSE) {
6129637SRandy.Fishel@Sun.COM 		sp->v.ready = B_TRUE;
6139637SRandy.Fishel@Sun.COM 		return (0);
6149637SRandy.Fishel@Sun.COM 	}
6159637SRandy.Fishel@Sun.COM 
6169637SRandy.Fishel@Sun.COM 	return (1);
6179637SRandy.Fishel@Sun.COM }
6189637SRandy.Fishel@Sun.COM 
6199637SRandy.Fishel@Sun.COM static void
cpu_idle_exit_state(cpu_idle_cb_state_t * sp)6209637SRandy.Fishel@Sun.COM cpu_idle_exit_state(cpu_idle_cb_state_t *sp)
6219637SRandy.Fishel@Sun.COM {
6229637SRandy.Fishel@Sun.COM 	sp->v.idle_state->cipv_intptr = CPU_IDLE_STATE_NORMAL;
6239637SRandy.Fishel@Sun.COM 	sp->v.exit_ts->cipv_hrtime = gethrtime_unscaled();
6249637SRandy.Fishel@Sun.COM 	sp->v.last_idle->cipv_hrtime = sp->v.exit_ts->cipv_hrtime -
6259637SRandy.Fishel@Sun.COM 	    sp->v.enter_ts->cipv_hrtime;
6269637SRandy.Fishel@Sun.COM 	sp->v.total_idle->cipv_hrtime += sp->v.last_idle->cipv_hrtime;
6279637SRandy.Fishel@Sun.COM }
6289637SRandy.Fishel@Sun.COM 
6299637SRandy.Fishel@Sun.COM /*ARGSUSED*/
6309637SRandy.Fishel@Sun.COM int
cpu_idle_enter(int state,int flag,cpu_idle_check_wakeup_t check_func,void * check_arg)6319637SRandy.Fishel@Sun.COM cpu_idle_enter(int state, int flag,
6329637SRandy.Fishel@Sun.COM     cpu_idle_check_wakeup_t check_func, void *check_arg)
6339637SRandy.Fishel@Sun.COM {
6349637SRandy.Fishel@Sun.COM 	int i;
6359637SRandy.Fishel@Sun.COM 	cpu_idle_cb_item_t *cip;
6369637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state_t *sp;
6379637SRandy.Fishel@Sun.COM 	cpu_idle_callback_context_t ctx;
6389637SRandy.Fishel@Sun.COM #if defined(__x86)
6399637SRandy.Fishel@Sun.COM 	ulong_t iflags;
6409637SRandy.Fishel@Sun.COM #endif
6419637SRandy.Fishel@Sun.COM 
6429637SRandy.Fishel@Sun.COM 	ctx = CPU_IDLE_GET_CTX(CPU);
6439637SRandy.Fishel@Sun.COM 	ASSERT(CPU->cpu_seqid < max_ncpus);
6449637SRandy.Fishel@Sun.COM 	sp = &cpu_idle_cb_state[CPU->cpu_seqid];
6459637SRandy.Fishel@Sun.COM 	ASSERT(sp->v.index == 0);
646*12004Sjiang.liu@intel.com 	if (sp->v.enabled == B_FALSE) {
647*12004Sjiang.liu@intel.com #if defined(__x86)
648*12004Sjiang.liu@intel.com 		/* Intercept CPU at a safe point before powering off it. */
649*12004Sjiang.liu@intel.com 		if (CPU_IN_SET(cpu_idle_intercept_set, CPU->cpu_id)) {
650*12004Sjiang.liu@intel.com 			iflags = intr_clear();
651*12004Sjiang.liu@intel.com 			CPUSET_ATOMIC_DEL(cpu_idle_intercept_set, CPU->cpu_id);
652*12004Sjiang.liu@intel.com 			/*CONSTCOND*/
653*12004Sjiang.liu@intel.com 			while (1) {
654*12004Sjiang.liu@intel.com 				SMT_PAUSE();
655*12004Sjiang.liu@intel.com 			}
656*12004Sjiang.liu@intel.com 		}
657*12004Sjiang.liu@intel.com #endif
658*12004Sjiang.liu@intel.com 
659*12004Sjiang.liu@intel.com 		return (0);
660*12004Sjiang.liu@intel.com 	}
6619637SRandy.Fishel@Sun.COM 
6629637SRandy.Fishel@Sun.COM 	/*
6639637SRandy.Fishel@Sun.COM 	 * On x86, cpu_idle_enter can be called from idle thread with either
6649637SRandy.Fishel@Sun.COM 	 * interrupts enabled or disabled, so we need to make sure interrupts
6659637SRandy.Fishel@Sun.COM 	 * are disabled here.
6669637SRandy.Fishel@Sun.COM 	 * On SPARC, cpu_idle_enter will be called from idle thread with
6679637SRandy.Fishel@Sun.COM 	 * interrupt disabled, so no special handling necessary.
6689637SRandy.Fishel@Sun.COM 	 */
6699637SRandy.Fishel@Sun.COM #if defined(__x86)
6709637SRandy.Fishel@Sun.COM 	iflags = intr_clear();
6719637SRandy.Fishel@Sun.COM #endif
6729637SRandy.Fishel@Sun.COM 
6739637SRandy.Fishel@Sun.COM 	/* Skip calling callback if state is not ready for current CPU. */
6749637SRandy.Fishel@Sun.COM 	if (cpu_idle_enter_state(sp, state) == 0) {
6759637SRandy.Fishel@Sun.COM #if defined(__x86)
6769637SRandy.Fishel@Sun.COM 		intr_restore(iflags);
6779637SRandy.Fishel@Sun.COM #endif
6789637SRandy.Fishel@Sun.COM 		return (0);
6799637SRandy.Fishel@Sun.COM 	}
6809637SRandy.Fishel@Sun.COM 
6819637SRandy.Fishel@Sun.COM 	for (i = 0, cip = cpu_idle_cb_array; i < cpu_idle_cb_curr; i++, cip++) {
6829637SRandy.Fishel@Sun.COM 		/*
6839637SRandy.Fishel@Sun.COM 		 * Increase index so corresponding idle_exit callback
6849637SRandy.Fishel@Sun.COM 		 * will be invoked should interrupt happen during
6859637SRandy.Fishel@Sun.COM 		 * idle_enter callback.
6869637SRandy.Fishel@Sun.COM 		 */
6879637SRandy.Fishel@Sun.COM 		sp->v.index++;
6889637SRandy.Fishel@Sun.COM 
6899637SRandy.Fishel@Sun.COM 		/* Call idle_enter callback function if it's not NULL. */
6909637SRandy.Fishel@Sun.COM 		if (cip->enter != NULL) {
6919637SRandy.Fishel@Sun.COM 			cip->enter(cip->arg, ctx, check_func, check_arg);
6929637SRandy.Fishel@Sun.COM 
6939637SRandy.Fishel@Sun.COM 			/*
6949637SRandy.Fishel@Sun.COM 			 * cpu_idle_enter runs with interrupts
6959637SRandy.Fishel@Sun.COM 			 * disabled, so the idle_enter callbacks will
6969637SRandy.Fishel@Sun.COM 			 * also be called with interrupts disabled.
6979637SRandy.Fishel@Sun.COM 			 * It is permissible for the callbacks to
6989637SRandy.Fishel@Sun.COM 			 * enable the interrupts, if they can also
6999637SRandy.Fishel@Sun.COM 			 * handle the condition if the interrupt
7009637SRandy.Fishel@Sun.COM 			 * occurs.
7019637SRandy.Fishel@Sun.COM 			 *
7029637SRandy.Fishel@Sun.COM 			 * However, if an interrupt occurs and we
7039637SRandy.Fishel@Sun.COM 			 * return here without dealing with it, we
7049637SRandy.Fishel@Sun.COM 			 * return to the cpu_idle_enter() caller
7059637SRandy.Fishel@Sun.COM 			 * with an EBUSY, and the caller will not
7069637SRandy.Fishel@Sun.COM 			 * enter the idle state.
7079637SRandy.Fishel@Sun.COM 			 *
7089637SRandy.Fishel@Sun.COM 			 * We detect the interrupt, by checking the
7099637SRandy.Fishel@Sun.COM 			 * index value of the state pointer.  If it
7109637SRandy.Fishel@Sun.COM 			 * is not the index we incremented above,
7119637SRandy.Fishel@Sun.COM 			 * then it was cleared while processing
7129637SRandy.Fishel@Sun.COM 			 * the interrupt.
7139637SRandy.Fishel@Sun.COM 			 *
7149637SRandy.Fishel@Sun.COM 			 * Also note, that at this point of the code
7159637SRandy.Fishel@Sun.COM 			 * the normal index value will be one greater
7169637SRandy.Fishel@Sun.COM 			 * than the variable 'i' in the loop, as it
7179637SRandy.Fishel@Sun.COM 			 * hasn't yet been incremented.
7189637SRandy.Fishel@Sun.COM 			 */
7199637SRandy.Fishel@Sun.COM 			if (sp->v.index != i + 1) {
7209637SRandy.Fishel@Sun.COM #if defined(__x86)
7219637SRandy.Fishel@Sun.COM 				intr_restore(iflags);
7229637SRandy.Fishel@Sun.COM #endif
7239637SRandy.Fishel@Sun.COM 				return (EBUSY);
7249637SRandy.Fishel@Sun.COM 			}
7259637SRandy.Fishel@Sun.COM 		}
7269637SRandy.Fishel@Sun.COM 	}
7279637SRandy.Fishel@Sun.COM #if defined(__x86)
7289637SRandy.Fishel@Sun.COM 	intr_restore(iflags);
7299637SRandy.Fishel@Sun.COM #endif
7309637SRandy.Fishel@Sun.COM 
7319637SRandy.Fishel@Sun.COM 	return (0);
7329637SRandy.Fishel@Sun.COM }
7339637SRandy.Fishel@Sun.COM 
7349637SRandy.Fishel@Sun.COM void
cpu_idle_exit(int flag)7359637SRandy.Fishel@Sun.COM cpu_idle_exit(int flag)
7369637SRandy.Fishel@Sun.COM {
7379637SRandy.Fishel@Sun.COM 	int i;
7389637SRandy.Fishel@Sun.COM 	cpu_idle_cb_item_t *cip;
7399637SRandy.Fishel@Sun.COM 	cpu_idle_cb_state_t *sp;
7409637SRandy.Fishel@Sun.COM 	cpu_idle_callback_context_t ctx;
7419637SRandy.Fishel@Sun.COM #if defined(__x86)
7429637SRandy.Fishel@Sun.COM 	ulong_t iflags;
7439637SRandy.Fishel@Sun.COM #endif
7449637SRandy.Fishel@Sun.COM 
7459637SRandy.Fishel@Sun.COM 	ASSERT(CPU->cpu_seqid < max_ncpus);
7469637SRandy.Fishel@Sun.COM 	sp = &cpu_idle_cb_state[CPU->cpu_seqid];
7479637SRandy.Fishel@Sun.COM 
7489637SRandy.Fishel@Sun.COM #if defined(__sparc)
7499637SRandy.Fishel@Sun.COM 	/*
7509637SRandy.Fishel@Sun.COM 	 * On SPARC, cpu_idle_exit will only be called from idle thread
7519637SRandy.Fishel@Sun.COM 	 * with interrupt disabled.
7529637SRandy.Fishel@Sun.COM 	 */
7539637SRandy.Fishel@Sun.COM 
7549637SRandy.Fishel@Sun.COM 	if (sp->v.index != 0) {
7559637SRandy.Fishel@Sun.COM 		ctx = CPU_IDLE_GET_CTX(CPU);
7569637SRandy.Fishel@Sun.COM 		cpu_idle_exit_state(sp);
7579637SRandy.Fishel@Sun.COM 		for (i = sp->v.index - 1; i >= 0; i--) {
7589637SRandy.Fishel@Sun.COM 			cip = &cpu_idle_cb_array[i];
7599637SRandy.Fishel@Sun.COM 			if (cip->exit != NULL) {
7609637SRandy.Fishel@Sun.COM 				cip->exit(cip->arg, ctx, flag);
7619637SRandy.Fishel@Sun.COM 			}
7629637SRandy.Fishel@Sun.COM 		}
7639637SRandy.Fishel@Sun.COM 		sp->v.index = 0;
7649637SRandy.Fishel@Sun.COM 	}
7659637SRandy.Fishel@Sun.COM #elif defined(__x86)
7669637SRandy.Fishel@Sun.COM 	/*
7679637SRandy.Fishel@Sun.COM 	 * On x86, cpu_idle_exit will be called from idle thread or interrupt
7689637SRandy.Fishel@Sun.COM 	 * handler. When called from interrupt handler, interrupts will be
7699637SRandy.Fishel@Sun.COM 	 * disabled. When called from idle thread, interrupts may be disabled
7709637SRandy.Fishel@Sun.COM 	 * or enabled.
7719637SRandy.Fishel@Sun.COM 	 */
7729637SRandy.Fishel@Sun.COM 
7739637SRandy.Fishel@Sun.COM 	/* Called from interrupt, interrupts are already disabled. */
7749637SRandy.Fishel@Sun.COM 	if (flag & CPU_IDLE_CB_FLAG_INTR) {
7759637SRandy.Fishel@Sun.COM 		/*
7769637SRandy.Fishel@Sun.COM 		 * return if cpu_idle_exit already called or
7779637SRandy.Fishel@Sun.COM 		 * there is no registered callback.
7789637SRandy.Fishel@Sun.COM 		 */
7799637SRandy.Fishel@Sun.COM 		if (sp->v.index == 0) {
7809637SRandy.Fishel@Sun.COM 			return;
7819637SRandy.Fishel@Sun.COM 		}
7829637SRandy.Fishel@Sun.COM 		ctx = CPU_IDLE_GET_CTX(CPU);
7839637SRandy.Fishel@Sun.COM 		cpu_idle_exit_state(sp);
7849637SRandy.Fishel@Sun.COM 		for (i = sp->v.index - 1; i >= 0; i--) {
7859637SRandy.Fishel@Sun.COM 			cip = &cpu_idle_cb_array[i];
7869637SRandy.Fishel@Sun.COM 			if (cip->exit != NULL) {
7879637SRandy.Fishel@Sun.COM 				cip->exit(cip->arg, ctx, flag);
7889637SRandy.Fishel@Sun.COM 			}
7899637SRandy.Fishel@Sun.COM 		}
7909637SRandy.Fishel@Sun.COM 		sp->v.index = 0;
7919637SRandy.Fishel@Sun.COM 
7929637SRandy.Fishel@Sun.COM 	/* Called from idle thread, need to disable interrupt. */
7939637SRandy.Fishel@Sun.COM 	} else {
7949637SRandy.Fishel@Sun.COM 		iflags = intr_clear();
7959637SRandy.Fishel@Sun.COM 		if (sp->v.index != 0) {
7969637SRandy.Fishel@Sun.COM 			ctx = CPU_IDLE_GET_CTX(CPU);
7979637SRandy.Fishel@Sun.COM 			cpu_idle_exit_state(sp);
7989637SRandy.Fishel@Sun.COM 			for (i = sp->v.index - 1; i >= 0; i--) {
7999637SRandy.Fishel@Sun.COM 				cip = &cpu_idle_cb_array[i];
8009637SRandy.Fishel@Sun.COM 				if (cip->exit != NULL) {
8019637SRandy.Fishel@Sun.COM 					cip->exit(cip->arg, ctx, flag);
8029637SRandy.Fishel@Sun.COM 				}
8039637SRandy.Fishel@Sun.COM 			}
8049637SRandy.Fishel@Sun.COM 			sp->v.index = 0;
8059637SRandy.Fishel@Sun.COM 		}
8069637SRandy.Fishel@Sun.COM 		intr_restore(iflags);
8079637SRandy.Fishel@Sun.COM 	}
8089637SRandy.Fishel@Sun.COM #endif
8099637SRandy.Fishel@Sun.COM }
8109637SRandy.Fishel@Sun.COM 
8119637SRandy.Fishel@Sun.COM cpu_idle_callback_context_t
cpu_idle_get_context(void)8129637SRandy.Fishel@Sun.COM cpu_idle_get_context(void)
8139637SRandy.Fishel@Sun.COM {
8149637SRandy.Fishel@Sun.COM 	return (CPU_IDLE_GET_CTX(CPU));
8159637SRandy.Fishel@Sun.COM }
8169637SRandy.Fishel@Sun.COM 
8179637SRandy.Fishel@Sun.COM /*
8189637SRandy.Fishel@Sun.COM  * Allocate property structure in group of CPU_IDLE_VALUE_GROUP_SIZE to improve
8199637SRandy.Fishel@Sun.COM  * cache efficiency. To simplify implementation, allocated memory for property
8209637SRandy.Fishel@Sun.COM  * structure won't be freed.
8219637SRandy.Fishel@Sun.COM  */
8229637SRandy.Fishel@Sun.COM static void
cpu_idle_prop_allocate_impl(void)8239637SRandy.Fishel@Sun.COM cpu_idle_prop_allocate_impl(void)
8249637SRandy.Fishel@Sun.COM {
8259637SRandy.Fishel@Sun.COM 	int i;
8269637SRandy.Fishel@Sun.COM 	size_t sz;
8279637SRandy.Fishel@Sun.COM 	intptr_t buf;
8289637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop;
8299637SRandy.Fishel@Sun.COM 	cpu_idle_prop_value_t *valp;
8309637SRandy.Fishel@Sun.COM 
8319637SRandy.Fishel@Sun.COM 	ASSERT(!CPU_ON_INTR(CPU));
8329637SRandy.Fishel@Sun.COM 	prop = kmem_zalloc(sizeof (*prop) * CPU_IDLE_VALUE_GROUP_SIZE,
8339637SRandy.Fishel@Sun.COM 	    KM_SLEEP);
8349637SRandy.Fishel@Sun.COM 	sz = sizeof (*valp) * CPU_IDLE_VALUE_GROUP_SIZE * max_ncpus;
8359637SRandy.Fishel@Sun.COM 	sz += CPU_CACHE_COHERENCE_SIZE;
8369637SRandy.Fishel@Sun.COM 	buf = (intptr_t)kmem_zalloc(sz, KM_SLEEP);
8379637SRandy.Fishel@Sun.COM 	valp = (cpu_idle_prop_value_t *)P2ROUNDUP(buf,
8389637SRandy.Fishel@Sun.COM 	    CPU_CACHE_COHERENCE_SIZE);
8399637SRandy.Fishel@Sun.COM 
8409637SRandy.Fishel@Sun.COM 	for (i = 0; i < CPU_IDLE_VALUE_GROUP_SIZE; i++, prop++, valp++) {
8419637SRandy.Fishel@Sun.COM 		prop->value = valp;
8429637SRandy.Fishel@Sun.COM 		prop->next = cpu_idle_prop_free;
8439637SRandy.Fishel@Sun.COM 		cpu_idle_prop_free = prop;
8449637SRandy.Fishel@Sun.COM 	}
8459637SRandy.Fishel@Sun.COM }
8469637SRandy.Fishel@Sun.COM 
8479637SRandy.Fishel@Sun.COM int
cpu_idle_prop_create_property(const char * name,cpu_idle_prop_type_t type,cpu_idle_prop_update_t update,void * arg,cpu_idle_prop_handle_t * hdlp)8489637SRandy.Fishel@Sun.COM cpu_idle_prop_create_property(const char *name, cpu_idle_prop_type_t type,
8499637SRandy.Fishel@Sun.COM     cpu_idle_prop_update_t update, void *arg, cpu_idle_prop_handle_t *hdlp)
8509637SRandy.Fishel@Sun.COM {
8519637SRandy.Fishel@Sun.COM 	int rc = EEXIST;
8529637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop;
8539637SRandy.Fishel@Sun.COM 
8549637SRandy.Fishel@Sun.COM 	ASSERT(!CPU_ON_INTR(CPU));
8559637SRandy.Fishel@Sun.COM 	if (name == NULL || hdlp == NULL) {
8569637SRandy.Fishel@Sun.COM 		cmn_err(CE_WARN,
8579637SRandy.Fishel@Sun.COM 		    "!cpu_event: NULL parameters in create_property.");
8589637SRandy.Fishel@Sun.COM 		return (EINVAL);
8599637SRandy.Fishel@Sun.COM 	}
8609637SRandy.Fishel@Sun.COM 
8619637SRandy.Fishel@Sun.COM 	mutex_enter(&cpu_idle_prop_lock);
8629637SRandy.Fishel@Sun.COM 	for (prop = cpu_idle_prop_busy; prop != NULL; prop = prop->next) {
8639637SRandy.Fishel@Sun.COM 		if (strcmp(prop->name, name) == 0) {
8649637SRandy.Fishel@Sun.COM 			cmn_err(CE_NOTE,
8659637SRandy.Fishel@Sun.COM 			    "!cpu_event: property %s already exists.", name);
8669637SRandy.Fishel@Sun.COM 			break;
8679637SRandy.Fishel@Sun.COM 		}
8689637SRandy.Fishel@Sun.COM 	}
8699637SRandy.Fishel@Sun.COM 	if (prop == NULL) {
8709637SRandy.Fishel@Sun.COM 		if (cpu_idle_prop_free == NULL) {
8719637SRandy.Fishel@Sun.COM 			cpu_idle_prop_allocate_impl();
8729637SRandy.Fishel@Sun.COM 		}
8739637SRandy.Fishel@Sun.COM 		ASSERT(cpu_idle_prop_free != NULL);
8749637SRandy.Fishel@Sun.COM 		prop = cpu_idle_prop_free;
8759637SRandy.Fishel@Sun.COM 		cpu_idle_prop_free = prop->next;
8769637SRandy.Fishel@Sun.COM 		prop->next = cpu_idle_prop_busy;
8779637SRandy.Fishel@Sun.COM 		cpu_idle_prop_busy = prop;
8789637SRandy.Fishel@Sun.COM 
8799637SRandy.Fishel@Sun.COM 		ASSERT(prop->value != NULL);
8809637SRandy.Fishel@Sun.COM 		prop->name = strdup(name);
8819637SRandy.Fishel@Sun.COM 		prop->type = type;
8829637SRandy.Fishel@Sun.COM 		prop->update = update;
8839637SRandy.Fishel@Sun.COM 		prop->private = arg;
8849637SRandy.Fishel@Sun.COM 		prop->refcnt = 1;
8859637SRandy.Fishel@Sun.COM 		*hdlp = prop;
8869637SRandy.Fishel@Sun.COM 		rc = 0;
8879637SRandy.Fishel@Sun.COM 	}
8889637SRandy.Fishel@Sun.COM 	mutex_exit(&cpu_idle_prop_lock);
8899637SRandy.Fishel@Sun.COM 
8909637SRandy.Fishel@Sun.COM 	return (rc);
8919637SRandy.Fishel@Sun.COM }
8929637SRandy.Fishel@Sun.COM 
8939637SRandy.Fishel@Sun.COM int
cpu_idle_prop_destroy_property(cpu_idle_prop_handle_t hdl)8949637SRandy.Fishel@Sun.COM cpu_idle_prop_destroy_property(cpu_idle_prop_handle_t hdl)
8959637SRandy.Fishel@Sun.COM {
8969637SRandy.Fishel@Sun.COM 	int rc = ENODEV;
8979637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop, **propp;
8989637SRandy.Fishel@Sun.COM 	cpu_idle_prop_value_t *valp;
8999637SRandy.Fishel@Sun.COM 
9009637SRandy.Fishel@Sun.COM 	ASSERT(!CPU_ON_INTR(CPU));
9019637SRandy.Fishel@Sun.COM 	if (hdl == NULL) {
9029637SRandy.Fishel@Sun.COM 		cmn_err(CE_WARN,
9039637SRandy.Fishel@Sun.COM 		    "!cpu_event: hdl is NULL in destroy_property.");
9049637SRandy.Fishel@Sun.COM 		return (EINVAL);
9059637SRandy.Fishel@Sun.COM 	}
9069637SRandy.Fishel@Sun.COM 
9079637SRandy.Fishel@Sun.COM 	prop = (cpu_idle_prop_impl_t *)hdl;
9089637SRandy.Fishel@Sun.COM 	mutex_enter(&cpu_idle_prop_lock);
9099637SRandy.Fishel@Sun.COM 	for (propp = &cpu_idle_prop_busy; *propp != NULL;
9109637SRandy.Fishel@Sun.COM 	    propp = &(*propp)->next) {
9119637SRandy.Fishel@Sun.COM 		if (*propp == prop) {
9129637SRandy.Fishel@Sun.COM 			ASSERT(prop->refcnt > 0);
9139637SRandy.Fishel@Sun.COM 			if (atomic_cas_32(&prop->refcnt, 1, 0) == 1) {
9149637SRandy.Fishel@Sun.COM 				*propp = prop->next;
9159637SRandy.Fishel@Sun.COM 				strfree(prop->name);
9169637SRandy.Fishel@Sun.COM 				valp = prop->value;
9179637SRandy.Fishel@Sun.COM 				bzero(prop, sizeof (*prop));
9189637SRandy.Fishel@Sun.COM 				prop->value = valp;
9199637SRandy.Fishel@Sun.COM 				prop->next = cpu_idle_prop_free;
9209637SRandy.Fishel@Sun.COM 				cpu_idle_prop_free = prop;
9219637SRandy.Fishel@Sun.COM 				rc = 0;
9229637SRandy.Fishel@Sun.COM 			} else {
9239637SRandy.Fishel@Sun.COM 				rc = EBUSY;
9249637SRandy.Fishel@Sun.COM 			}
9259637SRandy.Fishel@Sun.COM 			break;
9269637SRandy.Fishel@Sun.COM 		}
9279637SRandy.Fishel@Sun.COM 	}
9289637SRandy.Fishel@Sun.COM 	mutex_exit(&cpu_idle_prop_lock);
9299637SRandy.Fishel@Sun.COM 
9309637SRandy.Fishel@Sun.COM 	return (rc);
9319637SRandy.Fishel@Sun.COM }
9329637SRandy.Fishel@Sun.COM 
9339637SRandy.Fishel@Sun.COM int
cpu_idle_prop_create_handle(const char * name,cpu_idle_prop_handle_t * hdlp)9349637SRandy.Fishel@Sun.COM cpu_idle_prop_create_handle(const char *name, cpu_idle_prop_handle_t *hdlp)
9359637SRandy.Fishel@Sun.COM {
9369637SRandy.Fishel@Sun.COM 	int rc = ENODEV;
9379637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop;
9389637SRandy.Fishel@Sun.COM 
9399637SRandy.Fishel@Sun.COM 	ASSERT(!CPU_ON_INTR(CPU));
9409637SRandy.Fishel@Sun.COM 	if (name == NULL || hdlp == NULL) {
9419637SRandy.Fishel@Sun.COM 		cmn_err(CE_WARN,
9429637SRandy.Fishel@Sun.COM 		    "!cpu_event: NULL parameters in create_handle.");
9439637SRandy.Fishel@Sun.COM 		return (EINVAL);
9449637SRandy.Fishel@Sun.COM 	}
9459637SRandy.Fishel@Sun.COM 
9469637SRandy.Fishel@Sun.COM 	mutex_enter(&cpu_idle_prop_lock);
9479637SRandy.Fishel@Sun.COM 	for (prop = cpu_idle_prop_busy; prop != NULL; prop = prop->next) {
9489637SRandy.Fishel@Sun.COM 		if (strcmp(prop->name, name) == 0) {
9499637SRandy.Fishel@Sun.COM 			/* Hold one refcount on object. */
9509637SRandy.Fishel@Sun.COM 			ASSERT(prop->refcnt > 0);
9519637SRandy.Fishel@Sun.COM 			atomic_inc_32(&prop->refcnt);
9529637SRandy.Fishel@Sun.COM 			*hdlp = (cpu_idle_prop_handle_t)prop;
9539637SRandy.Fishel@Sun.COM 			rc = 0;
9549637SRandy.Fishel@Sun.COM 			break;
9559637SRandy.Fishel@Sun.COM 		}
9569637SRandy.Fishel@Sun.COM 	}
9579637SRandy.Fishel@Sun.COM 	mutex_exit(&cpu_idle_prop_lock);
9589637SRandy.Fishel@Sun.COM 
9599637SRandy.Fishel@Sun.COM 	return (rc);
9609637SRandy.Fishel@Sun.COM }
9619637SRandy.Fishel@Sun.COM 
9629637SRandy.Fishel@Sun.COM int
cpu_idle_prop_destroy_handle(cpu_idle_prop_handle_t hdl)9639637SRandy.Fishel@Sun.COM cpu_idle_prop_destroy_handle(cpu_idle_prop_handle_t hdl)
9649637SRandy.Fishel@Sun.COM {
9659637SRandy.Fishel@Sun.COM 	int rc = ENODEV;
9669637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop;
9679637SRandy.Fishel@Sun.COM 
9689637SRandy.Fishel@Sun.COM 	ASSERT(!CPU_ON_INTR(CPU));
9699637SRandy.Fishel@Sun.COM 	if (hdl == NULL) {
9709637SRandy.Fishel@Sun.COM 		cmn_err(CE_WARN,
9719637SRandy.Fishel@Sun.COM 		    "!cpu_event: hdl is NULL in destroy_handle.");
9729637SRandy.Fishel@Sun.COM 		return (EINVAL);
9739637SRandy.Fishel@Sun.COM 	}
9749637SRandy.Fishel@Sun.COM 
9759637SRandy.Fishel@Sun.COM 	mutex_enter(&cpu_idle_prop_lock);
9769637SRandy.Fishel@Sun.COM 	for (prop = cpu_idle_prop_busy; prop != NULL; prop = prop->next) {
9779637SRandy.Fishel@Sun.COM 		if (prop == hdl) {
9789637SRandy.Fishel@Sun.COM 			/* Release refcnt held in create_handle. */
9799637SRandy.Fishel@Sun.COM 			ASSERT(prop->refcnt > 1);
9809637SRandy.Fishel@Sun.COM 			atomic_dec_32(&prop->refcnt);
9819637SRandy.Fishel@Sun.COM 			rc = 0;
9829637SRandy.Fishel@Sun.COM 			break;
9839637SRandy.Fishel@Sun.COM 		}
9849637SRandy.Fishel@Sun.COM 	}
9859637SRandy.Fishel@Sun.COM 	mutex_exit(&cpu_idle_prop_lock);
9869637SRandy.Fishel@Sun.COM 
9879637SRandy.Fishel@Sun.COM 	return (rc);
9889637SRandy.Fishel@Sun.COM }
9899637SRandy.Fishel@Sun.COM 
9909637SRandy.Fishel@Sun.COM cpu_idle_prop_type_t
cpu_idle_prop_get_type(cpu_idle_prop_handle_t hdl)9919637SRandy.Fishel@Sun.COM cpu_idle_prop_get_type(cpu_idle_prop_handle_t hdl)
9929637SRandy.Fishel@Sun.COM {
9939637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
9949637SRandy.Fishel@Sun.COM 	return (((cpu_idle_prop_impl_t *)hdl)->type);
9959637SRandy.Fishel@Sun.COM }
9969637SRandy.Fishel@Sun.COM 
9979637SRandy.Fishel@Sun.COM const char *
cpu_idle_prop_get_name(cpu_idle_prop_handle_t hdl)9989637SRandy.Fishel@Sun.COM cpu_idle_prop_get_name(cpu_idle_prop_handle_t hdl)
9999637SRandy.Fishel@Sun.COM {
10009637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
10019637SRandy.Fishel@Sun.COM 	return (((cpu_idle_prop_impl_t *)hdl)->name);
10029637SRandy.Fishel@Sun.COM }
10039637SRandy.Fishel@Sun.COM 
10049637SRandy.Fishel@Sun.COM int
cpu_idle_prop_get_value(cpu_idle_prop_handle_t hdl,cpu_idle_callback_context_t ctx,cpu_idle_prop_value_t * valp)10059637SRandy.Fishel@Sun.COM cpu_idle_prop_get_value(cpu_idle_prop_handle_t hdl,
10069637SRandy.Fishel@Sun.COM     cpu_idle_callback_context_t ctx, cpu_idle_prop_value_t *valp)
10079637SRandy.Fishel@Sun.COM {
10089637SRandy.Fishel@Sun.COM 	int idx, rc = 0;
10099637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop = (cpu_idle_prop_impl_t *)hdl;
10109637SRandy.Fishel@Sun.COM 
10119637SRandy.Fishel@Sun.COM 	ASSERT(CPU_IDLE_CTX2CPUID(ctx) < max_ncpus);
10129637SRandy.Fishel@Sun.COM 	if (hdl == NULL || valp == NULL) {
10139637SRandy.Fishel@Sun.COM 		cmn_err(CE_NOTE, "!cpu_event: NULL parameters in prop_get.");
10149637SRandy.Fishel@Sun.COM 		return (EINVAL);
10159637SRandy.Fishel@Sun.COM 	}
10169637SRandy.Fishel@Sun.COM 	idx = CPU_IDLE_CTX2IDX(ctx);
10179637SRandy.Fishel@Sun.COM 	if (prop->update != NULL) {
10189637SRandy.Fishel@Sun.COM 		cpu_idle_cb_state_t *sp;
10199637SRandy.Fishel@Sun.COM 
10209637SRandy.Fishel@Sun.COM 		ASSERT(CPU->cpu_seqid < max_ncpus);
10219637SRandy.Fishel@Sun.COM 		sp = &cpu_idle_cb_state[CPU->cpu_seqid];
10229637SRandy.Fishel@Sun.COM 		/* CPU's idle enter timestamp as sequence number. */
10239637SRandy.Fishel@Sun.COM 		rc = prop->update(prop->private,
10249637SRandy.Fishel@Sun.COM 		    (uint64_t)sp->v.enter_ts->cipv_hrtime, &prop->value[idx]);
10259637SRandy.Fishel@Sun.COM 	}
10269637SRandy.Fishel@Sun.COM 	if (rc == 0) {
10279637SRandy.Fishel@Sun.COM 		*valp = prop->value[idx];
10289637SRandy.Fishel@Sun.COM 	}
10299637SRandy.Fishel@Sun.COM 
10309637SRandy.Fishel@Sun.COM 	return (rc);
10319637SRandy.Fishel@Sun.COM }
10329637SRandy.Fishel@Sun.COM 
10339637SRandy.Fishel@Sun.COM uint32_t
cpu_idle_prop_get_uint32(cpu_idle_prop_handle_t hdl,cpu_idle_callback_context_t ctx)10349637SRandy.Fishel@Sun.COM cpu_idle_prop_get_uint32(cpu_idle_prop_handle_t hdl,
10359637SRandy.Fishel@Sun.COM     cpu_idle_callback_context_t ctx)
10369637SRandy.Fishel@Sun.COM {
10379637SRandy.Fishel@Sun.COM 	int idx;
10389637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop = (cpu_idle_prop_impl_t *)hdl;
10399637SRandy.Fishel@Sun.COM 
10409637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
10419637SRandy.Fishel@Sun.COM 	ASSERT(CPU_IDLE_CTX2CPUID(ctx) < max_ncpus);
10429637SRandy.Fishel@Sun.COM 	idx = CPU_IDLE_CTX2IDX(ctx);
10439637SRandy.Fishel@Sun.COM 	return (prop->value[idx].cipv_uint32);
10449637SRandy.Fishel@Sun.COM }
10459637SRandy.Fishel@Sun.COM 
10469637SRandy.Fishel@Sun.COM uint64_t
cpu_idle_prop_get_uint64(cpu_idle_prop_handle_t hdl,cpu_idle_callback_context_t ctx)10479637SRandy.Fishel@Sun.COM cpu_idle_prop_get_uint64(cpu_idle_prop_handle_t hdl,
10489637SRandy.Fishel@Sun.COM     cpu_idle_callback_context_t ctx)
10499637SRandy.Fishel@Sun.COM {
10509637SRandy.Fishel@Sun.COM 	int idx;
10519637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop = (cpu_idle_prop_impl_t *)hdl;
10529637SRandy.Fishel@Sun.COM 
10539637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
10549637SRandy.Fishel@Sun.COM 	ASSERT(CPU_IDLE_CTX2CPUID(ctx) < max_ncpus);
10559637SRandy.Fishel@Sun.COM 	idx = CPU_IDLE_CTX2IDX(ctx);
10569637SRandy.Fishel@Sun.COM 	return (prop->value[idx].cipv_uint64);
10579637SRandy.Fishel@Sun.COM }
10589637SRandy.Fishel@Sun.COM 
10599637SRandy.Fishel@Sun.COM intptr_t
cpu_idle_prop_get_intptr(cpu_idle_prop_handle_t hdl,cpu_idle_callback_context_t ctx)10609637SRandy.Fishel@Sun.COM cpu_idle_prop_get_intptr(cpu_idle_prop_handle_t hdl,
10619637SRandy.Fishel@Sun.COM     cpu_idle_callback_context_t ctx)
10629637SRandy.Fishel@Sun.COM {
10639637SRandy.Fishel@Sun.COM 	int idx;
10649637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop = (cpu_idle_prop_impl_t *)hdl;
10659637SRandy.Fishel@Sun.COM 
10669637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
10679637SRandy.Fishel@Sun.COM 	ASSERT(CPU_IDLE_CTX2CPUID(ctx) < max_ncpus);
10689637SRandy.Fishel@Sun.COM 	idx = CPU_IDLE_CTX2IDX(ctx);
10699637SRandy.Fishel@Sun.COM 	return (prop->value[idx].cipv_intptr);
10709637SRandy.Fishel@Sun.COM }
10719637SRandy.Fishel@Sun.COM 
10729637SRandy.Fishel@Sun.COM hrtime_t
cpu_idle_prop_get_hrtime(cpu_idle_prop_handle_t hdl,cpu_idle_callback_context_t ctx)10739637SRandy.Fishel@Sun.COM cpu_idle_prop_get_hrtime(cpu_idle_prop_handle_t hdl,
10749637SRandy.Fishel@Sun.COM     cpu_idle_callback_context_t ctx)
10759637SRandy.Fishel@Sun.COM {
10769637SRandy.Fishel@Sun.COM 	int idx;
10779637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop = (cpu_idle_prop_impl_t *)hdl;
10789637SRandy.Fishel@Sun.COM 
10799637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
10809637SRandy.Fishel@Sun.COM 	ASSERT(CPU_IDLE_CTX2CPUID(ctx) < max_ncpus);
10819637SRandy.Fishel@Sun.COM 	idx = CPU_IDLE_CTX2IDX(ctx);
10829637SRandy.Fishel@Sun.COM 	return (prop->value[idx].cipv_hrtime);
10839637SRandy.Fishel@Sun.COM }
10849637SRandy.Fishel@Sun.COM 
10859637SRandy.Fishel@Sun.COM void
cpu_idle_prop_set_value(cpu_idle_prop_handle_t hdl,cpu_idle_callback_context_t ctx,cpu_idle_prop_value_t val)10869637SRandy.Fishel@Sun.COM cpu_idle_prop_set_value(cpu_idle_prop_handle_t hdl,
10879637SRandy.Fishel@Sun.COM     cpu_idle_callback_context_t ctx, cpu_idle_prop_value_t val)
10889637SRandy.Fishel@Sun.COM {
10899637SRandy.Fishel@Sun.COM 	int idx;
10909637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop = (cpu_idle_prop_impl_t *)hdl;
10919637SRandy.Fishel@Sun.COM 
10929637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
10939637SRandy.Fishel@Sun.COM 	ASSERT(CPU_IDLE_CTX2CPUID(ctx) < max_ncpus);
10949637SRandy.Fishel@Sun.COM 	idx = CPU_IDLE_CTX2IDX(ctx);
10959637SRandy.Fishel@Sun.COM 	prop->value[idx] = val;
10969637SRandy.Fishel@Sun.COM }
10979637SRandy.Fishel@Sun.COM 
10989637SRandy.Fishel@Sun.COM void
cpu_idle_prop_set_all(cpu_idle_prop_handle_t hdl,cpu_idle_prop_value_t val)10999637SRandy.Fishel@Sun.COM cpu_idle_prop_set_all(cpu_idle_prop_handle_t hdl, cpu_idle_prop_value_t val)
11009637SRandy.Fishel@Sun.COM {
11019637SRandy.Fishel@Sun.COM 	int i, idx;
11029637SRandy.Fishel@Sun.COM 	cpu_idle_prop_impl_t *prop = (cpu_idle_prop_impl_t *)hdl;
11039637SRandy.Fishel@Sun.COM 
11049637SRandy.Fishel@Sun.COM 	ASSERT(hdl != NULL);
11059637SRandy.Fishel@Sun.COM 	for (i = 0; i < max_ncpus; i++) {
11069637SRandy.Fishel@Sun.COM 		idx = CPU_IDLE_CTX2IDX(i);
11079637SRandy.Fishel@Sun.COM 		prop->value[idx] = val;
11089637SRandy.Fishel@Sun.COM 	}
11099637SRandy.Fishel@Sun.COM }
11109637SRandy.Fishel@Sun.COM 
11119637SRandy.Fishel@Sun.COM /*ARGSUSED*/
cpu_idle_prop_update_intr_cnt(void * arg,uint64_t seqnum,cpu_idle_prop_value_t * valp)11129637SRandy.Fishel@Sun.COM static int cpu_idle_prop_update_intr_cnt(void *arg, uint64_t seqnum,
11139637SRandy.Fishel@Sun.COM     cpu_idle_prop_value_t *valp)
11149637SRandy.Fishel@Sun.COM {
11159637SRandy.Fishel@Sun.COM 	int i;
11169637SRandy.Fishel@Sun.COM 	uint64_t val;
11179637SRandy.Fishel@Sun.COM 
11189637SRandy.Fishel@Sun.COM 	for (val = 0, i = 0; i < PIL_MAX; i++) {
11199637SRandy.Fishel@Sun.COM 		val += CPU->cpu_stats.sys.intr[i];
11209637SRandy.Fishel@Sun.COM 	}
11219637SRandy.Fishel@Sun.COM 	valp->cipv_uint64 = val;
11229637SRandy.Fishel@Sun.COM 
11239637SRandy.Fishel@Sun.COM 	return (0);
11249637SRandy.Fishel@Sun.COM }
11259637SRandy.Fishel@Sun.COM 
11269637SRandy.Fishel@Sun.COM uint_t
cpu_idle_get_cpu_state(cpu_t * cp)11279637SRandy.Fishel@Sun.COM cpu_idle_get_cpu_state(cpu_t *cp)
11289637SRandy.Fishel@Sun.COM {
11299637SRandy.Fishel@Sun.COM 	ASSERT(cp != NULL && cp->cpu_seqid < max_ncpus);
11309637SRandy.Fishel@Sun.COM 	return ((uint_t)cpu_idle_prop_get_uint32(
11319637SRandy.Fishel@Sun.COM 	    cpu_idle_prop_array[CPU_IDLE_PROP_IDX_IDLE_STATE].handle,
11329637SRandy.Fishel@Sun.COM 	    CPU_IDLE_GET_CTX(cp)));
11339637SRandy.Fishel@Sun.COM }
1134*12004Sjiang.liu@intel.com 
1135*12004Sjiang.liu@intel.com #if defined(__x86)
1136*12004Sjiang.liu@intel.com /*
1137*12004Sjiang.liu@intel.com  * Intercept CPU at a safe point in idle() before powering it off.
1138*12004Sjiang.liu@intel.com  */
1139*12004Sjiang.liu@intel.com void
cpu_idle_intercept_cpu(cpu_t * cp)1140*12004Sjiang.liu@intel.com cpu_idle_intercept_cpu(cpu_t *cp)
1141*12004Sjiang.liu@intel.com {
1142*12004Sjiang.liu@intel.com 	ASSERT(cp->cpu_seqid < max_ncpus);
1143*12004Sjiang.liu@intel.com 	ASSERT(cpu_idle_cb_state[cp->cpu_seqid].v.enabled == B_FALSE);
1144*12004Sjiang.liu@intel.com 
1145*12004Sjiang.liu@intel.com 	/* Set flag to intercept CPU. */
1146*12004Sjiang.liu@intel.com 	CPUSET_ATOMIC_ADD(cpu_idle_intercept_set, cp->cpu_id);
1147*12004Sjiang.liu@intel.com 	/* Wake up CPU from possible sleep state. */
1148*12004Sjiang.liu@intel.com 	poke_cpu(cp->cpu_id);
1149*12004Sjiang.liu@intel.com 	while (CPU_IN_SET(cpu_idle_intercept_set, cp->cpu_id)) {
1150*12004Sjiang.liu@intel.com 		DELAY(1);
1151*12004Sjiang.liu@intel.com 	}
1152*12004Sjiang.liu@intel.com 	/*
1153*12004Sjiang.liu@intel.com 	 * Now target CPU is spinning in a pause loop with interrupts disabled.
1154*12004Sjiang.liu@intel.com 	 */
1155*12004Sjiang.liu@intel.com }
1156*12004Sjiang.liu@intel.com #endif
1157