xref: /titanic_54/usr/src/uts/common/os/kcpc.c (revision b9e93c10c0a2a4bb069d38bb311021a9478c4711)
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
544961713Sgirish  * Common Development and Distribution License (the "License").
644961713Sgirish  * 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  */
217aec1d6eScindi 
227c478bd9Sstevel@tonic-gate /*
23*b9e93c10SJonathan Haslam  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/param.h>
287c478bd9Sstevel@tonic-gate #include <sys/thread.h>
297c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
307c478bd9Sstevel@tonic-gate #include <sys/inttypes.h>
317c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
327c478bd9Sstevel@tonic-gate #include <sys/time.h>
334568bee7Strevtom #include <sys/ksynch.h>
347c478bd9Sstevel@tonic-gate #include <sys/systm.h>
357c478bd9Sstevel@tonic-gate #include <sys/kcpc.h>
367c478bd9Sstevel@tonic-gate #include <sys/cpc_impl.h>
377c478bd9Sstevel@tonic-gate #include <sys/cpc_pcbe.h>
387c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
397c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
407c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
417c478bd9Sstevel@tonic-gate #include <sys/sdt.h>
427c478bd9Sstevel@tonic-gate #if defined(__x86)
437c478bd9Sstevel@tonic-gate #include <asm/clock.h>
447c478bd9Sstevel@tonic-gate #endif
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate kmutex_t	kcpc_ctx_llock[CPC_HASH_BUCKETS];	/* protects ctx_list */
477c478bd9Sstevel@tonic-gate kcpc_ctx_t	*kcpc_ctx_list[CPC_HASH_BUCKETS];	/* head of list */
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate krwlock_t	kcpc_cpuctx_lock;	/* lock for 'kcpc_cpuctx' below */
517c478bd9Sstevel@tonic-gate int		kcpc_cpuctx;		/* number of cpu-specific contexts */
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate int kcpc_counts_include_idle = 1; /* Project Private /etc/system variable */
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate /*
567c478bd9Sstevel@tonic-gate  * These are set when a PCBE module is loaded.
577c478bd9Sstevel@tonic-gate  */
587c478bd9Sstevel@tonic-gate uint_t		cpc_ncounters = 0;
597c478bd9Sstevel@tonic-gate pcbe_ops_t	*pcbe_ops = NULL;
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate /*
627c478bd9Sstevel@tonic-gate  * Statistics on (mis)behavior
637c478bd9Sstevel@tonic-gate  */
647c478bd9Sstevel@tonic-gate static uint32_t kcpc_intrctx_count;    /* # overflows in an interrupt handler */
657c478bd9Sstevel@tonic-gate static uint32_t kcpc_nullctx_count;    /* # overflows in a thread with no ctx */
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate /*
68*b9e93c10SJonathan Haslam  * By setting 'kcpc_nullctx_panic' to 1, any overflow interrupts in a thread
69*b9e93c10SJonathan Haslam  * with no valid context will result in a panic.
707c478bd9Sstevel@tonic-gate  */
717c478bd9Sstevel@tonic-gate static int kcpc_nullctx_panic = 0;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate static void kcpc_lwp_create(kthread_t *t, kthread_t *ct);
747c478bd9Sstevel@tonic-gate static void kcpc_restore(kcpc_ctx_t *ctx);
757c478bd9Sstevel@tonic-gate static void kcpc_save(kcpc_ctx_t *ctx);
767c478bd9Sstevel@tonic-gate static void kcpc_free(kcpc_ctx_t *ctx, int isexec);
777c478bd9Sstevel@tonic-gate static void kcpc_ctx_clone(kcpc_ctx_t *ctx, kcpc_ctx_t *cctx);
787c478bd9Sstevel@tonic-gate static int kcpc_tryassign(kcpc_set_t *set, int starting_req, int *scratch);
797c478bd9Sstevel@tonic-gate static kcpc_set_t *kcpc_dup_set(kcpc_set_t *set);
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate void
827c478bd9Sstevel@tonic-gate kcpc_register_pcbe(pcbe_ops_t *ops)
837c478bd9Sstevel@tonic-gate {
847c478bd9Sstevel@tonic-gate 	pcbe_ops = ops;
857c478bd9Sstevel@tonic-gate 	cpc_ncounters = pcbe_ops->pcbe_ncounters();
867c478bd9Sstevel@tonic-gate }
877c478bd9Sstevel@tonic-gate 
88*b9e93c10SJonathan Haslam void
89*b9e93c10SJonathan Haslam kcpc_register_dcpc(void (*func)(uint64_t))
90*b9e93c10SJonathan Haslam {
91*b9e93c10SJonathan Haslam 	dtrace_cpc_fire = func;
92*b9e93c10SJonathan Haslam }
93*b9e93c10SJonathan Haslam 
94*b9e93c10SJonathan Haslam void
95*b9e93c10SJonathan Haslam kcpc_unregister_dcpc(void)
96*b9e93c10SJonathan Haslam {
97*b9e93c10SJonathan Haslam 	dtrace_cpc_fire = NULL;
98*b9e93c10SJonathan Haslam }
99*b9e93c10SJonathan Haslam 
1007c478bd9Sstevel@tonic-gate int
1017c478bd9Sstevel@tonic-gate kcpc_bind_cpu(kcpc_set_t *set, processorid_t cpuid, int *subcode)
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate 	cpu_t		*cp;
1047c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx;
1057c478bd9Sstevel@tonic-gate 	int		error;
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	ctx = kcpc_ctx_alloc();
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	if (kcpc_assign_reqs(set, ctx) != 0) {
1107c478bd9Sstevel@tonic-gate 		kcpc_ctx_free(ctx);
1117c478bd9Sstevel@tonic-gate 		*subcode = CPC_RESOURCE_UNAVAIL;
1127c478bd9Sstevel@tonic-gate 		return (EINVAL);
1137c478bd9Sstevel@tonic-gate 	}
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	ctx->kc_cpuid = cpuid;
1167c478bd9Sstevel@tonic-gate 	ctx->kc_thread = curthread;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	set->ks_data = kmem_zalloc(set->ks_nreqs * sizeof (uint64_t), KM_SLEEP);
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 	if ((error = kcpc_configure_reqs(ctx, set, subcode)) != 0) {
1217c478bd9Sstevel@tonic-gate 		kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
1227c478bd9Sstevel@tonic-gate 		kcpc_ctx_free(ctx);
1237c478bd9Sstevel@tonic-gate 		return (error);
1247c478bd9Sstevel@tonic-gate 	}
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	set->ks_ctx = ctx;
1277c478bd9Sstevel@tonic-gate 	ctx->kc_set = set;
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	/*
1307c478bd9Sstevel@tonic-gate 	 * We must hold cpu_lock to prevent DR, offlining, or unbinding while
1317c478bd9Sstevel@tonic-gate 	 * we are manipulating the cpu_t and programming the hardware, else the
1327c478bd9Sstevel@tonic-gate 	 * the cpu_t could go away while we're looking at it.
1337c478bd9Sstevel@tonic-gate 	 */
1347c478bd9Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
1357c478bd9Sstevel@tonic-gate 	cp = cpu_get(cpuid);
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	if (cp == NULL)
1387c478bd9Sstevel@tonic-gate 		/*
1397c478bd9Sstevel@tonic-gate 		 * The CPU could have been DRd out while we were getting set up.
1407c478bd9Sstevel@tonic-gate 		 */
1417c478bd9Sstevel@tonic-gate 		goto unbound;
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	mutex_enter(&cp->cpu_cpc_ctxlock);
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	if (cp->cpu_cpc_ctx != NULL) {
1467c478bd9Sstevel@tonic-gate 		/*
1477c478bd9Sstevel@tonic-gate 		 * If this CPU already has a bound set, return an error.
1487c478bd9Sstevel@tonic-gate 		 */
1497c478bd9Sstevel@tonic-gate 		mutex_exit(&cp->cpu_cpc_ctxlock);
1507c478bd9Sstevel@tonic-gate 		goto unbound;
1517c478bd9Sstevel@tonic-gate 	}
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	if (curthread->t_bind_cpu != cpuid) {
1547c478bd9Sstevel@tonic-gate 		mutex_exit(&cp->cpu_cpc_ctxlock);
1557c478bd9Sstevel@tonic-gate 		goto unbound;
1567c478bd9Sstevel@tonic-gate 	}
1577c478bd9Sstevel@tonic-gate 	cp->cpu_cpc_ctx = ctx;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	/*
1607c478bd9Sstevel@tonic-gate 	 * Kernel preemption must be disabled while fiddling with the hardware
1617c478bd9Sstevel@tonic-gate 	 * registers to prevent partial updates.
1627c478bd9Sstevel@tonic-gate 	 */
1637c478bd9Sstevel@tonic-gate 	kpreempt_disable();
1647c478bd9Sstevel@tonic-gate 	ctx->kc_rawtick = KCPC_GET_TICK();
1657c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_program(ctx);
1667c478bd9Sstevel@tonic-gate 	kpreempt_enable();
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	mutex_exit(&cp->cpu_cpc_ctxlock);
1697c478bd9Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
1707c478bd9Sstevel@tonic-gate 
1714568bee7Strevtom 	mutex_enter(&set->ks_lock);
1724568bee7Strevtom 	set->ks_state |= KCPC_SET_BOUND;
1734568bee7Strevtom 	cv_signal(&set->ks_condv);
1744568bee7Strevtom 	mutex_exit(&set->ks_lock);
1754568bee7Strevtom 
1767c478bd9Sstevel@tonic-gate 	return (0);
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate unbound:
1797c478bd9Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
1807c478bd9Sstevel@tonic-gate 	set->ks_ctx = NULL;
1817c478bd9Sstevel@tonic-gate 	kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
1827c478bd9Sstevel@tonic-gate 	kcpc_ctx_free(ctx);
1837c478bd9Sstevel@tonic-gate 	return (EAGAIN);
1847c478bd9Sstevel@tonic-gate }
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate int
1877c478bd9Sstevel@tonic-gate kcpc_bind_thread(kcpc_set_t *set, kthread_t *t, int *subcode)
1887c478bd9Sstevel@tonic-gate {
1897c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx;
1907c478bd9Sstevel@tonic-gate 	int		error;
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/*
1937c478bd9Sstevel@tonic-gate 	 * Only one set is allowed per context, so ensure there is no
1947c478bd9Sstevel@tonic-gate 	 * existing context.
1957c478bd9Sstevel@tonic-gate 	 */
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	if (t->t_cpc_ctx != NULL)
1987c478bd9Sstevel@tonic-gate 		return (EEXIST);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	ctx = kcpc_ctx_alloc();
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	/*
2037c478bd9Sstevel@tonic-gate 	 * The context must begin life frozen until it has been properly
2047c478bd9Sstevel@tonic-gate 	 * programmed onto the hardware. This prevents the context ops from
2057c478bd9Sstevel@tonic-gate 	 * worrying about it until we're ready.
2067c478bd9Sstevel@tonic-gate 	 */
2077c478bd9Sstevel@tonic-gate 	ctx->kc_flags |= KCPC_CTX_FREEZE;
2087c478bd9Sstevel@tonic-gate 	ctx->kc_hrtime = gethrtime();
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	if (kcpc_assign_reqs(set, ctx) != 0) {
2117c478bd9Sstevel@tonic-gate 		kcpc_ctx_free(ctx);
2127c478bd9Sstevel@tonic-gate 		*subcode = CPC_RESOURCE_UNAVAIL;
2137c478bd9Sstevel@tonic-gate 		return (EINVAL);
2147c478bd9Sstevel@tonic-gate 	}
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	ctx->kc_cpuid = -1;
2177c478bd9Sstevel@tonic-gate 	if (set->ks_flags & CPC_BIND_LWP_INHERIT)
2187c478bd9Sstevel@tonic-gate 		ctx->kc_flags |= KCPC_CTX_LWPINHERIT;
2197c478bd9Sstevel@tonic-gate 	ctx->kc_thread = t;
2207c478bd9Sstevel@tonic-gate 	t->t_cpc_ctx = ctx;
2217c478bd9Sstevel@tonic-gate 	/*
2227c478bd9Sstevel@tonic-gate 	 * Permit threads to look at their own hardware counters from userland.
2237c478bd9Sstevel@tonic-gate 	 */
2247c478bd9Sstevel@tonic-gate 	ctx->kc_flags |= KCPC_CTX_NONPRIV;
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	/*
2277c478bd9Sstevel@tonic-gate 	 * Create the data store for this set.
2287c478bd9Sstevel@tonic-gate 	 */
2297c478bd9Sstevel@tonic-gate 	set->ks_data = kmem_alloc(set->ks_nreqs * sizeof (uint64_t), KM_SLEEP);
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	if ((error = kcpc_configure_reqs(ctx, set, subcode)) != 0) {
2327c478bd9Sstevel@tonic-gate 		kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
2337c478bd9Sstevel@tonic-gate 		kcpc_ctx_free(ctx);
2347c478bd9Sstevel@tonic-gate 		t->t_cpc_ctx = NULL;
2357c478bd9Sstevel@tonic-gate 		return (error);
2367c478bd9Sstevel@tonic-gate 	}
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	set->ks_ctx = ctx;
2397c478bd9Sstevel@tonic-gate 	ctx->kc_set = set;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	/*
2427c478bd9Sstevel@tonic-gate 	 * Add a device context to the subject thread.
2437c478bd9Sstevel@tonic-gate 	 */
2447c478bd9Sstevel@tonic-gate 	installctx(t, ctx, kcpc_save, kcpc_restore, NULL,
2457c478bd9Sstevel@tonic-gate 	    kcpc_lwp_create, NULL, kcpc_free);
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	/*
2487c478bd9Sstevel@tonic-gate 	 * Ask the backend to program the hardware.
2497c478bd9Sstevel@tonic-gate 	 */
2507c478bd9Sstevel@tonic-gate 	if (t == curthread) {
2517c478bd9Sstevel@tonic-gate 		kpreempt_disable();
2527c478bd9Sstevel@tonic-gate 		ctx->kc_rawtick = KCPC_GET_TICK();
2537c478bd9Sstevel@tonic-gate 		atomic_and_uint(&ctx->kc_flags, ~KCPC_CTX_FREEZE);
2547c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_program(ctx);
2557c478bd9Sstevel@tonic-gate 		kpreempt_enable();
2567c478bd9Sstevel@tonic-gate 	} else
2577c478bd9Sstevel@tonic-gate 		/*
2587c478bd9Sstevel@tonic-gate 		 * Since we are the agent LWP, we know the victim LWP is stopped
2597c478bd9Sstevel@tonic-gate 		 * until we're done here; no need to worry about preemption or
2607c478bd9Sstevel@tonic-gate 		 * migration here. We still use an atomic op to clear the flag
2617c478bd9Sstevel@tonic-gate 		 * to ensure the flags are always self-consistent; they can
2627c478bd9Sstevel@tonic-gate 		 * still be accessed from, for instance, another CPU doing a
2637c478bd9Sstevel@tonic-gate 		 * kcpc_invalidate_all().
2647c478bd9Sstevel@tonic-gate 		 */
2657c478bd9Sstevel@tonic-gate 		atomic_and_uint(&ctx->kc_flags, ~KCPC_CTX_FREEZE);
2667c478bd9Sstevel@tonic-gate 
2674568bee7Strevtom 	mutex_enter(&set->ks_lock);
2684568bee7Strevtom 	set->ks_state |= KCPC_SET_BOUND;
2694568bee7Strevtom 	cv_signal(&set->ks_condv);
2704568bee7Strevtom 	mutex_exit(&set->ks_lock);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	return (0);
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate /*
2767c478bd9Sstevel@tonic-gate  * Walk through each request in the set and ask the PCBE to configure a
2777c478bd9Sstevel@tonic-gate  * corresponding counter.
2787c478bd9Sstevel@tonic-gate  */
279*b9e93c10SJonathan Haslam int
2807c478bd9Sstevel@tonic-gate kcpc_configure_reqs(kcpc_ctx_t *ctx, kcpc_set_t *set, int *subcode)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	int		i;
2837c478bd9Sstevel@tonic-gate 	int		ret;
2847c478bd9Sstevel@tonic-gate 	kcpc_request_t	*rp;
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++) {
2877c478bd9Sstevel@tonic-gate 		int n;
2887c478bd9Sstevel@tonic-gate 		rp = &set->ks_req[i];
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 		n = rp->kr_picnum;
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 		ASSERT(n >= 0 && n < cpc_ncounters);
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 		ASSERT(ctx->kc_pics[n].kp_req == NULL);
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 		if (rp->kr_flags & CPC_OVF_NOTIFY_EMT) {
2977c478bd9Sstevel@tonic-gate 			if ((pcbe_ops->pcbe_caps & CPC_CAP_OVERFLOW_INTERRUPT)
2987c478bd9Sstevel@tonic-gate 			    == 0) {
2997c478bd9Sstevel@tonic-gate 				*subcode = -1;
3007c478bd9Sstevel@tonic-gate 				return (ENOTSUP);
3017c478bd9Sstevel@tonic-gate 			}
3027c478bd9Sstevel@tonic-gate 			/*
3037c478bd9Sstevel@tonic-gate 			 * If any of the counters have requested overflow
3047c478bd9Sstevel@tonic-gate 			 * notification, we flag the context as being one that
3057c478bd9Sstevel@tonic-gate 			 * cares about overflow.
3067c478bd9Sstevel@tonic-gate 			 */
3077c478bd9Sstevel@tonic-gate 			ctx->kc_flags |= KCPC_CTX_SIGOVF;
3087c478bd9Sstevel@tonic-gate 		}
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 		rp->kr_config = NULL;
3117c478bd9Sstevel@tonic-gate 		if ((ret = pcbe_ops->pcbe_configure(n, rp->kr_event,
3127c478bd9Sstevel@tonic-gate 		    rp->kr_preset, rp->kr_flags, rp->kr_nattrs, rp->kr_attr,
3137c478bd9Sstevel@tonic-gate 		    &(rp->kr_config), (void *)ctx)) != 0) {
3147c478bd9Sstevel@tonic-gate 			kcpc_free_configs(set);
3157c478bd9Sstevel@tonic-gate 			*subcode = ret;
3168d4e547dSae112802 			switch (ret) {
3178d4e547dSae112802 			case CPC_ATTR_REQUIRES_PRIVILEGE:
3188d4e547dSae112802 			case CPC_HV_NO_ACCESS:
3197c478bd9Sstevel@tonic-gate 				return (EACCES);
3208d4e547dSae112802 			default:
3217c478bd9Sstevel@tonic-gate 				return (EINVAL);
3227c478bd9Sstevel@tonic-gate 			}
3238d4e547dSae112802 		}
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 		ctx->kc_pics[n].kp_req = rp;
3267c478bd9Sstevel@tonic-gate 		rp->kr_picp = &ctx->kc_pics[n];
3277c478bd9Sstevel@tonic-gate 		rp->kr_data = set->ks_data + rp->kr_index;
3287c478bd9Sstevel@tonic-gate 		*rp->kr_data = rp->kr_preset;
3297c478bd9Sstevel@tonic-gate 	}
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	return (0);
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate 
334*b9e93c10SJonathan Haslam void
3357c478bd9Sstevel@tonic-gate kcpc_free_configs(kcpc_set_t *set)
3367c478bd9Sstevel@tonic-gate {
3377c478bd9Sstevel@tonic-gate 	int i;
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++)
3407c478bd9Sstevel@tonic-gate 		if (set->ks_req[i].kr_config != NULL)
3417c478bd9Sstevel@tonic-gate 			pcbe_ops->pcbe_free(set->ks_req[i].kr_config);
3427c478bd9Sstevel@tonic-gate }
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate /*
3457c478bd9Sstevel@tonic-gate  * buf points to a user address and the data should be copied out to that
3467c478bd9Sstevel@tonic-gate  * address in the current process.
3477c478bd9Sstevel@tonic-gate  */
3487c478bd9Sstevel@tonic-gate int
3497c478bd9Sstevel@tonic-gate kcpc_sample(kcpc_set_t *set, uint64_t *buf, hrtime_t *hrtime, uint64_t *tick)
3507c478bd9Sstevel@tonic-gate {
3517c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx = set->ks_ctx;
3527c478bd9Sstevel@tonic-gate 	uint64_t	curtick = KCPC_GET_TICK();
3537c478bd9Sstevel@tonic-gate 
3544568bee7Strevtom 	mutex_enter(&set->ks_lock);
3554568bee7Strevtom 	if ((set->ks_state & KCPC_SET_BOUND) == 0) {
3564568bee7Strevtom 		mutex_exit(&set->ks_lock);
3577c478bd9Sstevel@tonic-gate 		return (EINVAL);
3584568bee7Strevtom 	}
3594568bee7Strevtom 	mutex_exit(&set->ks_lock);
3604568bee7Strevtom 
3614568bee7Strevtom 	if (ctx->kc_flags & KCPC_CTX_INVALID)
3627c478bd9Sstevel@tonic-gate 		return (EAGAIN);
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	if ((ctx->kc_flags & KCPC_CTX_FREEZE) == 0) {
3657c478bd9Sstevel@tonic-gate 		/*
3667c478bd9Sstevel@tonic-gate 		 * Kernel preemption must be disabled while reading the
3677c478bd9Sstevel@tonic-gate 		 * hardware regs, and if this is a CPU-bound context, while
3687c478bd9Sstevel@tonic-gate 		 * checking the CPU binding of the current thread.
3697c478bd9Sstevel@tonic-gate 		 */
3707c478bd9Sstevel@tonic-gate 		kpreempt_disable();
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 		if (ctx->kc_cpuid != -1) {
3737c478bd9Sstevel@tonic-gate 			if (curthread->t_bind_cpu != ctx->kc_cpuid) {
3747c478bd9Sstevel@tonic-gate 				kpreempt_enable();
3757c478bd9Sstevel@tonic-gate 				return (EAGAIN);
3767c478bd9Sstevel@tonic-gate 			}
3777c478bd9Sstevel@tonic-gate 		}
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 		if (ctx->kc_thread == curthread) {
3807c478bd9Sstevel@tonic-gate 			ctx->kc_hrtime = gethrtime();
3817c478bd9Sstevel@tonic-gate 			pcbe_ops->pcbe_sample(ctx);
3827c478bd9Sstevel@tonic-gate 			ctx->kc_vtick += curtick - ctx->kc_rawtick;
3837c478bd9Sstevel@tonic-gate 			ctx->kc_rawtick = curtick;
3847c478bd9Sstevel@tonic-gate 		}
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		kpreempt_enable();
3878d4e547dSae112802 
3888d4e547dSae112802 		/*
3898d4e547dSae112802 		 * The config may have been invalidated by
3908d4e547dSae112802 		 * the pcbe_sample op.
3918d4e547dSae112802 		 */
3928d4e547dSae112802 		if (ctx->kc_flags & KCPC_CTX_INVALID)
3938d4e547dSae112802 			return (EAGAIN);
3947c478bd9Sstevel@tonic-gate 	}
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	if (copyout(set->ks_data, buf,
3977c478bd9Sstevel@tonic-gate 	    set->ks_nreqs * sizeof (uint64_t)) == -1)
3987c478bd9Sstevel@tonic-gate 		return (EFAULT);
3997c478bd9Sstevel@tonic-gate 	if (copyout(&ctx->kc_hrtime, hrtime, sizeof (uint64_t)) == -1)
4007c478bd9Sstevel@tonic-gate 		return (EFAULT);
4017c478bd9Sstevel@tonic-gate 	if (copyout(&ctx->kc_vtick, tick, sizeof (uint64_t)) == -1)
4027c478bd9Sstevel@tonic-gate 		return (EFAULT);
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	return (0);
4057c478bd9Sstevel@tonic-gate }
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate /*
4087c478bd9Sstevel@tonic-gate  * Stop the counters on the CPU this context is bound to.
4097c478bd9Sstevel@tonic-gate  */
4107c478bd9Sstevel@tonic-gate static void
4117c478bd9Sstevel@tonic-gate kcpc_stop_hw(kcpc_ctx_t *ctx)
4127c478bd9Sstevel@tonic-gate {
4137c478bd9Sstevel@tonic-gate 	cpu_t *cp;
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	ASSERT((ctx->kc_flags & (KCPC_CTX_INVALID | KCPC_CTX_INVALID_STOPPED))
4167c478bd9Sstevel@tonic-gate 	    == KCPC_CTX_INVALID);
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	kpreempt_disable();
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	cp = cpu_get(ctx->kc_cpuid);
4217c478bd9Sstevel@tonic-gate 	ASSERT(cp != NULL);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	if (cp == CPU) {
4247c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_allstop();
4257c478bd9Sstevel@tonic-gate 		atomic_or_uint(&ctx->kc_flags,
4267c478bd9Sstevel@tonic-gate 		    KCPC_CTX_INVALID_STOPPED);
4277c478bd9Sstevel@tonic-gate 	} else
4287c478bd9Sstevel@tonic-gate 		kcpc_remote_stop(cp);
4297c478bd9Sstevel@tonic-gate 	kpreempt_enable();
4307c478bd9Sstevel@tonic-gate }
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate int
4337c478bd9Sstevel@tonic-gate kcpc_unbind(kcpc_set_t *set)
4347c478bd9Sstevel@tonic-gate {
4354568bee7Strevtom 	kcpc_ctx_t	*ctx;
4367c478bd9Sstevel@tonic-gate 	kthread_t	*t;
4377c478bd9Sstevel@tonic-gate 
4384568bee7Strevtom 	/*
4394568bee7Strevtom 	 * We could be racing with the process's agent thread as it
4404568bee7Strevtom 	 * binds the set; we must wait for the set to finish binding
4414568bee7Strevtom 	 * before attempting to tear it down.
4424568bee7Strevtom 	 */
4434568bee7Strevtom 	mutex_enter(&set->ks_lock);
4444568bee7Strevtom 	while ((set->ks_state & KCPC_SET_BOUND) == 0)
4454568bee7Strevtom 		cv_wait(&set->ks_condv, &set->ks_lock);
4464568bee7Strevtom 	mutex_exit(&set->ks_lock);
4477c478bd9Sstevel@tonic-gate 
4484568bee7Strevtom 	ctx = set->ks_ctx;
4494568bee7Strevtom 
4504568bee7Strevtom 	/*
4514568bee7Strevtom 	 * Use kc_lock to synchronize with kcpc_restore().
4524568bee7Strevtom 	 */
4534568bee7Strevtom 	mutex_enter(&ctx->kc_lock);
4544568bee7Strevtom 	ctx->kc_flags |= KCPC_CTX_INVALID;
4554568bee7Strevtom 	mutex_exit(&ctx->kc_lock);
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	if (ctx->kc_cpuid == -1) {
4587c478bd9Sstevel@tonic-gate 		t = ctx->kc_thread;
4597c478bd9Sstevel@tonic-gate 		/*
4607c478bd9Sstevel@tonic-gate 		 * The context is thread-bound and therefore has a device
4617c478bd9Sstevel@tonic-gate 		 * context.  It will be freed via removectx() calling
4627c478bd9Sstevel@tonic-gate 		 * freectx() calling kcpc_free().
4637c478bd9Sstevel@tonic-gate 		 */
4647c478bd9Sstevel@tonic-gate 		if (t == curthread &&
4657c478bd9Sstevel@tonic-gate 		    (ctx->kc_flags & KCPC_CTX_INVALID_STOPPED) == 0) {
4667c478bd9Sstevel@tonic-gate 			kpreempt_disable();
4677c478bd9Sstevel@tonic-gate 			pcbe_ops->pcbe_allstop();
4687c478bd9Sstevel@tonic-gate 			atomic_or_uint(&ctx->kc_flags,
4697c478bd9Sstevel@tonic-gate 			    KCPC_CTX_INVALID_STOPPED);
4707c478bd9Sstevel@tonic-gate 			kpreempt_enable();
4717c478bd9Sstevel@tonic-gate 		}
4727c478bd9Sstevel@tonic-gate #ifdef DEBUG
4737c478bd9Sstevel@tonic-gate 		if (removectx(t, ctx, kcpc_save, kcpc_restore, NULL,
4747c478bd9Sstevel@tonic-gate 		    kcpc_lwp_create, NULL, kcpc_free) == 0)
4757c478bd9Sstevel@tonic-gate 			panic("kcpc_unbind: context %p not preset on thread %p",
4768793b36bSNick Todd 			    (void *)ctx, (void *)t);
4777c478bd9Sstevel@tonic-gate #else
4787c478bd9Sstevel@tonic-gate 		(void) removectx(t, ctx, kcpc_save, kcpc_restore, NULL,
4797c478bd9Sstevel@tonic-gate 		    kcpc_lwp_create, NULL, kcpc_free);
4807c478bd9Sstevel@tonic-gate #endif /* DEBUG */
4817c478bd9Sstevel@tonic-gate 		t->t_cpc_set = NULL;
4827c478bd9Sstevel@tonic-gate 		t->t_cpc_ctx = NULL;
4837c478bd9Sstevel@tonic-gate 	} else {
4847c478bd9Sstevel@tonic-gate 		/*
4857c478bd9Sstevel@tonic-gate 		 * If we are unbinding a CPU-bound set from a remote CPU, the
4867c478bd9Sstevel@tonic-gate 		 * native CPU's idle thread could be in the midst of programming
4877c478bd9Sstevel@tonic-gate 		 * this context onto the CPU. We grab the context's lock here to
4887c478bd9Sstevel@tonic-gate 		 * ensure that the idle thread is done with it. When we release
4897c478bd9Sstevel@tonic-gate 		 * the lock, the CPU no longer has a context and the idle thread
4907c478bd9Sstevel@tonic-gate 		 * will move on.
4917c478bd9Sstevel@tonic-gate 		 *
4927c478bd9Sstevel@tonic-gate 		 * cpu_lock must be held to prevent the CPU from being DR'd out
4937c478bd9Sstevel@tonic-gate 		 * while we disassociate the context from the cpu_t.
4947c478bd9Sstevel@tonic-gate 		 */
4957c478bd9Sstevel@tonic-gate 		cpu_t *cp;
4967c478bd9Sstevel@tonic-gate 		mutex_enter(&cpu_lock);
4977c478bd9Sstevel@tonic-gate 		cp = cpu_get(ctx->kc_cpuid);
4987c478bd9Sstevel@tonic-gate 		if (cp != NULL) {
4997c478bd9Sstevel@tonic-gate 			/*
5007c478bd9Sstevel@tonic-gate 			 * The CPU may have been DR'd out of the system.
5017c478bd9Sstevel@tonic-gate 			 */
5027c478bd9Sstevel@tonic-gate 			mutex_enter(&cp->cpu_cpc_ctxlock);
5037c478bd9Sstevel@tonic-gate 			if ((ctx->kc_flags & KCPC_CTX_INVALID_STOPPED) == 0)
5047c478bd9Sstevel@tonic-gate 				kcpc_stop_hw(ctx);
5057c478bd9Sstevel@tonic-gate 			ASSERT(ctx->kc_flags & KCPC_CTX_INVALID_STOPPED);
5067c478bd9Sstevel@tonic-gate 			cp->cpu_cpc_ctx = NULL;
5077c478bd9Sstevel@tonic-gate 			mutex_exit(&cp->cpu_cpc_ctxlock);
5087c478bd9Sstevel@tonic-gate 		}
5097c478bd9Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
5107c478bd9Sstevel@tonic-gate 		if (ctx->kc_thread == curthread) {
5117c478bd9Sstevel@tonic-gate 			kcpc_free(ctx, 0);
5127c478bd9Sstevel@tonic-gate 			curthread->t_cpc_set = NULL;
5137c478bd9Sstevel@tonic-gate 		}
5147c478bd9Sstevel@tonic-gate 	}
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	return (0);
5177c478bd9Sstevel@tonic-gate }
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate int
5207c478bd9Sstevel@tonic-gate kcpc_preset(kcpc_set_t *set, int index, uint64_t preset)
5217c478bd9Sstevel@tonic-gate {
5227c478bd9Sstevel@tonic-gate 	int i;
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	ASSERT(set != NULL);
5254568bee7Strevtom 	ASSERT(set->ks_state & KCPC_SET_BOUND);
5267c478bd9Sstevel@tonic-gate 	ASSERT(set->ks_ctx->kc_thread == curthread);
5277c478bd9Sstevel@tonic-gate 	ASSERT(set->ks_ctx->kc_cpuid == -1);
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	if (index < 0 || index >= set->ks_nreqs)
5307c478bd9Sstevel@tonic-gate 		return (EINVAL);
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++)
5337c478bd9Sstevel@tonic-gate 		if (set->ks_req[i].kr_index == index)
5347c478bd9Sstevel@tonic-gate 			break;
5357c478bd9Sstevel@tonic-gate 	ASSERT(i != set->ks_nreqs);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	set->ks_req[i].kr_preset = preset;
5387c478bd9Sstevel@tonic-gate 	return (0);
5397c478bd9Sstevel@tonic-gate }
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate int
5427c478bd9Sstevel@tonic-gate kcpc_restart(kcpc_set_t *set)
5437c478bd9Sstevel@tonic-gate {
5447c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx = set->ks_ctx;
5457c478bd9Sstevel@tonic-gate 	int		i;
5467c478bd9Sstevel@tonic-gate 
5474568bee7Strevtom 	ASSERT(set->ks_state & KCPC_SET_BOUND);
5487c478bd9Sstevel@tonic-gate 	ASSERT(ctx->kc_thread == curthread);
5497c478bd9Sstevel@tonic-gate 	ASSERT(ctx->kc_cpuid == -1);
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	kpreempt_disable();
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	/*
5547c478bd9Sstevel@tonic-gate 	 * If the user is doing this on a running set, make sure the counters
5557c478bd9Sstevel@tonic-gate 	 * are stopped first.
5567c478bd9Sstevel@tonic-gate 	 */
5577c478bd9Sstevel@tonic-gate 	if ((ctx->kc_flags & KCPC_CTX_FREEZE) == 0)
5587c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_allstop();
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++) {
5617c478bd9Sstevel@tonic-gate 		*(set->ks_req[i].kr_data) = set->ks_req[i].kr_preset;
5627c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_configure(0, NULL, set->ks_req[i].kr_preset,
5637c478bd9Sstevel@tonic-gate 		    0, 0, NULL, &set->ks_req[i].kr_config, NULL);
5647c478bd9Sstevel@tonic-gate 	}
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 	/*
5677c478bd9Sstevel@tonic-gate 	 * Ask the backend to program the hardware.
5687c478bd9Sstevel@tonic-gate 	 */
5697c478bd9Sstevel@tonic-gate 	ctx->kc_rawtick = KCPC_GET_TICK();
5707c478bd9Sstevel@tonic-gate 	atomic_and_uint(&ctx->kc_flags, ~KCPC_CTX_FREEZE);
5717c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_program(ctx);
5727c478bd9Sstevel@tonic-gate 	kpreempt_enable();
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	return (0);
5757c478bd9Sstevel@tonic-gate }
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate /*
5787c478bd9Sstevel@tonic-gate  * Caller must hold kcpc_cpuctx_lock.
5797c478bd9Sstevel@tonic-gate  */
5807c478bd9Sstevel@tonic-gate int
5817c478bd9Sstevel@tonic-gate kcpc_enable(kthread_t *t, int cmd, int enable)
5827c478bd9Sstevel@tonic-gate {
5837c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx = t->t_cpc_ctx;
5847c478bd9Sstevel@tonic-gate 	kcpc_set_t	*set = t->t_cpc_set;
5857c478bd9Sstevel@tonic-gate 	kcpc_set_t	*newset;
5867c478bd9Sstevel@tonic-gate 	int		i;
5877c478bd9Sstevel@tonic-gate 	int		flag;
5887c478bd9Sstevel@tonic-gate 	int		err;
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 	ASSERT(RW_READ_HELD(&kcpc_cpuctx_lock));
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	if (ctx == NULL) {
5937c478bd9Sstevel@tonic-gate 		/*
5947c478bd9Sstevel@tonic-gate 		 * This thread has a set but no context; it must be a
5957c478bd9Sstevel@tonic-gate 		 * CPU-bound set.
5967c478bd9Sstevel@tonic-gate 		 */
5977c478bd9Sstevel@tonic-gate 		ASSERT(t->t_cpc_set != NULL);
5987c478bd9Sstevel@tonic-gate 		ASSERT(t->t_cpc_set->ks_ctx->kc_cpuid != -1);
5997c478bd9Sstevel@tonic-gate 		return (EINVAL);
6007c478bd9Sstevel@tonic-gate 	} else if (ctx->kc_flags & KCPC_CTX_INVALID)
6017c478bd9Sstevel@tonic-gate 		return (EAGAIN);
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	if (cmd == CPC_ENABLE) {
6047c478bd9Sstevel@tonic-gate 		if ((ctx->kc_flags & KCPC_CTX_FREEZE) == 0)
6057c478bd9Sstevel@tonic-gate 			return (EINVAL);
6067c478bd9Sstevel@tonic-gate 		kpreempt_disable();
6077c478bd9Sstevel@tonic-gate 		atomic_and_uint(&ctx->kc_flags, ~KCPC_CTX_FREEZE);
6087c478bd9Sstevel@tonic-gate 		kcpc_restore(ctx);
6097c478bd9Sstevel@tonic-gate 		kpreempt_enable();
6107c478bd9Sstevel@tonic-gate 	} else if (cmd == CPC_DISABLE) {
6117c478bd9Sstevel@tonic-gate 		if (ctx->kc_flags & KCPC_CTX_FREEZE)
6127c478bd9Sstevel@tonic-gate 			return (EINVAL);
6137c478bd9Sstevel@tonic-gate 		kpreempt_disable();
6147c478bd9Sstevel@tonic-gate 		kcpc_save(ctx);
6157c478bd9Sstevel@tonic-gate 		atomic_or_uint(&ctx->kc_flags, KCPC_CTX_FREEZE);
6167c478bd9Sstevel@tonic-gate 		kpreempt_enable();
6177c478bd9Sstevel@tonic-gate 	} else if (cmd == CPC_USR_EVENTS || cmd == CPC_SYS_EVENTS) {
6187c478bd9Sstevel@tonic-gate 		/*
6197c478bd9Sstevel@tonic-gate 		 * Strategy for usr/sys: stop counters and update set's presets
6207c478bd9Sstevel@tonic-gate 		 * with current counter values, unbind, update requests with
6217c478bd9Sstevel@tonic-gate 		 * new config, then re-bind.
6227c478bd9Sstevel@tonic-gate 		 */
6237c478bd9Sstevel@tonic-gate 		flag = (cmd == CPC_USR_EVENTS) ?
6247c478bd9Sstevel@tonic-gate 		    CPC_COUNT_USER: CPC_COUNT_SYSTEM;
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 		kpreempt_disable();
6277c478bd9Sstevel@tonic-gate 		atomic_or_uint(&ctx->kc_flags,
6287c478bd9Sstevel@tonic-gate 		    KCPC_CTX_INVALID | KCPC_CTX_INVALID_STOPPED);
6297c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_allstop();
6307c478bd9Sstevel@tonic-gate 		kpreempt_enable();
6317c478bd9Sstevel@tonic-gate 		for (i = 0; i < set->ks_nreqs; i++) {
6327c478bd9Sstevel@tonic-gate 			set->ks_req[i].kr_preset = *(set->ks_req[i].kr_data);
6337c478bd9Sstevel@tonic-gate 			if (enable)
6347c478bd9Sstevel@tonic-gate 				set->ks_req[i].kr_flags |= flag;
6357c478bd9Sstevel@tonic-gate 			else
6367c478bd9Sstevel@tonic-gate 				set->ks_req[i].kr_flags &= ~flag;
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 		newset = kcpc_dup_set(set);
6397c478bd9Sstevel@tonic-gate 		if (kcpc_unbind(set) != 0)
6407c478bd9Sstevel@tonic-gate 			return (EINVAL);
6417c478bd9Sstevel@tonic-gate 		t->t_cpc_set = newset;
6427c478bd9Sstevel@tonic-gate 		if (kcpc_bind_thread(newset, t, &err) != 0) {
6437c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
6447c478bd9Sstevel@tonic-gate 			kcpc_free_set(newset);
6457c478bd9Sstevel@tonic-gate 			return (EINVAL);
6467c478bd9Sstevel@tonic-gate 		}
6477c478bd9Sstevel@tonic-gate 	} else
6487c478bd9Sstevel@tonic-gate 		return (EINVAL);
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	return (0);
6517c478bd9Sstevel@tonic-gate }
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate /*
6547c478bd9Sstevel@tonic-gate  * Provide PCBEs with a way of obtaining the configs of every counter which will
6557c478bd9Sstevel@tonic-gate  * be programmed together.
6567c478bd9Sstevel@tonic-gate  *
6577c478bd9Sstevel@tonic-gate  * If current is NULL, provide the first config.
6587c478bd9Sstevel@tonic-gate  *
6597c478bd9Sstevel@tonic-gate  * If data != NULL, caller wants to know where the data store associated with
6607c478bd9Sstevel@tonic-gate  * the config we return is located.
6617c478bd9Sstevel@tonic-gate  */
6627c478bd9Sstevel@tonic-gate void *
6637c478bd9Sstevel@tonic-gate kcpc_next_config(void *token, void *current, uint64_t **data)
6647c478bd9Sstevel@tonic-gate {
6657c478bd9Sstevel@tonic-gate 	int		i;
6667c478bd9Sstevel@tonic-gate 	kcpc_pic_t	*pic;
6677c478bd9Sstevel@tonic-gate 	kcpc_ctx_t *ctx = (kcpc_ctx_t *)token;
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	if (current == NULL) {
6707c478bd9Sstevel@tonic-gate 		/*
6717c478bd9Sstevel@tonic-gate 		 * Client would like the first config, which may not be in
6727c478bd9Sstevel@tonic-gate 		 * counter 0; we need to search through the counters for the
6737c478bd9Sstevel@tonic-gate 		 * first config.
6747c478bd9Sstevel@tonic-gate 		 */
6757c478bd9Sstevel@tonic-gate 		for (i = 0; i < cpc_ncounters; i++)
6767c478bd9Sstevel@tonic-gate 			if (ctx->kc_pics[i].kp_req != NULL)
6777c478bd9Sstevel@tonic-gate 				break;
6787c478bd9Sstevel@tonic-gate 		/*
6797c478bd9Sstevel@tonic-gate 		 * There are no counters configured for the given context.
6807c478bd9Sstevel@tonic-gate 		 */
6817c478bd9Sstevel@tonic-gate 		if (i == cpc_ncounters)
6827c478bd9Sstevel@tonic-gate 			return (NULL);
6837c478bd9Sstevel@tonic-gate 	} else {
6847c478bd9Sstevel@tonic-gate 		/*
6857c478bd9Sstevel@tonic-gate 		 * There surely is a faster way to do this.
6867c478bd9Sstevel@tonic-gate 		 */
6877c478bd9Sstevel@tonic-gate 		for (i = 0; i < cpc_ncounters; i++) {
6887c478bd9Sstevel@tonic-gate 			pic = &ctx->kc_pics[i];
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 			if (pic->kp_req != NULL &&
6917c478bd9Sstevel@tonic-gate 			    current == pic->kp_req->kr_config)
6927c478bd9Sstevel@tonic-gate 				break;
6937c478bd9Sstevel@tonic-gate 		}
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 		/*
6967c478bd9Sstevel@tonic-gate 		 * We found the current config at picnum i. Now search for the
6977c478bd9Sstevel@tonic-gate 		 * next configured PIC.
6987c478bd9Sstevel@tonic-gate 		 */
6997c478bd9Sstevel@tonic-gate 		for (i++; i < cpc_ncounters; i++) {
7007c478bd9Sstevel@tonic-gate 			pic = &ctx->kc_pics[i];
7017c478bd9Sstevel@tonic-gate 			if (pic->kp_req != NULL)
7027c478bd9Sstevel@tonic-gate 				break;
7037c478bd9Sstevel@tonic-gate 		}
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 		if (i == cpc_ncounters)
7067c478bd9Sstevel@tonic-gate 			return (NULL);
7077c478bd9Sstevel@tonic-gate 	}
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 	if (data != NULL) {
7107c478bd9Sstevel@tonic-gate 		*data = ctx->kc_pics[i].kp_req->kr_data;
7117c478bd9Sstevel@tonic-gate 	}
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 	return (ctx->kc_pics[i].kp_req->kr_config);
7147c478bd9Sstevel@tonic-gate }
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 
717*b9e93c10SJonathan Haslam kcpc_ctx_t *
7187c478bd9Sstevel@tonic-gate kcpc_ctx_alloc(void)
7197c478bd9Sstevel@tonic-gate {
7207c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx;
7217c478bd9Sstevel@tonic-gate 	long		hash;
7227c478bd9Sstevel@tonic-gate 
7234568bee7Strevtom 	ctx = (kcpc_ctx_t *)kmem_zalloc(sizeof (kcpc_ctx_t), KM_SLEEP);
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	hash = CPC_HASH_CTX(ctx);
7267c478bd9Sstevel@tonic-gate 	mutex_enter(&kcpc_ctx_llock[hash]);
7277c478bd9Sstevel@tonic-gate 	ctx->kc_next = kcpc_ctx_list[hash];
7287c478bd9Sstevel@tonic-gate 	kcpc_ctx_list[hash] = ctx;
7297c478bd9Sstevel@tonic-gate 	mutex_exit(&kcpc_ctx_llock[hash]);
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	ctx->kc_pics = (kcpc_pic_t *)kmem_zalloc(sizeof (kcpc_pic_t) *
7327c478bd9Sstevel@tonic-gate 	    cpc_ncounters, KM_SLEEP);
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	ctx->kc_cpuid = -1;
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 	return (ctx);
7377c478bd9Sstevel@tonic-gate }
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate /*
7407c478bd9Sstevel@tonic-gate  * Copy set from ctx to the child context, cctx, if it has CPC_BIND_LWP_INHERIT
7417c478bd9Sstevel@tonic-gate  * in the flags.
7427c478bd9Sstevel@tonic-gate  */
7437c478bd9Sstevel@tonic-gate static void
7447c478bd9Sstevel@tonic-gate kcpc_ctx_clone(kcpc_ctx_t *ctx, kcpc_ctx_t *cctx)
7457c478bd9Sstevel@tonic-gate {
7467c478bd9Sstevel@tonic-gate 	kcpc_set_t	*ks = ctx->kc_set, *cks;
7477c478bd9Sstevel@tonic-gate 	int		i, j;
7487c478bd9Sstevel@tonic-gate 	int		code;
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	ASSERT(ks != NULL);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	if ((ks->ks_flags & CPC_BIND_LWP_INHERIT) == 0)
7537c478bd9Sstevel@tonic-gate 		return;
7547c478bd9Sstevel@tonic-gate 
7554568bee7Strevtom 	cks = kmem_zalloc(sizeof (*cks), KM_SLEEP);
7564568bee7Strevtom 	cks->ks_state &= ~KCPC_SET_BOUND;
7577c478bd9Sstevel@tonic-gate 	cctx->kc_set = cks;
7587c478bd9Sstevel@tonic-gate 	cks->ks_flags = ks->ks_flags;
7597c478bd9Sstevel@tonic-gate 	cks->ks_nreqs = ks->ks_nreqs;
7607c478bd9Sstevel@tonic-gate 	cks->ks_req = kmem_alloc(cks->ks_nreqs *
7617c478bd9Sstevel@tonic-gate 	    sizeof (kcpc_request_t), KM_SLEEP);
7627c478bd9Sstevel@tonic-gate 	cks->ks_data = kmem_alloc(cks->ks_nreqs * sizeof (uint64_t),
7637c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
7647c478bd9Sstevel@tonic-gate 	cks->ks_ctx = cctx;
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	for (i = 0; i < cks->ks_nreqs; i++) {
7677c478bd9Sstevel@tonic-gate 		cks->ks_req[i].kr_index = ks->ks_req[i].kr_index;
7687c478bd9Sstevel@tonic-gate 		cks->ks_req[i].kr_picnum = ks->ks_req[i].kr_picnum;
7697c478bd9Sstevel@tonic-gate 		(void) strncpy(cks->ks_req[i].kr_event,
7707c478bd9Sstevel@tonic-gate 		    ks->ks_req[i].kr_event, CPC_MAX_EVENT_LEN);
7717c478bd9Sstevel@tonic-gate 		cks->ks_req[i].kr_preset = ks->ks_req[i].kr_preset;
7727c478bd9Sstevel@tonic-gate 		cks->ks_req[i].kr_flags = ks->ks_req[i].kr_flags;
7737c478bd9Sstevel@tonic-gate 		cks->ks_req[i].kr_nattrs = ks->ks_req[i].kr_nattrs;
7747c478bd9Sstevel@tonic-gate 		if (ks->ks_req[i].kr_nattrs > 0) {
7757c478bd9Sstevel@tonic-gate 			cks->ks_req[i].kr_attr =
7767c478bd9Sstevel@tonic-gate 			    kmem_alloc(ks->ks_req[i].kr_nattrs *
7777c478bd9Sstevel@tonic-gate 			    sizeof (kcpc_attr_t), KM_SLEEP);
7787c478bd9Sstevel@tonic-gate 		}
7797c478bd9Sstevel@tonic-gate 		for (j = 0; j < ks->ks_req[i].kr_nattrs; j++) {
7807c478bd9Sstevel@tonic-gate 			(void) strncpy(cks->ks_req[i].kr_attr[j].ka_name,
7817c478bd9Sstevel@tonic-gate 			    ks->ks_req[i].kr_attr[j].ka_name,
7827c478bd9Sstevel@tonic-gate 			    CPC_MAX_ATTR_LEN);
7837c478bd9Sstevel@tonic-gate 			cks->ks_req[i].kr_attr[j].ka_val =
7847c478bd9Sstevel@tonic-gate 			    ks->ks_req[i].kr_attr[j].ka_val;
7857c478bd9Sstevel@tonic-gate 		}
7867c478bd9Sstevel@tonic-gate 	}
7877c478bd9Sstevel@tonic-gate 	if (kcpc_configure_reqs(cctx, cks, &code) != 0)
7888d4e547dSae112802 		kcpc_invalidate_config(cctx);
7894568bee7Strevtom 
7904568bee7Strevtom 	mutex_enter(&cks->ks_lock);
7914568bee7Strevtom 	cks->ks_state |= KCPC_SET_BOUND;
7924568bee7Strevtom 	cv_signal(&cks->ks_condv);
7934568bee7Strevtom 	mutex_exit(&cks->ks_lock);
7947c478bd9Sstevel@tonic-gate }
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 
797*b9e93c10SJonathan Haslam void
7987c478bd9Sstevel@tonic-gate kcpc_ctx_free(kcpc_ctx_t *ctx)
7997c478bd9Sstevel@tonic-gate {
8007c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	**loc;
8017c478bd9Sstevel@tonic-gate 	long		hash = CPC_HASH_CTX(ctx);
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	mutex_enter(&kcpc_ctx_llock[hash]);
8047c478bd9Sstevel@tonic-gate 	loc = &kcpc_ctx_list[hash];
8057c478bd9Sstevel@tonic-gate 	ASSERT(*loc != NULL);
8067c478bd9Sstevel@tonic-gate 	while (*loc != ctx)
8077c478bd9Sstevel@tonic-gate 		loc = &(*loc)->kc_next;
8087c478bd9Sstevel@tonic-gate 	*loc = ctx->kc_next;
8097c478bd9Sstevel@tonic-gate 	mutex_exit(&kcpc_ctx_llock[hash]);
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	kmem_free(ctx->kc_pics, cpc_ncounters * sizeof (kcpc_pic_t));
8124568bee7Strevtom 	cv_destroy(&ctx->kc_condv);
8134568bee7Strevtom 	mutex_destroy(&ctx->kc_lock);
8147c478bd9Sstevel@tonic-gate 	kmem_free(ctx, sizeof (*ctx));
8157c478bd9Sstevel@tonic-gate }
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate /*
8187c478bd9Sstevel@tonic-gate  * Generic interrupt handler used on hardware that generates
8197c478bd9Sstevel@tonic-gate  * overflow interrupts.
8207c478bd9Sstevel@tonic-gate  *
8217c478bd9Sstevel@tonic-gate  * Note: executed at high-level interrupt context!
8227c478bd9Sstevel@tonic-gate  */
8237c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8247c478bd9Sstevel@tonic-gate kcpc_ctx_t *
8257c478bd9Sstevel@tonic-gate kcpc_overflow_intr(caddr_t arg, uint64_t bitmap)
8267c478bd9Sstevel@tonic-gate {
8277c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx;
8287c478bd9Sstevel@tonic-gate 	kthread_t	*t = curthread;
8297c478bd9Sstevel@tonic-gate 	int		i;
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	/*
8327c478bd9Sstevel@tonic-gate 	 * On both x86 and UltraSPARC, we may deliver the high-level
8337c478bd9Sstevel@tonic-gate 	 * interrupt in kernel mode, just after we've started to run an
8347c478bd9Sstevel@tonic-gate 	 * interrupt thread.  (That's because the hardware helpfully
8357c478bd9Sstevel@tonic-gate 	 * delivers the overflow interrupt some random number of cycles
8367c478bd9Sstevel@tonic-gate 	 * after the instruction that caused the overflow by which time
8377c478bd9Sstevel@tonic-gate 	 * we're in some part of the kernel, not necessarily running on
8387c478bd9Sstevel@tonic-gate 	 * the right thread).
8397c478bd9Sstevel@tonic-gate 	 *
8407c478bd9Sstevel@tonic-gate 	 * Check for this case here -- find the pinned thread
8417c478bd9Sstevel@tonic-gate 	 * that was running when the interrupt went off.
8427c478bd9Sstevel@tonic-gate 	 */
8437c478bd9Sstevel@tonic-gate 	if (t->t_flag & T_INTR_THREAD) {
8447c478bd9Sstevel@tonic-gate 		klwp_t *lwp;
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 		atomic_add_32(&kcpc_intrctx_count, 1);
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 		/*
8497c478bd9Sstevel@tonic-gate 		 * Note that t_lwp is always set to point at the underlying
8507c478bd9Sstevel@tonic-gate 		 * thread, thus this will work in the presence of nested
8517c478bd9Sstevel@tonic-gate 		 * interrupts.
8527c478bd9Sstevel@tonic-gate 		 */
8537c478bd9Sstevel@tonic-gate 		ctx = NULL;
8547c478bd9Sstevel@tonic-gate 		if ((lwp = t->t_lwp) != NULL) {
8557c478bd9Sstevel@tonic-gate 			t = lwptot(lwp);
8567c478bd9Sstevel@tonic-gate 			ctx = t->t_cpc_ctx;
8577c478bd9Sstevel@tonic-gate 		}
8587c478bd9Sstevel@tonic-gate 	} else
8597c478bd9Sstevel@tonic-gate 		ctx = t->t_cpc_ctx;
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	if (ctx == NULL) {
8627c478bd9Sstevel@tonic-gate 		/*
8637c478bd9Sstevel@tonic-gate 		 * This can easily happen if we're using the counters in
8647c478bd9Sstevel@tonic-gate 		 * "shared" mode, for example, and an overflow interrupt
8657c478bd9Sstevel@tonic-gate 		 * occurs while we are running cpustat.  In that case, the
8667c478bd9Sstevel@tonic-gate 		 * bound thread that has the context that belongs to this
8677c478bd9Sstevel@tonic-gate 		 * CPU is almost certainly sleeping (if it was running on
8687c478bd9Sstevel@tonic-gate 		 * the CPU we'd have found it above), and the actual
8697c478bd9Sstevel@tonic-gate 		 * interrupted thread has no knowledge of performance counters!
8707c478bd9Sstevel@tonic-gate 		 */
8717c478bd9Sstevel@tonic-gate 		ctx = curthread->t_cpu->cpu_cpc_ctx;
8727c478bd9Sstevel@tonic-gate 		if (ctx != NULL) {
8737c478bd9Sstevel@tonic-gate 			/*
8747c478bd9Sstevel@tonic-gate 			 * Return the bound context for this CPU to
8757c478bd9Sstevel@tonic-gate 			 * the interrupt handler so that it can synchronously
8767c478bd9Sstevel@tonic-gate 			 * sample the hardware counters and restart them.
8777c478bd9Sstevel@tonic-gate 			 */
8787c478bd9Sstevel@tonic-gate 			return (ctx);
8797c478bd9Sstevel@tonic-gate 		}
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 		/*
8827c478bd9Sstevel@tonic-gate 		 * As long as the overflow interrupt really is delivered early
8837c478bd9Sstevel@tonic-gate 		 * enough after trapping into the kernel to avoid switching
8847c478bd9Sstevel@tonic-gate 		 * threads, we must always be able to find the cpc context,
8857c478bd9Sstevel@tonic-gate 		 * or something went terribly wrong i.e. we ended up
8867c478bd9Sstevel@tonic-gate 		 * running a passivated interrupt thread, a kernel
8877c478bd9Sstevel@tonic-gate 		 * thread or we interrupted idle, all of which are Very Bad.
888*b9e93c10SJonathan Haslam 		 *
889*b9e93c10SJonathan Haslam 		 * We also could end up here owing to an incredibly unlikely
890*b9e93c10SJonathan Haslam 		 * race condition that exists on x86 based architectures when
891*b9e93c10SJonathan Haslam 		 * the cpc provider is in use; overflow interrupts are directed
892*b9e93c10SJonathan Haslam 		 * to the cpc provider if the 'dtrace_cpc_in_use' variable is
893*b9e93c10SJonathan Haslam 		 * set when we enter the handler. This variable is unset after
894*b9e93c10SJonathan Haslam 		 * overflow interrupts have been disabled on all CPUs and all
895*b9e93c10SJonathan Haslam 		 * contexts have been torn down. To stop interrupts, the cpc
896*b9e93c10SJonathan Haslam 		 * provider issues a xcall to the remote CPU before it tears
897*b9e93c10SJonathan Haslam 		 * down that CPUs context. As high priority xcalls, on an x86
898*b9e93c10SJonathan Haslam 		 * architecture, execute at a higher PIL than this handler, it
899*b9e93c10SJonathan Haslam 		 * is possible (though extremely unlikely) that the xcall could
900*b9e93c10SJonathan Haslam 		 * interrupt the overflow handler before the handler has
901*b9e93c10SJonathan Haslam 		 * checked the 'dtrace_cpc_in_use' variable, stop the counters,
902*b9e93c10SJonathan Haslam 		 * return to the cpc provider which could then rip down
903*b9e93c10SJonathan Haslam 		 * contexts and unset 'dtrace_cpc_in_use' *before* the CPUs
904*b9e93c10SJonathan Haslam 		 * overflow handler has had a chance to check the variable. In
905*b9e93c10SJonathan Haslam 		 * that case, the handler would direct the overflow into this
906*b9e93c10SJonathan Haslam 		 * code and no valid context will be found. The default behavior
907*b9e93c10SJonathan Haslam 		 * when no valid context is found is now to shout a warning to
908*b9e93c10SJonathan Haslam 		 * the console and bump the 'kcpc_nullctx_count' variable.
9097c478bd9Sstevel@tonic-gate 		 */
9107c478bd9Sstevel@tonic-gate 		if (kcpc_nullctx_panic)
9117c478bd9Sstevel@tonic-gate 			panic("null cpc context, thread %p", (void *)t);
912*b9e93c10SJonathan Haslam 
913*b9e93c10SJonathan Haslam 		cmn_err(CE_WARN,
914*b9e93c10SJonathan Haslam 		    "null cpc context found in overflow handler!\n");
9157c478bd9Sstevel@tonic-gate 		atomic_add_32(&kcpc_nullctx_count, 1);
9167c478bd9Sstevel@tonic-gate 	} else if ((ctx->kc_flags & KCPC_CTX_INVALID) == 0) {
9177c478bd9Sstevel@tonic-gate 		/*
9187c478bd9Sstevel@tonic-gate 		 * Schedule an ast to sample the counters, which will
9197c478bd9Sstevel@tonic-gate 		 * propagate any overflow into the virtualized performance
9207c478bd9Sstevel@tonic-gate 		 * counter(s), and may deliver a signal.
9217c478bd9Sstevel@tonic-gate 		 */
9227c478bd9Sstevel@tonic-gate 		ttolwp(t)->lwp_pcb.pcb_flags |= CPC_OVERFLOW;
9237c478bd9Sstevel@tonic-gate 		/*
9247c478bd9Sstevel@tonic-gate 		 * If a counter has overflowed which was counting on behalf of
9257c478bd9Sstevel@tonic-gate 		 * a request which specified CPC_OVF_NOTIFY_EMT, send the
9267c478bd9Sstevel@tonic-gate 		 * process a signal.
9277c478bd9Sstevel@tonic-gate 		 */
9287c478bd9Sstevel@tonic-gate 		for (i = 0; i < cpc_ncounters; i++) {
9297c478bd9Sstevel@tonic-gate 			if (ctx->kc_pics[i].kp_req != NULL &&
9307c478bd9Sstevel@tonic-gate 			    bitmap & (1 << i) &&
9317c478bd9Sstevel@tonic-gate 			    ctx->kc_pics[i].kp_req->kr_flags &
9327c478bd9Sstevel@tonic-gate 			    CPC_OVF_NOTIFY_EMT) {
9337c478bd9Sstevel@tonic-gate 				/*
9347c478bd9Sstevel@tonic-gate 				 * A signal has been requested for this PIC, so
9357c478bd9Sstevel@tonic-gate 				 * so freeze the context. The interrupt handler
9367c478bd9Sstevel@tonic-gate 				 * has already stopped the counter hardware.
9377c478bd9Sstevel@tonic-gate 				 */
9387c478bd9Sstevel@tonic-gate 				atomic_or_uint(&ctx->kc_flags, KCPC_CTX_FREEZE);
9397c478bd9Sstevel@tonic-gate 				atomic_or_uint(&ctx->kc_pics[i].kp_flags,
9407c478bd9Sstevel@tonic-gate 				    KCPC_PIC_OVERFLOWED);
9417c478bd9Sstevel@tonic-gate 			}
9427c478bd9Sstevel@tonic-gate 		}
9437c478bd9Sstevel@tonic-gate 		aston(t);
9447c478bd9Sstevel@tonic-gate 	}
9457c478bd9Sstevel@tonic-gate 	return (NULL);
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate /*
9497c478bd9Sstevel@tonic-gate  * The current thread context had an overflow interrupt; we're
9507c478bd9Sstevel@tonic-gate  * executing here in high-level interrupt context.
9517c478bd9Sstevel@tonic-gate  */
9527c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9537c478bd9Sstevel@tonic-gate uint_t
9547c478bd9Sstevel@tonic-gate kcpc_hw_overflow_intr(caddr_t arg1, caddr_t arg2)
9557c478bd9Sstevel@tonic-gate {
9567c478bd9Sstevel@tonic-gate 	kcpc_ctx_t *ctx;
9577c478bd9Sstevel@tonic-gate 	uint64_t bitmap;
958*b9e93c10SJonathan Haslam 	uint8_t *state;
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	if (pcbe_ops == NULL ||
9617c478bd9Sstevel@tonic-gate 	    (bitmap = pcbe_ops->pcbe_overflow_bitmap()) == 0)
9627c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
963bb4f5042Sha137994 
9647c478bd9Sstevel@tonic-gate 	/*
9657c478bd9Sstevel@tonic-gate 	 * Prevent any further interrupts.
9667c478bd9Sstevel@tonic-gate 	 */
9677c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_allstop();
9687c478bd9Sstevel@tonic-gate 
969*b9e93c10SJonathan Haslam 	if (dtrace_cpc_in_use) {
970*b9e93c10SJonathan Haslam 		state = &cpu_core[CPU->cpu_id].cpuc_dcpc_intr_state;
971*b9e93c10SJonathan Haslam 
9727c478bd9Sstevel@tonic-gate 		/*
973*b9e93c10SJonathan Haslam 		 * Set the per-CPU state bit to indicate that we are currently
974*b9e93c10SJonathan Haslam 		 * processing an interrupt if it is currently free. Drop the
975*b9e93c10SJonathan Haslam 		 * interrupt if the state isn't free (i.e. a configuration
976*b9e93c10SJonathan Haslam 		 * event is taking place).
977*b9e93c10SJonathan Haslam 		 */
978*b9e93c10SJonathan Haslam 		if (atomic_cas_8(state, DCPC_INTR_FREE,
979*b9e93c10SJonathan Haslam 		    DCPC_INTR_PROCESSING) == DCPC_INTR_FREE) {
980*b9e93c10SJonathan Haslam 			int i;
981*b9e93c10SJonathan Haslam 			kcpc_request_t req;
982*b9e93c10SJonathan Haslam 
983*b9e93c10SJonathan Haslam 			ASSERT(dtrace_cpc_fire != NULL);
984*b9e93c10SJonathan Haslam 
985*b9e93c10SJonathan Haslam 			(*dtrace_cpc_fire)(bitmap);
986*b9e93c10SJonathan Haslam 
987*b9e93c10SJonathan Haslam 			ctx = curthread->t_cpu->cpu_cpc_ctx;
988*b9e93c10SJonathan Haslam 
989*b9e93c10SJonathan Haslam 			/* Reset any counters that have overflowed */
990*b9e93c10SJonathan Haslam 			for (i = 0; i < ctx->kc_set->ks_nreqs; i++) {
991*b9e93c10SJonathan Haslam 				req = ctx->kc_set->ks_req[i];
992*b9e93c10SJonathan Haslam 
993*b9e93c10SJonathan Haslam 				if (bitmap & (1 << req.kr_picnum)) {
994*b9e93c10SJonathan Haslam 					pcbe_ops->pcbe_configure(req.kr_picnum,
995*b9e93c10SJonathan Haslam 					    req.kr_event, req.kr_preset,
996*b9e93c10SJonathan Haslam 					    req.kr_flags, req.kr_nattrs,
997*b9e93c10SJonathan Haslam 					    req.kr_attr, &(req.kr_config),
998*b9e93c10SJonathan Haslam 					    (void *)ctx);
999*b9e93c10SJonathan Haslam 				}
1000*b9e93c10SJonathan Haslam 			}
1001*b9e93c10SJonathan Haslam 			pcbe_ops->pcbe_program(ctx);
1002*b9e93c10SJonathan Haslam 
1003*b9e93c10SJonathan Haslam 			/*
1004*b9e93c10SJonathan Haslam 			 * We've finished processing the interrupt so set
1005*b9e93c10SJonathan Haslam 			 * the state back to free.
1006*b9e93c10SJonathan Haslam 			 */
1007*b9e93c10SJonathan Haslam 			cpu_core[CPU->cpu_id].cpuc_dcpc_intr_state =
1008*b9e93c10SJonathan Haslam 			    DCPC_INTR_FREE;
1009*b9e93c10SJonathan Haslam 			membar_producer();
1010*b9e93c10SJonathan Haslam 		}
1011*b9e93c10SJonathan Haslam 		return (DDI_INTR_CLAIMED);
1012*b9e93c10SJonathan Haslam 	}
1013*b9e93c10SJonathan Haslam 
1014*b9e93c10SJonathan Haslam 	/*
1015*b9e93c10SJonathan Haslam 	 * DTrace isn't involved so pass on accordingly.
10167c478bd9Sstevel@tonic-gate 	 *
10177c478bd9Sstevel@tonic-gate 	 * If the interrupt has occurred in the context of an lwp owning
10187c478bd9Sstevel@tonic-gate 	 * the counters, then the handler posts an AST to the lwp to
10197c478bd9Sstevel@tonic-gate 	 * trigger the actual sampling, and optionally deliver a signal or
10207c478bd9Sstevel@tonic-gate 	 * restart the counters, on the way out of the kernel using
10217c478bd9Sstevel@tonic-gate 	 * kcpc_hw_overflow_ast() (see below).
10227c478bd9Sstevel@tonic-gate 	 *
10237c478bd9Sstevel@tonic-gate 	 * On the other hand, if the handler returns the context to us
10247c478bd9Sstevel@tonic-gate 	 * directly, then it means that there are no other threads in
10257c478bd9Sstevel@tonic-gate 	 * the middle of updating it, no AST has been posted, and so we
10267c478bd9Sstevel@tonic-gate 	 * should sample the counters here, and restart them with no
10277c478bd9Sstevel@tonic-gate 	 * further fuss.
10287c478bd9Sstevel@tonic-gate 	 */
10297c478bd9Sstevel@tonic-gate 	if ((ctx = kcpc_overflow_intr(arg1, bitmap)) != NULL) {
10307c478bd9Sstevel@tonic-gate 		uint64_t curtick = KCPC_GET_TICK();
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 		ctx->kc_hrtime = gethrtime_waitfree();
10337c478bd9Sstevel@tonic-gate 		ctx->kc_vtick += curtick - ctx->kc_rawtick;
10347c478bd9Sstevel@tonic-gate 		ctx->kc_rawtick = curtick;
10357c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_sample(ctx);
10367c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_program(ctx);
10377c478bd9Sstevel@tonic-gate 	}
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
10407c478bd9Sstevel@tonic-gate }
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate /*
10437c478bd9Sstevel@tonic-gate  * Called from trap() when processing the ast posted by the high-level
10447c478bd9Sstevel@tonic-gate  * interrupt handler.
10457c478bd9Sstevel@tonic-gate  */
10467c478bd9Sstevel@tonic-gate int
10477c478bd9Sstevel@tonic-gate kcpc_overflow_ast()
10487c478bd9Sstevel@tonic-gate {
10497c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx = curthread->t_cpc_ctx;
10507c478bd9Sstevel@tonic-gate 	int		i;
10517c478bd9Sstevel@tonic-gate 	int		found = 0;
10527c478bd9Sstevel@tonic-gate 	uint64_t	curtick = KCPC_GET_TICK();
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	ASSERT(ctx != NULL);	/* Beware of interrupt skid. */
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 	/*
10577c478bd9Sstevel@tonic-gate 	 * An overflow happened: sample the context to ensure that
10587c478bd9Sstevel@tonic-gate 	 * the overflow is propagated into the upper bits of the
10597c478bd9Sstevel@tonic-gate 	 * virtualized 64-bit counter(s).
10607c478bd9Sstevel@tonic-gate 	 */
10617c478bd9Sstevel@tonic-gate 	kpreempt_disable();
10627c478bd9Sstevel@tonic-gate 	ctx->kc_hrtime = gethrtime_waitfree();
10637c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_sample(ctx);
10647c478bd9Sstevel@tonic-gate 	kpreempt_enable();
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	ctx->kc_vtick += curtick - ctx->kc_rawtick;
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	/*
10697c478bd9Sstevel@tonic-gate 	 * The interrupt handler has marked any pics with KCPC_PIC_OVERFLOWED
10707c478bd9Sstevel@tonic-gate 	 * if that pic generated an overflow and if the request it was counting
10717c478bd9Sstevel@tonic-gate 	 * on behalf of had CPC_OVERFLOW_REQUEST specified. We go through all
10727c478bd9Sstevel@tonic-gate 	 * pics in the context and clear the KCPC_PIC_OVERFLOWED flags. If we
10737c478bd9Sstevel@tonic-gate 	 * found any overflowed pics, keep the context frozen and return true
10747c478bd9Sstevel@tonic-gate 	 * (thus causing a signal to be sent).
10757c478bd9Sstevel@tonic-gate 	 */
10767c478bd9Sstevel@tonic-gate 	for (i = 0; i < cpc_ncounters; i++) {
10777c478bd9Sstevel@tonic-gate 		if (ctx->kc_pics[i].kp_flags & KCPC_PIC_OVERFLOWED) {
10787c478bd9Sstevel@tonic-gate 			atomic_and_uint(&ctx->kc_pics[i].kp_flags,
10797c478bd9Sstevel@tonic-gate 			    ~KCPC_PIC_OVERFLOWED);
10807c478bd9Sstevel@tonic-gate 			found = 1;
10817c478bd9Sstevel@tonic-gate 		}
10827c478bd9Sstevel@tonic-gate 	}
10837c478bd9Sstevel@tonic-gate 	if (found)
10847c478bd9Sstevel@tonic-gate 		return (1);
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 	/*
10877c478bd9Sstevel@tonic-gate 	 * Otherwise, re-enable the counters and continue life as before.
10887c478bd9Sstevel@tonic-gate 	 */
10897c478bd9Sstevel@tonic-gate 	kpreempt_disable();
10907c478bd9Sstevel@tonic-gate 	atomic_and_uint(&ctx->kc_flags, ~KCPC_CTX_FREEZE);
10917c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_program(ctx);
10927c478bd9Sstevel@tonic-gate 	kpreempt_enable();
10937c478bd9Sstevel@tonic-gate 	return (0);
10947c478bd9Sstevel@tonic-gate }
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate /*
10977c478bd9Sstevel@tonic-gate  * Called when switching away from current thread.
10987c478bd9Sstevel@tonic-gate  */
10997c478bd9Sstevel@tonic-gate static void
11007c478bd9Sstevel@tonic-gate kcpc_save(kcpc_ctx_t *ctx)
11017c478bd9Sstevel@tonic-gate {
11027c478bd9Sstevel@tonic-gate 	if (ctx->kc_flags & KCPC_CTX_INVALID) {
11037c478bd9Sstevel@tonic-gate 		if (ctx->kc_flags & KCPC_CTX_INVALID_STOPPED)
11047c478bd9Sstevel@tonic-gate 			return;
11057c478bd9Sstevel@tonic-gate 		/*
11067c478bd9Sstevel@tonic-gate 		 * This context has been invalidated but the counters have not
11077c478bd9Sstevel@tonic-gate 		 * been stopped. Stop them here and mark the context stopped.
11087c478bd9Sstevel@tonic-gate 		 */
11097c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_allstop();
11107c478bd9Sstevel@tonic-gate 		atomic_or_uint(&ctx->kc_flags, KCPC_CTX_INVALID_STOPPED);
11117c478bd9Sstevel@tonic-gate 		return;
11127c478bd9Sstevel@tonic-gate 	}
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_allstop();
11157c478bd9Sstevel@tonic-gate 	if (ctx->kc_flags & KCPC_CTX_FREEZE)
11167c478bd9Sstevel@tonic-gate 		return;
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 	/*
11197c478bd9Sstevel@tonic-gate 	 * Need to sample for all reqs into each req's current mpic.
11207c478bd9Sstevel@tonic-gate 	 */
11217c478bd9Sstevel@tonic-gate 	ctx->kc_hrtime = gethrtime();
11227c478bd9Sstevel@tonic-gate 	ctx->kc_vtick += KCPC_GET_TICK() - ctx->kc_rawtick;
11237c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_sample(ctx);
11247c478bd9Sstevel@tonic-gate }
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate static void
11277c478bd9Sstevel@tonic-gate kcpc_restore(kcpc_ctx_t *ctx)
11287c478bd9Sstevel@tonic-gate {
11294568bee7Strevtom 	mutex_enter(&ctx->kc_lock);
11307c478bd9Sstevel@tonic-gate 	if ((ctx->kc_flags & (KCPC_CTX_INVALID | KCPC_CTX_INVALID_STOPPED)) ==
11317c478bd9Sstevel@tonic-gate 	    KCPC_CTX_INVALID)
11327c478bd9Sstevel@tonic-gate 		/*
11337c478bd9Sstevel@tonic-gate 		 * The context is invalidated but has not been marked stopped.
11347c478bd9Sstevel@tonic-gate 		 * We mark it as such here because we will not start the
11357c478bd9Sstevel@tonic-gate 		 * counters during this context switch.
11367c478bd9Sstevel@tonic-gate 		 */
11374568bee7Strevtom 		ctx->kc_flags |= KCPC_CTX_INVALID_STOPPED;
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 
11404568bee7Strevtom 	if (ctx->kc_flags & (KCPC_CTX_INVALID | KCPC_CTX_FREEZE)) {
11414568bee7Strevtom 		mutex_exit(&ctx->kc_lock);
11427c478bd9Sstevel@tonic-gate 		return;
11434568bee7Strevtom 	}
11444568bee7Strevtom 
11454568bee7Strevtom 	/*
11464568bee7Strevtom 	 * Set kc_flags to show that a kcpc_restore() is in progress to avoid
11474568bee7Strevtom 	 * ctx & set related memory objects being freed without us knowing.
11484568bee7Strevtom 	 * This can happen if an agent thread is executing a kcpc_unbind(),
11494568bee7Strevtom 	 * with this thread as the target, whilst we're concurrently doing a
11504568bee7Strevtom 	 * restorectx() during, for example, a proc_exit().  Effectively, by
11514568bee7Strevtom 	 * doing this, we're asking kcpc_free() to cv_wait() until
11524568bee7Strevtom 	 * kcpc_restore() has completed.
11534568bee7Strevtom 	 */
11544568bee7Strevtom 	ctx->kc_flags |= KCPC_CTX_RESTORE;
11554568bee7Strevtom 	mutex_exit(&ctx->kc_lock);
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 	/*
11587c478bd9Sstevel@tonic-gate 	 * While programming the hardware, the counters should be stopped. We
11597c478bd9Sstevel@tonic-gate 	 * don't do an explicit pcbe_allstop() here because they should have
11607c478bd9Sstevel@tonic-gate 	 * been stopped already by the last consumer.
11617c478bd9Sstevel@tonic-gate 	 */
11627c478bd9Sstevel@tonic-gate 	ctx->kc_rawtick = KCPC_GET_TICK();
11637c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_program(ctx);
11644568bee7Strevtom 
11654568bee7Strevtom 	/*
11664568bee7Strevtom 	 * Wake the agent thread if it's waiting in kcpc_free().
11674568bee7Strevtom 	 */
11684568bee7Strevtom 	mutex_enter(&ctx->kc_lock);
11694568bee7Strevtom 	ctx->kc_flags &= ~KCPC_CTX_RESTORE;
11704568bee7Strevtom 	cv_signal(&ctx->kc_condv);
11714568bee7Strevtom 	mutex_exit(&ctx->kc_lock);
11727c478bd9Sstevel@tonic-gate }
11737c478bd9Sstevel@tonic-gate 
11747c478bd9Sstevel@tonic-gate /*
11757c478bd9Sstevel@tonic-gate  * If kcpc_counts_include_idle is set to 0 by the sys admin, we add the the
11767c478bd9Sstevel@tonic-gate  * following context operators to the idle thread on each CPU. They stop the
11777c478bd9Sstevel@tonic-gate  * counters when the idle thread is switched on, and they start them again when
11787c478bd9Sstevel@tonic-gate  * it is switched off.
11797c478bd9Sstevel@tonic-gate  */
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11827c478bd9Sstevel@tonic-gate void
11837c478bd9Sstevel@tonic-gate kcpc_idle_save(struct cpu *cp)
11847c478bd9Sstevel@tonic-gate {
11857c478bd9Sstevel@tonic-gate 	/*
11867c478bd9Sstevel@tonic-gate 	 * The idle thread shouldn't be run anywhere else.
11877c478bd9Sstevel@tonic-gate 	 */
11887c478bd9Sstevel@tonic-gate 	ASSERT(CPU == cp);
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 	/*
11917c478bd9Sstevel@tonic-gate 	 * We must hold the CPU's context lock to ensure the context isn't freed
11927c478bd9Sstevel@tonic-gate 	 * while we're looking at it.
11937c478bd9Sstevel@tonic-gate 	 */
11947c478bd9Sstevel@tonic-gate 	mutex_enter(&cp->cpu_cpc_ctxlock);
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 	if ((cp->cpu_cpc_ctx == NULL) ||
11977c478bd9Sstevel@tonic-gate 	    (cp->cpu_cpc_ctx->kc_flags & KCPC_CTX_INVALID)) {
11987c478bd9Sstevel@tonic-gate 		mutex_exit(&cp->cpu_cpc_ctxlock);
11997c478bd9Sstevel@tonic-gate 		return;
12007c478bd9Sstevel@tonic-gate 	}
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_program(cp->cpu_cpc_ctx);
12037c478bd9Sstevel@tonic-gate 	mutex_exit(&cp->cpu_cpc_ctxlock);
12047c478bd9Sstevel@tonic-gate }
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate void
12077c478bd9Sstevel@tonic-gate kcpc_idle_restore(struct cpu *cp)
12087c478bd9Sstevel@tonic-gate {
12097c478bd9Sstevel@tonic-gate 	/*
12107c478bd9Sstevel@tonic-gate 	 * The idle thread shouldn't be run anywhere else.
12117c478bd9Sstevel@tonic-gate 	 */
12127c478bd9Sstevel@tonic-gate 	ASSERT(CPU == cp);
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	/*
12157c478bd9Sstevel@tonic-gate 	 * We must hold the CPU's context lock to ensure the context isn't freed
12167c478bd9Sstevel@tonic-gate 	 * while we're looking at it.
12177c478bd9Sstevel@tonic-gate 	 */
12187c478bd9Sstevel@tonic-gate 	mutex_enter(&cp->cpu_cpc_ctxlock);
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 	if ((cp->cpu_cpc_ctx == NULL) ||
12217c478bd9Sstevel@tonic-gate 	    (cp->cpu_cpc_ctx->kc_flags & KCPC_CTX_INVALID)) {
12227c478bd9Sstevel@tonic-gate 		mutex_exit(&cp->cpu_cpc_ctxlock);
12237c478bd9Sstevel@tonic-gate 		return;
12247c478bd9Sstevel@tonic-gate 	}
12257c478bd9Sstevel@tonic-gate 
12267c478bd9Sstevel@tonic-gate 	pcbe_ops->pcbe_allstop();
12277c478bd9Sstevel@tonic-gate 	mutex_exit(&cp->cpu_cpc_ctxlock);
12287c478bd9Sstevel@tonic-gate }
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate /*ARGSUSED*/
12317c478bd9Sstevel@tonic-gate static void
12327c478bd9Sstevel@tonic-gate kcpc_lwp_create(kthread_t *t, kthread_t *ct)
12337c478bd9Sstevel@tonic-gate {
12347c478bd9Sstevel@tonic-gate 	kcpc_ctx_t	*ctx = t->t_cpc_ctx, *cctx;
12357c478bd9Sstevel@tonic-gate 	int		i;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	if (ctx == NULL || (ctx->kc_flags & KCPC_CTX_LWPINHERIT) == 0)
12387c478bd9Sstevel@tonic-gate 		return;
12397c478bd9Sstevel@tonic-gate 
12407c478bd9Sstevel@tonic-gate 	rw_enter(&kcpc_cpuctx_lock, RW_READER);
12417c478bd9Sstevel@tonic-gate 	if (ctx->kc_flags & KCPC_CTX_INVALID) {
12427c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
12437c478bd9Sstevel@tonic-gate 		return;
12447c478bd9Sstevel@tonic-gate 	}
12457c478bd9Sstevel@tonic-gate 	cctx = kcpc_ctx_alloc();
12467c478bd9Sstevel@tonic-gate 	kcpc_ctx_clone(ctx, cctx);
12477c478bd9Sstevel@tonic-gate 	rw_exit(&kcpc_cpuctx_lock);
12487c478bd9Sstevel@tonic-gate 
12498d4e547dSae112802 	/*
12508d4e547dSae112802 	 * Copy the parent context's kc_flags field, but don't overwrite
12518d4e547dSae112802 	 * the child's in case it was modified during kcpc_ctx_clone.
12528d4e547dSae112802 	 */
12538d4e547dSae112802 	cctx->kc_flags |= ctx->kc_flags;
12547c478bd9Sstevel@tonic-gate 	cctx->kc_thread = ct;
12557c478bd9Sstevel@tonic-gate 	cctx->kc_cpuid = -1;
12567c478bd9Sstevel@tonic-gate 	ct->t_cpc_set = cctx->kc_set;
12577c478bd9Sstevel@tonic-gate 	ct->t_cpc_ctx = cctx;
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	if (cctx->kc_flags & KCPC_CTX_SIGOVF) {
12607c478bd9Sstevel@tonic-gate 		kcpc_set_t *ks = cctx->kc_set;
12617c478bd9Sstevel@tonic-gate 		/*
12627c478bd9Sstevel@tonic-gate 		 * Our contract with the user requires us to immediately send an
12637c478bd9Sstevel@tonic-gate 		 * overflow signal to all children if we have the LWPINHERIT
12647c478bd9Sstevel@tonic-gate 		 * and SIGOVF flags set. In addition, all counters should be
12657c478bd9Sstevel@tonic-gate 		 * set to UINT64_MAX, and their pic's overflow flag turned on
12667c478bd9Sstevel@tonic-gate 		 * so that our trap() processing knows to send a signal.
12677c478bd9Sstevel@tonic-gate 		 */
12687c478bd9Sstevel@tonic-gate 		atomic_or_uint(&cctx->kc_flags, KCPC_CTX_FREEZE);
12697c478bd9Sstevel@tonic-gate 		for (i = 0; i < ks->ks_nreqs; i++) {
12707c478bd9Sstevel@tonic-gate 			kcpc_request_t *kr = &ks->ks_req[i];
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 			if (kr->kr_flags & CPC_OVF_NOTIFY_EMT) {
12737c478bd9Sstevel@tonic-gate 				*(kr->kr_data) = UINT64_MAX;
12747c478bd9Sstevel@tonic-gate 				kr->kr_picp->kp_flags |= KCPC_PIC_OVERFLOWED;
12757c478bd9Sstevel@tonic-gate 			}
12767c478bd9Sstevel@tonic-gate 		}
12777c478bd9Sstevel@tonic-gate 		ttolwp(ct)->lwp_pcb.pcb_flags |= CPC_OVERFLOW;
12787c478bd9Sstevel@tonic-gate 		aston(ct);
12797c478bd9Sstevel@tonic-gate 	}
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate 	installctx(ct, cctx, kcpc_save, kcpc_restore,
12827c478bd9Sstevel@tonic-gate 	    NULL, kcpc_lwp_create, NULL, kcpc_free);
12837c478bd9Sstevel@tonic-gate }
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate /*
12867c478bd9Sstevel@tonic-gate  * Counter Stoppage Theory
12877c478bd9Sstevel@tonic-gate  *
12887c478bd9Sstevel@tonic-gate  * The counters may need to be stopped properly at the following occasions:
12897c478bd9Sstevel@tonic-gate  *
12907c478bd9Sstevel@tonic-gate  * 1) An LWP exits.
12917c478bd9Sstevel@tonic-gate  * 2) A thread exits.
12927c478bd9Sstevel@tonic-gate  * 3) An LWP performs an exec().
12937c478bd9Sstevel@tonic-gate  * 4) A bound set is unbound.
12947c478bd9Sstevel@tonic-gate  *
12957c478bd9Sstevel@tonic-gate  * In addition to stopping the counters, the CPC context (a kcpc_ctx_t) may need
12967c478bd9Sstevel@tonic-gate  * to be freed as well.
12977c478bd9Sstevel@tonic-gate  *
12987c478bd9Sstevel@tonic-gate  * Case 1: kcpc_passivate(), called via lwp_exit(), stops the counters. Later on
12997c478bd9Sstevel@tonic-gate  * when the thread is freed, kcpc_free(), called by freectx(), frees the
13007c478bd9Sstevel@tonic-gate  * context.
13017c478bd9Sstevel@tonic-gate  *
13027c478bd9Sstevel@tonic-gate  * Case 2: same as case 1 except kcpc_passivate is called from thread_exit().
13037c478bd9Sstevel@tonic-gate  *
13047c478bd9Sstevel@tonic-gate  * Case 3: kcpc_free(), called via freectx() via exec(), recognizes that it has
13057c478bd9Sstevel@tonic-gate  * been called from exec. It stops the counters _and_ frees the context.
13067c478bd9Sstevel@tonic-gate  *
13077c478bd9Sstevel@tonic-gate  * Case 4: kcpc_unbind() stops the hardware _and_ frees the context.
13087c478bd9Sstevel@tonic-gate  *
13097c478bd9Sstevel@tonic-gate  * CPU-bound counters are always stopped via kcpc_unbind().
13107c478bd9Sstevel@tonic-gate  */
13117c478bd9Sstevel@tonic-gate 
13127c478bd9Sstevel@tonic-gate /*
13137c478bd9Sstevel@tonic-gate  * We're being called to delete the context; we ensure that all associated data
13147c478bd9Sstevel@tonic-gate  * structures are freed, and that the hardware is passivated if this is an exec.
13157c478bd9Sstevel@tonic-gate  */
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate /*ARGSUSED*/
13187c478bd9Sstevel@tonic-gate static void
13197c478bd9Sstevel@tonic-gate kcpc_free(kcpc_ctx_t *ctx, int isexec)
13207c478bd9Sstevel@tonic-gate {
13217c478bd9Sstevel@tonic-gate 	int		i;
13227c478bd9Sstevel@tonic-gate 	kcpc_set_t	*set = ctx->kc_set;
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	ASSERT(set != NULL);
13257c478bd9Sstevel@tonic-gate 
13264568bee7Strevtom 	/*
13274568bee7Strevtom 	 * Wait for kcpc_restore() to finish before we tear things down.
13284568bee7Strevtom 	 */
13294568bee7Strevtom 	mutex_enter(&ctx->kc_lock);
13304568bee7Strevtom 	while (ctx->kc_flags & KCPC_CTX_RESTORE)
13314568bee7Strevtom 		cv_wait(&ctx->kc_condv, &ctx->kc_lock);
13324568bee7Strevtom 	ctx->kc_flags |= KCPC_CTX_INVALID;
13334568bee7Strevtom 	mutex_exit(&ctx->kc_lock);
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate 	if (isexec) {
13367c478bd9Sstevel@tonic-gate 		/*
13377c478bd9Sstevel@tonic-gate 		 * This thread is execing, and after the exec it should not have
13387c478bd9Sstevel@tonic-gate 		 * any performance counter context. Stop the counters properly
13397c478bd9Sstevel@tonic-gate 		 * here so the system isn't surprised by an overflow interrupt
13407c478bd9Sstevel@tonic-gate 		 * later.
13417c478bd9Sstevel@tonic-gate 		 */
13427c478bd9Sstevel@tonic-gate 		if (ctx->kc_cpuid != -1) {
13437c478bd9Sstevel@tonic-gate 			cpu_t *cp;
13447c478bd9Sstevel@tonic-gate 			/*
13457c478bd9Sstevel@tonic-gate 			 * CPU-bound context; stop the appropriate CPU's ctrs.
13467c478bd9Sstevel@tonic-gate 			 * Hold cpu_lock while examining the CPU to ensure it
13477c478bd9Sstevel@tonic-gate 			 * doesn't go away.
13487c478bd9Sstevel@tonic-gate 			 */
13497c478bd9Sstevel@tonic-gate 			mutex_enter(&cpu_lock);
13507c478bd9Sstevel@tonic-gate 			cp = cpu_get(ctx->kc_cpuid);
13517c478bd9Sstevel@tonic-gate 			/*
13527c478bd9Sstevel@tonic-gate 			 * The CPU could have been DR'd out, so only stop the
13537c478bd9Sstevel@tonic-gate 			 * CPU and clear its context pointer if the CPU still
13547c478bd9Sstevel@tonic-gate 			 * exists.
13557c478bd9Sstevel@tonic-gate 			 */
13567c478bd9Sstevel@tonic-gate 			if (cp != NULL) {
13577c478bd9Sstevel@tonic-gate 				mutex_enter(&cp->cpu_cpc_ctxlock);
13587c478bd9Sstevel@tonic-gate 				kcpc_stop_hw(ctx);
13597c478bd9Sstevel@tonic-gate 				cp->cpu_cpc_ctx = NULL;
13607c478bd9Sstevel@tonic-gate 				mutex_exit(&cp->cpu_cpc_ctxlock);
13617c478bd9Sstevel@tonic-gate 			}
13627c478bd9Sstevel@tonic-gate 			mutex_exit(&cpu_lock);
13637c478bd9Sstevel@tonic-gate 			ASSERT(curthread->t_cpc_ctx == NULL);
13647c478bd9Sstevel@tonic-gate 		} else {
13657c478bd9Sstevel@tonic-gate 			/*
13667c478bd9Sstevel@tonic-gate 			 * Thread-bound context; stop _this_ CPU's counters.
13677c478bd9Sstevel@tonic-gate 			 */
13687c478bd9Sstevel@tonic-gate 			kpreempt_disable();
13697c478bd9Sstevel@tonic-gate 			pcbe_ops->pcbe_allstop();
13707c478bd9Sstevel@tonic-gate 			atomic_or_uint(&ctx->kc_flags,
13717c478bd9Sstevel@tonic-gate 			    KCPC_CTX_INVALID_STOPPED);
13727c478bd9Sstevel@tonic-gate 			kpreempt_enable();
13737c478bd9Sstevel@tonic-gate 			curthread->t_cpc_ctx = NULL;
13747c478bd9Sstevel@tonic-gate 		}
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate 		/*
13777c478bd9Sstevel@tonic-gate 		 * Since we are being called from an exec and we know that
13787c478bd9Sstevel@tonic-gate 		 * exec is not permitted via the agent thread, we should clean
13797c478bd9Sstevel@tonic-gate 		 * up this thread's CPC state completely, and not leave dangling
13807c478bd9Sstevel@tonic-gate 		 * CPC pointers behind.
13817c478bd9Sstevel@tonic-gate 		 */
13827c478bd9Sstevel@tonic-gate 		ASSERT(ctx->kc_thread == curthread);
13837c478bd9Sstevel@tonic-gate 		curthread->t_cpc_set = NULL;
13847c478bd9Sstevel@tonic-gate 	}
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 	/*
13877c478bd9Sstevel@tonic-gate 	 * Walk through each request in this context's set and free the PCBE's
13887c478bd9Sstevel@tonic-gate 	 * configuration if it exists.
13897c478bd9Sstevel@tonic-gate 	 */
13907c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++) {
13917c478bd9Sstevel@tonic-gate 		if (set->ks_req[i].kr_config != NULL)
13927c478bd9Sstevel@tonic-gate 			pcbe_ops->pcbe_free(set->ks_req[i].kr_config);
13937c478bd9Sstevel@tonic-gate 	}
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
13967c478bd9Sstevel@tonic-gate 	kcpc_ctx_free(ctx);
13977c478bd9Sstevel@tonic-gate 	kcpc_free_set(set);
13987c478bd9Sstevel@tonic-gate }
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate /*
14017c478bd9Sstevel@tonic-gate  * Free the memory associated with a request set.
14027c478bd9Sstevel@tonic-gate  */
14037c478bd9Sstevel@tonic-gate void
14047c478bd9Sstevel@tonic-gate kcpc_free_set(kcpc_set_t *set)
14057c478bd9Sstevel@tonic-gate {
14067c478bd9Sstevel@tonic-gate 	int		i;
14077c478bd9Sstevel@tonic-gate 	kcpc_request_t	*req;
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 	ASSERT(set->ks_req != NULL);
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++) {
14127c478bd9Sstevel@tonic-gate 		req = &set->ks_req[i];
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 		if (req->kr_nattrs != 0) {
14157c478bd9Sstevel@tonic-gate 			kmem_free(req->kr_attr,
14167c478bd9Sstevel@tonic-gate 			    req->kr_nattrs * sizeof (kcpc_attr_t));
14177c478bd9Sstevel@tonic-gate 		}
14187c478bd9Sstevel@tonic-gate 	}
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 	kmem_free(set->ks_req, sizeof (kcpc_request_t) * set->ks_nreqs);
14214568bee7Strevtom 	cv_destroy(&set->ks_condv);
14224568bee7Strevtom 	mutex_destroy(&set->ks_lock);
14237c478bd9Sstevel@tonic-gate 	kmem_free(set, sizeof (kcpc_set_t));
14247c478bd9Sstevel@tonic-gate }
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate /*
14277c478bd9Sstevel@tonic-gate  * Grab every existing context and mark it as invalid.
14287c478bd9Sstevel@tonic-gate  */
14297c478bd9Sstevel@tonic-gate void
14307c478bd9Sstevel@tonic-gate kcpc_invalidate_all(void)
14317c478bd9Sstevel@tonic-gate {
14327c478bd9Sstevel@tonic-gate 	kcpc_ctx_t *ctx;
14337c478bd9Sstevel@tonic-gate 	long hash;
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 	for (hash = 0; hash < CPC_HASH_BUCKETS; hash++) {
14367c478bd9Sstevel@tonic-gate 		mutex_enter(&kcpc_ctx_llock[hash]);
14377c478bd9Sstevel@tonic-gate 		for (ctx = kcpc_ctx_list[hash]; ctx; ctx = ctx->kc_next)
14387c478bd9Sstevel@tonic-gate 			atomic_or_uint(&ctx->kc_flags, KCPC_CTX_INVALID);
14397c478bd9Sstevel@tonic-gate 		mutex_exit(&kcpc_ctx_llock[hash]);
14407c478bd9Sstevel@tonic-gate 	}
14417c478bd9Sstevel@tonic-gate }
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate /*
14448d4e547dSae112802  * Interface for PCBEs to signal that an existing configuration has suddenly
14458d4e547dSae112802  * become invalid.
14468d4e547dSae112802  */
14478d4e547dSae112802 void
14488d4e547dSae112802 kcpc_invalidate_config(void *token)
14498d4e547dSae112802 {
14508d4e547dSae112802 	kcpc_ctx_t *ctx = token;
14518d4e547dSae112802 
14528d4e547dSae112802 	ASSERT(ctx != NULL);
14538d4e547dSae112802 
14548d4e547dSae112802 	atomic_or_uint(&ctx->kc_flags, KCPC_CTX_INVALID);
14558d4e547dSae112802 }
14568d4e547dSae112802 
14578d4e547dSae112802 /*
14587c478bd9Sstevel@tonic-gate  * Called from lwp_exit() and thread_exit()
14597c478bd9Sstevel@tonic-gate  */
14607c478bd9Sstevel@tonic-gate void
14617c478bd9Sstevel@tonic-gate kcpc_passivate(void)
14627c478bd9Sstevel@tonic-gate {
14637c478bd9Sstevel@tonic-gate 	kcpc_ctx_t *ctx = curthread->t_cpc_ctx;
14647c478bd9Sstevel@tonic-gate 	kcpc_set_t *set = curthread->t_cpc_set;
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 	if (set == NULL)
14677c478bd9Sstevel@tonic-gate 		return;
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	/*
14707c478bd9Sstevel@tonic-gate 	 * We're cleaning up after this thread; ensure there are no dangling
14717c478bd9Sstevel@tonic-gate 	 * CPC pointers left behind. The context and set will be freed by
14727c478bd9Sstevel@tonic-gate 	 * freectx() in the case of an LWP-bound set, and by kcpc_unbind() in
14737c478bd9Sstevel@tonic-gate 	 * the case of a CPU-bound set.
14747c478bd9Sstevel@tonic-gate 	 */
14757c478bd9Sstevel@tonic-gate 	curthread->t_cpc_ctx = NULL;
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate 	if (ctx == NULL) {
14787c478bd9Sstevel@tonic-gate 		/*
14797c478bd9Sstevel@tonic-gate 		 * This thread has a set but no context; it must be a CPU-bound
14807c478bd9Sstevel@tonic-gate 		 * set. The hardware will be stopped via kcpc_unbind() when the
14817c478bd9Sstevel@tonic-gate 		 * process exits and closes its file descriptors with
14827c478bd9Sstevel@tonic-gate 		 * kcpc_close(). Our only job here is to clean up this thread's
14837c478bd9Sstevel@tonic-gate 		 * state; the set will be freed with the unbind().
14847c478bd9Sstevel@tonic-gate 		 */
14857c478bd9Sstevel@tonic-gate 		(void) kcpc_unbind(set);
14867c478bd9Sstevel@tonic-gate 		/*
14877c478bd9Sstevel@tonic-gate 		 * Unbinding a set belonging to the current thread should clear
14887c478bd9Sstevel@tonic-gate 		 * its set pointer.
14897c478bd9Sstevel@tonic-gate 		 */
14907c478bd9Sstevel@tonic-gate 		ASSERT(curthread->t_cpc_set == NULL);
14917c478bd9Sstevel@tonic-gate 		return;
14927c478bd9Sstevel@tonic-gate 	}
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate 	curthread->t_cpc_set = NULL;
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	/*
14977c478bd9Sstevel@tonic-gate 	 * This thread/LWP is exiting but context switches will continue to
14987c478bd9Sstevel@tonic-gate 	 * happen for a bit as the exit proceeds.  Kernel preemption must be
14997c478bd9Sstevel@tonic-gate 	 * disabled here to prevent a race between checking or setting the
15007c478bd9Sstevel@tonic-gate 	 * INVALID_STOPPED flag here and kcpc_restore() setting the flag during
15017c478bd9Sstevel@tonic-gate 	 * a context switch.
15027c478bd9Sstevel@tonic-gate 	 */
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate 	kpreempt_disable();
15057c478bd9Sstevel@tonic-gate 	if ((ctx->kc_flags & KCPC_CTX_INVALID_STOPPED) == 0) {
15067c478bd9Sstevel@tonic-gate 		pcbe_ops->pcbe_allstop();
15077c478bd9Sstevel@tonic-gate 		atomic_or_uint(&ctx->kc_flags,
15087c478bd9Sstevel@tonic-gate 		    KCPC_CTX_INVALID | KCPC_CTX_INVALID_STOPPED);
15097c478bd9Sstevel@tonic-gate 	}
15107c478bd9Sstevel@tonic-gate 	kpreempt_enable();
15117c478bd9Sstevel@tonic-gate }
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate /*
15147c478bd9Sstevel@tonic-gate  * Assign the requests in the given set to the PICs in the context.
15157c478bd9Sstevel@tonic-gate  * Returns 0 if successful, -1 on failure.
15167c478bd9Sstevel@tonic-gate  */
15177c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1518*b9e93c10SJonathan Haslam int
15197c478bd9Sstevel@tonic-gate kcpc_assign_reqs(kcpc_set_t *set, kcpc_ctx_t *ctx)
15207c478bd9Sstevel@tonic-gate {
15217c478bd9Sstevel@tonic-gate 	int i;
15227c478bd9Sstevel@tonic-gate 	int *picnum_save;
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 	ASSERT(set->ks_nreqs <= cpc_ncounters);
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate 	/*
15277c478bd9Sstevel@tonic-gate 	 * Provide kcpc_tryassign() with scratch space to avoid doing an
15287c478bd9Sstevel@tonic-gate 	 * alloc/free with every invocation.
15297c478bd9Sstevel@tonic-gate 	 */
15307c478bd9Sstevel@tonic-gate 	picnum_save = kmem_alloc(set->ks_nreqs * sizeof (int), KM_SLEEP);
15317c478bd9Sstevel@tonic-gate 	/*
15327c478bd9Sstevel@tonic-gate 	 * kcpc_tryassign() blindly walks through each request in the set,
15337c478bd9Sstevel@tonic-gate 	 * seeing if a counter can count its event. If yes, it assigns that
15347c478bd9Sstevel@tonic-gate 	 * counter. However, that counter may have been the only capable counter
15357c478bd9Sstevel@tonic-gate 	 * for _another_ request's event. The solution is to try every possible
15367c478bd9Sstevel@tonic-gate 	 * request first. Note that this does not cover all solutions, as
15377c478bd9Sstevel@tonic-gate 	 * that would require all unique orderings of requests, an n^n operation
15387c478bd9Sstevel@tonic-gate 	 * which would be unacceptable for architectures with many counters.
15397c478bd9Sstevel@tonic-gate 	 */
15407c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++)
15417c478bd9Sstevel@tonic-gate 		if (kcpc_tryassign(set, i, picnum_save) == 0)
15427c478bd9Sstevel@tonic-gate 			break;
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate 	kmem_free(picnum_save, set->ks_nreqs * sizeof (int));
15457c478bd9Sstevel@tonic-gate 	if (i == set->ks_nreqs)
15467c478bd9Sstevel@tonic-gate 		return (-1);
15477c478bd9Sstevel@tonic-gate 	return (0);
15487c478bd9Sstevel@tonic-gate }
15497c478bd9Sstevel@tonic-gate 
15507c478bd9Sstevel@tonic-gate static int
15517c478bd9Sstevel@tonic-gate kcpc_tryassign(kcpc_set_t *set, int starting_req, int *scratch)
15527c478bd9Sstevel@tonic-gate {
15537c478bd9Sstevel@tonic-gate 	int		i;
15547c478bd9Sstevel@tonic-gate 	int		j;
15557c478bd9Sstevel@tonic-gate 	uint64_t	bitmap = 0, resmap = 0;
15567c478bd9Sstevel@tonic-gate 	uint64_t	ctrmap;
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	/*
15597c478bd9Sstevel@tonic-gate 	 * We are attempting to assign the reqs to pics, but we may fail. If we
15607c478bd9Sstevel@tonic-gate 	 * fail, we need to restore the state of the requests to what it was
15617c478bd9Sstevel@tonic-gate 	 * when we found it, as some reqs may have been explicitly assigned to
15627c478bd9Sstevel@tonic-gate 	 * a specific PIC beforehand. We do this by snapshotting the assignments
15637c478bd9Sstevel@tonic-gate 	 * now and restoring from it later if we fail.
15647c478bd9Sstevel@tonic-gate 	 *
15657c478bd9Sstevel@tonic-gate 	 * Also we note here which counters have already been claimed by
15667c478bd9Sstevel@tonic-gate 	 * requests with explicit counter assignments.
15677c478bd9Sstevel@tonic-gate 	 */
15687c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++) {
15697c478bd9Sstevel@tonic-gate 		scratch[i] = set->ks_req[i].kr_picnum;
15707c478bd9Sstevel@tonic-gate 		if (set->ks_req[i].kr_picnum != -1)
15717c478bd9Sstevel@tonic-gate 			resmap |= (1 << set->ks_req[i].kr_picnum);
15727c478bd9Sstevel@tonic-gate 	}
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 	/*
15757c478bd9Sstevel@tonic-gate 	 * Walk through requests assigning them to the first PIC that is
15767c478bd9Sstevel@tonic-gate 	 * capable.
15777c478bd9Sstevel@tonic-gate 	 */
15787c478bd9Sstevel@tonic-gate 	i = starting_req;
15797c478bd9Sstevel@tonic-gate 	do {
15807c478bd9Sstevel@tonic-gate 		if (set->ks_req[i].kr_picnum != -1) {
15817c478bd9Sstevel@tonic-gate 			ASSERT((bitmap & (1 << set->ks_req[i].kr_picnum)) == 0);
15827c478bd9Sstevel@tonic-gate 			bitmap |= (1 << set->ks_req[i].kr_picnum);
15837c478bd9Sstevel@tonic-gate 			if (++i == set->ks_nreqs)
15847c478bd9Sstevel@tonic-gate 				i = 0;
15857c478bd9Sstevel@tonic-gate 			continue;
15867c478bd9Sstevel@tonic-gate 		}
15877c478bd9Sstevel@tonic-gate 
15887c478bd9Sstevel@tonic-gate 		ctrmap = pcbe_ops->pcbe_event_coverage(set->ks_req[i].kr_event);
15897c478bd9Sstevel@tonic-gate 		for (j = 0; j < cpc_ncounters; j++) {
15907c478bd9Sstevel@tonic-gate 			if (ctrmap & (1 << j) && (bitmap & (1 << j)) == 0 &&
15917c478bd9Sstevel@tonic-gate 			    (resmap & (1 << j)) == 0) {
15927c478bd9Sstevel@tonic-gate 				/*
15937c478bd9Sstevel@tonic-gate 				 * We can assign this counter because:
15947c478bd9Sstevel@tonic-gate 				 *
15957c478bd9Sstevel@tonic-gate 				 * 1. It can count the event (ctrmap)
15967c478bd9Sstevel@tonic-gate 				 * 2. It hasn't been assigned yet (bitmap)
15977c478bd9Sstevel@tonic-gate 				 * 3. It wasn't reserved by a request (resmap)
15987c478bd9Sstevel@tonic-gate 				 */
15997c478bd9Sstevel@tonic-gate 				bitmap |= (1 << j);
16007c478bd9Sstevel@tonic-gate 				break;
16017c478bd9Sstevel@tonic-gate 			}
16027c478bd9Sstevel@tonic-gate 		}
16037c478bd9Sstevel@tonic-gate 		if (j == cpc_ncounters) {
16047c478bd9Sstevel@tonic-gate 			for (i = 0; i < set->ks_nreqs; i++)
16057c478bd9Sstevel@tonic-gate 				set->ks_req[i].kr_picnum = scratch[i];
16067c478bd9Sstevel@tonic-gate 			return (-1);
16077c478bd9Sstevel@tonic-gate 		}
16087c478bd9Sstevel@tonic-gate 		set->ks_req[i].kr_picnum = j;
16097c478bd9Sstevel@tonic-gate 
16107c478bd9Sstevel@tonic-gate 		if (++i == set->ks_nreqs)
16117c478bd9Sstevel@tonic-gate 			i = 0;
16127c478bd9Sstevel@tonic-gate 	} while (i != starting_req);
16137c478bd9Sstevel@tonic-gate 
16147c478bd9Sstevel@tonic-gate 	return (0);
16157c478bd9Sstevel@tonic-gate }
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate kcpc_set_t *
16187c478bd9Sstevel@tonic-gate kcpc_dup_set(kcpc_set_t *set)
16197c478bd9Sstevel@tonic-gate {
16207c478bd9Sstevel@tonic-gate 	kcpc_set_t	*new;
16217c478bd9Sstevel@tonic-gate 	int		i;
16227c478bd9Sstevel@tonic-gate 	int		j;
16237c478bd9Sstevel@tonic-gate 
16244568bee7Strevtom 	new = kmem_zalloc(sizeof (*new), KM_SLEEP);
16254568bee7Strevtom 	new->ks_state &= ~KCPC_SET_BOUND;
16267c478bd9Sstevel@tonic-gate 	new->ks_flags = set->ks_flags;
16277c478bd9Sstevel@tonic-gate 	new->ks_nreqs = set->ks_nreqs;
16287c478bd9Sstevel@tonic-gate 	new->ks_req = kmem_alloc(set->ks_nreqs * sizeof (kcpc_request_t),
16297c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
16307c478bd9Sstevel@tonic-gate 	new->ks_data = NULL;
16317c478bd9Sstevel@tonic-gate 	new->ks_ctx = NULL;
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 	for (i = 0; i < new->ks_nreqs; i++) {
16347c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_config = NULL;
16357c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_index = set->ks_req[i].kr_index;
16367c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_picnum = set->ks_req[i].kr_picnum;
16377c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_picp = NULL;
16387c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_data = NULL;
16397c478bd9Sstevel@tonic-gate 		(void) strncpy(new->ks_req[i].kr_event, set->ks_req[i].kr_event,
16407c478bd9Sstevel@tonic-gate 		    CPC_MAX_EVENT_LEN);
16417c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_preset = set->ks_req[i].kr_preset;
16427c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_flags = set->ks_req[i].kr_flags;
16437c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_nattrs = set->ks_req[i].kr_nattrs;
16447c478bd9Sstevel@tonic-gate 		new->ks_req[i].kr_attr = kmem_alloc(new->ks_req[i].kr_nattrs *
16457c478bd9Sstevel@tonic-gate 		    sizeof (kcpc_attr_t), KM_SLEEP);
16467c478bd9Sstevel@tonic-gate 		for (j = 0; j < new->ks_req[i].kr_nattrs; j++) {
16477c478bd9Sstevel@tonic-gate 			new->ks_req[i].kr_attr[j].ka_val =
16487c478bd9Sstevel@tonic-gate 			    set->ks_req[i].kr_attr[j].ka_val;
16497c478bd9Sstevel@tonic-gate 			(void) strncpy(new->ks_req[i].kr_attr[j].ka_name,
16507c478bd9Sstevel@tonic-gate 			    set->ks_req[i].kr_attr[j].ka_name,
16517c478bd9Sstevel@tonic-gate 			    CPC_MAX_ATTR_LEN);
16527c478bd9Sstevel@tonic-gate 		}
16537c478bd9Sstevel@tonic-gate 	}
16547c478bd9Sstevel@tonic-gate 
16557c478bd9Sstevel@tonic-gate 	return (new);
16567c478bd9Sstevel@tonic-gate }
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate int
16597c478bd9Sstevel@tonic-gate kcpc_allow_nonpriv(void *token)
16607c478bd9Sstevel@tonic-gate {
16617c478bd9Sstevel@tonic-gate 	return (((kcpc_ctx_t *)token)->kc_flags & KCPC_CTX_NONPRIV);
16627c478bd9Sstevel@tonic-gate }
16637c478bd9Sstevel@tonic-gate 
16647c478bd9Sstevel@tonic-gate void
16657c478bd9Sstevel@tonic-gate kcpc_invalidate(kthread_t *t)
16667c478bd9Sstevel@tonic-gate {
16677c478bd9Sstevel@tonic-gate 	kcpc_ctx_t *ctx = t->t_cpc_ctx;
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 	if (ctx != NULL)
16707c478bd9Sstevel@tonic-gate 		atomic_or_uint(&ctx->kc_flags, KCPC_CTX_INVALID);
16717c478bd9Sstevel@tonic-gate }
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate /*
16747c478bd9Sstevel@tonic-gate  * Given a PCBE ID, attempt to load a matching PCBE module. The strings given
16757c478bd9Sstevel@tonic-gate  * are used to construct PCBE names, starting with the most specific,
16767c478bd9Sstevel@tonic-gate  * "pcbe.first.second.third.fourth" and ending with the least specific,
16777c478bd9Sstevel@tonic-gate  * "pcbe.first".
16787c478bd9Sstevel@tonic-gate  *
16797c478bd9Sstevel@tonic-gate  * Returns 0 if a PCBE was successfully loaded and -1 upon error.
16807c478bd9Sstevel@tonic-gate  */
16817c478bd9Sstevel@tonic-gate int
16827c478bd9Sstevel@tonic-gate kcpc_pcbe_tryload(const char *prefix, uint_t first, uint_t second, uint_t third)
16837c478bd9Sstevel@tonic-gate {
16847aec1d6eScindi 	uint_t s[3];
16857c478bd9Sstevel@tonic-gate 
16867aec1d6eScindi 	s[0] = first;
16877aec1d6eScindi 	s[1] = second;
16887aec1d6eScindi 	s[2] = third;
16897c478bd9Sstevel@tonic-gate 
16907aec1d6eScindi 	return (modload_qualified("pcbe",
169120c794b3Sgavinm 	    "pcbe", prefix, ".", s, 3, NULL) < 0 ? -1 : 0);
16927c478bd9Sstevel@tonic-gate }
1693*b9e93c10SJonathan Haslam 
1694*b9e93c10SJonathan Haslam char *
1695*b9e93c10SJonathan Haslam kcpc_list_attrs(void)
1696*b9e93c10SJonathan Haslam {
1697*b9e93c10SJonathan Haslam 	ASSERT(pcbe_ops != NULL);
1698*b9e93c10SJonathan Haslam 
1699*b9e93c10SJonathan Haslam 	return (pcbe_ops->pcbe_list_attrs());
1700*b9e93c10SJonathan Haslam }
1701*b9e93c10SJonathan Haslam 
1702*b9e93c10SJonathan Haslam char *
1703*b9e93c10SJonathan Haslam kcpc_list_events(uint_t pic)
1704*b9e93c10SJonathan Haslam {
1705*b9e93c10SJonathan Haslam 	ASSERT(pcbe_ops != NULL);
1706*b9e93c10SJonathan Haslam 
1707*b9e93c10SJonathan Haslam 	return (pcbe_ops->pcbe_list_events(pic));
1708*b9e93c10SJonathan Haslam }
1709*b9e93c10SJonathan Haslam 
1710*b9e93c10SJonathan Haslam uint_t
1711*b9e93c10SJonathan Haslam kcpc_pcbe_capabilities(void)
1712*b9e93c10SJonathan Haslam {
1713*b9e93c10SJonathan Haslam 	ASSERT(pcbe_ops != NULL);
1714*b9e93c10SJonathan Haslam 
1715*b9e93c10SJonathan Haslam 	return (pcbe_ops->pcbe_caps);
1716*b9e93c10SJonathan Haslam }
1717*b9e93c10SJonathan Haslam 
1718*b9e93c10SJonathan Haslam int
1719*b9e93c10SJonathan Haslam kcpc_pcbe_loaded(void)
1720*b9e93c10SJonathan Haslam {
1721*b9e93c10SJonathan Haslam 	return (pcbe_ops == NULL ? -1 : 0);
1722*b9e93c10SJonathan Haslam }
1723