xref: /onnv-gate/usr/src/uts/common/dtrace/dcpc.c (revision 9397:e667d620a75c)
18803SJonathan.Haslam@Sun.COM /*
28803SJonathan.Haslam@Sun.COM  * CDDL HEADER START
38803SJonathan.Haslam@Sun.COM  *
48803SJonathan.Haslam@Sun.COM  * The contents of this file are subject to the terms of the
58803SJonathan.Haslam@Sun.COM  * Common Development and Distribution License (the "License").
68803SJonathan.Haslam@Sun.COM  * You may not use this file except in compliance with the License.
78803SJonathan.Haslam@Sun.COM  *
88803SJonathan.Haslam@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98803SJonathan.Haslam@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108803SJonathan.Haslam@Sun.COM  * See the License for the specific language governing permissions
118803SJonathan.Haslam@Sun.COM  * and limitations under the License.
128803SJonathan.Haslam@Sun.COM  *
138803SJonathan.Haslam@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148803SJonathan.Haslam@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158803SJonathan.Haslam@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168803SJonathan.Haslam@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178803SJonathan.Haslam@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188803SJonathan.Haslam@Sun.COM  *
198803SJonathan.Haslam@Sun.COM  * CDDL HEADER END
208803SJonathan.Haslam@Sun.COM  */
218803SJonathan.Haslam@Sun.COM 
228803SJonathan.Haslam@Sun.COM /*
238803SJonathan.Haslam@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
248803SJonathan.Haslam@Sun.COM  * Use is subject to license terms.
258803SJonathan.Haslam@Sun.COM  */
268803SJonathan.Haslam@Sun.COM 
278803SJonathan.Haslam@Sun.COM #include <sys/errno.h>
288803SJonathan.Haslam@Sun.COM #include <sys/cpuvar.h>
298803SJonathan.Haslam@Sun.COM #include <sys/stat.h>
308803SJonathan.Haslam@Sun.COM #include <sys/modctl.h>
318803SJonathan.Haslam@Sun.COM #include <sys/cmn_err.h>
328803SJonathan.Haslam@Sun.COM #include <sys/ddi.h>
338803SJonathan.Haslam@Sun.COM #include <sys/sunddi.h>
348803SJonathan.Haslam@Sun.COM #include <sys/ksynch.h>
358803SJonathan.Haslam@Sun.COM #include <sys/conf.h>
368803SJonathan.Haslam@Sun.COM #include <sys/kmem.h>
378803SJonathan.Haslam@Sun.COM #include <sys/kcpc.h>
388803SJonathan.Haslam@Sun.COM #include <sys/cpc_pcbe.h>
398803SJonathan.Haslam@Sun.COM #include <sys/cpc_impl.h>
408803SJonathan.Haslam@Sun.COM #include <sys/dtrace_impl.h>
418803SJonathan.Haslam@Sun.COM 
428803SJonathan.Haslam@Sun.COM /*
438803SJonathan.Haslam@Sun.COM  * DTrace CPU Performance Counter Provider
448803SJonathan.Haslam@Sun.COM  * ---------------------------------------
458803SJonathan.Haslam@Sun.COM  *
468803SJonathan.Haslam@Sun.COM  * The DTrace cpc provider allows DTrace consumers to access the CPU
478803SJonathan.Haslam@Sun.COM  * performance counter overflow mechanism of a CPU. The configuration
488803SJonathan.Haslam@Sun.COM  * presented in a probe specification is programmed into the performance
498803SJonathan.Haslam@Sun.COM  * counter hardware of all available CPUs on a system. Programming the
508803SJonathan.Haslam@Sun.COM  * hardware causes a counter on each CPU to begin counting events of the
518803SJonathan.Haslam@Sun.COM  * given type. When the specified number of events have occurred, an overflow
528803SJonathan.Haslam@Sun.COM  * interrupt will be generated and the probe is fired.
538803SJonathan.Haslam@Sun.COM  *
548803SJonathan.Haslam@Sun.COM  * The required configuration for the performance counter is encoded into
558803SJonathan.Haslam@Sun.COM  * the probe specification and this includes the performance counter event
568803SJonathan.Haslam@Sun.COM  * name, processor mode, overflow rate and an optional unit mask.
578803SJonathan.Haslam@Sun.COM  *
588803SJonathan.Haslam@Sun.COM  * Most processors provide several counters (PICs) which can count all or a
598803SJonathan.Haslam@Sun.COM  * subset of the events available for a given CPU. However, when overflow
608803SJonathan.Haslam@Sun.COM  * profiling is being used, not all CPUs can detect which counter generated the
618803SJonathan.Haslam@Sun.COM  * overflow interrupt. In this case we cannot reliably determine which counter
628803SJonathan.Haslam@Sun.COM  * overflowed and we therefore only allow such CPUs to configure one event at
638803SJonathan.Haslam@Sun.COM  * a time. Processors that can determine the counter which overflowed are
648803SJonathan.Haslam@Sun.COM  * allowed to program as many events at one time as possible (in theory up to
658803SJonathan.Haslam@Sun.COM  * the number of instrumentation counters supported by that platform).
668803SJonathan.Haslam@Sun.COM  * Therefore, multiple consumers can enable multiple probes at the same time
678803SJonathan.Haslam@Sun.COM  * on such platforms. Platforms which cannot determine the source of an
688803SJonathan.Haslam@Sun.COM  * overflow interrupt are only allowed to program a single event at one time.
698803SJonathan.Haslam@Sun.COM  *
708803SJonathan.Haslam@Sun.COM  * The performance counter hardware is made available to consumers on a
718803SJonathan.Haslam@Sun.COM  * first-come, first-served basis. Only a finite amount of hardware resource
728803SJonathan.Haslam@Sun.COM  * is available and, while we make every attempt to accomodate requests from
738803SJonathan.Haslam@Sun.COM  * consumers, we must deny requests when hardware resources have been exhausted.
748803SJonathan.Haslam@Sun.COM  * A consumer will fail to enable probes when resources are currently in use.
758803SJonathan.Haslam@Sun.COM  *
768803SJonathan.Haslam@Sun.COM  * The cpc provider contends for shared hardware resources along with other
778803SJonathan.Haslam@Sun.COM  * consumers of the kernel CPU performance counter subsystem (e.g. cpustat(1M)).
788803SJonathan.Haslam@Sun.COM  * Only one such consumer can use the performance counters at any one time and
798803SJonathan.Haslam@Sun.COM  * counters are made available on a first-come, first-served basis. As with
808803SJonathan.Haslam@Sun.COM  * cpustat, the cpc provider has priority over per-LWP libcpc usage (e.g.
818803SJonathan.Haslam@Sun.COM  * cputrack(1)). Invoking the cpc provider will cause all existing per-LWP
828803SJonathan.Haslam@Sun.COM  * counter contexts to be invalidated.
838803SJonathan.Haslam@Sun.COM  */
848803SJonathan.Haslam@Sun.COM 
858803SJonathan.Haslam@Sun.COM typedef struct dcpc_probe {
868803SJonathan.Haslam@Sun.COM 	char		dcpc_event_name[CPC_MAX_EVENT_LEN];
878803SJonathan.Haslam@Sun.COM 	int		dcpc_flag;	/* flags (USER/SYS) */
888803SJonathan.Haslam@Sun.COM 	uint32_t	dcpc_ovfval;	/* overflow value */
898803SJonathan.Haslam@Sun.COM 	int64_t		dcpc_umask;	/* umask/emask for this event */
908803SJonathan.Haslam@Sun.COM 	int		dcpc_picno;	/* pic this event is programmed in */
918803SJonathan.Haslam@Sun.COM 	int		dcpc_enabled;	/* probe is actually enabled? */
928803SJonathan.Haslam@Sun.COM 	int		dcpc_disabling;	/* probe is currently being disabled */
938803SJonathan.Haslam@Sun.COM 	dtrace_id_t	dcpc_id;	/* probeid this request is enabling */
948803SJonathan.Haslam@Sun.COM 	int		dcpc_actv_req_idx;	/* idx into dcpc_actv_reqs[] */
958803SJonathan.Haslam@Sun.COM } dcpc_probe_t;
968803SJonathan.Haslam@Sun.COM 
978803SJonathan.Haslam@Sun.COM static dev_info_t			*dcpc_devi;
988803SJonathan.Haslam@Sun.COM static dtrace_provider_id_t		dcpc_pid;
998803SJonathan.Haslam@Sun.COM static dcpc_probe_t			**dcpc_actv_reqs;
1008803SJonathan.Haslam@Sun.COM static uint32_t				dcpc_enablings = 0;
1018803SJonathan.Haslam@Sun.COM static int				dcpc_ovf_mask = 0;
1028803SJonathan.Haslam@Sun.COM static int				dcpc_mult_ovf_cap = 0;
1038803SJonathan.Haslam@Sun.COM static int				dcpc_mask_type = 0;
1048803SJonathan.Haslam@Sun.COM 
1058803SJonathan.Haslam@Sun.COM /*
1068803SJonathan.Haslam@Sun.COM  * When the dcpc provider is loaded, dcpc_min_overflow is set to either
1078803SJonathan.Haslam@Sun.COM  * DCPC_MIN_OVF_DEFAULT or the value that dcpc-min-overflow is set to in
1088803SJonathan.Haslam@Sun.COM  * the dcpc.conf file. Decrease this value to set probes with smaller
1098803SJonathan.Haslam@Sun.COM  * overflow values. Remember that very small values could render a system
1108803SJonathan.Haslam@Sun.COM  * unusable with frequently occurring events.
1118803SJonathan.Haslam@Sun.COM  */
1128803SJonathan.Haslam@Sun.COM #define	DCPC_MIN_OVF_DEFAULT		5000
1138803SJonathan.Haslam@Sun.COM static uint32_t				dcpc_min_overflow;
1148803SJonathan.Haslam@Sun.COM 
1158803SJonathan.Haslam@Sun.COM static int dcpc_aframes = 0;	/* override for artificial frame setting */
1168803SJonathan.Haslam@Sun.COM #if defined(__x86)
1178803SJonathan.Haslam@Sun.COM #define	DCPC_ARTIFICIAL_FRAMES	8
1188803SJonathan.Haslam@Sun.COM #elif defined(__sparc)
1198803SJonathan.Haslam@Sun.COM #define	DCPC_ARTIFICIAL_FRAMES	2
1208803SJonathan.Haslam@Sun.COM #endif
1218803SJonathan.Haslam@Sun.COM 
1228803SJonathan.Haslam@Sun.COM /*
1238803SJonathan.Haslam@Sun.COM  * Called from the platform overflow interrupt handler. 'bitmap' is a mask
1248803SJonathan.Haslam@Sun.COM  * which contains the pic(s) that have overflowed.
1258803SJonathan.Haslam@Sun.COM  */
1268803SJonathan.Haslam@Sun.COM static void
1278803SJonathan.Haslam@Sun.COM dcpc_fire(uint64_t bitmap)
1288803SJonathan.Haslam@Sun.COM {
1298803SJonathan.Haslam@Sun.COM 	int i;
1308803SJonathan.Haslam@Sun.COM 
1318803SJonathan.Haslam@Sun.COM 	/*
1328803SJonathan.Haslam@Sun.COM 	 * No counter was marked as overflowing. Shout about it and get out.
1338803SJonathan.Haslam@Sun.COM 	 */
1348803SJonathan.Haslam@Sun.COM 	if ((bitmap & dcpc_ovf_mask) == 0) {
1358803SJonathan.Haslam@Sun.COM 		cmn_err(CE_NOTE, "dcpc_fire: no counter overflow found\n");
1368803SJonathan.Haslam@Sun.COM 		return;
1378803SJonathan.Haslam@Sun.COM 	}
1388803SJonathan.Haslam@Sun.COM 
1398803SJonathan.Haslam@Sun.COM 	/*
1408803SJonathan.Haslam@Sun.COM 	 * This is the common case of a processor that doesn't support
1418803SJonathan.Haslam@Sun.COM 	 * multiple overflow events. Such systems are only allowed a single
1428803SJonathan.Haslam@Sun.COM 	 * enabling and therefore we just look for the first entry in
1438803SJonathan.Haslam@Sun.COM 	 * the active request array.
1448803SJonathan.Haslam@Sun.COM 	 */
1458803SJonathan.Haslam@Sun.COM 	if (!dcpc_mult_ovf_cap) {
1468803SJonathan.Haslam@Sun.COM 		for (i = 0; i < cpc_ncounters; i++) {
1478803SJonathan.Haslam@Sun.COM 			if (dcpc_actv_reqs[i] != NULL) {
1488803SJonathan.Haslam@Sun.COM 				dtrace_probe(dcpc_actv_reqs[i]->dcpc_id,
1498803SJonathan.Haslam@Sun.COM 				    CPU->cpu_cpcprofile_pc,
1508803SJonathan.Haslam@Sun.COM 				    CPU->cpu_cpcprofile_upc, 0, 0, 0);
1518803SJonathan.Haslam@Sun.COM 				return;
1528803SJonathan.Haslam@Sun.COM 			}
1538803SJonathan.Haslam@Sun.COM 		}
1548803SJonathan.Haslam@Sun.COM 		return;
1558803SJonathan.Haslam@Sun.COM 	}
1568803SJonathan.Haslam@Sun.COM 
1578803SJonathan.Haslam@Sun.COM 	/*
1588803SJonathan.Haslam@Sun.COM 	 * This is a processor capable of handling multiple overflow events.
1598803SJonathan.Haslam@Sun.COM 	 * Iterate over the array of active requests and locate the counters
1608803SJonathan.Haslam@Sun.COM 	 * that overflowed (note: it is possible for more than one counter to
1618803SJonathan.Haslam@Sun.COM 	 * have overflowed at the same time).
1628803SJonathan.Haslam@Sun.COM 	 */
1638803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
1648803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] != NULL &&
1658803SJonathan.Haslam@Sun.COM 		    (bitmap & (1ULL << dcpc_actv_reqs[i]->dcpc_picno))) {
1668803SJonathan.Haslam@Sun.COM 			dtrace_probe(dcpc_actv_reqs[i]->dcpc_id,
1678803SJonathan.Haslam@Sun.COM 			    CPU->cpu_cpcprofile_pc,
1688803SJonathan.Haslam@Sun.COM 			    CPU->cpu_cpcprofile_upc, 0, 0, 0);
1698803SJonathan.Haslam@Sun.COM 		}
1708803SJonathan.Haslam@Sun.COM 	}
1718803SJonathan.Haslam@Sun.COM }
1728803SJonathan.Haslam@Sun.COM 
1738803SJonathan.Haslam@Sun.COM static void
1748803SJonathan.Haslam@Sun.COM dcpc_create_probe(dtrace_provider_id_t id, const char *probename,
1758803SJonathan.Haslam@Sun.COM     char *eventname, int64_t umask, uint32_t ovfval, char flag)
1768803SJonathan.Haslam@Sun.COM {
1778803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp;
1788803SJonathan.Haslam@Sun.COM 	int nr_frames = DCPC_ARTIFICIAL_FRAMES + dtrace_mach_aframes();
1798803SJonathan.Haslam@Sun.COM 
1808803SJonathan.Haslam@Sun.COM 	if (dcpc_aframes)
1818803SJonathan.Haslam@Sun.COM 		nr_frames = dcpc_aframes;
1828803SJonathan.Haslam@Sun.COM 
1838803SJonathan.Haslam@Sun.COM 	if (dtrace_probe_lookup(id, NULL, NULL, probename) != 0)
1848803SJonathan.Haslam@Sun.COM 		return;
1858803SJonathan.Haslam@Sun.COM 
1868803SJonathan.Haslam@Sun.COM 	pp = kmem_zalloc(sizeof (dcpc_probe_t), KM_SLEEP);
1878803SJonathan.Haslam@Sun.COM 	(void) strncpy(pp->dcpc_event_name, eventname,
1888803SJonathan.Haslam@Sun.COM 	    sizeof (pp->dcpc_event_name) - 1);
1898803SJonathan.Haslam@Sun.COM 	pp->dcpc_event_name[sizeof (pp->dcpc_event_name) - 1] = '\0';
1908803SJonathan.Haslam@Sun.COM 	pp->dcpc_flag = flag | CPC_OVF_NOTIFY_EMT;
1918803SJonathan.Haslam@Sun.COM 	pp->dcpc_ovfval = ovfval;
1928803SJonathan.Haslam@Sun.COM 	pp->dcpc_umask = umask;
1938803SJonathan.Haslam@Sun.COM 	pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1;
1948803SJonathan.Haslam@Sun.COM 
1958803SJonathan.Haslam@Sun.COM 	pp->dcpc_id = dtrace_probe_create(id, NULL, NULL, probename,
1968803SJonathan.Haslam@Sun.COM 	    nr_frames, pp);
1978803SJonathan.Haslam@Sun.COM }
1988803SJonathan.Haslam@Sun.COM 
1998803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
2008803SJonathan.Haslam@Sun.COM static void
2018803SJonathan.Haslam@Sun.COM dcpc_provide(void *arg, const dtrace_probedesc_t *desc)
2028803SJonathan.Haslam@Sun.COM {
2038803SJonathan.Haslam@Sun.COM 	/*
2048803SJonathan.Haslam@Sun.COM 	 * The format of a probe is:
2058803SJonathan.Haslam@Sun.COM 	 *
2068803SJonathan.Haslam@Sun.COM 	 *	event_name-mode-{optional_umask}-overflow_rate
2078803SJonathan.Haslam@Sun.COM 	 * e.g.
2088803SJonathan.Haslam@Sun.COM 	 *	DC_refill_from_system-user-0x1e-50000, or,
2098803SJonathan.Haslam@Sun.COM 	 *	DC_refill_from_system-all-10000
2108803SJonathan.Haslam@Sun.COM 	 *
2118803SJonathan.Haslam@Sun.COM 	 */
2128803SJonathan.Haslam@Sun.COM 	char *str, *end, *p;
2138803SJonathan.Haslam@Sun.COM 	int i, flag = 0;
2148803SJonathan.Haslam@Sun.COM 	char event[CPC_MAX_EVENT_LEN];
2158803SJonathan.Haslam@Sun.COM 	long umask = -1, val = 0;
2168803SJonathan.Haslam@Sun.COM 	size_t evlen, len;
2178803SJonathan.Haslam@Sun.COM 
2188803SJonathan.Haslam@Sun.COM 	/*
2198803SJonathan.Haslam@Sun.COM 	 * The 'cpc' provider offers no probes by default.
2208803SJonathan.Haslam@Sun.COM 	 */
2218803SJonathan.Haslam@Sun.COM 	if (desc == NULL)
2228803SJonathan.Haslam@Sun.COM 		return;
2238803SJonathan.Haslam@Sun.COM 
2248803SJonathan.Haslam@Sun.COM 	len = strlen(desc->dtpd_name);
2258803SJonathan.Haslam@Sun.COM 	p = str = kmem_alloc(len + 1, KM_SLEEP);
2268803SJonathan.Haslam@Sun.COM 	(void) strcpy(str, desc->dtpd_name);
2278803SJonathan.Haslam@Sun.COM 
2288803SJonathan.Haslam@Sun.COM 	/*
2298803SJonathan.Haslam@Sun.COM 	 * We have a poor man's strtok() going on here. Replace any hyphens
2308803SJonathan.Haslam@Sun.COM 	 * in the the probe name with NULL characters in order to make it
2318803SJonathan.Haslam@Sun.COM 	 * easy to parse the string with regular string functions.
2328803SJonathan.Haslam@Sun.COM 	 */
2338803SJonathan.Haslam@Sun.COM 	for (i = 0; i < len; i++) {
2348803SJonathan.Haslam@Sun.COM 		if (str[i] == '-')
2358803SJonathan.Haslam@Sun.COM 			str[i] = '\0';
2368803SJonathan.Haslam@Sun.COM 	}
2378803SJonathan.Haslam@Sun.COM 
2388803SJonathan.Haslam@Sun.COM 	/*
2398803SJonathan.Haslam@Sun.COM 	 * The first part of the string must be either a platform event
2408803SJonathan.Haslam@Sun.COM 	 * name or a generic event name.
2418803SJonathan.Haslam@Sun.COM 	 */
2428803SJonathan.Haslam@Sun.COM 	evlen = strlen(p);
2438803SJonathan.Haslam@Sun.COM 	(void) strncpy(event, p, CPC_MAX_EVENT_LEN - 1);
2448803SJonathan.Haslam@Sun.COM 	event[CPC_MAX_EVENT_LEN - 1] = '\0';
2458803SJonathan.Haslam@Sun.COM 
2468803SJonathan.Haslam@Sun.COM 	/*
2478803SJonathan.Haslam@Sun.COM 	 * The next part of the name is the mode specification. Valid
2488803SJonathan.Haslam@Sun.COM 	 * settings are "user", "kernel" or "all".
2498803SJonathan.Haslam@Sun.COM 	 */
2508803SJonathan.Haslam@Sun.COM 	p += evlen + 1;
2518803SJonathan.Haslam@Sun.COM 
2528803SJonathan.Haslam@Sun.COM 	if (strcmp(p, "user") == 0)
2538803SJonathan.Haslam@Sun.COM 		flag |= CPC_COUNT_USER;
2548803SJonathan.Haslam@Sun.COM 	else if (strcmp(p, "kernel") == 0)
2558803SJonathan.Haslam@Sun.COM 		flag |= CPC_COUNT_SYSTEM;
2568803SJonathan.Haslam@Sun.COM 	else if (strcmp(p, "all") == 0)
2578803SJonathan.Haslam@Sun.COM 		flag |= CPC_COUNT_USER | CPC_COUNT_SYSTEM;
2588803SJonathan.Haslam@Sun.COM 	else
2598803SJonathan.Haslam@Sun.COM 		goto err;
2608803SJonathan.Haslam@Sun.COM 
2618803SJonathan.Haslam@Sun.COM 	/*
2628803SJonathan.Haslam@Sun.COM 	 * Next we either have a mask specification followed by an overflow
2638803SJonathan.Haslam@Sun.COM 	 * rate or just an overflow rate on its own.
2648803SJonathan.Haslam@Sun.COM 	 */
2658803SJonathan.Haslam@Sun.COM 	p += strlen(p) + 1;
2668803SJonathan.Haslam@Sun.COM 	if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
2678803SJonathan.Haslam@Sun.COM 		/*
2688803SJonathan.Haslam@Sun.COM 		 * A unit mask can only be specified if:
2698803SJonathan.Haslam@Sun.COM 		 * 1) this performance counter back end supports masks.
2708803SJonathan.Haslam@Sun.COM 		 * 2) the specified event is platform specific.
2718803SJonathan.Haslam@Sun.COM 		 * 3) a valid hex number is converted.
2728803SJonathan.Haslam@Sun.COM 		 * 4) no extraneous characters follow the mask specification.
2738803SJonathan.Haslam@Sun.COM 		 */
2748803SJonathan.Haslam@Sun.COM 		if (dcpc_mask_type != 0 && strncmp(event, "PAPI", 4) != 0 &&
2758803SJonathan.Haslam@Sun.COM 		    ddi_strtol(p, &end, 16, &umask) == 0 &&
2768803SJonathan.Haslam@Sun.COM 		    end == p + strlen(p)) {
2778803SJonathan.Haslam@Sun.COM 			p += strlen(p) + 1;
2788803SJonathan.Haslam@Sun.COM 		} else {
2798803SJonathan.Haslam@Sun.COM 			goto err;
2808803SJonathan.Haslam@Sun.COM 		}
2818803SJonathan.Haslam@Sun.COM 	}
2828803SJonathan.Haslam@Sun.COM 
2838803SJonathan.Haslam@Sun.COM 	/*
2848803SJonathan.Haslam@Sun.COM 	 * This final part must be an overflow value which has to be greater
2858803SJonathan.Haslam@Sun.COM 	 * than the minimum permissible overflow rate.
2868803SJonathan.Haslam@Sun.COM 	 */
2878803SJonathan.Haslam@Sun.COM 	if ((ddi_strtol(p, &end, 10, &val) != 0) || end != p + strlen(p) ||
2888803SJonathan.Haslam@Sun.COM 	    val < dcpc_min_overflow)
2898803SJonathan.Haslam@Sun.COM 		goto err;
2908803SJonathan.Haslam@Sun.COM 
2918803SJonathan.Haslam@Sun.COM 	/*
2928803SJonathan.Haslam@Sun.COM 	 * Validate the event and create the probe.
2938803SJonathan.Haslam@Sun.COM 	 */
2948803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
295*9397SJonathan.Haslam@Sun.COM 		char *events, *cp, *p, *end;
296*9397SJonathan.Haslam@Sun.COM 		int found = 0, j;
297*9397SJonathan.Haslam@Sun.COM 		size_t llen;
298*9397SJonathan.Haslam@Sun.COM 
299*9397SJonathan.Haslam@Sun.COM 		if ((events = kcpc_list_events(i)) == NULL)
300*9397SJonathan.Haslam@Sun.COM 			goto err;
301*9397SJonathan.Haslam@Sun.COM 
302*9397SJonathan.Haslam@Sun.COM 		llen = strlen(events);
303*9397SJonathan.Haslam@Sun.COM 		p = cp = ddi_strdup(events, KM_NOSLEEP);
304*9397SJonathan.Haslam@Sun.COM 		end = cp + llen;
305*9397SJonathan.Haslam@Sun.COM 
306*9397SJonathan.Haslam@Sun.COM 		for (j = 0; j < llen; j++) {
307*9397SJonathan.Haslam@Sun.COM 			if (cp[j] == ',')
308*9397SJonathan.Haslam@Sun.COM 				cp[j] = '\0';
309*9397SJonathan.Haslam@Sun.COM 		}
310*9397SJonathan.Haslam@Sun.COM 
311*9397SJonathan.Haslam@Sun.COM 		while (p < end && found == 0) {
312*9397SJonathan.Haslam@Sun.COM 			if (strcmp(p, event) == 0) {
313*9397SJonathan.Haslam@Sun.COM 				dcpc_create_probe(dcpc_pid, desc->dtpd_name,
314*9397SJonathan.Haslam@Sun.COM 				    event, umask, (uint32_t)val, flag);
315*9397SJonathan.Haslam@Sun.COM 				found = 1;
316*9397SJonathan.Haslam@Sun.COM 			}
317*9397SJonathan.Haslam@Sun.COM 			p += strlen(p) + 1;
318*9397SJonathan.Haslam@Sun.COM 		}
319*9397SJonathan.Haslam@Sun.COM 		kmem_free(cp, llen + 1);
320*9397SJonathan.Haslam@Sun.COM 
321*9397SJonathan.Haslam@Sun.COM 		if (found)
322*9397SJonathan.Haslam@Sun.COM 			break;
3238803SJonathan.Haslam@Sun.COM 	}
3248803SJonathan.Haslam@Sun.COM 
3258803SJonathan.Haslam@Sun.COM err:
3268803SJonathan.Haslam@Sun.COM 	kmem_free(str, len + 1);
3278803SJonathan.Haslam@Sun.COM }
3288803SJonathan.Haslam@Sun.COM 
3298803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
3308803SJonathan.Haslam@Sun.COM static void
3318803SJonathan.Haslam@Sun.COM dcpc_destroy(void *arg, dtrace_id_t id, void *parg)
3328803SJonathan.Haslam@Sun.COM {
3338803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp = parg;
3348803SJonathan.Haslam@Sun.COM 
3358803SJonathan.Haslam@Sun.COM 	ASSERT(pp->dcpc_enabled == 0);
3368803SJonathan.Haslam@Sun.COM 	kmem_free(pp, sizeof (dcpc_probe_t));
3378803SJonathan.Haslam@Sun.COM }
3388803SJonathan.Haslam@Sun.COM 
3398803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
3408803SJonathan.Haslam@Sun.COM static int
3418803SJonathan.Haslam@Sun.COM dcpc_usermode(void *arg, dtrace_id_t id, void *parg)
3428803SJonathan.Haslam@Sun.COM {
3438803SJonathan.Haslam@Sun.COM 	return (CPU->cpu_cpcprofile_pc == 0);
3448803SJonathan.Haslam@Sun.COM }
3458803SJonathan.Haslam@Sun.COM 
3468803SJonathan.Haslam@Sun.COM static void
3478803SJonathan.Haslam@Sun.COM dcpc_populate_set(cpu_t *c, dcpc_probe_t *pp, kcpc_set_t *set, int reqno)
3488803SJonathan.Haslam@Sun.COM {
3498803SJonathan.Haslam@Sun.COM 	kcpc_set_t *oset;
3508803SJonathan.Haslam@Sun.COM 	int i;
3518803SJonathan.Haslam@Sun.COM 
3528803SJonathan.Haslam@Sun.COM 	(void) strncpy(set->ks_req[reqno].kr_event, pp->dcpc_event_name,
3538803SJonathan.Haslam@Sun.COM 	    CPC_MAX_EVENT_LEN);
3548803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_config = NULL;
3558803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_index = reqno;
3568803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_picnum = -1;
3578803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_flags =  pp->dcpc_flag;
3588803SJonathan.Haslam@Sun.COM 
3598803SJonathan.Haslam@Sun.COM 	/*
3608803SJonathan.Haslam@Sun.COM 	 * If a unit mask has been specified then detect which attribute
3618803SJonathan.Haslam@Sun.COM 	 * the platform needs. For now, it's either "umask" or "emask".
3628803SJonathan.Haslam@Sun.COM 	 */
3638803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_umask >= 0) {
3648803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_attr =
3658803SJonathan.Haslam@Sun.COM 		    kmem_zalloc(sizeof (kcpc_attr_t), KM_SLEEP);
3668803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_nattrs = 1;
3678803SJonathan.Haslam@Sun.COM 		if (dcpc_mask_type & DCPC_UMASK)
3688803SJonathan.Haslam@Sun.COM 			(void) strncpy(set->ks_req[reqno].kr_attr->ka_name,
3698803SJonathan.Haslam@Sun.COM 			    "umask", 5);
3708803SJonathan.Haslam@Sun.COM 		else
3718803SJonathan.Haslam@Sun.COM 			(void) strncpy(set->ks_req[reqno].kr_attr->ka_name,
3728803SJonathan.Haslam@Sun.COM 			    "emask", 5);
3738803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_attr->ka_val = pp->dcpc_umask;
3748803SJonathan.Haslam@Sun.COM 	} else {
3758803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_attr = NULL;
3768803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_nattrs = 0;
3778803SJonathan.Haslam@Sun.COM 	}
3788803SJonathan.Haslam@Sun.COM 
3798803SJonathan.Haslam@Sun.COM 	/*
3808803SJonathan.Haslam@Sun.COM 	 * If this probe is enabled, obtain its current countdown value
3818803SJonathan.Haslam@Sun.COM 	 * and use that. The CPUs cpc context might not exist yet if we
3828803SJonathan.Haslam@Sun.COM 	 * are dealing with a CPU that is just coming online.
3838803SJonathan.Haslam@Sun.COM 	 */
3848803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_enabled && (c->cpu_cpc_ctx != NULL)) {
3858803SJonathan.Haslam@Sun.COM 		oset = c->cpu_cpc_ctx->kc_set;
3868803SJonathan.Haslam@Sun.COM 
3878803SJonathan.Haslam@Sun.COM 		for (i = 0; i < oset->ks_nreqs; i++) {
3888803SJonathan.Haslam@Sun.COM 			if (strcmp(oset->ks_req[i].kr_event,
3898803SJonathan.Haslam@Sun.COM 			    set->ks_req[reqno].kr_event) == 0) {
3908803SJonathan.Haslam@Sun.COM 				set->ks_req[reqno].kr_preset =
3918803SJonathan.Haslam@Sun.COM 				    *(oset->ks_req[i].kr_data);
3928803SJonathan.Haslam@Sun.COM 			}
3938803SJonathan.Haslam@Sun.COM 		}
3948803SJonathan.Haslam@Sun.COM 	} else {
3958803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_preset = UINT64_MAX - pp->dcpc_ovfval;
3968803SJonathan.Haslam@Sun.COM 	}
3978803SJonathan.Haslam@Sun.COM 
3988803SJonathan.Haslam@Sun.COM 	set->ks_nreqs++;
3998803SJonathan.Haslam@Sun.COM }
4008803SJonathan.Haslam@Sun.COM 
4018803SJonathan.Haslam@Sun.COM 
4028803SJonathan.Haslam@Sun.COM /*
4038803SJonathan.Haslam@Sun.COM  * Create a fresh request set for the enablings represented in the
4048803SJonathan.Haslam@Sun.COM  * 'dcpc_actv_reqs' array which contains the probes we want to be
4058803SJonathan.Haslam@Sun.COM  * in the set. This can be called for several reasons:
4068803SJonathan.Haslam@Sun.COM  *
4078803SJonathan.Haslam@Sun.COM  * 1)	We are on a single or multi overflow platform and we have no
4088803SJonathan.Haslam@Sun.COM  *	current events so we can just create the set and initialize it.
4098803SJonathan.Haslam@Sun.COM  * 2)	We are on a multi-overflow platform and we already have one or
4108803SJonathan.Haslam@Sun.COM  *	more existing events and we are adding a new enabling. Create a
4118803SJonathan.Haslam@Sun.COM  *	new set and copy old requests in and then add the new request.
4128803SJonathan.Haslam@Sun.COM  * 3)	We are on a multi-overflow platform and we have just removed an
4138803SJonathan.Haslam@Sun.COM  *	enabling but we still have enablings whch are valid. Create a new
4148803SJonathan.Haslam@Sun.COM  *	set and copy in still valid requests.
4158803SJonathan.Haslam@Sun.COM  */
4168803SJonathan.Haslam@Sun.COM static kcpc_set_t *
4178803SJonathan.Haslam@Sun.COM dcpc_create_set(cpu_t *c)
4188803SJonathan.Haslam@Sun.COM {
4198803SJonathan.Haslam@Sun.COM 	int i, reqno = 0;
4208803SJonathan.Haslam@Sun.COM 	int active_requests = 0;
4218803SJonathan.Haslam@Sun.COM 	kcpc_set_t *set;
4228803SJonathan.Haslam@Sun.COM 
4238803SJonathan.Haslam@Sun.COM 	/*
4248803SJonathan.Haslam@Sun.COM 	 * First get a count of the number of currently active requests.
4258803SJonathan.Haslam@Sun.COM 	 * Note that dcpc_actv_reqs[] should always reflect which requests
4268803SJonathan.Haslam@Sun.COM 	 * we want to be in the set that is to be created. It is the
4278803SJonathan.Haslam@Sun.COM 	 * responsibility of the caller of dcpc_create_set() to adjust that
4288803SJonathan.Haslam@Sun.COM 	 * array accordingly beforehand.
4298803SJonathan.Haslam@Sun.COM 	 */
4308803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
4318803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] != NULL)
4328803SJonathan.Haslam@Sun.COM 			active_requests++;
4338803SJonathan.Haslam@Sun.COM 	}
4348803SJonathan.Haslam@Sun.COM 
4358803SJonathan.Haslam@Sun.COM 	set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP);
4368803SJonathan.Haslam@Sun.COM 
4378803SJonathan.Haslam@Sun.COM 	set->ks_req =
4388803SJonathan.Haslam@Sun.COM 	    kmem_zalloc(sizeof (kcpc_request_t) * active_requests, KM_SLEEP);
4398803SJonathan.Haslam@Sun.COM 
4408803SJonathan.Haslam@Sun.COM 	set->ks_data =
4418803SJonathan.Haslam@Sun.COM 	    kmem_zalloc(active_requests * sizeof (uint64_t), KM_SLEEP);
4428803SJonathan.Haslam@Sun.COM 
4438803SJonathan.Haslam@Sun.COM 	/*
4448803SJonathan.Haslam@Sun.COM 	 * Look for valid entries in the active requests array and populate
4458803SJonathan.Haslam@Sun.COM 	 * the request set for any entries found.
4468803SJonathan.Haslam@Sun.COM 	 */
4478803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
4488803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] != NULL) {
4498803SJonathan.Haslam@Sun.COM 			dcpc_populate_set(c, dcpc_actv_reqs[i], set, reqno);
4508803SJonathan.Haslam@Sun.COM 			reqno++;
4518803SJonathan.Haslam@Sun.COM 		}
4528803SJonathan.Haslam@Sun.COM 	}
4538803SJonathan.Haslam@Sun.COM 
4548803SJonathan.Haslam@Sun.COM 	return (set);
4558803SJonathan.Haslam@Sun.COM }
4568803SJonathan.Haslam@Sun.COM 
4578803SJonathan.Haslam@Sun.COM static int
4588803SJonathan.Haslam@Sun.COM dcpc_program_cpu_event(cpu_t *c)
4598803SJonathan.Haslam@Sun.COM {
4608803SJonathan.Haslam@Sun.COM 	int i, j, subcode;
4618803SJonathan.Haslam@Sun.COM 	kcpc_ctx_t *ctx, *octx;
4628803SJonathan.Haslam@Sun.COM 	kcpc_set_t *set;
4638803SJonathan.Haslam@Sun.COM 
4648803SJonathan.Haslam@Sun.COM 	set = dcpc_create_set(c);
4658803SJonathan.Haslam@Sun.COM 
4668803SJonathan.Haslam@Sun.COM 	octx = NULL;
4678803SJonathan.Haslam@Sun.COM 	set->ks_ctx = ctx = kcpc_ctx_alloc();
4688803SJonathan.Haslam@Sun.COM 	ctx->kc_set = set;
4698803SJonathan.Haslam@Sun.COM 	ctx->kc_cpuid = c->cpu_id;
4708803SJonathan.Haslam@Sun.COM 
4718803SJonathan.Haslam@Sun.COM 	if (kcpc_assign_reqs(set, ctx) != 0)
4728803SJonathan.Haslam@Sun.COM 		goto err;
4738803SJonathan.Haslam@Sun.COM 
4748803SJonathan.Haslam@Sun.COM 	if (kcpc_configure_reqs(ctx, set, &subcode) != 0)
4758803SJonathan.Haslam@Sun.COM 		goto err;
4768803SJonathan.Haslam@Sun.COM 
4778803SJonathan.Haslam@Sun.COM 	for (i = 0; i < set->ks_nreqs; i++) {
4788803SJonathan.Haslam@Sun.COM 		for (j = 0; j < cpc_ncounters; j++) {
4798803SJonathan.Haslam@Sun.COM 			if (dcpc_actv_reqs[j] != NULL &&
4808803SJonathan.Haslam@Sun.COM 			    strcmp(set->ks_req[i].kr_event,
4818803SJonathan.Haslam@Sun.COM 			    dcpc_actv_reqs[j]->dcpc_event_name) == 0) {
4828803SJonathan.Haslam@Sun.COM 				dcpc_actv_reqs[j]->dcpc_picno =
4838803SJonathan.Haslam@Sun.COM 				    set->ks_req[i].kr_picnum;
4848803SJonathan.Haslam@Sun.COM 			}
4858803SJonathan.Haslam@Sun.COM 		}
4868803SJonathan.Haslam@Sun.COM 	}
4878803SJonathan.Haslam@Sun.COM 
4888803SJonathan.Haslam@Sun.COM 	/*
4898803SJonathan.Haslam@Sun.COM 	 * If we already have an active enabling then save the current cpc
4908803SJonathan.Haslam@Sun.COM 	 * context away.
4918803SJonathan.Haslam@Sun.COM 	 */
4928803SJonathan.Haslam@Sun.COM 	if (c->cpu_cpc_ctx != NULL)
4938803SJonathan.Haslam@Sun.COM 		octx = c->cpu_cpc_ctx;
4948803SJonathan.Haslam@Sun.COM 
4958803SJonathan.Haslam@Sun.COM 	c->cpu_cpc_ctx = ctx;
4968803SJonathan.Haslam@Sun.COM 	kcpc_remote_program(c);
4978803SJonathan.Haslam@Sun.COM 
4988803SJonathan.Haslam@Sun.COM 	if (octx != NULL) {
4998803SJonathan.Haslam@Sun.COM 		kcpc_set_t *oset = octx->kc_set;
5008803SJonathan.Haslam@Sun.COM 		kmem_free(oset->ks_data, oset->ks_nreqs * sizeof (uint64_t));
5018803SJonathan.Haslam@Sun.COM 		kcpc_free_set(oset);
5028803SJonathan.Haslam@Sun.COM 		kcpc_ctx_free(octx);
5038803SJonathan.Haslam@Sun.COM 	}
5048803SJonathan.Haslam@Sun.COM 
5058803SJonathan.Haslam@Sun.COM 	return (0);
5068803SJonathan.Haslam@Sun.COM 
5078803SJonathan.Haslam@Sun.COM err:
5088803SJonathan.Haslam@Sun.COM 	/*
5098803SJonathan.Haslam@Sun.COM 	 * We failed to configure this request up so free things up and
5108803SJonathan.Haslam@Sun.COM 	 * get out.
5118803SJonathan.Haslam@Sun.COM 	 */
5128803SJonathan.Haslam@Sun.COM 	kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
5138803SJonathan.Haslam@Sun.COM 	kcpc_free_set(set);
5148803SJonathan.Haslam@Sun.COM 	kcpc_ctx_free(ctx);
5158803SJonathan.Haslam@Sun.COM 
5168803SJonathan.Haslam@Sun.COM 	return (-1);
5178803SJonathan.Haslam@Sun.COM }
5188803SJonathan.Haslam@Sun.COM 
5198803SJonathan.Haslam@Sun.COM static void
5208803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(cpu_t *c)
5218803SJonathan.Haslam@Sun.COM {
5228803SJonathan.Haslam@Sun.COM 	kcpc_ctx_t *ctx;
5238803SJonathan.Haslam@Sun.COM 	kcpc_set_t *set;
5248803SJonathan.Haslam@Sun.COM 
5258803SJonathan.Haslam@Sun.COM 	/*
5268803SJonathan.Haslam@Sun.COM 	 * Leave this CPU alone if it's already offline.
5278803SJonathan.Haslam@Sun.COM 	 */
5288803SJonathan.Haslam@Sun.COM 	if (c->cpu_flags & CPU_OFFLINE)
5298803SJonathan.Haslam@Sun.COM 		return;
5308803SJonathan.Haslam@Sun.COM 
5318803SJonathan.Haslam@Sun.COM 	kcpc_remote_stop(c);
5328803SJonathan.Haslam@Sun.COM 
5338803SJonathan.Haslam@Sun.COM 	ctx = c->cpu_cpc_ctx;
5348803SJonathan.Haslam@Sun.COM 	set = ctx->kc_set;
5358803SJonathan.Haslam@Sun.COM 
5368803SJonathan.Haslam@Sun.COM 	kcpc_free_configs(set);
5378803SJonathan.Haslam@Sun.COM 
5388803SJonathan.Haslam@Sun.COM 	kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
5398803SJonathan.Haslam@Sun.COM 	kcpc_free_set(set);
5408803SJonathan.Haslam@Sun.COM 	kcpc_ctx_free(ctx);
5418803SJonathan.Haslam@Sun.COM 	c->cpu_cpc_ctx = NULL;
5428803SJonathan.Haslam@Sun.COM }
5438803SJonathan.Haslam@Sun.COM 
5448803SJonathan.Haslam@Sun.COM /*
5458803SJonathan.Haslam@Sun.COM  * Stop overflow interrupts being actively processed so that per-CPU
5468803SJonathan.Haslam@Sun.COM  * configuration state can be changed safely and correctly. Each CPU has a
5478803SJonathan.Haslam@Sun.COM  * dcpc interrupt state byte which is transitioned from DCPC_INTR_FREE (the
5488803SJonathan.Haslam@Sun.COM  * "free" state) to DCPC_INTR_CONFIG (the "configuration in process" state)
5498803SJonathan.Haslam@Sun.COM  * before any configuration state is changed on any CPUs. The hardware overflow
5508803SJonathan.Haslam@Sun.COM  * handler, kcpc_hw_overflow_intr(), will only process an interrupt when a
5518803SJonathan.Haslam@Sun.COM  * configuration is not in process (i.e. the state is marked as free). During
5528803SJonathan.Haslam@Sun.COM  * interrupt processing the state is set to DCPC_INTR_PROCESSING by the
5538803SJonathan.Haslam@Sun.COM  * overflow handler.
5548803SJonathan.Haslam@Sun.COM  */
5558803SJonathan.Haslam@Sun.COM static void
5568803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(void)
5578803SJonathan.Haslam@Sun.COM {
5588803SJonathan.Haslam@Sun.COM 	cpu_t *c;
5598803SJonathan.Haslam@Sun.COM 	uint8_t *state;
5608803SJonathan.Haslam@Sun.COM 
5618803SJonathan.Haslam@Sun.COM 	c = cpu_list;
5628803SJonathan.Haslam@Sun.COM 
5638803SJonathan.Haslam@Sun.COM 	do {
5648803SJonathan.Haslam@Sun.COM 		state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state;
5658803SJonathan.Haslam@Sun.COM 
5668803SJonathan.Haslam@Sun.COM 		while (atomic_cas_8(state, DCPC_INTR_FREE,
5678803SJonathan.Haslam@Sun.COM 		    DCPC_INTR_CONFIG) != DCPC_INTR_FREE)
5688803SJonathan.Haslam@Sun.COM 			continue;
5698803SJonathan.Haslam@Sun.COM 
5708803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list);
5718803SJonathan.Haslam@Sun.COM }
5728803SJonathan.Haslam@Sun.COM 
5738803SJonathan.Haslam@Sun.COM /*
5748803SJonathan.Haslam@Sun.COM  * Set all CPUs dcpc interrupt state to DCPC_INTR_FREE to indicate that
5758803SJonathan.Haslam@Sun.COM  * overflow interrupts can be processed safely.
5768803SJonathan.Haslam@Sun.COM  */
5778803SJonathan.Haslam@Sun.COM static void
5788803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(void)
5798803SJonathan.Haslam@Sun.COM {
5808803SJonathan.Haslam@Sun.COM 	cpu_t *c = cpu_list;
5818803SJonathan.Haslam@Sun.COM 
5828803SJonathan.Haslam@Sun.COM 	do {
5838803SJonathan.Haslam@Sun.COM 		cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE;
5848803SJonathan.Haslam@Sun.COM 		membar_producer();
5858803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list);
5868803SJonathan.Haslam@Sun.COM }
5878803SJonathan.Haslam@Sun.COM 
5888803SJonathan.Haslam@Sun.COM /*
5898803SJonathan.Haslam@Sun.COM  * dcpc_program_event() can be called owing to a new enabling or if a multi
5908803SJonathan.Haslam@Sun.COM  * overflow platform has disabled a request but needs to  program the requests
5918803SJonathan.Haslam@Sun.COM  * that are still valid.
5928803SJonathan.Haslam@Sun.COM  *
5938803SJonathan.Haslam@Sun.COM  * Every invocation of dcpc_program_event() will create a new kcpc_ctx_t
5948803SJonathan.Haslam@Sun.COM  * and a new request set which contains the new enabling and any old enablings
5958803SJonathan.Haslam@Sun.COM  * which are still valid (possible with multi-overflow platforms).
5968803SJonathan.Haslam@Sun.COM  */
5978803SJonathan.Haslam@Sun.COM static int
5988803SJonathan.Haslam@Sun.COM dcpc_program_event(dcpc_probe_t *pp)
5998803SJonathan.Haslam@Sun.COM {
6008803SJonathan.Haslam@Sun.COM 	cpu_t *c;
6018803SJonathan.Haslam@Sun.COM 	int ret = 0;
6028803SJonathan.Haslam@Sun.COM 
6038803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
6048803SJonathan.Haslam@Sun.COM 
6058803SJonathan.Haslam@Sun.COM 	kpreempt_disable();
6068803SJonathan.Haslam@Sun.COM 
6078803SJonathan.Haslam@Sun.COM 	dcpc_block_interrupts();
6088803SJonathan.Haslam@Sun.COM 
6098803SJonathan.Haslam@Sun.COM 	c = cpu_list;
6108803SJonathan.Haslam@Sun.COM 
6118803SJonathan.Haslam@Sun.COM 	do {
6128803SJonathan.Haslam@Sun.COM 		/*
6138803SJonathan.Haslam@Sun.COM 		 * Skip CPUs that are currently offline.
6148803SJonathan.Haslam@Sun.COM 		 */
6158803SJonathan.Haslam@Sun.COM 		if (c->cpu_flags & CPU_OFFLINE)
6168803SJonathan.Haslam@Sun.COM 			continue;
6178803SJonathan.Haslam@Sun.COM 
6188803SJonathan.Haslam@Sun.COM 		if (c->cpu_cpc_ctx != NULL)
6198803SJonathan.Haslam@Sun.COM 			kcpc_remote_stop(c);
6208803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list);
6218803SJonathan.Haslam@Sun.COM 
6228803SJonathan.Haslam@Sun.COM 	dcpc_release_interrupts();
6238803SJonathan.Haslam@Sun.COM 
6248803SJonathan.Haslam@Sun.COM 	/*
6258803SJonathan.Haslam@Sun.COM 	 * If this enabling is being removed (in the case of a multi event
6268803SJonathan.Haslam@Sun.COM 	 * capable system with more than one active enabling), we can now
6278803SJonathan.Haslam@Sun.COM 	 * update the active request array to reflect the enablings that need
6288803SJonathan.Haslam@Sun.COM 	 * to be reprogrammed.
6298803SJonathan.Haslam@Sun.COM 	 */
6308803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_disabling == 1)
6318803SJonathan.Haslam@Sun.COM 		dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
6328803SJonathan.Haslam@Sun.COM 
6338803SJonathan.Haslam@Sun.COM 	do {
6348803SJonathan.Haslam@Sun.COM 		/*
6358803SJonathan.Haslam@Sun.COM 		 * Skip CPUs that are currently offline.
6368803SJonathan.Haslam@Sun.COM 		 */
6378803SJonathan.Haslam@Sun.COM 		if (c->cpu_flags & CPU_OFFLINE)
6388803SJonathan.Haslam@Sun.COM 			continue;
6398803SJonathan.Haslam@Sun.COM 
6408803SJonathan.Haslam@Sun.COM 		ret = dcpc_program_cpu_event(c);
6418803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list && ret == 0);
6428803SJonathan.Haslam@Sun.COM 
6438803SJonathan.Haslam@Sun.COM 	/*
6448803SJonathan.Haslam@Sun.COM 	 * If dcpc_program_cpu_event() fails then it is because we couldn't
6458803SJonathan.Haslam@Sun.COM 	 * configure the requests in the set for the CPU and not because of
6468803SJonathan.Haslam@Sun.COM 	 * an error programming the hardware. If we have a failure here then
6478803SJonathan.Haslam@Sun.COM 	 * we assume no CPUs have been programmed in the above step as they
6488803SJonathan.Haslam@Sun.COM 	 * are all configured identically.
6498803SJonathan.Haslam@Sun.COM 	 */
6508803SJonathan.Haslam@Sun.COM 	if (ret != 0) {
6518803SJonathan.Haslam@Sun.COM 		pp->dcpc_enabled = 0;
6528803SJonathan.Haslam@Sun.COM 		kpreempt_enable();
6538803SJonathan.Haslam@Sun.COM 		return (-1);
6548803SJonathan.Haslam@Sun.COM 	}
6558803SJonathan.Haslam@Sun.COM 
6568803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_disabling != 1)
6578803SJonathan.Haslam@Sun.COM 		pp->dcpc_enabled = 1;
6588803SJonathan.Haslam@Sun.COM 
6598803SJonathan.Haslam@Sun.COM 	kpreempt_enable();
6608803SJonathan.Haslam@Sun.COM 
6618803SJonathan.Haslam@Sun.COM 	return (0);
6628803SJonathan.Haslam@Sun.COM }
6638803SJonathan.Haslam@Sun.COM 
6648803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
6658803SJonathan.Haslam@Sun.COM static int
6668803SJonathan.Haslam@Sun.COM dcpc_enable(void *arg, dtrace_id_t id, void *parg)
6678803SJonathan.Haslam@Sun.COM {
6688803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp = parg;
6698803SJonathan.Haslam@Sun.COM 	int i, found = 0;
6708803SJonathan.Haslam@Sun.COM 	cpu_t *c;
6718803SJonathan.Haslam@Sun.COM 
6728803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
6738803SJonathan.Haslam@Sun.COM 
6748803SJonathan.Haslam@Sun.COM 	/*
6758803SJonathan.Haslam@Sun.COM 	 * Bail out if the counters are being used by a libcpc consumer.
6768803SJonathan.Haslam@Sun.COM 	 */
6778803SJonathan.Haslam@Sun.COM 	rw_enter(&kcpc_cpuctx_lock, RW_READER);
6788803SJonathan.Haslam@Sun.COM 	if (kcpc_cpuctx > 0) {
6798803SJonathan.Haslam@Sun.COM 		rw_exit(&kcpc_cpuctx_lock);
6808803SJonathan.Haslam@Sun.COM 		return (-1);
6818803SJonathan.Haslam@Sun.COM 	}
6828803SJonathan.Haslam@Sun.COM 
6838803SJonathan.Haslam@Sun.COM 	dtrace_cpc_in_use++;
6848803SJonathan.Haslam@Sun.COM 	rw_exit(&kcpc_cpuctx_lock);
6858803SJonathan.Haslam@Sun.COM 
6868803SJonathan.Haslam@Sun.COM 	/*
6878803SJonathan.Haslam@Sun.COM 	 * Locate this enabling in the first free entry of the active
6888803SJonathan.Haslam@Sun.COM 	 * request array.
6898803SJonathan.Haslam@Sun.COM 	 */
6908803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
6918803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] == NULL) {
6928803SJonathan.Haslam@Sun.COM 			dcpc_actv_reqs[i] = pp;
6938803SJonathan.Haslam@Sun.COM 			pp->dcpc_actv_req_idx = i;
6948803SJonathan.Haslam@Sun.COM 			found = 1;
6958803SJonathan.Haslam@Sun.COM 			break;
6968803SJonathan.Haslam@Sun.COM 		}
6978803SJonathan.Haslam@Sun.COM 	}
6988803SJonathan.Haslam@Sun.COM 
6998803SJonathan.Haslam@Sun.COM 	/*
7008803SJonathan.Haslam@Sun.COM 	 * If we couldn't find a slot for this probe then there is no
7018803SJonathan.Haslam@Sun.COM 	 * room at the inn.
7028803SJonathan.Haslam@Sun.COM 	 */
7038803SJonathan.Haslam@Sun.COM 	if (!found) {
7048803SJonathan.Haslam@Sun.COM 		dtrace_cpc_in_use--;
7058803SJonathan.Haslam@Sun.COM 		return (-1);
7068803SJonathan.Haslam@Sun.COM 	}
7078803SJonathan.Haslam@Sun.COM 
7088803SJonathan.Haslam@Sun.COM 	ASSERT(pp->dcpc_actv_req_idx >= 0);
7098803SJonathan.Haslam@Sun.COM 
7108803SJonathan.Haslam@Sun.COM 	/*
7118803SJonathan.Haslam@Sun.COM 	 * The following must hold true if we are to (attempt to) enable
7128803SJonathan.Haslam@Sun.COM 	 * this request:
7138803SJonathan.Haslam@Sun.COM 	 *
7148803SJonathan.Haslam@Sun.COM 	 * 1) No enablings currently exist. We allow all platforms to
7158803SJonathan.Haslam@Sun.COM 	 * proceed if this is true.
7168803SJonathan.Haslam@Sun.COM 	 *
7178803SJonathan.Haslam@Sun.COM 	 * OR
7188803SJonathan.Haslam@Sun.COM 	 *
7198803SJonathan.Haslam@Sun.COM 	 * 2) If the platform is multi overflow capable and there are
7208803SJonathan.Haslam@Sun.COM 	 * less valid enablings than there are counters. There is no
7218803SJonathan.Haslam@Sun.COM 	 * guarantee that a platform can accommodate as many events as
7228803SJonathan.Haslam@Sun.COM 	 * it has counters for but we will at least try to program
7238803SJonathan.Haslam@Sun.COM 	 * up to that many requests.
7248803SJonathan.Haslam@Sun.COM 	 *
7258803SJonathan.Haslam@Sun.COM 	 * The 'dcpc_enablings' variable is implictly protected by locking
7268803SJonathan.Haslam@Sun.COM 	 * provided by the DTrace framework and the cpu management framework.
7278803SJonathan.Haslam@Sun.COM 	 */
7288803SJonathan.Haslam@Sun.COM 	if (dcpc_enablings == 0 || (dcpc_mult_ovf_cap &&
7298803SJonathan.Haslam@Sun.COM 	    dcpc_enablings < cpc_ncounters)) {
7308803SJonathan.Haslam@Sun.COM 		/*
7318803SJonathan.Haslam@Sun.COM 		 * Before attempting to program the first enabling we need to
7328803SJonathan.Haslam@Sun.COM 		 * invalidate any lwp-based contexts.
7338803SJonathan.Haslam@Sun.COM 		 */
7348803SJonathan.Haslam@Sun.COM 		if (dcpc_enablings == 0)
7358803SJonathan.Haslam@Sun.COM 			kcpc_invalidate_all();
7368803SJonathan.Haslam@Sun.COM 
7378803SJonathan.Haslam@Sun.COM 		if (dcpc_program_event(pp) == 0) {
7388803SJonathan.Haslam@Sun.COM 			dcpc_enablings++;
7398803SJonathan.Haslam@Sun.COM 			return (0);
7408803SJonathan.Haslam@Sun.COM 		}
7418803SJonathan.Haslam@Sun.COM 	}
7428803SJonathan.Haslam@Sun.COM 
7438803SJonathan.Haslam@Sun.COM 	/*
7448803SJonathan.Haslam@Sun.COM 	 * If active enablings existed before we failed to enable this probe
7458803SJonathan.Haslam@Sun.COM 	 * on a multi event capable platform then we need to restart counters
7468803SJonathan.Haslam@Sun.COM 	 * as they will have been stopped in the attempted configuration. The
7478803SJonathan.Haslam@Sun.COM 	 * context should now just contain the request prior to this failed
7488803SJonathan.Haslam@Sun.COM 	 * enabling.
7498803SJonathan.Haslam@Sun.COM 	 */
7508803SJonathan.Haslam@Sun.COM 	if (dcpc_enablings > 0 && dcpc_mult_ovf_cap) {
7518803SJonathan.Haslam@Sun.COM 		c = cpu_list;
7528803SJonathan.Haslam@Sun.COM 
7538803SJonathan.Haslam@Sun.COM 		ASSERT(dcpc_mult_ovf_cap == 1);
7548803SJonathan.Haslam@Sun.COM 		do {
7558803SJonathan.Haslam@Sun.COM 			/*
7568803SJonathan.Haslam@Sun.COM 			 * Skip CPUs that are currently offline.
7578803SJonathan.Haslam@Sun.COM 			 */
7588803SJonathan.Haslam@Sun.COM 			if (c->cpu_flags & CPU_OFFLINE)
7598803SJonathan.Haslam@Sun.COM 				continue;
7608803SJonathan.Haslam@Sun.COM 
7618803SJonathan.Haslam@Sun.COM 			kcpc_remote_program(c);
7628803SJonathan.Haslam@Sun.COM 		} while ((c = c->cpu_next) != cpu_list);
7638803SJonathan.Haslam@Sun.COM 	}
7648803SJonathan.Haslam@Sun.COM 
7658803SJonathan.Haslam@Sun.COM 	dtrace_cpc_in_use--;
7668803SJonathan.Haslam@Sun.COM 	dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
7678803SJonathan.Haslam@Sun.COM 	pp->dcpc_actv_req_idx = pp->dcpc_picno = -1;
7688803SJonathan.Haslam@Sun.COM 
7698803SJonathan.Haslam@Sun.COM 	return (-1);
7708803SJonathan.Haslam@Sun.COM }
7718803SJonathan.Haslam@Sun.COM 
7728803SJonathan.Haslam@Sun.COM /*
7738803SJonathan.Haslam@Sun.COM  * If only one enabling is active then remove the context and free
7748803SJonathan.Haslam@Sun.COM  * everything up. If there are multiple enablings active then remove this
7758803SJonathan.Haslam@Sun.COM  * one, its associated meta-data and re-program the hardware.
7768803SJonathan.Haslam@Sun.COM  */
7778803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
7788803SJonathan.Haslam@Sun.COM static void
7798803SJonathan.Haslam@Sun.COM dcpc_disable(void *arg, dtrace_id_t id, void *parg)
7808803SJonathan.Haslam@Sun.COM {
7818803SJonathan.Haslam@Sun.COM 	cpu_t *c;
7828803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp = parg;
7838803SJonathan.Haslam@Sun.COM 
7848803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
7858803SJonathan.Haslam@Sun.COM 
7868803SJonathan.Haslam@Sun.COM 	kpreempt_disable();
7878803SJonathan.Haslam@Sun.COM 
7888803SJonathan.Haslam@Sun.COM 	/*
7898803SJonathan.Haslam@Sun.COM 	 * This probe didn't actually make it as far as being fully enabled
7908803SJonathan.Haslam@Sun.COM 	 * so we needn't do anything with it.
7918803SJonathan.Haslam@Sun.COM 	 */
7928803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_enabled == 0) {
7938803SJonathan.Haslam@Sun.COM 		/*
7948803SJonathan.Haslam@Sun.COM 		 * If we actually allocated this request a slot in the
7958803SJonathan.Haslam@Sun.COM 		 * request array but failed to enabled it then remove the
7968803SJonathan.Haslam@Sun.COM 		 * entry in the array.
7978803SJonathan.Haslam@Sun.COM 		 */
7988803SJonathan.Haslam@Sun.COM 		if (pp->dcpc_actv_req_idx >= 0) {
7998803SJonathan.Haslam@Sun.COM 			dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
8008803SJonathan.Haslam@Sun.COM 			pp->dcpc_actv_req_idx = pp->dcpc_picno =
8018803SJonathan.Haslam@Sun.COM 			    pp->dcpc_disabling = -1;
8028803SJonathan.Haslam@Sun.COM 		}
8038803SJonathan.Haslam@Sun.COM 
8048803SJonathan.Haslam@Sun.COM 		kpreempt_enable();
8058803SJonathan.Haslam@Sun.COM 		return;
8068803SJonathan.Haslam@Sun.COM 	}
8078803SJonathan.Haslam@Sun.COM 
8088803SJonathan.Haslam@Sun.COM 	/*
8098803SJonathan.Haslam@Sun.COM 	 * If this is the only enabling then stop all the counters and
8108803SJonathan.Haslam@Sun.COM 	 * free up the meta-data.
8118803SJonathan.Haslam@Sun.COM 	 */
8128803SJonathan.Haslam@Sun.COM 	if (dcpc_enablings == 1) {
8138803SJonathan.Haslam@Sun.COM 		ASSERT(dtrace_cpc_in_use == 1);
8148803SJonathan.Haslam@Sun.COM 
8158803SJonathan.Haslam@Sun.COM 		dcpc_block_interrupts();
8168803SJonathan.Haslam@Sun.COM 
8178803SJonathan.Haslam@Sun.COM 		c = cpu_list;
8188803SJonathan.Haslam@Sun.COM 
8198803SJonathan.Haslam@Sun.COM 		do {
8208803SJonathan.Haslam@Sun.COM 			dcpc_disable_cpu(c);
8218803SJonathan.Haslam@Sun.COM 		} while ((c = c->cpu_next) != cpu_list);
8228803SJonathan.Haslam@Sun.COM 
8238803SJonathan.Haslam@Sun.COM 		dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
8248803SJonathan.Haslam@Sun.COM 		dcpc_release_interrupts();
8258803SJonathan.Haslam@Sun.COM 	} else {
8268803SJonathan.Haslam@Sun.COM 		/*
8278803SJonathan.Haslam@Sun.COM 		 * This platform can support multiple overflow events and
8288803SJonathan.Haslam@Sun.COM 		 * the enabling being disabled is not the last one. Remove this
8298803SJonathan.Haslam@Sun.COM 		 * enabling and re-program the hardware with the new config.
8308803SJonathan.Haslam@Sun.COM 		 */
8318803SJonathan.Haslam@Sun.COM 		ASSERT(dcpc_mult_ovf_cap);
8328803SJonathan.Haslam@Sun.COM 		ASSERT(dcpc_enablings > 1);
8338803SJonathan.Haslam@Sun.COM 
8348803SJonathan.Haslam@Sun.COM 		pp->dcpc_disabling = 1;
8358803SJonathan.Haslam@Sun.COM 		(void) dcpc_program_event(pp);
8368803SJonathan.Haslam@Sun.COM 	}
8378803SJonathan.Haslam@Sun.COM 
8388803SJonathan.Haslam@Sun.COM 	kpreempt_enable();
8398803SJonathan.Haslam@Sun.COM 
8408803SJonathan.Haslam@Sun.COM 	dcpc_enablings--;
8418803SJonathan.Haslam@Sun.COM 	dtrace_cpc_in_use--;
8428803SJonathan.Haslam@Sun.COM 	pp->dcpc_enabled = 0;
8438803SJonathan.Haslam@Sun.COM 	pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1;
8448803SJonathan.Haslam@Sun.COM }
8458803SJonathan.Haslam@Sun.COM 
8468803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
8478803SJonathan.Haslam@Sun.COM static int
8488803SJonathan.Haslam@Sun.COM dcpc_cpu_setup(cpu_setup_t what, processorid_t cpu, void *arg)
8498803SJonathan.Haslam@Sun.COM {
8508803SJonathan.Haslam@Sun.COM 	cpu_t *c;
8518803SJonathan.Haslam@Sun.COM 	uint8_t *state;
8528803SJonathan.Haslam@Sun.COM 
8538803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
8548803SJonathan.Haslam@Sun.COM 
8558803SJonathan.Haslam@Sun.COM 	switch (what) {
8568803SJonathan.Haslam@Sun.COM 	case CPU_OFF:
8578803SJonathan.Haslam@Sun.COM 		/*
8588803SJonathan.Haslam@Sun.COM 		 * Offline CPUs are not allowed to take part so remove this
8598803SJonathan.Haslam@Sun.COM 		 * CPU if we are actively tracing.
8608803SJonathan.Haslam@Sun.COM 		 */
8618803SJonathan.Haslam@Sun.COM 		if (dtrace_cpc_in_use) {
8628803SJonathan.Haslam@Sun.COM 			c = cpu_get(cpu);
8638803SJonathan.Haslam@Sun.COM 			state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state;
8648803SJonathan.Haslam@Sun.COM 
8658803SJonathan.Haslam@Sun.COM 			/*
8668803SJonathan.Haslam@Sun.COM 			 * Indicate that a configuration is in process in
8678803SJonathan.Haslam@Sun.COM 			 * order to stop overflow interrupts being processed
8688803SJonathan.Haslam@Sun.COM 			 * on this CPU while we disable it.
8698803SJonathan.Haslam@Sun.COM 			 */
8708803SJonathan.Haslam@Sun.COM 			while (atomic_cas_8(state, DCPC_INTR_FREE,
8718803SJonathan.Haslam@Sun.COM 			    DCPC_INTR_CONFIG) != DCPC_INTR_FREE)
8728803SJonathan.Haslam@Sun.COM 				continue;
8738803SJonathan.Haslam@Sun.COM 
8748803SJonathan.Haslam@Sun.COM 			dcpc_disable_cpu(c);
8758803SJonathan.Haslam@Sun.COM 
8768803SJonathan.Haslam@Sun.COM 			/*
8778803SJonathan.Haslam@Sun.COM 			 * Reset this CPUs interrupt state as the configuration
8788803SJonathan.Haslam@Sun.COM 			 * has ended.
8798803SJonathan.Haslam@Sun.COM 			 */
8808803SJonathan.Haslam@Sun.COM 			cpu_core[c->cpu_id].cpuc_dcpc_intr_state =
8818803SJonathan.Haslam@Sun.COM 			    DCPC_INTR_FREE;
8828803SJonathan.Haslam@Sun.COM 			membar_producer();
8838803SJonathan.Haslam@Sun.COM 		}
8848803SJonathan.Haslam@Sun.COM 		break;
8858803SJonathan.Haslam@Sun.COM 
8868803SJonathan.Haslam@Sun.COM 	case CPU_ON:
8878803SJonathan.Haslam@Sun.COM 	case CPU_SETUP:
8888803SJonathan.Haslam@Sun.COM 		/*
8898803SJonathan.Haslam@Sun.COM 		 * This CPU is being initialized or brought online so program
8908803SJonathan.Haslam@Sun.COM 		 * it with the current request set if we are actively tracing.
8918803SJonathan.Haslam@Sun.COM 		 */
8928803SJonathan.Haslam@Sun.COM 		if (dtrace_cpc_in_use) {
8938803SJonathan.Haslam@Sun.COM 			c = cpu_get(cpu);
8948803SJonathan.Haslam@Sun.COM 
8958803SJonathan.Haslam@Sun.COM 			(void) dcpc_program_cpu_event(c);
8968803SJonathan.Haslam@Sun.COM 		}
8978803SJonathan.Haslam@Sun.COM 		break;
8988803SJonathan.Haslam@Sun.COM 
8998803SJonathan.Haslam@Sun.COM 	default:
9008803SJonathan.Haslam@Sun.COM 		break;
9018803SJonathan.Haslam@Sun.COM 	}
9028803SJonathan.Haslam@Sun.COM 
9038803SJonathan.Haslam@Sun.COM 	return (0);
9048803SJonathan.Haslam@Sun.COM }
9058803SJonathan.Haslam@Sun.COM 
9068803SJonathan.Haslam@Sun.COM static dtrace_pattr_t dcpc_attr = {
9078803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
9088803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
9098803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
9108803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU },
9118803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
9128803SJonathan.Haslam@Sun.COM };
9138803SJonathan.Haslam@Sun.COM 
9148803SJonathan.Haslam@Sun.COM static dtrace_pops_t dcpc_pops = {
9158803SJonathan.Haslam@Sun.COM     dcpc_provide,
9168803SJonathan.Haslam@Sun.COM     NULL,
9178803SJonathan.Haslam@Sun.COM     dcpc_enable,
9188803SJonathan.Haslam@Sun.COM     dcpc_disable,
9198803SJonathan.Haslam@Sun.COM     NULL,
9208803SJonathan.Haslam@Sun.COM     NULL,
9218803SJonathan.Haslam@Sun.COM     NULL,
9228803SJonathan.Haslam@Sun.COM     NULL,
9238803SJonathan.Haslam@Sun.COM     dcpc_usermode,
9248803SJonathan.Haslam@Sun.COM     dcpc_destroy
9258803SJonathan.Haslam@Sun.COM };
9268803SJonathan.Haslam@Sun.COM 
9278803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
9288803SJonathan.Haslam@Sun.COM static int
9298803SJonathan.Haslam@Sun.COM dcpc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
9308803SJonathan.Haslam@Sun.COM {
9318803SJonathan.Haslam@Sun.COM 	return (0);
9328803SJonathan.Haslam@Sun.COM }
9338803SJonathan.Haslam@Sun.COM 
9348803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
9358803SJonathan.Haslam@Sun.COM static int
9368803SJonathan.Haslam@Sun.COM dcpc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
9378803SJonathan.Haslam@Sun.COM {
9388803SJonathan.Haslam@Sun.COM 	int error;
9398803SJonathan.Haslam@Sun.COM 
9408803SJonathan.Haslam@Sun.COM 	switch (infocmd) {
9418803SJonathan.Haslam@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
9428803SJonathan.Haslam@Sun.COM 		*result = (void *)dcpc_devi;
9438803SJonathan.Haslam@Sun.COM 		error = DDI_SUCCESS;
9448803SJonathan.Haslam@Sun.COM 		break;
9458803SJonathan.Haslam@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
9468803SJonathan.Haslam@Sun.COM 		*result = (void *)0;
9478803SJonathan.Haslam@Sun.COM 		error = DDI_SUCCESS;
9488803SJonathan.Haslam@Sun.COM 		break;
9498803SJonathan.Haslam@Sun.COM 	default:
9508803SJonathan.Haslam@Sun.COM 		error = DDI_FAILURE;
9518803SJonathan.Haslam@Sun.COM 	}
9528803SJonathan.Haslam@Sun.COM 	return (error);
9538803SJonathan.Haslam@Sun.COM }
9548803SJonathan.Haslam@Sun.COM 
9558803SJonathan.Haslam@Sun.COM static int
9568803SJonathan.Haslam@Sun.COM dcpc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
9578803SJonathan.Haslam@Sun.COM {
9588803SJonathan.Haslam@Sun.COM 	switch (cmd) {
9598803SJonathan.Haslam@Sun.COM 	case DDI_DETACH:
9608803SJonathan.Haslam@Sun.COM 		break;
9618803SJonathan.Haslam@Sun.COM 	case DDI_SUSPEND:
9628803SJonathan.Haslam@Sun.COM 		return (DDI_SUCCESS);
9638803SJonathan.Haslam@Sun.COM 	default:
9648803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
9658803SJonathan.Haslam@Sun.COM 	}
9668803SJonathan.Haslam@Sun.COM 
9678803SJonathan.Haslam@Sun.COM 	if (dtrace_unregister(dcpc_pid) != 0)
9688803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
9698803SJonathan.Haslam@Sun.COM 
9708803SJonathan.Haslam@Sun.COM 	ddi_remove_minor_node(devi, NULL);
9718803SJonathan.Haslam@Sun.COM 
9728803SJonathan.Haslam@Sun.COM 	mutex_enter(&cpu_lock);
9738803SJonathan.Haslam@Sun.COM 	unregister_cpu_setup_func(dcpc_cpu_setup, NULL);
9748803SJonathan.Haslam@Sun.COM 	mutex_exit(&cpu_lock);
9758803SJonathan.Haslam@Sun.COM 
9768803SJonathan.Haslam@Sun.COM 	kmem_free(dcpc_actv_reqs, cpc_ncounters * sizeof (dcpc_probe_t *));
9778803SJonathan.Haslam@Sun.COM 
9788803SJonathan.Haslam@Sun.COM 	kcpc_unregister_dcpc();
9798803SJonathan.Haslam@Sun.COM 
9808803SJonathan.Haslam@Sun.COM 	return (DDI_SUCCESS);
9818803SJonathan.Haslam@Sun.COM }
9828803SJonathan.Haslam@Sun.COM 
9838803SJonathan.Haslam@Sun.COM static int
9848803SJonathan.Haslam@Sun.COM dcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
9858803SJonathan.Haslam@Sun.COM {
9868803SJonathan.Haslam@Sun.COM 	uint_t caps;
9878803SJonathan.Haslam@Sun.COM 	char *attrs;
9888803SJonathan.Haslam@Sun.COM 
9898803SJonathan.Haslam@Sun.COM 	switch (cmd) {
9908803SJonathan.Haslam@Sun.COM 	case DDI_ATTACH:
9918803SJonathan.Haslam@Sun.COM 		break;
9928803SJonathan.Haslam@Sun.COM 	case DDI_RESUME:
9938803SJonathan.Haslam@Sun.COM 		return (DDI_SUCCESS);
9948803SJonathan.Haslam@Sun.COM 	default:
9958803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
9968803SJonathan.Haslam@Sun.COM 	}
9978803SJonathan.Haslam@Sun.COM 
9988803SJonathan.Haslam@Sun.COM 	if (kcpc_pcbe_loaded() == -1)
9998803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
10008803SJonathan.Haslam@Sun.COM 
10018803SJonathan.Haslam@Sun.COM 	caps = kcpc_pcbe_capabilities();
10028803SJonathan.Haslam@Sun.COM 
10038803SJonathan.Haslam@Sun.COM 	if (!(caps & CPC_CAP_OVERFLOW_INTERRUPT)) {
1004*9397SJonathan.Haslam@Sun.COM 		cmn_err(CE_NOTE, "!dcpc: Counter Overflow not supported"\
1005*9397SJonathan.Haslam@Sun.COM 		    " on this processor");
10068803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
10078803SJonathan.Haslam@Sun.COM 	}
10088803SJonathan.Haslam@Sun.COM 
10098803SJonathan.Haslam@Sun.COM 	if (ddi_create_minor_node(devi, "dcpc", S_IFCHR, 0,
10108803SJonathan.Haslam@Sun.COM 	    DDI_PSEUDO, NULL) == DDI_FAILURE ||
10118803SJonathan.Haslam@Sun.COM 	    dtrace_register("cpc", &dcpc_attr, DTRACE_PRIV_KERNEL,
10128803SJonathan.Haslam@Sun.COM 	    NULL, &dcpc_pops, NULL, &dcpc_pid) != 0) {
10138803SJonathan.Haslam@Sun.COM 		ddi_remove_minor_node(devi, NULL);
10148803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
10158803SJonathan.Haslam@Sun.COM 	}
10168803SJonathan.Haslam@Sun.COM 
10178803SJonathan.Haslam@Sun.COM 	mutex_enter(&cpu_lock);
10188803SJonathan.Haslam@Sun.COM 	register_cpu_setup_func(dcpc_cpu_setup, NULL);
10198803SJonathan.Haslam@Sun.COM 	mutex_exit(&cpu_lock);
10208803SJonathan.Haslam@Sun.COM 
10218803SJonathan.Haslam@Sun.COM 	dcpc_ovf_mask = (1 << cpc_ncounters) - 1;
10228803SJonathan.Haslam@Sun.COM 	ASSERT(dcpc_ovf_mask != 0);
10238803SJonathan.Haslam@Sun.COM 
10248803SJonathan.Haslam@Sun.COM 	if (caps & CPC_CAP_OVERFLOW_PRECISE)
10258803SJonathan.Haslam@Sun.COM 		dcpc_mult_ovf_cap = 1;
10268803SJonathan.Haslam@Sun.COM 
10278803SJonathan.Haslam@Sun.COM 	/*
10288803SJonathan.Haslam@Sun.COM 	 * Determine which, if any, mask attribute the back-end can use.
10298803SJonathan.Haslam@Sun.COM 	 */
10308803SJonathan.Haslam@Sun.COM 	attrs = kcpc_list_attrs();
10318803SJonathan.Haslam@Sun.COM 	if (strstr(attrs, "umask") != NULL)
10328803SJonathan.Haslam@Sun.COM 		dcpc_mask_type |= DCPC_UMASK;
10338803SJonathan.Haslam@Sun.COM 	else if (strstr(attrs, "emask") != NULL)
10348803SJonathan.Haslam@Sun.COM 		dcpc_mask_type |= DCPC_EMASK;
10358803SJonathan.Haslam@Sun.COM 
10368803SJonathan.Haslam@Sun.COM 	/*
10378803SJonathan.Haslam@Sun.COM 	 * The dcpc_actv_reqs array is used to store the requests that
10388803SJonathan.Haslam@Sun.COM 	 * we currently have programmed. The order of requests in this
10398803SJonathan.Haslam@Sun.COM 	 * array is not necessarily the order that the event appears in
10408803SJonathan.Haslam@Sun.COM 	 * the kcpc_request_t array. Once entered into a slot in the array
10418803SJonathan.Haslam@Sun.COM 	 * the entry is not moved until it's removed.
10428803SJonathan.Haslam@Sun.COM 	 */
10438803SJonathan.Haslam@Sun.COM 	dcpc_actv_reqs =
10448803SJonathan.Haslam@Sun.COM 	    kmem_zalloc(cpc_ncounters * sizeof (dcpc_probe_t *), KM_SLEEP);
10458803SJonathan.Haslam@Sun.COM 
10468803SJonathan.Haslam@Sun.COM 	dcpc_min_overflow = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
10478803SJonathan.Haslam@Sun.COM 	    DDI_PROP_DONTPASS, "dcpc-min-overflow", DCPC_MIN_OVF_DEFAULT);
10488803SJonathan.Haslam@Sun.COM 
10498803SJonathan.Haslam@Sun.COM 	kcpc_register_dcpc(dcpc_fire);
10508803SJonathan.Haslam@Sun.COM 
10518803SJonathan.Haslam@Sun.COM 	ddi_report_dev(devi);
10528803SJonathan.Haslam@Sun.COM 	dcpc_devi = devi;
10538803SJonathan.Haslam@Sun.COM 
10548803SJonathan.Haslam@Sun.COM 	return (DDI_SUCCESS);
10558803SJonathan.Haslam@Sun.COM }
10568803SJonathan.Haslam@Sun.COM 
10578803SJonathan.Haslam@Sun.COM static struct cb_ops dcpc_cb_ops = {
10588803SJonathan.Haslam@Sun.COM 	dcpc_open,		/* open */
10598803SJonathan.Haslam@Sun.COM 	nodev,			/* close */
10608803SJonathan.Haslam@Sun.COM 	nulldev,		/* strategy */
10618803SJonathan.Haslam@Sun.COM 	nulldev,		/* print */
10628803SJonathan.Haslam@Sun.COM 	nodev,			/* dump */
10638803SJonathan.Haslam@Sun.COM 	nodev,			/* read */
10648803SJonathan.Haslam@Sun.COM 	nodev,			/* write */
10658803SJonathan.Haslam@Sun.COM 	nodev,			/* ioctl */
10668803SJonathan.Haslam@Sun.COM 	nodev,			/* devmap */
10678803SJonathan.Haslam@Sun.COM 	nodev,			/* mmap */
10688803SJonathan.Haslam@Sun.COM 	nodev,			/* segmap */
10698803SJonathan.Haslam@Sun.COM 	nochpoll,		/* poll */
10708803SJonathan.Haslam@Sun.COM 	ddi_prop_op,		/* cb_prop_op */
10718803SJonathan.Haslam@Sun.COM 	0,			/* streamtab  */
10728803SJonathan.Haslam@Sun.COM 	D_NEW | D_MP		/* Driver compatibility flag */
10738803SJonathan.Haslam@Sun.COM };
10748803SJonathan.Haslam@Sun.COM 
10758803SJonathan.Haslam@Sun.COM static struct dev_ops dcpc_ops = {
10768803SJonathan.Haslam@Sun.COM 	DEVO_REV,		/* devo_rev, */
10778803SJonathan.Haslam@Sun.COM 	0,			/* refcnt  */
10788803SJonathan.Haslam@Sun.COM 	dcpc_info,		/* get_dev_info */
10798803SJonathan.Haslam@Sun.COM 	nulldev,		/* identify */
10808803SJonathan.Haslam@Sun.COM 	nulldev,		/* probe */
10818803SJonathan.Haslam@Sun.COM 	dcpc_attach,		/* attach */
10828803SJonathan.Haslam@Sun.COM 	dcpc_detach,		/* detach */
10838803SJonathan.Haslam@Sun.COM 	nodev,			/* reset */
10848803SJonathan.Haslam@Sun.COM 	&dcpc_cb_ops,		/* driver operations */
10858803SJonathan.Haslam@Sun.COM 	NULL,			/* bus operations */
10868803SJonathan.Haslam@Sun.COM 	nodev,			/* dev power */
10878803SJonathan.Haslam@Sun.COM 	ddi_quiesce_not_needed	/* quiesce */
10888803SJonathan.Haslam@Sun.COM };
10898803SJonathan.Haslam@Sun.COM 
10908803SJonathan.Haslam@Sun.COM /*
10918803SJonathan.Haslam@Sun.COM  * Module linkage information for the kernel.
10928803SJonathan.Haslam@Sun.COM  */
10938803SJonathan.Haslam@Sun.COM static struct modldrv modldrv = {
10948803SJonathan.Haslam@Sun.COM 	&mod_driverops,		/* module type */
10958803SJonathan.Haslam@Sun.COM 	"DTrace CPC Module",	/* name of module */
10968803SJonathan.Haslam@Sun.COM 	&dcpc_ops,		/* driver ops */
10978803SJonathan.Haslam@Sun.COM };
10988803SJonathan.Haslam@Sun.COM 
10998803SJonathan.Haslam@Sun.COM static struct modlinkage modlinkage = {
11008803SJonathan.Haslam@Sun.COM 	MODREV_1,
11018803SJonathan.Haslam@Sun.COM 	(void *)&modldrv,
11028803SJonathan.Haslam@Sun.COM 	NULL
11038803SJonathan.Haslam@Sun.COM };
11048803SJonathan.Haslam@Sun.COM 
11058803SJonathan.Haslam@Sun.COM int
11068803SJonathan.Haslam@Sun.COM _init(void)
11078803SJonathan.Haslam@Sun.COM {
11088803SJonathan.Haslam@Sun.COM 	return (mod_install(&modlinkage));
11098803SJonathan.Haslam@Sun.COM }
11108803SJonathan.Haslam@Sun.COM 
11118803SJonathan.Haslam@Sun.COM int
11128803SJonathan.Haslam@Sun.COM _info(struct modinfo *modinfop)
11138803SJonathan.Haslam@Sun.COM {
11148803SJonathan.Haslam@Sun.COM 	return (mod_info(&modlinkage, modinfop));
11158803SJonathan.Haslam@Sun.COM }
11168803SJonathan.Haslam@Sun.COM 
11178803SJonathan.Haslam@Sun.COM int
11188803SJonathan.Haslam@Sun.COM _fini(void)
11198803SJonathan.Haslam@Sun.COM {
11208803SJonathan.Haslam@Sun.COM 	return (mod_remove(&modlinkage));
11218803SJonathan.Haslam@Sun.COM }
1122