xref: /onnv-gate/usr/src/uts/common/dtrace/dcpc.c (revision 8803:8c01b39012c9)
1*8803SJonathan.Haslam@Sun.COM /*
2*8803SJonathan.Haslam@Sun.COM  * CDDL HEADER START
3*8803SJonathan.Haslam@Sun.COM  *
4*8803SJonathan.Haslam@Sun.COM  * The contents of this file are subject to the terms of the
5*8803SJonathan.Haslam@Sun.COM  * Common Development and Distribution License (the "License").
6*8803SJonathan.Haslam@Sun.COM  * You may not use this file except in compliance with the License.
7*8803SJonathan.Haslam@Sun.COM  *
8*8803SJonathan.Haslam@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*8803SJonathan.Haslam@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*8803SJonathan.Haslam@Sun.COM  * See the License for the specific language governing permissions
11*8803SJonathan.Haslam@Sun.COM  * and limitations under the License.
12*8803SJonathan.Haslam@Sun.COM  *
13*8803SJonathan.Haslam@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*8803SJonathan.Haslam@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*8803SJonathan.Haslam@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*8803SJonathan.Haslam@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*8803SJonathan.Haslam@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*8803SJonathan.Haslam@Sun.COM  *
19*8803SJonathan.Haslam@Sun.COM  * CDDL HEADER END
20*8803SJonathan.Haslam@Sun.COM  */
21*8803SJonathan.Haslam@Sun.COM 
22*8803SJonathan.Haslam@Sun.COM /*
23*8803SJonathan.Haslam@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*8803SJonathan.Haslam@Sun.COM  * Use is subject to license terms.
25*8803SJonathan.Haslam@Sun.COM  */
26*8803SJonathan.Haslam@Sun.COM 
27*8803SJonathan.Haslam@Sun.COM #include <sys/errno.h>
28*8803SJonathan.Haslam@Sun.COM #include <sys/cpuvar.h>
29*8803SJonathan.Haslam@Sun.COM #include <sys/stat.h>
30*8803SJonathan.Haslam@Sun.COM #include <sys/modctl.h>
31*8803SJonathan.Haslam@Sun.COM #include <sys/cmn_err.h>
32*8803SJonathan.Haslam@Sun.COM #include <sys/ddi.h>
33*8803SJonathan.Haslam@Sun.COM #include <sys/sunddi.h>
34*8803SJonathan.Haslam@Sun.COM #include <sys/ksynch.h>
35*8803SJonathan.Haslam@Sun.COM #include <sys/conf.h>
36*8803SJonathan.Haslam@Sun.COM #include <sys/kmem.h>
37*8803SJonathan.Haslam@Sun.COM #include <sys/kcpc.h>
38*8803SJonathan.Haslam@Sun.COM #include <sys/cpc_pcbe.h>
39*8803SJonathan.Haslam@Sun.COM #include <sys/cpc_impl.h>
40*8803SJonathan.Haslam@Sun.COM #include <sys/dtrace_impl.h>
41*8803SJonathan.Haslam@Sun.COM 
42*8803SJonathan.Haslam@Sun.COM /*
43*8803SJonathan.Haslam@Sun.COM  * DTrace CPU Performance Counter Provider
44*8803SJonathan.Haslam@Sun.COM  * ---------------------------------------
45*8803SJonathan.Haslam@Sun.COM  *
46*8803SJonathan.Haslam@Sun.COM  * The DTrace cpc provider allows DTrace consumers to access the CPU
47*8803SJonathan.Haslam@Sun.COM  * performance counter overflow mechanism of a CPU. The configuration
48*8803SJonathan.Haslam@Sun.COM  * presented in a probe specification is programmed into the performance
49*8803SJonathan.Haslam@Sun.COM  * counter hardware of all available CPUs on a system. Programming the
50*8803SJonathan.Haslam@Sun.COM  * hardware causes a counter on each CPU to begin counting events of the
51*8803SJonathan.Haslam@Sun.COM  * given type. When the specified number of events have occurred, an overflow
52*8803SJonathan.Haslam@Sun.COM  * interrupt will be generated and the probe is fired.
53*8803SJonathan.Haslam@Sun.COM  *
54*8803SJonathan.Haslam@Sun.COM  * The required configuration for the performance counter is encoded into
55*8803SJonathan.Haslam@Sun.COM  * the probe specification and this includes the performance counter event
56*8803SJonathan.Haslam@Sun.COM  * name, processor mode, overflow rate and an optional unit mask.
57*8803SJonathan.Haslam@Sun.COM  *
58*8803SJonathan.Haslam@Sun.COM  * Most processors provide several counters (PICs) which can count all or a
59*8803SJonathan.Haslam@Sun.COM  * subset of the events available for a given CPU. However, when overflow
60*8803SJonathan.Haslam@Sun.COM  * profiling is being used, not all CPUs can detect which counter generated the
61*8803SJonathan.Haslam@Sun.COM  * overflow interrupt. In this case we cannot reliably determine which counter
62*8803SJonathan.Haslam@Sun.COM  * overflowed and we therefore only allow such CPUs to configure one event at
63*8803SJonathan.Haslam@Sun.COM  * a time. Processors that can determine the counter which overflowed are
64*8803SJonathan.Haslam@Sun.COM  * allowed to program as many events at one time as possible (in theory up to
65*8803SJonathan.Haslam@Sun.COM  * the number of instrumentation counters supported by that platform).
66*8803SJonathan.Haslam@Sun.COM  * Therefore, multiple consumers can enable multiple probes at the same time
67*8803SJonathan.Haslam@Sun.COM  * on such platforms. Platforms which cannot determine the source of an
68*8803SJonathan.Haslam@Sun.COM  * overflow interrupt are only allowed to program a single event at one time.
69*8803SJonathan.Haslam@Sun.COM  *
70*8803SJonathan.Haslam@Sun.COM  * The performance counter hardware is made available to consumers on a
71*8803SJonathan.Haslam@Sun.COM  * first-come, first-served basis. Only a finite amount of hardware resource
72*8803SJonathan.Haslam@Sun.COM  * is available and, while we make every attempt to accomodate requests from
73*8803SJonathan.Haslam@Sun.COM  * consumers, we must deny requests when hardware resources have been exhausted.
74*8803SJonathan.Haslam@Sun.COM  * A consumer will fail to enable probes when resources are currently in use.
75*8803SJonathan.Haslam@Sun.COM  *
76*8803SJonathan.Haslam@Sun.COM  * The cpc provider contends for shared hardware resources along with other
77*8803SJonathan.Haslam@Sun.COM  * consumers of the kernel CPU performance counter subsystem (e.g. cpustat(1M)).
78*8803SJonathan.Haslam@Sun.COM  * Only one such consumer can use the performance counters at any one time and
79*8803SJonathan.Haslam@Sun.COM  * counters are made available on a first-come, first-served basis. As with
80*8803SJonathan.Haslam@Sun.COM  * cpustat, the cpc provider has priority over per-LWP libcpc usage (e.g.
81*8803SJonathan.Haslam@Sun.COM  * cputrack(1)). Invoking the cpc provider will cause all existing per-LWP
82*8803SJonathan.Haslam@Sun.COM  * counter contexts to be invalidated.
83*8803SJonathan.Haslam@Sun.COM  */
84*8803SJonathan.Haslam@Sun.COM 
85*8803SJonathan.Haslam@Sun.COM typedef struct dcpc_probe {
86*8803SJonathan.Haslam@Sun.COM 	char		dcpc_event_name[CPC_MAX_EVENT_LEN];
87*8803SJonathan.Haslam@Sun.COM 	int		dcpc_flag;	/* flags (USER/SYS) */
88*8803SJonathan.Haslam@Sun.COM 	uint32_t	dcpc_ovfval;	/* overflow value */
89*8803SJonathan.Haslam@Sun.COM 	int64_t		dcpc_umask;	/* umask/emask for this event */
90*8803SJonathan.Haslam@Sun.COM 	int		dcpc_picno;	/* pic this event is programmed in */
91*8803SJonathan.Haslam@Sun.COM 	int		dcpc_enabled;	/* probe is actually enabled? */
92*8803SJonathan.Haslam@Sun.COM 	int		dcpc_disabling;	/* probe is currently being disabled */
93*8803SJonathan.Haslam@Sun.COM 	dtrace_id_t	dcpc_id;	/* probeid this request is enabling */
94*8803SJonathan.Haslam@Sun.COM 	int		dcpc_actv_req_idx;	/* idx into dcpc_actv_reqs[] */
95*8803SJonathan.Haslam@Sun.COM } dcpc_probe_t;
96*8803SJonathan.Haslam@Sun.COM 
97*8803SJonathan.Haslam@Sun.COM static dev_info_t			*dcpc_devi;
98*8803SJonathan.Haslam@Sun.COM static dtrace_provider_id_t		dcpc_pid;
99*8803SJonathan.Haslam@Sun.COM static dcpc_probe_t			**dcpc_actv_reqs;
100*8803SJonathan.Haslam@Sun.COM static uint32_t				dcpc_enablings = 0;
101*8803SJonathan.Haslam@Sun.COM static int				dcpc_ovf_mask = 0;
102*8803SJonathan.Haslam@Sun.COM static int				dcpc_mult_ovf_cap = 0;
103*8803SJonathan.Haslam@Sun.COM static int				dcpc_mask_type = 0;
104*8803SJonathan.Haslam@Sun.COM 
105*8803SJonathan.Haslam@Sun.COM /*
106*8803SJonathan.Haslam@Sun.COM  * When the dcpc provider is loaded, dcpc_min_overflow is set to either
107*8803SJonathan.Haslam@Sun.COM  * DCPC_MIN_OVF_DEFAULT or the value that dcpc-min-overflow is set to in
108*8803SJonathan.Haslam@Sun.COM  * the dcpc.conf file. Decrease this value to set probes with smaller
109*8803SJonathan.Haslam@Sun.COM  * overflow values. Remember that very small values could render a system
110*8803SJonathan.Haslam@Sun.COM  * unusable with frequently occurring events.
111*8803SJonathan.Haslam@Sun.COM  */
112*8803SJonathan.Haslam@Sun.COM #define	DCPC_MIN_OVF_DEFAULT		5000
113*8803SJonathan.Haslam@Sun.COM static uint32_t				dcpc_min_overflow;
114*8803SJonathan.Haslam@Sun.COM 
115*8803SJonathan.Haslam@Sun.COM static int dcpc_aframes = 0;	/* override for artificial frame setting */
116*8803SJonathan.Haslam@Sun.COM #if defined(__x86)
117*8803SJonathan.Haslam@Sun.COM #define	DCPC_ARTIFICIAL_FRAMES	8
118*8803SJonathan.Haslam@Sun.COM #elif defined(__sparc)
119*8803SJonathan.Haslam@Sun.COM #define	DCPC_ARTIFICIAL_FRAMES	2
120*8803SJonathan.Haslam@Sun.COM #endif
121*8803SJonathan.Haslam@Sun.COM 
122*8803SJonathan.Haslam@Sun.COM /*
123*8803SJonathan.Haslam@Sun.COM  * Called from the platform overflow interrupt handler. 'bitmap' is a mask
124*8803SJonathan.Haslam@Sun.COM  * which contains the pic(s) that have overflowed.
125*8803SJonathan.Haslam@Sun.COM  */
126*8803SJonathan.Haslam@Sun.COM static void
127*8803SJonathan.Haslam@Sun.COM dcpc_fire(uint64_t bitmap)
128*8803SJonathan.Haslam@Sun.COM {
129*8803SJonathan.Haslam@Sun.COM 	int i;
130*8803SJonathan.Haslam@Sun.COM 
131*8803SJonathan.Haslam@Sun.COM 	/*
132*8803SJonathan.Haslam@Sun.COM 	 * No counter was marked as overflowing. Shout about it and get out.
133*8803SJonathan.Haslam@Sun.COM 	 */
134*8803SJonathan.Haslam@Sun.COM 	if ((bitmap & dcpc_ovf_mask) == 0) {
135*8803SJonathan.Haslam@Sun.COM 		cmn_err(CE_NOTE, "dcpc_fire: no counter overflow found\n");
136*8803SJonathan.Haslam@Sun.COM 		return;
137*8803SJonathan.Haslam@Sun.COM 	}
138*8803SJonathan.Haslam@Sun.COM 
139*8803SJonathan.Haslam@Sun.COM 	/*
140*8803SJonathan.Haslam@Sun.COM 	 * This is the common case of a processor that doesn't support
141*8803SJonathan.Haslam@Sun.COM 	 * multiple overflow events. Such systems are only allowed a single
142*8803SJonathan.Haslam@Sun.COM 	 * enabling and therefore we just look for the first entry in
143*8803SJonathan.Haslam@Sun.COM 	 * the active request array.
144*8803SJonathan.Haslam@Sun.COM 	 */
145*8803SJonathan.Haslam@Sun.COM 	if (!dcpc_mult_ovf_cap) {
146*8803SJonathan.Haslam@Sun.COM 		for (i = 0; i < cpc_ncounters; i++) {
147*8803SJonathan.Haslam@Sun.COM 			if (dcpc_actv_reqs[i] != NULL) {
148*8803SJonathan.Haslam@Sun.COM 				dtrace_probe(dcpc_actv_reqs[i]->dcpc_id,
149*8803SJonathan.Haslam@Sun.COM 				    CPU->cpu_cpcprofile_pc,
150*8803SJonathan.Haslam@Sun.COM 				    CPU->cpu_cpcprofile_upc, 0, 0, 0);
151*8803SJonathan.Haslam@Sun.COM 				return;
152*8803SJonathan.Haslam@Sun.COM 			}
153*8803SJonathan.Haslam@Sun.COM 		}
154*8803SJonathan.Haslam@Sun.COM 		return;
155*8803SJonathan.Haslam@Sun.COM 	}
156*8803SJonathan.Haslam@Sun.COM 
157*8803SJonathan.Haslam@Sun.COM 	/*
158*8803SJonathan.Haslam@Sun.COM 	 * This is a processor capable of handling multiple overflow events.
159*8803SJonathan.Haslam@Sun.COM 	 * Iterate over the array of active requests and locate the counters
160*8803SJonathan.Haslam@Sun.COM 	 * that overflowed (note: it is possible for more than one counter to
161*8803SJonathan.Haslam@Sun.COM 	 * have overflowed at the same time).
162*8803SJonathan.Haslam@Sun.COM 	 */
163*8803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
164*8803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] != NULL &&
165*8803SJonathan.Haslam@Sun.COM 		    (bitmap & (1ULL << dcpc_actv_reqs[i]->dcpc_picno))) {
166*8803SJonathan.Haslam@Sun.COM 			dtrace_probe(dcpc_actv_reqs[i]->dcpc_id,
167*8803SJonathan.Haslam@Sun.COM 			    CPU->cpu_cpcprofile_pc,
168*8803SJonathan.Haslam@Sun.COM 			    CPU->cpu_cpcprofile_upc, 0, 0, 0);
169*8803SJonathan.Haslam@Sun.COM 		}
170*8803SJonathan.Haslam@Sun.COM 	}
171*8803SJonathan.Haslam@Sun.COM }
172*8803SJonathan.Haslam@Sun.COM 
173*8803SJonathan.Haslam@Sun.COM static void
174*8803SJonathan.Haslam@Sun.COM dcpc_create_probe(dtrace_provider_id_t id, const char *probename,
175*8803SJonathan.Haslam@Sun.COM     char *eventname, int64_t umask, uint32_t ovfval, char flag)
176*8803SJonathan.Haslam@Sun.COM {
177*8803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp;
178*8803SJonathan.Haslam@Sun.COM 	int nr_frames = DCPC_ARTIFICIAL_FRAMES + dtrace_mach_aframes();
179*8803SJonathan.Haslam@Sun.COM 
180*8803SJonathan.Haslam@Sun.COM 	if (dcpc_aframes)
181*8803SJonathan.Haslam@Sun.COM 		nr_frames = dcpc_aframes;
182*8803SJonathan.Haslam@Sun.COM 
183*8803SJonathan.Haslam@Sun.COM 	if (dtrace_probe_lookup(id, NULL, NULL, probename) != 0)
184*8803SJonathan.Haslam@Sun.COM 		return;
185*8803SJonathan.Haslam@Sun.COM 
186*8803SJonathan.Haslam@Sun.COM 	pp = kmem_zalloc(sizeof (dcpc_probe_t), KM_SLEEP);
187*8803SJonathan.Haslam@Sun.COM 	(void) strncpy(pp->dcpc_event_name, eventname,
188*8803SJonathan.Haslam@Sun.COM 	    sizeof (pp->dcpc_event_name) - 1);
189*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_event_name[sizeof (pp->dcpc_event_name) - 1] = '\0';
190*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_flag = flag | CPC_OVF_NOTIFY_EMT;
191*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_ovfval = ovfval;
192*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_umask = umask;
193*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1;
194*8803SJonathan.Haslam@Sun.COM 
195*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_id = dtrace_probe_create(id, NULL, NULL, probename,
196*8803SJonathan.Haslam@Sun.COM 	    nr_frames, pp);
197*8803SJonathan.Haslam@Sun.COM }
198*8803SJonathan.Haslam@Sun.COM 
199*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
200*8803SJonathan.Haslam@Sun.COM static void
201*8803SJonathan.Haslam@Sun.COM dcpc_provide(void *arg, const dtrace_probedesc_t *desc)
202*8803SJonathan.Haslam@Sun.COM {
203*8803SJonathan.Haslam@Sun.COM 	/*
204*8803SJonathan.Haslam@Sun.COM 	 * The format of a probe is:
205*8803SJonathan.Haslam@Sun.COM 	 *
206*8803SJonathan.Haslam@Sun.COM 	 *	event_name-mode-{optional_umask}-overflow_rate
207*8803SJonathan.Haslam@Sun.COM 	 * e.g.
208*8803SJonathan.Haslam@Sun.COM 	 *	DC_refill_from_system-user-0x1e-50000, or,
209*8803SJonathan.Haslam@Sun.COM 	 *	DC_refill_from_system-all-10000
210*8803SJonathan.Haslam@Sun.COM 	 *
211*8803SJonathan.Haslam@Sun.COM 	 */
212*8803SJonathan.Haslam@Sun.COM 	char *str, *end, *p;
213*8803SJonathan.Haslam@Sun.COM 	int i, flag = 0;
214*8803SJonathan.Haslam@Sun.COM 	char event[CPC_MAX_EVENT_LEN];
215*8803SJonathan.Haslam@Sun.COM 	long umask = -1, val = 0;
216*8803SJonathan.Haslam@Sun.COM 	size_t evlen, len;
217*8803SJonathan.Haslam@Sun.COM 
218*8803SJonathan.Haslam@Sun.COM 	/*
219*8803SJonathan.Haslam@Sun.COM 	 * The 'cpc' provider offers no probes by default.
220*8803SJonathan.Haslam@Sun.COM 	 */
221*8803SJonathan.Haslam@Sun.COM 	if (desc == NULL)
222*8803SJonathan.Haslam@Sun.COM 		return;
223*8803SJonathan.Haslam@Sun.COM 
224*8803SJonathan.Haslam@Sun.COM 	len = strlen(desc->dtpd_name);
225*8803SJonathan.Haslam@Sun.COM 	p = str = kmem_alloc(len + 1, KM_SLEEP);
226*8803SJonathan.Haslam@Sun.COM 	(void) strcpy(str, desc->dtpd_name);
227*8803SJonathan.Haslam@Sun.COM 
228*8803SJonathan.Haslam@Sun.COM 	/*
229*8803SJonathan.Haslam@Sun.COM 	 * We have a poor man's strtok() going on here. Replace any hyphens
230*8803SJonathan.Haslam@Sun.COM 	 * in the the probe name with NULL characters in order to make it
231*8803SJonathan.Haslam@Sun.COM 	 * easy to parse the string with regular string functions.
232*8803SJonathan.Haslam@Sun.COM 	 */
233*8803SJonathan.Haslam@Sun.COM 	for (i = 0; i < len; i++) {
234*8803SJonathan.Haslam@Sun.COM 		if (str[i] == '-')
235*8803SJonathan.Haslam@Sun.COM 			str[i] = '\0';
236*8803SJonathan.Haslam@Sun.COM 	}
237*8803SJonathan.Haslam@Sun.COM 
238*8803SJonathan.Haslam@Sun.COM 	/*
239*8803SJonathan.Haslam@Sun.COM 	 * The first part of the string must be either a platform event
240*8803SJonathan.Haslam@Sun.COM 	 * name or a generic event name.
241*8803SJonathan.Haslam@Sun.COM 	 */
242*8803SJonathan.Haslam@Sun.COM 	evlen = strlen(p);
243*8803SJonathan.Haslam@Sun.COM 	(void) strncpy(event, p, CPC_MAX_EVENT_LEN - 1);
244*8803SJonathan.Haslam@Sun.COM 	event[CPC_MAX_EVENT_LEN - 1] = '\0';
245*8803SJonathan.Haslam@Sun.COM 
246*8803SJonathan.Haslam@Sun.COM 	/*
247*8803SJonathan.Haslam@Sun.COM 	 * The next part of the name is the mode specification. Valid
248*8803SJonathan.Haslam@Sun.COM 	 * settings are "user", "kernel" or "all".
249*8803SJonathan.Haslam@Sun.COM 	 */
250*8803SJonathan.Haslam@Sun.COM 	p += evlen + 1;
251*8803SJonathan.Haslam@Sun.COM 
252*8803SJonathan.Haslam@Sun.COM 	if (strcmp(p, "user") == 0)
253*8803SJonathan.Haslam@Sun.COM 		flag |= CPC_COUNT_USER;
254*8803SJonathan.Haslam@Sun.COM 	else if (strcmp(p, "kernel") == 0)
255*8803SJonathan.Haslam@Sun.COM 		flag |= CPC_COUNT_SYSTEM;
256*8803SJonathan.Haslam@Sun.COM 	else if (strcmp(p, "all") == 0)
257*8803SJonathan.Haslam@Sun.COM 		flag |= CPC_COUNT_USER | CPC_COUNT_SYSTEM;
258*8803SJonathan.Haslam@Sun.COM 	else
259*8803SJonathan.Haslam@Sun.COM 		goto err;
260*8803SJonathan.Haslam@Sun.COM 
261*8803SJonathan.Haslam@Sun.COM 	/*
262*8803SJonathan.Haslam@Sun.COM 	 * Next we either have a mask specification followed by an overflow
263*8803SJonathan.Haslam@Sun.COM 	 * rate or just an overflow rate on its own.
264*8803SJonathan.Haslam@Sun.COM 	 */
265*8803SJonathan.Haslam@Sun.COM 	p += strlen(p) + 1;
266*8803SJonathan.Haslam@Sun.COM 	if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
267*8803SJonathan.Haslam@Sun.COM 		/*
268*8803SJonathan.Haslam@Sun.COM 		 * A unit mask can only be specified if:
269*8803SJonathan.Haslam@Sun.COM 		 * 1) this performance counter back end supports masks.
270*8803SJonathan.Haslam@Sun.COM 		 * 2) the specified event is platform specific.
271*8803SJonathan.Haslam@Sun.COM 		 * 3) a valid hex number is converted.
272*8803SJonathan.Haslam@Sun.COM 		 * 4) no extraneous characters follow the mask specification.
273*8803SJonathan.Haslam@Sun.COM 		 */
274*8803SJonathan.Haslam@Sun.COM 		if (dcpc_mask_type != 0 && strncmp(event, "PAPI", 4) != 0 &&
275*8803SJonathan.Haslam@Sun.COM 		    ddi_strtol(p, &end, 16, &umask) == 0 &&
276*8803SJonathan.Haslam@Sun.COM 		    end == p + strlen(p)) {
277*8803SJonathan.Haslam@Sun.COM 			p += strlen(p) + 1;
278*8803SJonathan.Haslam@Sun.COM 		} else {
279*8803SJonathan.Haslam@Sun.COM 			goto err;
280*8803SJonathan.Haslam@Sun.COM 		}
281*8803SJonathan.Haslam@Sun.COM 	}
282*8803SJonathan.Haslam@Sun.COM 
283*8803SJonathan.Haslam@Sun.COM 	/*
284*8803SJonathan.Haslam@Sun.COM 	 * This final part must be an overflow value which has to be greater
285*8803SJonathan.Haslam@Sun.COM 	 * than the minimum permissible overflow rate.
286*8803SJonathan.Haslam@Sun.COM 	 */
287*8803SJonathan.Haslam@Sun.COM 	if ((ddi_strtol(p, &end, 10, &val) != 0) || end != p + strlen(p) ||
288*8803SJonathan.Haslam@Sun.COM 	    val < dcpc_min_overflow)
289*8803SJonathan.Haslam@Sun.COM 		goto err;
290*8803SJonathan.Haslam@Sun.COM 
291*8803SJonathan.Haslam@Sun.COM 	/*
292*8803SJonathan.Haslam@Sun.COM 	 * Validate the event and create the probe.
293*8803SJonathan.Haslam@Sun.COM 	 */
294*8803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
295*8803SJonathan.Haslam@Sun.COM 		if (strstr(kcpc_list_events(i), event) != NULL)
296*8803SJonathan.Haslam@Sun.COM 			dcpc_create_probe(dcpc_pid, desc->dtpd_name, event,
297*8803SJonathan.Haslam@Sun.COM 			    umask, (uint32_t)val, flag);
298*8803SJonathan.Haslam@Sun.COM 	}
299*8803SJonathan.Haslam@Sun.COM 
300*8803SJonathan.Haslam@Sun.COM err:
301*8803SJonathan.Haslam@Sun.COM 	kmem_free(str, len + 1);
302*8803SJonathan.Haslam@Sun.COM }
303*8803SJonathan.Haslam@Sun.COM 
304*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
305*8803SJonathan.Haslam@Sun.COM static void
306*8803SJonathan.Haslam@Sun.COM dcpc_destroy(void *arg, dtrace_id_t id, void *parg)
307*8803SJonathan.Haslam@Sun.COM {
308*8803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp = parg;
309*8803SJonathan.Haslam@Sun.COM 
310*8803SJonathan.Haslam@Sun.COM 	ASSERT(pp->dcpc_enabled == 0);
311*8803SJonathan.Haslam@Sun.COM 	kmem_free(pp, sizeof (dcpc_probe_t));
312*8803SJonathan.Haslam@Sun.COM }
313*8803SJonathan.Haslam@Sun.COM 
314*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
315*8803SJonathan.Haslam@Sun.COM static int
316*8803SJonathan.Haslam@Sun.COM dcpc_usermode(void *arg, dtrace_id_t id, void *parg)
317*8803SJonathan.Haslam@Sun.COM {
318*8803SJonathan.Haslam@Sun.COM 	return (CPU->cpu_cpcprofile_pc == 0);
319*8803SJonathan.Haslam@Sun.COM }
320*8803SJonathan.Haslam@Sun.COM 
321*8803SJonathan.Haslam@Sun.COM static void
322*8803SJonathan.Haslam@Sun.COM dcpc_populate_set(cpu_t *c, dcpc_probe_t *pp, kcpc_set_t *set, int reqno)
323*8803SJonathan.Haslam@Sun.COM {
324*8803SJonathan.Haslam@Sun.COM 	kcpc_set_t *oset;
325*8803SJonathan.Haslam@Sun.COM 	int i;
326*8803SJonathan.Haslam@Sun.COM 
327*8803SJonathan.Haslam@Sun.COM 	(void) strncpy(set->ks_req[reqno].kr_event, pp->dcpc_event_name,
328*8803SJonathan.Haslam@Sun.COM 	    CPC_MAX_EVENT_LEN);
329*8803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_config = NULL;
330*8803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_index = reqno;
331*8803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_picnum = -1;
332*8803SJonathan.Haslam@Sun.COM 	set->ks_req[reqno].kr_flags =  pp->dcpc_flag;
333*8803SJonathan.Haslam@Sun.COM 
334*8803SJonathan.Haslam@Sun.COM 	/*
335*8803SJonathan.Haslam@Sun.COM 	 * If a unit mask has been specified then detect which attribute
336*8803SJonathan.Haslam@Sun.COM 	 * the platform needs. For now, it's either "umask" or "emask".
337*8803SJonathan.Haslam@Sun.COM 	 */
338*8803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_umask >= 0) {
339*8803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_attr =
340*8803SJonathan.Haslam@Sun.COM 		    kmem_zalloc(sizeof (kcpc_attr_t), KM_SLEEP);
341*8803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_nattrs = 1;
342*8803SJonathan.Haslam@Sun.COM 		if (dcpc_mask_type & DCPC_UMASK)
343*8803SJonathan.Haslam@Sun.COM 			(void) strncpy(set->ks_req[reqno].kr_attr->ka_name,
344*8803SJonathan.Haslam@Sun.COM 			    "umask", 5);
345*8803SJonathan.Haslam@Sun.COM 		else
346*8803SJonathan.Haslam@Sun.COM 			(void) strncpy(set->ks_req[reqno].kr_attr->ka_name,
347*8803SJonathan.Haslam@Sun.COM 			    "emask", 5);
348*8803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_attr->ka_val = pp->dcpc_umask;
349*8803SJonathan.Haslam@Sun.COM 	} else {
350*8803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_attr = NULL;
351*8803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_nattrs = 0;
352*8803SJonathan.Haslam@Sun.COM 	}
353*8803SJonathan.Haslam@Sun.COM 
354*8803SJonathan.Haslam@Sun.COM 	/*
355*8803SJonathan.Haslam@Sun.COM 	 * If this probe is enabled, obtain its current countdown value
356*8803SJonathan.Haslam@Sun.COM 	 * and use that. The CPUs cpc context might not exist yet if we
357*8803SJonathan.Haslam@Sun.COM 	 * are dealing with a CPU that is just coming online.
358*8803SJonathan.Haslam@Sun.COM 	 */
359*8803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_enabled && (c->cpu_cpc_ctx != NULL)) {
360*8803SJonathan.Haslam@Sun.COM 		oset = c->cpu_cpc_ctx->kc_set;
361*8803SJonathan.Haslam@Sun.COM 
362*8803SJonathan.Haslam@Sun.COM 		for (i = 0; i < oset->ks_nreqs; i++) {
363*8803SJonathan.Haslam@Sun.COM 			if (strcmp(oset->ks_req[i].kr_event,
364*8803SJonathan.Haslam@Sun.COM 			    set->ks_req[reqno].kr_event) == 0) {
365*8803SJonathan.Haslam@Sun.COM 				set->ks_req[reqno].kr_preset =
366*8803SJonathan.Haslam@Sun.COM 				    *(oset->ks_req[i].kr_data);
367*8803SJonathan.Haslam@Sun.COM 			}
368*8803SJonathan.Haslam@Sun.COM 		}
369*8803SJonathan.Haslam@Sun.COM 	} else {
370*8803SJonathan.Haslam@Sun.COM 		set->ks_req[reqno].kr_preset = UINT64_MAX - pp->dcpc_ovfval;
371*8803SJonathan.Haslam@Sun.COM 	}
372*8803SJonathan.Haslam@Sun.COM 
373*8803SJonathan.Haslam@Sun.COM 	set->ks_nreqs++;
374*8803SJonathan.Haslam@Sun.COM }
375*8803SJonathan.Haslam@Sun.COM 
376*8803SJonathan.Haslam@Sun.COM 
377*8803SJonathan.Haslam@Sun.COM /*
378*8803SJonathan.Haslam@Sun.COM  * Create a fresh request set for the enablings represented in the
379*8803SJonathan.Haslam@Sun.COM  * 'dcpc_actv_reqs' array which contains the probes we want to be
380*8803SJonathan.Haslam@Sun.COM  * in the set. This can be called for several reasons:
381*8803SJonathan.Haslam@Sun.COM  *
382*8803SJonathan.Haslam@Sun.COM  * 1)	We are on a single or multi overflow platform and we have no
383*8803SJonathan.Haslam@Sun.COM  *	current events so we can just create the set and initialize it.
384*8803SJonathan.Haslam@Sun.COM  * 2)	We are on a multi-overflow platform and we already have one or
385*8803SJonathan.Haslam@Sun.COM  *	more existing events and we are adding a new enabling. Create a
386*8803SJonathan.Haslam@Sun.COM  *	new set and copy old requests in and then add the new request.
387*8803SJonathan.Haslam@Sun.COM  * 3)	We are on a multi-overflow platform and we have just removed an
388*8803SJonathan.Haslam@Sun.COM  *	enabling but we still have enablings whch are valid. Create a new
389*8803SJonathan.Haslam@Sun.COM  *	set and copy in still valid requests.
390*8803SJonathan.Haslam@Sun.COM  */
391*8803SJonathan.Haslam@Sun.COM static kcpc_set_t *
392*8803SJonathan.Haslam@Sun.COM dcpc_create_set(cpu_t *c)
393*8803SJonathan.Haslam@Sun.COM {
394*8803SJonathan.Haslam@Sun.COM 	int i, reqno = 0;
395*8803SJonathan.Haslam@Sun.COM 	int active_requests = 0;
396*8803SJonathan.Haslam@Sun.COM 	kcpc_set_t *set;
397*8803SJonathan.Haslam@Sun.COM 
398*8803SJonathan.Haslam@Sun.COM 	/*
399*8803SJonathan.Haslam@Sun.COM 	 * First get a count of the number of currently active requests.
400*8803SJonathan.Haslam@Sun.COM 	 * Note that dcpc_actv_reqs[] should always reflect which requests
401*8803SJonathan.Haslam@Sun.COM 	 * we want to be in the set that is to be created. It is the
402*8803SJonathan.Haslam@Sun.COM 	 * responsibility of the caller of dcpc_create_set() to adjust that
403*8803SJonathan.Haslam@Sun.COM 	 * array accordingly beforehand.
404*8803SJonathan.Haslam@Sun.COM 	 */
405*8803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
406*8803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] != NULL)
407*8803SJonathan.Haslam@Sun.COM 			active_requests++;
408*8803SJonathan.Haslam@Sun.COM 	}
409*8803SJonathan.Haslam@Sun.COM 
410*8803SJonathan.Haslam@Sun.COM 	set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP);
411*8803SJonathan.Haslam@Sun.COM 
412*8803SJonathan.Haslam@Sun.COM 	set->ks_req =
413*8803SJonathan.Haslam@Sun.COM 	    kmem_zalloc(sizeof (kcpc_request_t) * active_requests, KM_SLEEP);
414*8803SJonathan.Haslam@Sun.COM 
415*8803SJonathan.Haslam@Sun.COM 	set->ks_data =
416*8803SJonathan.Haslam@Sun.COM 	    kmem_zalloc(active_requests * sizeof (uint64_t), KM_SLEEP);
417*8803SJonathan.Haslam@Sun.COM 
418*8803SJonathan.Haslam@Sun.COM 	/*
419*8803SJonathan.Haslam@Sun.COM 	 * Look for valid entries in the active requests array and populate
420*8803SJonathan.Haslam@Sun.COM 	 * the request set for any entries found.
421*8803SJonathan.Haslam@Sun.COM 	 */
422*8803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
423*8803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] != NULL) {
424*8803SJonathan.Haslam@Sun.COM 			dcpc_populate_set(c, dcpc_actv_reqs[i], set, reqno);
425*8803SJonathan.Haslam@Sun.COM 			reqno++;
426*8803SJonathan.Haslam@Sun.COM 		}
427*8803SJonathan.Haslam@Sun.COM 	}
428*8803SJonathan.Haslam@Sun.COM 
429*8803SJonathan.Haslam@Sun.COM 	return (set);
430*8803SJonathan.Haslam@Sun.COM }
431*8803SJonathan.Haslam@Sun.COM 
432*8803SJonathan.Haslam@Sun.COM static int
433*8803SJonathan.Haslam@Sun.COM dcpc_program_cpu_event(cpu_t *c)
434*8803SJonathan.Haslam@Sun.COM {
435*8803SJonathan.Haslam@Sun.COM 	int i, j, subcode;
436*8803SJonathan.Haslam@Sun.COM 	kcpc_ctx_t *ctx, *octx;
437*8803SJonathan.Haslam@Sun.COM 	kcpc_set_t *set;
438*8803SJonathan.Haslam@Sun.COM 
439*8803SJonathan.Haslam@Sun.COM 	set = dcpc_create_set(c);
440*8803SJonathan.Haslam@Sun.COM 
441*8803SJonathan.Haslam@Sun.COM 	octx = NULL;
442*8803SJonathan.Haslam@Sun.COM 	set->ks_ctx = ctx = kcpc_ctx_alloc();
443*8803SJonathan.Haslam@Sun.COM 	ctx->kc_set = set;
444*8803SJonathan.Haslam@Sun.COM 	ctx->kc_cpuid = c->cpu_id;
445*8803SJonathan.Haslam@Sun.COM 
446*8803SJonathan.Haslam@Sun.COM 	if (kcpc_assign_reqs(set, ctx) != 0)
447*8803SJonathan.Haslam@Sun.COM 		goto err;
448*8803SJonathan.Haslam@Sun.COM 
449*8803SJonathan.Haslam@Sun.COM 	if (kcpc_configure_reqs(ctx, set, &subcode) != 0)
450*8803SJonathan.Haslam@Sun.COM 		goto err;
451*8803SJonathan.Haslam@Sun.COM 
452*8803SJonathan.Haslam@Sun.COM 	for (i = 0; i < set->ks_nreqs; i++) {
453*8803SJonathan.Haslam@Sun.COM 		for (j = 0; j < cpc_ncounters; j++) {
454*8803SJonathan.Haslam@Sun.COM 			if (dcpc_actv_reqs[j] != NULL &&
455*8803SJonathan.Haslam@Sun.COM 			    strcmp(set->ks_req[i].kr_event,
456*8803SJonathan.Haslam@Sun.COM 			    dcpc_actv_reqs[j]->dcpc_event_name) == 0) {
457*8803SJonathan.Haslam@Sun.COM 				dcpc_actv_reqs[j]->dcpc_picno =
458*8803SJonathan.Haslam@Sun.COM 				    set->ks_req[i].kr_picnum;
459*8803SJonathan.Haslam@Sun.COM 			}
460*8803SJonathan.Haslam@Sun.COM 		}
461*8803SJonathan.Haslam@Sun.COM 	}
462*8803SJonathan.Haslam@Sun.COM 
463*8803SJonathan.Haslam@Sun.COM 	/*
464*8803SJonathan.Haslam@Sun.COM 	 * If we already have an active enabling then save the current cpc
465*8803SJonathan.Haslam@Sun.COM 	 * context away.
466*8803SJonathan.Haslam@Sun.COM 	 */
467*8803SJonathan.Haslam@Sun.COM 	if (c->cpu_cpc_ctx != NULL)
468*8803SJonathan.Haslam@Sun.COM 		octx = c->cpu_cpc_ctx;
469*8803SJonathan.Haslam@Sun.COM 
470*8803SJonathan.Haslam@Sun.COM 	c->cpu_cpc_ctx = ctx;
471*8803SJonathan.Haslam@Sun.COM 	kcpc_remote_program(c);
472*8803SJonathan.Haslam@Sun.COM 
473*8803SJonathan.Haslam@Sun.COM 	if (octx != NULL) {
474*8803SJonathan.Haslam@Sun.COM 		kcpc_set_t *oset = octx->kc_set;
475*8803SJonathan.Haslam@Sun.COM 		kmem_free(oset->ks_data, oset->ks_nreqs * sizeof (uint64_t));
476*8803SJonathan.Haslam@Sun.COM 		kcpc_free_set(oset);
477*8803SJonathan.Haslam@Sun.COM 		kcpc_ctx_free(octx);
478*8803SJonathan.Haslam@Sun.COM 	}
479*8803SJonathan.Haslam@Sun.COM 
480*8803SJonathan.Haslam@Sun.COM 	return (0);
481*8803SJonathan.Haslam@Sun.COM 
482*8803SJonathan.Haslam@Sun.COM err:
483*8803SJonathan.Haslam@Sun.COM 	/*
484*8803SJonathan.Haslam@Sun.COM 	 * We failed to configure this request up so free things up and
485*8803SJonathan.Haslam@Sun.COM 	 * get out.
486*8803SJonathan.Haslam@Sun.COM 	 */
487*8803SJonathan.Haslam@Sun.COM 	kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
488*8803SJonathan.Haslam@Sun.COM 	kcpc_free_set(set);
489*8803SJonathan.Haslam@Sun.COM 	kcpc_ctx_free(ctx);
490*8803SJonathan.Haslam@Sun.COM 
491*8803SJonathan.Haslam@Sun.COM 	return (-1);
492*8803SJonathan.Haslam@Sun.COM }
493*8803SJonathan.Haslam@Sun.COM 
494*8803SJonathan.Haslam@Sun.COM static void
495*8803SJonathan.Haslam@Sun.COM dcpc_disable_cpu(cpu_t *c)
496*8803SJonathan.Haslam@Sun.COM {
497*8803SJonathan.Haslam@Sun.COM 	kcpc_ctx_t *ctx;
498*8803SJonathan.Haslam@Sun.COM 	kcpc_set_t *set;
499*8803SJonathan.Haslam@Sun.COM 
500*8803SJonathan.Haslam@Sun.COM 	/*
501*8803SJonathan.Haslam@Sun.COM 	 * Leave this CPU alone if it's already offline.
502*8803SJonathan.Haslam@Sun.COM 	 */
503*8803SJonathan.Haslam@Sun.COM 	if (c->cpu_flags & CPU_OFFLINE)
504*8803SJonathan.Haslam@Sun.COM 		return;
505*8803SJonathan.Haslam@Sun.COM 
506*8803SJonathan.Haslam@Sun.COM 	kcpc_remote_stop(c);
507*8803SJonathan.Haslam@Sun.COM 
508*8803SJonathan.Haslam@Sun.COM 	ctx = c->cpu_cpc_ctx;
509*8803SJonathan.Haslam@Sun.COM 	set = ctx->kc_set;
510*8803SJonathan.Haslam@Sun.COM 
511*8803SJonathan.Haslam@Sun.COM 	kcpc_free_configs(set);
512*8803SJonathan.Haslam@Sun.COM 
513*8803SJonathan.Haslam@Sun.COM 	kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t));
514*8803SJonathan.Haslam@Sun.COM 	kcpc_free_set(set);
515*8803SJonathan.Haslam@Sun.COM 	kcpc_ctx_free(ctx);
516*8803SJonathan.Haslam@Sun.COM 	c->cpu_cpc_ctx = NULL;
517*8803SJonathan.Haslam@Sun.COM }
518*8803SJonathan.Haslam@Sun.COM 
519*8803SJonathan.Haslam@Sun.COM /*
520*8803SJonathan.Haslam@Sun.COM  * Stop overflow interrupts being actively processed so that per-CPU
521*8803SJonathan.Haslam@Sun.COM  * configuration state can be changed safely and correctly. Each CPU has a
522*8803SJonathan.Haslam@Sun.COM  * dcpc interrupt state byte which is transitioned from DCPC_INTR_FREE (the
523*8803SJonathan.Haslam@Sun.COM  * "free" state) to DCPC_INTR_CONFIG (the "configuration in process" state)
524*8803SJonathan.Haslam@Sun.COM  * before any configuration state is changed on any CPUs. The hardware overflow
525*8803SJonathan.Haslam@Sun.COM  * handler, kcpc_hw_overflow_intr(), will only process an interrupt when a
526*8803SJonathan.Haslam@Sun.COM  * configuration is not in process (i.e. the state is marked as free). During
527*8803SJonathan.Haslam@Sun.COM  * interrupt processing the state is set to DCPC_INTR_PROCESSING by the
528*8803SJonathan.Haslam@Sun.COM  * overflow handler.
529*8803SJonathan.Haslam@Sun.COM  */
530*8803SJonathan.Haslam@Sun.COM static void
531*8803SJonathan.Haslam@Sun.COM dcpc_block_interrupts(void)
532*8803SJonathan.Haslam@Sun.COM {
533*8803SJonathan.Haslam@Sun.COM 	cpu_t *c;
534*8803SJonathan.Haslam@Sun.COM 	uint8_t *state;
535*8803SJonathan.Haslam@Sun.COM 
536*8803SJonathan.Haslam@Sun.COM 	c = cpu_list;
537*8803SJonathan.Haslam@Sun.COM 
538*8803SJonathan.Haslam@Sun.COM 	do {
539*8803SJonathan.Haslam@Sun.COM 		state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state;
540*8803SJonathan.Haslam@Sun.COM 
541*8803SJonathan.Haslam@Sun.COM 		while (atomic_cas_8(state, DCPC_INTR_FREE,
542*8803SJonathan.Haslam@Sun.COM 		    DCPC_INTR_CONFIG) != DCPC_INTR_FREE)
543*8803SJonathan.Haslam@Sun.COM 			continue;
544*8803SJonathan.Haslam@Sun.COM 
545*8803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list);
546*8803SJonathan.Haslam@Sun.COM }
547*8803SJonathan.Haslam@Sun.COM 
548*8803SJonathan.Haslam@Sun.COM /*
549*8803SJonathan.Haslam@Sun.COM  * Set all CPUs dcpc interrupt state to DCPC_INTR_FREE to indicate that
550*8803SJonathan.Haslam@Sun.COM  * overflow interrupts can be processed safely.
551*8803SJonathan.Haslam@Sun.COM  */
552*8803SJonathan.Haslam@Sun.COM static void
553*8803SJonathan.Haslam@Sun.COM dcpc_release_interrupts(void)
554*8803SJonathan.Haslam@Sun.COM {
555*8803SJonathan.Haslam@Sun.COM 	cpu_t *c = cpu_list;
556*8803SJonathan.Haslam@Sun.COM 
557*8803SJonathan.Haslam@Sun.COM 	do {
558*8803SJonathan.Haslam@Sun.COM 		cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE;
559*8803SJonathan.Haslam@Sun.COM 		membar_producer();
560*8803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list);
561*8803SJonathan.Haslam@Sun.COM }
562*8803SJonathan.Haslam@Sun.COM 
563*8803SJonathan.Haslam@Sun.COM /*
564*8803SJonathan.Haslam@Sun.COM  * dcpc_program_event() can be called owing to a new enabling or if a multi
565*8803SJonathan.Haslam@Sun.COM  * overflow platform has disabled a request but needs to  program the requests
566*8803SJonathan.Haslam@Sun.COM  * that are still valid.
567*8803SJonathan.Haslam@Sun.COM  *
568*8803SJonathan.Haslam@Sun.COM  * Every invocation of dcpc_program_event() will create a new kcpc_ctx_t
569*8803SJonathan.Haslam@Sun.COM  * and a new request set which contains the new enabling and any old enablings
570*8803SJonathan.Haslam@Sun.COM  * which are still valid (possible with multi-overflow platforms).
571*8803SJonathan.Haslam@Sun.COM  */
572*8803SJonathan.Haslam@Sun.COM static int
573*8803SJonathan.Haslam@Sun.COM dcpc_program_event(dcpc_probe_t *pp)
574*8803SJonathan.Haslam@Sun.COM {
575*8803SJonathan.Haslam@Sun.COM 	cpu_t *c;
576*8803SJonathan.Haslam@Sun.COM 	int ret = 0;
577*8803SJonathan.Haslam@Sun.COM 
578*8803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
579*8803SJonathan.Haslam@Sun.COM 
580*8803SJonathan.Haslam@Sun.COM 	kpreempt_disable();
581*8803SJonathan.Haslam@Sun.COM 
582*8803SJonathan.Haslam@Sun.COM 	dcpc_block_interrupts();
583*8803SJonathan.Haslam@Sun.COM 
584*8803SJonathan.Haslam@Sun.COM 	c = cpu_list;
585*8803SJonathan.Haslam@Sun.COM 
586*8803SJonathan.Haslam@Sun.COM 	do {
587*8803SJonathan.Haslam@Sun.COM 		/*
588*8803SJonathan.Haslam@Sun.COM 		 * Skip CPUs that are currently offline.
589*8803SJonathan.Haslam@Sun.COM 		 */
590*8803SJonathan.Haslam@Sun.COM 		if (c->cpu_flags & CPU_OFFLINE)
591*8803SJonathan.Haslam@Sun.COM 			continue;
592*8803SJonathan.Haslam@Sun.COM 
593*8803SJonathan.Haslam@Sun.COM 		if (c->cpu_cpc_ctx != NULL)
594*8803SJonathan.Haslam@Sun.COM 			kcpc_remote_stop(c);
595*8803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list);
596*8803SJonathan.Haslam@Sun.COM 
597*8803SJonathan.Haslam@Sun.COM 	dcpc_release_interrupts();
598*8803SJonathan.Haslam@Sun.COM 
599*8803SJonathan.Haslam@Sun.COM 	/*
600*8803SJonathan.Haslam@Sun.COM 	 * If this enabling is being removed (in the case of a multi event
601*8803SJonathan.Haslam@Sun.COM 	 * capable system with more than one active enabling), we can now
602*8803SJonathan.Haslam@Sun.COM 	 * update the active request array to reflect the enablings that need
603*8803SJonathan.Haslam@Sun.COM 	 * to be reprogrammed.
604*8803SJonathan.Haslam@Sun.COM 	 */
605*8803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_disabling == 1)
606*8803SJonathan.Haslam@Sun.COM 		dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
607*8803SJonathan.Haslam@Sun.COM 
608*8803SJonathan.Haslam@Sun.COM 	do {
609*8803SJonathan.Haslam@Sun.COM 		/*
610*8803SJonathan.Haslam@Sun.COM 		 * Skip CPUs that are currently offline.
611*8803SJonathan.Haslam@Sun.COM 		 */
612*8803SJonathan.Haslam@Sun.COM 		if (c->cpu_flags & CPU_OFFLINE)
613*8803SJonathan.Haslam@Sun.COM 			continue;
614*8803SJonathan.Haslam@Sun.COM 
615*8803SJonathan.Haslam@Sun.COM 		ret = dcpc_program_cpu_event(c);
616*8803SJonathan.Haslam@Sun.COM 	} while ((c = c->cpu_next) != cpu_list && ret == 0);
617*8803SJonathan.Haslam@Sun.COM 
618*8803SJonathan.Haslam@Sun.COM 	/*
619*8803SJonathan.Haslam@Sun.COM 	 * If dcpc_program_cpu_event() fails then it is because we couldn't
620*8803SJonathan.Haslam@Sun.COM 	 * configure the requests in the set for the CPU and not because of
621*8803SJonathan.Haslam@Sun.COM 	 * an error programming the hardware. If we have a failure here then
622*8803SJonathan.Haslam@Sun.COM 	 * we assume no CPUs have been programmed in the above step as they
623*8803SJonathan.Haslam@Sun.COM 	 * are all configured identically.
624*8803SJonathan.Haslam@Sun.COM 	 */
625*8803SJonathan.Haslam@Sun.COM 	if (ret != 0) {
626*8803SJonathan.Haslam@Sun.COM 		pp->dcpc_enabled = 0;
627*8803SJonathan.Haslam@Sun.COM 		kpreempt_enable();
628*8803SJonathan.Haslam@Sun.COM 		return (-1);
629*8803SJonathan.Haslam@Sun.COM 	}
630*8803SJonathan.Haslam@Sun.COM 
631*8803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_disabling != 1)
632*8803SJonathan.Haslam@Sun.COM 		pp->dcpc_enabled = 1;
633*8803SJonathan.Haslam@Sun.COM 
634*8803SJonathan.Haslam@Sun.COM 	kpreempt_enable();
635*8803SJonathan.Haslam@Sun.COM 
636*8803SJonathan.Haslam@Sun.COM 	return (0);
637*8803SJonathan.Haslam@Sun.COM }
638*8803SJonathan.Haslam@Sun.COM 
639*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
640*8803SJonathan.Haslam@Sun.COM static int
641*8803SJonathan.Haslam@Sun.COM dcpc_enable(void *arg, dtrace_id_t id, void *parg)
642*8803SJonathan.Haslam@Sun.COM {
643*8803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp = parg;
644*8803SJonathan.Haslam@Sun.COM 	int i, found = 0;
645*8803SJonathan.Haslam@Sun.COM 	cpu_t *c;
646*8803SJonathan.Haslam@Sun.COM 
647*8803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
648*8803SJonathan.Haslam@Sun.COM 
649*8803SJonathan.Haslam@Sun.COM 	/*
650*8803SJonathan.Haslam@Sun.COM 	 * Bail out if the counters are being used by a libcpc consumer.
651*8803SJonathan.Haslam@Sun.COM 	 */
652*8803SJonathan.Haslam@Sun.COM 	rw_enter(&kcpc_cpuctx_lock, RW_READER);
653*8803SJonathan.Haslam@Sun.COM 	if (kcpc_cpuctx > 0) {
654*8803SJonathan.Haslam@Sun.COM 		rw_exit(&kcpc_cpuctx_lock);
655*8803SJonathan.Haslam@Sun.COM 		return (-1);
656*8803SJonathan.Haslam@Sun.COM 	}
657*8803SJonathan.Haslam@Sun.COM 
658*8803SJonathan.Haslam@Sun.COM 	dtrace_cpc_in_use++;
659*8803SJonathan.Haslam@Sun.COM 	rw_exit(&kcpc_cpuctx_lock);
660*8803SJonathan.Haslam@Sun.COM 
661*8803SJonathan.Haslam@Sun.COM 	/*
662*8803SJonathan.Haslam@Sun.COM 	 * Locate this enabling in the first free entry of the active
663*8803SJonathan.Haslam@Sun.COM 	 * request array.
664*8803SJonathan.Haslam@Sun.COM 	 */
665*8803SJonathan.Haslam@Sun.COM 	for (i = 0; i < cpc_ncounters; i++) {
666*8803SJonathan.Haslam@Sun.COM 		if (dcpc_actv_reqs[i] == NULL) {
667*8803SJonathan.Haslam@Sun.COM 			dcpc_actv_reqs[i] = pp;
668*8803SJonathan.Haslam@Sun.COM 			pp->dcpc_actv_req_idx = i;
669*8803SJonathan.Haslam@Sun.COM 			found = 1;
670*8803SJonathan.Haslam@Sun.COM 			break;
671*8803SJonathan.Haslam@Sun.COM 		}
672*8803SJonathan.Haslam@Sun.COM 	}
673*8803SJonathan.Haslam@Sun.COM 
674*8803SJonathan.Haslam@Sun.COM 	/*
675*8803SJonathan.Haslam@Sun.COM 	 * If we couldn't find a slot for this probe then there is no
676*8803SJonathan.Haslam@Sun.COM 	 * room at the inn.
677*8803SJonathan.Haslam@Sun.COM 	 */
678*8803SJonathan.Haslam@Sun.COM 	if (!found) {
679*8803SJonathan.Haslam@Sun.COM 		dtrace_cpc_in_use--;
680*8803SJonathan.Haslam@Sun.COM 		return (-1);
681*8803SJonathan.Haslam@Sun.COM 	}
682*8803SJonathan.Haslam@Sun.COM 
683*8803SJonathan.Haslam@Sun.COM 	ASSERT(pp->dcpc_actv_req_idx >= 0);
684*8803SJonathan.Haslam@Sun.COM 
685*8803SJonathan.Haslam@Sun.COM 	/*
686*8803SJonathan.Haslam@Sun.COM 	 * The following must hold true if we are to (attempt to) enable
687*8803SJonathan.Haslam@Sun.COM 	 * this request:
688*8803SJonathan.Haslam@Sun.COM 	 *
689*8803SJonathan.Haslam@Sun.COM 	 * 1) No enablings currently exist. We allow all platforms to
690*8803SJonathan.Haslam@Sun.COM 	 * proceed if this is true.
691*8803SJonathan.Haslam@Sun.COM 	 *
692*8803SJonathan.Haslam@Sun.COM 	 * OR
693*8803SJonathan.Haslam@Sun.COM 	 *
694*8803SJonathan.Haslam@Sun.COM 	 * 2) If the platform is multi overflow capable and there are
695*8803SJonathan.Haslam@Sun.COM 	 * less valid enablings than there are counters. There is no
696*8803SJonathan.Haslam@Sun.COM 	 * guarantee that a platform can accommodate as many events as
697*8803SJonathan.Haslam@Sun.COM 	 * it has counters for but we will at least try to program
698*8803SJonathan.Haslam@Sun.COM 	 * up to that many requests.
699*8803SJonathan.Haslam@Sun.COM 	 *
700*8803SJonathan.Haslam@Sun.COM 	 * The 'dcpc_enablings' variable is implictly protected by locking
701*8803SJonathan.Haslam@Sun.COM 	 * provided by the DTrace framework and the cpu management framework.
702*8803SJonathan.Haslam@Sun.COM 	 */
703*8803SJonathan.Haslam@Sun.COM 	if (dcpc_enablings == 0 || (dcpc_mult_ovf_cap &&
704*8803SJonathan.Haslam@Sun.COM 	    dcpc_enablings < cpc_ncounters)) {
705*8803SJonathan.Haslam@Sun.COM 		/*
706*8803SJonathan.Haslam@Sun.COM 		 * Before attempting to program the first enabling we need to
707*8803SJonathan.Haslam@Sun.COM 		 * invalidate any lwp-based contexts.
708*8803SJonathan.Haslam@Sun.COM 		 */
709*8803SJonathan.Haslam@Sun.COM 		if (dcpc_enablings == 0)
710*8803SJonathan.Haslam@Sun.COM 			kcpc_invalidate_all();
711*8803SJonathan.Haslam@Sun.COM 
712*8803SJonathan.Haslam@Sun.COM 		if (dcpc_program_event(pp) == 0) {
713*8803SJonathan.Haslam@Sun.COM 			dcpc_enablings++;
714*8803SJonathan.Haslam@Sun.COM 			return (0);
715*8803SJonathan.Haslam@Sun.COM 		}
716*8803SJonathan.Haslam@Sun.COM 	}
717*8803SJonathan.Haslam@Sun.COM 
718*8803SJonathan.Haslam@Sun.COM 	/*
719*8803SJonathan.Haslam@Sun.COM 	 * If active enablings existed before we failed to enable this probe
720*8803SJonathan.Haslam@Sun.COM 	 * on a multi event capable platform then we need to restart counters
721*8803SJonathan.Haslam@Sun.COM 	 * as they will have been stopped in the attempted configuration. The
722*8803SJonathan.Haslam@Sun.COM 	 * context should now just contain the request prior to this failed
723*8803SJonathan.Haslam@Sun.COM 	 * enabling.
724*8803SJonathan.Haslam@Sun.COM 	 */
725*8803SJonathan.Haslam@Sun.COM 	if (dcpc_enablings > 0 && dcpc_mult_ovf_cap) {
726*8803SJonathan.Haslam@Sun.COM 		c = cpu_list;
727*8803SJonathan.Haslam@Sun.COM 
728*8803SJonathan.Haslam@Sun.COM 		ASSERT(dcpc_mult_ovf_cap == 1);
729*8803SJonathan.Haslam@Sun.COM 		do {
730*8803SJonathan.Haslam@Sun.COM 			/*
731*8803SJonathan.Haslam@Sun.COM 			 * Skip CPUs that are currently offline.
732*8803SJonathan.Haslam@Sun.COM 			 */
733*8803SJonathan.Haslam@Sun.COM 			if (c->cpu_flags & CPU_OFFLINE)
734*8803SJonathan.Haslam@Sun.COM 				continue;
735*8803SJonathan.Haslam@Sun.COM 
736*8803SJonathan.Haslam@Sun.COM 			kcpc_remote_program(c);
737*8803SJonathan.Haslam@Sun.COM 		} while ((c = c->cpu_next) != cpu_list);
738*8803SJonathan.Haslam@Sun.COM 	}
739*8803SJonathan.Haslam@Sun.COM 
740*8803SJonathan.Haslam@Sun.COM 	dtrace_cpc_in_use--;
741*8803SJonathan.Haslam@Sun.COM 	dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
742*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_actv_req_idx = pp->dcpc_picno = -1;
743*8803SJonathan.Haslam@Sun.COM 
744*8803SJonathan.Haslam@Sun.COM 	return (-1);
745*8803SJonathan.Haslam@Sun.COM }
746*8803SJonathan.Haslam@Sun.COM 
747*8803SJonathan.Haslam@Sun.COM /*
748*8803SJonathan.Haslam@Sun.COM  * If only one enabling is active then remove the context and free
749*8803SJonathan.Haslam@Sun.COM  * everything up. If there are multiple enablings active then remove this
750*8803SJonathan.Haslam@Sun.COM  * one, its associated meta-data and re-program the hardware.
751*8803SJonathan.Haslam@Sun.COM  */
752*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
753*8803SJonathan.Haslam@Sun.COM static void
754*8803SJonathan.Haslam@Sun.COM dcpc_disable(void *arg, dtrace_id_t id, void *parg)
755*8803SJonathan.Haslam@Sun.COM {
756*8803SJonathan.Haslam@Sun.COM 	cpu_t *c;
757*8803SJonathan.Haslam@Sun.COM 	dcpc_probe_t *pp = parg;
758*8803SJonathan.Haslam@Sun.COM 
759*8803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
760*8803SJonathan.Haslam@Sun.COM 
761*8803SJonathan.Haslam@Sun.COM 	kpreempt_disable();
762*8803SJonathan.Haslam@Sun.COM 
763*8803SJonathan.Haslam@Sun.COM 	/*
764*8803SJonathan.Haslam@Sun.COM 	 * This probe didn't actually make it as far as being fully enabled
765*8803SJonathan.Haslam@Sun.COM 	 * so we needn't do anything with it.
766*8803SJonathan.Haslam@Sun.COM 	 */
767*8803SJonathan.Haslam@Sun.COM 	if (pp->dcpc_enabled == 0) {
768*8803SJonathan.Haslam@Sun.COM 		/*
769*8803SJonathan.Haslam@Sun.COM 		 * If we actually allocated this request a slot in the
770*8803SJonathan.Haslam@Sun.COM 		 * request array but failed to enabled it then remove the
771*8803SJonathan.Haslam@Sun.COM 		 * entry in the array.
772*8803SJonathan.Haslam@Sun.COM 		 */
773*8803SJonathan.Haslam@Sun.COM 		if (pp->dcpc_actv_req_idx >= 0) {
774*8803SJonathan.Haslam@Sun.COM 			dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
775*8803SJonathan.Haslam@Sun.COM 			pp->dcpc_actv_req_idx = pp->dcpc_picno =
776*8803SJonathan.Haslam@Sun.COM 			    pp->dcpc_disabling = -1;
777*8803SJonathan.Haslam@Sun.COM 		}
778*8803SJonathan.Haslam@Sun.COM 
779*8803SJonathan.Haslam@Sun.COM 		kpreempt_enable();
780*8803SJonathan.Haslam@Sun.COM 		return;
781*8803SJonathan.Haslam@Sun.COM 	}
782*8803SJonathan.Haslam@Sun.COM 
783*8803SJonathan.Haslam@Sun.COM 	/*
784*8803SJonathan.Haslam@Sun.COM 	 * If this is the only enabling then stop all the counters and
785*8803SJonathan.Haslam@Sun.COM 	 * free up the meta-data.
786*8803SJonathan.Haslam@Sun.COM 	 */
787*8803SJonathan.Haslam@Sun.COM 	if (dcpc_enablings == 1) {
788*8803SJonathan.Haslam@Sun.COM 		ASSERT(dtrace_cpc_in_use == 1);
789*8803SJonathan.Haslam@Sun.COM 
790*8803SJonathan.Haslam@Sun.COM 		dcpc_block_interrupts();
791*8803SJonathan.Haslam@Sun.COM 
792*8803SJonathan.Haslam@Sun.COM 		c = cpu_list;
793*8803SJonathan.Haslam@Sun.COM 
794*8803SJonathan.Haslam@Sun.COM 		do {
795*8803SJonathan.Haslam@Sun.COM 			dcpc_disable_cpu(c);
796*8803SJonathan.Haslam@Sun.COM 		} while ((c = c->cpu_next) != cpu_list);
797*8803SJonathan.Haslam@Sun.COM 
798*8803SJonathan.Haslam@Sun.COM 		dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL;
799*8803SJonathan.Haslam@Sun.COM 		dcpc_release_interrupts();
800*8803SJonathan.Haslam@Sun.COM 	} else {
801*8803SJonathan.Haslam@Sun.COM 		/*
802*8803SJonathan.Haslam@Sun.COM 		 * This platform can support multiple overflow events and
803*8803SJonathan.Haslam@Sun.COM 		 * the enabling being disabled is not the last one. Remove this
804*8803SJonathan.Haslam@Sun.COM 		 * enabling and re-program the hardware with the new config.
805*8803SJonathan.Haslam@Sun.COM 		 */
806*8803SJonathan.Haslam@Sun.COM 		ASSERT(dcpc_mult_ovf_cap);
807*8803SJonathan.Haslam@Sun.COM 		ASSERT(dcpc_enablings > 1);
808*8803SJonathan.Haslam@Sun.COM 
809*8803SJonathan.Haslam@Sun.COM 		pp->dcpc_disabling = 1;
810*8803SJonathan.Haslam@Sun.COM 		(void) dcpc_program_event(pp);
811*8803SJonathan.Haslam@Sun.COM 	}
812*8803SJonathan.Haslam@Sun.COM 
813*8803SJonathan.Haslam@Sun.COM 	kpreempt_enable();
814*8803SJonathan.Haslam@Sun.COM 
815*8803SJonathan.Haslam@Sun.COM 	dcpc_enablings--;
816*8803SJonathan.Haslam@Sun.COM 	dtrace_cpc_in_use--;
817*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_enabled = 0;
818*8803SJonathan.Haslam@Sun.COM 	pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1;
819*8803SJonathan.Haslam@Sun.COM }
820*8803SJonathan.Haslam@Sun.COM 
821*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
822*8803SJonathan.Haslam@Sun.COM static int
823*8803SJonathan.Haslam@Sun.COM dcpc_cpu_setup(cpu_setup_t what, processorid_t cpu, void *arg)
824*8803SJonathan.Haslam@Sun.COM {
825*8803SJonathan.Haslam@Sun.COM 	cpu_t *c;
826*8803SJonathan.Haslam@Sun.COM 	uint8_t *state;
827*8803SJonathan.Haslam@Sun.COM 
828*8803SJonathan.Haslam@Sun.COM 	ASSERT(MUTEX_HELD(&cpu_lock));
829*8803SJonathan.Haslam@Sun.COM 
830*8803SJonathan.Haslam@Sun.COM 	switch (what) {
831*8803SJonathan.Haslam@Sun.COM 	case CPU_OFF:
832*8803SJonathan.Haslam@Sun.COM 		/*
833*8803SJonathan.Haslam@Sun.COM 		 * Offline CPUs are not allowed to take part so remove this
834*8803SJonathan.Haslam@Sun.COM 		 * CPU if we are actively tracing.
835*8803SJonathan.Haslam@Sun.COM 		 */
836*8803SJonathan.Haslam@Sun.COM 		if (dtrace_cpc_in_use) {
837*8803SJonathan.Haslam@Sun.COM 			c = cpu_get(cpu);
838*8803SJonathan.Haslam@Sun.COM 			state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state;
839*8803SJonathan.Haslam@Sun.COM 
840*8803SJonathan.Haslam@Sun.COM 			/*
841*8803SJonathan.Haslam@Sun.COM 			 * Indicate that a configuration is in process in
842*8803SJonathan.Haslam@Sun.COM 			 * order to stop overflow interrupts being processed
843*8803SJonathan.Haslam@Sun.COM 			 * on this CPU while we disable it.
844*8803SJonathan.Haslam@Sun.COM 			 */
845*8803SJonathan.Haslam@Sun.COM 			while (atomic_cas_8(state, DCPC_INTR_FREE,
846*8803SJonathan.Haslam@Sun.COM 			    DCPC_INTR_CONFIG) != DCPC_INTR_FREE)
847*8803SJonathan.Haslam@Sun.COM 				continue;
848*8803SJonathan.Haslam@Sun.COM 
849*8803SJonathan.Haslam@Sun.COM 			dcpc_disable_cpu(c);
850*8803SJonathan.Haslam@Sun.COM 
851*8803SJonathan.Haslam@Sun.COM 			/*
852*8803SJonathan.Haslam@Sun.COM 			 * Reset this CPUs interrupt state as the configuration
853*8803SJonathan.Haslam@Sun.COM 			 * has ended.
854*8803SJonathan.Haslam@Sun.COM 			 */
855*8803SJonathan.Haslam@Sun.COM 			cpu_core[c->cpu_id].cpuc_dcpc_intr_state =
856*8803SJonathan.Haslam@Sun.COM 			    DCPC_INTR_FREE;
857*8803SJonathan.Haslam@Sun.COM 			membar_producer();
858*8803SJonathan.Haslam@Sun.COM 		}
859*8803SJonathan.Haslam@Sun.COM 		break;
860*8803SJonathan.Haslam@Sun.COM 
861*8803SJonathan.Haslam@Sun.COM 	case CPU_ON:
862*8803SJonathan.Haslam@Sun.COM 	case CPU_SETUP:
863*8803SJonathan.Haslam@Sun.COM 		/*
864*8803SJonathan.Haslam@Sun.COM 		 * This CPU is being initialized or brought online so program
865*8803SJonathan.Haslam@Sun.COM 		 * it with the current request set if we are actively tracing.
866*8803SJonathan.Haslam@Sun.COM 		 */
867*8803SJonathan.Haslam@Sun.COM 		if (dtrace_cpc_in_use) {
868*8803SJonathan.Haslam@Sun.COM 			c = cpu_get(cpu);
869*8803SJonathan.Haslam@Sun.COM 
870*8803SJonathan.Haslam@Sun.COM 			(void) dcpc_program_cpu_event(c);
871*8803SJonathan.Haslam@Sun.COM 		}
872*8803SJonathan.Haslam@Sun.COM 		break;
873*8803SJonathan.Haslam@Sun.COM 
874*8803SJonathan.Haslam@Sun.COM 	default:
875*8803SJonathan.Haslam@Sun.COM 		break;
876*8803SJonathan.Haslam@Sun.COM 	}
877*8803SJonathan.Haslam@Sun.COM 
878*8803SJonathan.Haslam@Sun.COM 	return (0);
879*8803SJonathan.Haslam@Sun.COM }
880*8803SJonathan.Haslam@Sun.COM 
881*8803SJonathan.Haslam@Sun.COM static dtrace_pattr_t dcpc_attr = {
882*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
883*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
884*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
885*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU },
886*8803SJonathan.Haslam@Sun.COM { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
887*8803SJonathan.Haslam@Sun.COM };
888*8803SJonathan.Haslam@Sun.COM 
889*8803SJonathan.Haslam@Sun.COM static dtrace_pops_t dcpc_pops = {
890*8803SJonathan.Haslam@Sun.COM     dcpc_provide,
891*8803SJonathan.Haslam@Sun.COM     NULL,
892*8803SJonathan.Haslam@Sun.COM     dcpc_enable,
893*8803SJonathan.Haslam@Sun.COM     dcpc_disable,
894*8803SJonathan.Haslam@Sun.COM     NULL,
895*8803SJonathan.Haslam@Sun.COM     NULL,
896*8803SJonathan.Haslam@Sun.COM     NULL,
897*8803SJonathan.Haslam@Sun.COM     NULL,
898*8803SJonathan.Haslam@Sun.COM     dcpc_usermode,
899*8803SJonathan.Haslam@Sun.COM     dcpc_destroy
900*8803SJonathan.Haslam@Sun.COM };
901*8803SJonathan.Haslam@Sun.COM 
902*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
903*8803SJonathan.Haslam@Sun.COM static int
904*8803SJonathan.Haslam@Sun.COM dcpc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
905*8803SJonathan.Haslam@Sun.COM {
906*8803SJonathan.Haslam@Sun.COM 	return (0);
907*8803SJonathan.Haslam@Sun.COM }
908*8803SJonathan.Haslam@Sun.COM 
909*8803SJonathan.Haslam@Sun.COM /*ARGSUSED*/
910*8803SJonathan.Haslam@Sun.COM static int
911*8803SJonathan.Haslam@Sun.COM dcpc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
912*8803SJonathan.Haslam@Sun.COM {
913*8803SJonathan.Haslam@Sun.COM 	int error;
914*8803SJonathan.Haslam@Sun.COM 
915*8803SJonathan.Haslam@Sun.COM 	switch (infocmd) {
916*8803SJonathan.Haslam@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
917*8803SJonathan.Haslam@Sun.COM 		*result = (void *)dcpc_devi;
918*8803SJonathan.Haslam@Sun.COM 		error = DDI_SUCCESS;
919*8803SJonathan.Haslam@Sun.COM 		break;
920*8803SJonathan.Haslam@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
921*8803SJonathan.Haslam@Sun.COM 		*result = (void *)0;
922*8803SJonathan.Haslam@Sun.COM 		error = DDI_SUCCESS;
923*8803SJonathan.Haslam@Sun.COM 		break;
924*8803SJonathan.Haslam@Sun.COM 	default:
925*8803SJonathan.Haslam@Sun.COM 		error = DDI_FAILURE;
926*8803SJonathan.Haslam@Sun.COM 	}
927*8803SJonathan.Haslam@Sun.COM 	return (error);
928*8803SJonathan.Haslam@Sun.COM }
929*8803SJonathan.Haslam@Sun.COM 
930*8803SJonathan.Haslam@Sun.COM static int
931*8803SJonathan.Haslam@Sun.COM dcpc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
932*8803SJonathan.Haslam@Sun.COM {
933*8803SJonathan.Haslam@Sun.COM 	switch (cmd) {
934*8803SJonathan.Haslam@Sun.COM 	case DDI_DETACH:
935*8803SJonathan.Haslam@Sun.COM 		break;
936*8803SJonathan.Haslam@Sun.COM 	case DDI_SUSPEND:
937*8803SJonathan.Haslam@Sun.COM 		return (DDI_SUCCESS);
938*8803SJonathan.Haslam@Sun.COM 	default:
939*8803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
940*8803SJonathan.Haslam@Sun.COM 	}
941*8803SJonathan.Haslam@Sun.COM 
942*8803SJonathan.Haslam@Sun.COM 	if (dtrace_unregister(dcpc_pid) != 0)
943*8803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
944*8803SJonathan.Haslam@Sun.COM 
945*8803SJonathan.Haslam@Sun.COM 	ddi_remove_minor_node(devi, NULL);
946*8803SJonathan.Haslam@Sun.COM 
947*8803SJonathan.Haslam@Sun.COM 	mutex_enter(&cpu_lock);
948*8803SJonathan.Haslam@Sun.COM 	unregister_cpu_setup_func(dcpc_cpu_setup, NULL);
949*8803SJonathan.Haslam@Sun.COM 	mutex_exit(&cpu_lock);
950*8803SJonathan.Haslam@Sun.COM 
951*8803SJonathan.Haslam@Sun.COM 	kmem_free(dcpc_actv_reqs, cpc_ncounters * sizeof (dcpc_probe_t *));
952*8803SJonathan.Haslam@Sun.COM 
953*8803SJonathan.Haslam@Sun.COM 	kcpc_unregister_dcpc();
954*8803SJonathan.Haslam@Sun.COM 
955*8803SJonathan.Haslam@Sun.COM 	return (DDI_SUCCESS);
956*8803SJonathan.Haslam@Sun.COM }
957*8803SJonathan.Haslam@Sun.COM 
958*8803SJonathan.Haslam@Sun.COM static int
959*8803SJonathan.Haslam@Sun.COM dcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
960*8803SJonathan.Haslam@Sun.COM {
961*8803SJonathan.Haslam@Sun.COM 	uint_t caps;
962*8803SJonathan.Haslam@Sun.COM 	char *attrs;
963*8803SJonathan.Haslam@Sun.COM 
964*8803SJonathan.Haslam@Sun.COM 	switch (cmd) {
965*8803SJonathan.Haslam@Sun.COM 	case DDI_ATTACH:
966*8803SJonathan.Haslam@Sun.COM 		break;
967*8803SJonathan.Haslam@Sun.COM 	case DDI_RESUME:
968*8803SJonathan.Haslam@Sun.COM 		return (DDI_SUCCESS);
969*8803SJonathan.Haslam@Sun.COM 	default:
970*8803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
971*8803SJonathan.Haslam@Sun.COM 	}
972*8803SJonathan.Haslam@Sun.COM 
973*8803SJonathan.Haslam@Sun.COM 	if (kcpc_pcbe_loaded() == -1)
974*8803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
975*8803SJonathan.Haslam@Sun.COM 
976*8803SJonathan.Haslam@Sun.COM 	caps = kcpc_pcbe_capabilities();
977*8803SJonathan.Haslam@Sun.COM 
978*8803SJonathan.Haslam@Sun.COM 	if (!(caps & CPC_CAP_OVERFLOW_INTERRUPT)) {
979*8803SJonathan.Haslam@Sun.COM 		cmn_err(CE_WARN, "dcpc: Counter Overflow not supported"\
980*8803SJonathan.Haslam@Sun.COM 		    " on this processor\n");
981*8803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
982*8803SJonathan.Haslam@Sun.COM 	}
983*8803SJonathan.Haslam@Sun.COM 
984*8803SJonathan.Haslam@Sun.COM 	if (ddi_create_minor_node(devi, "dcpc", S_IFCHR, 0,
985*8803SJonathan.Haslam@Sun.COM 	    DDI_PSEUDO, NULL) == DDI_FAILURE ||
986*8803SJonathan.Haslam@Sun.COM 	    dtrace_register("cpc", &dcpc_attr, DTRACE_PRIV_KERNEL,
987*8803SJonathan.Haslam@Sun.COM 	    NULL, &dcpc_pops, NULL, &dcpc_pid) != 0) {
988*8803SJonathan.Haslam@Sun.COM 		ddi_remove_minor_node(devi, NULL);
989*8803SJonathan.Haslam@Sun.COM 		return (DDI_FAILURE);
990*8803SJonathan.Haslam@Sun.COM 	}
991*8803SJonathan.Haslam@Sun.COM 
992*8803SJonathan.Haslam@Sun.COM 	mutex_enter(&cpu_lock);
993*8803SJonathan.Haslam@Sun.COM 	register_cpu_setup_func(dcpc_cpu_setup, NULL);
994*8803SJonathan.Haslam@Sun.COM 	mutex_exit(&cpu_lock);
995*8803SJonathan.Haslam@Sun.COM 
996*8803SJonathan.Haslam@Sun.COM 	dcpc_ovf_mask = (1 << cpc_ncounters) - 1;
997*8803SJonathan.Haslam@Sun.COM 	ASSERT(dcpc_ovf_mask != 0);
998*8803SJonathan.Haslam@Sun.COM 
999*8803SJonathan.Haslam@Sun.COM 	if (caps & CPC_CAP_OVERFLOW_PRECISE)
1000*8803SJonathan.Haslam@Sun.COM 		dcpc_mult_ovf_cap = 1;
1001*8803SJonathan.Haslam@Sun.COM 
1002*8803SJonathan.Haslam@Sun.COM 	/*
1003*8803SJonathan.Haslam@Sun.COM 	 * Determine which, if any, mask attribute the back-end can use.
1004*8803SJonathan.Haslam@Sun.COM 	 */
1005*8803SJonathan.Haslam@Sun.COM 	attrs = kcpc_list_attrs();
1006*8803SJonathan.Haslam@Sun.COM 	if (strstr(attrs, "umask") != NULL)
1007*8803SJonathan.Haslam@Sun.COM 		dcpc_mask_type |= DCPC_UMASK;
1008*8803SJonathan.Haslam@Sun.COM 	else if (strstr(attrs, "emask") != NULL)
1009*8803SJonathan.Haslam@Sun.COM 		dcpc_mask_type |= DCPC_EMASK;
1010*8803SJonathan.Haslam@Sun.COM 
1011*8803SJonathan.Haslam@Sun.COM 	/*
1012*8803SJonathan.Haslam@Sun.COM 	 * The dcpc_actv_reqs array is used to store the requests that
1013*8803SJonathan.Haslam@Sun.COM 	 * we currently have programmed. The order of requests in this
1014*8803SJonathan.Haslam@Sun.COM 	 * array is not necessarily the order that the event appears in
1015*8803SJonathan.Haslam@Sun.COM 	 * the kcpc_request_t array. Once entered into a slot in the array
1016*8803SJonathan.Haslam@Sun.COM 	 * the entry is not moved until it's removed.
1017*8803SJonathan.Haslam@Sun.COM 	 */
1018*8803SJonathan.Haslam@Sun.COM 	dcpc_actv_reqs =
1019*8803SJonathan.Haslam@Sun.COM 	    kmem_zalloc(cpc_ncounters * sizeof (dcpc_probe_t *), KM_SLEEP);
1020*8803SJonathan.Haslam@Sun.COM 
1021*8803SJonathan.Haslam@Sun.COM 	dcpc_min_overflow = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
1022*8803SJonathan.Haslam@Sun.COM 	    DDI_PROP_DONTPASS, "dcpc-min-overflow", DCPC_MIN_OVF_DEFAULT);
1023*8803SJonathan.Haslam@Sun.COM 
1024*8803SJonathan.Haslam@Sun.COM 	kcpc_register_dcpc(dcpc_fire);
1025*8803SJonathan.Haslam@Sun.COM 
1026*8803SJonathan.Haslam@Sun.COM 	ddi_report_dev(devi);
1027*8803SJonathan.Haslam@Sun.COM 	dcpc_devi = devi;
1028*8803SJonathan.Haslam@Sun.COM 
1029*8803SJonathan.Haslam@Sun.COM 	return (DDI_SUCCESS);
1030*8803SJonathan.Haslam@Sun.COM }
1031*8803SJonathan.Haslam@Sun.COM 
1032*8803SJonathan.Haslam@Sun.COM static struct cb_ops dcpc_cb_ops = {
1033*8803SJonathan.Haslam@Sun.COM 	dcpc_open,		/* open */
1034*8803SJonathan.Haslam@Sun.COM 	nodev,			/* close */
1035*8803SJonathan.Haslam@Sun.COM 	nulldev,		/* strategy */
1036*8803SJonathan.Haslam@Sun.COM 	nulldev,		/* print */
1037*8803SJonathan.Haslam@Sun.COM 	nodev,			/* dump */
1038*8803SJonathan.Haslam@Sun.COM 	nodev,			/* read */
1039*8803SJonathan.Haslam@Sun.COM 	nodev,			/* write */
1040*8803SJonathan.Haslam@Sun.COM 	nodev,			/* ioctl */
1041*8803SJonathan.Haslam@Sun.COM 	nodev,			/* devmap */
1042*8803SJonathan.Haslam@Sun.COM 	nodev,			/* mmap */
1043*8803SJonathan.Haslam@Sun.COM 	nodev,			/* segmap */
1044*8803SJonathan.Haslam@Sun.COM 	nochpoll,		/* poll */
1045*8803SJonathan.Haslam@Sun.COM 	ddi_prop_op,		/* cb_prop_op */
1046*8803SJonathan.Haslam@Sun.COM 	0,			/* streamtab  */
1047*8803SJonathan.Haslam@Sun.COM 	D_NEW | D_MP		/* Driver compatibility flag */
1048*8803SJonathan.Haslam@Sun.COM };
1049*8803SJonathan.Haslam@Sun.COM 
1050*8803SJonathan.Haslam@Sun.COM static struct dev_ops dcpc_ops = {
1051*8803SJonathan.Haslam@Sun.COM 	DEVO_REV,		/* devo_rev, */
1052*8803SJonathan.Haslam@Sun.COM 	0,			/* refcnt  */
1053*8803SJonathan.Haslam@Sun.COM 	dcpc_info,		/* get_dev_info */
1054*8803SJonathan.Haslam@Sun.COM 	nulldev,		/* identify */
1055*8803SJonathan.Haslam@Sun.COM 	nulldev,		/* probe */
1056*8803SJonathan.Haslam@Sun.COM 	dcpc_attach,		/* attach */
1057*8803SJonathan.Haslam@Sun.COM 	dcpc_detach,		/* detach */
1058*8803SJonathan.Haslam@Sun.COM 	nodev,			/* reset */
1059*8803SJonathan.Haslam@Sun.COM 	&dcpc_cb_ops,		/* driver operations */
1060*8803SJonathan.Haslam@Sun.COM 	NULL,			/* bus operations */
1061*8803SJonathan.Haslam@Sun.COM 	nodev,			/* dev power */
1062*8803SJonathan.Haslam@Sun.COM 	ddi_quiesce_not_needed	/* quiesce */
1063*8803SJonathan.Haslam@Sun.COM };
1064*8803SJonathan.Haslam@Sun.COM 
1065*8803SJonathan.Haslam@Sun.COM /*
1066*8803SJonathan.Haslam@Sun.COM  * Module linkage information for the kernel.
1067*8803SJonathan.Haslam@Sun.COM  */
1068*8803SJonathan.Haslam@Sun.COM static struct modldrv modldrv = {
1069*8803SJonathan.Haslam@Sun.COM 	&mod_driverops,		/* module type */
1070*8803SJonathan.Haslam@Sun.COM 	"DTrace CPC Module",	/* name of module */
1071*8803SJonathan.Haslam@Sun.COM 	&dcpc_ops,		/* driver ops */
1072*8803SJonathan.Haslam@Sun.COM };
1073*8803SJonathan.Haslam@Sun.COM 
1074*8803SJonathan.Haslam@Sun.COM static struct modlinkage modlinkage = {
1075*8803SJonathan.Haslam@Sun.COM 	MODREV_1,
1076*8803SJonathan.Haslam@Sun.COM 	(void *)&modldrv,
1077*8803SJonathan.Haslam@Sun.COM 	NULL
1078*8803SJonathan.Haslam@Sun.COM };
1079*8803SJonathan.Haslam@Sun.COM 
1080*8803SJonathan.Haslam@Sun.COM int
1081*8803SJonathan.Haslam@Sun.COM _init(void)
1082*8803SJonathan.Haslam@Sun.COM {
1083*8803SJonathan.Haslam@Sun.COM 	return (mod_install(&modlinkage));
1084*8803SJonathan.Haslam@Sun.COM }
1085*8803SJonathan.Haslam@Sun.COM 
1086*8803SJonathan.Haslam@Sun.COM int
1087*8803SJonathan.Haslam@Sun.COM _info(struct modinfo *modinfop)
1088*8803SJonathan.Haslam@Sun.COM {
1089*8803SJonathan.Haslam@Sun.COM 	return (mod_info(&modlinkage, modinfop));
1090*8803SJonathan.Haslam@Sun.COM }
1091*8803SJonathan.Haslam@Sun.COM 
1092*8803SJonathan.Haslam@Sun.COM int
1093*8803SJonathan.Haslam@Sun.COM _fini(void)
1094*8803SJonathan.Haslam@Sun.COM {
1095*8803SJonathan.Haslam@Sun.COM 	return (mod_remove(&modlinkage));
1096*8803SJonathan.Haslam@Sun.COM }
1097