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