1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/errno.h>
30*0Sstevel@tonic-gate #include <sys/stat.h>
31*0Sstevel@tonic-gate #include <sys/modctl.h>
32*0Sstevel@tonic-gate #include <sys/conf.h>
33*0Sstevel@tonic-gate #include <sys/systm.h>
34*0Sstevel@tonic-gate #include <sys/ddi.h>
35*0Sstevel@tonic-gate #include <sys/sunddi.h>
36*0Sstevel@tonic-gate #include <sys/cpuvar.h>
37*0Sstevel@tonic-gate #include <sys/kmem.h>
38*0Sstevel@tonic-gate #include <sys/strsubr.h>
39*0Sstevel@tonic-gate #include <sys/dtrace.h>
40*0Sstevel@tonic-gate #include <sys/cyclic.h>
41*0Sstevel@tonic-gate #include <sys/atomic.h>
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate static dev_info_t *profile_devi;
44*0Sstevel@tonic-gate static dtrace_provider_id_t profile_id;
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate /*
47*0Sstevel@tonic-gate  * Regardless of platform, there are five artificial frames in the case of the
48*0Sstevel@tonic-gate  * profile provider:
49*0Sstevel@tonic-gate  *
50*0Sstevel@tonic-gate  *	profile_fire
51*0Sstevel@tonic-gate  *	cyclic_expire
52*0Sstevel@tonic-gate  *	cyclic_fire
53*0Sstevel@tonic-gate  *	[ cbe ]
54*0Sstevel@tonic-gate  *	[ locore ]
55*0Sstevel@tonic-gate  *
56*0Sstevel@tonic-gate  * On amd64, there are two frames associated with locore:  one in locore, and
57*0Sstevel@tonic-gate  * another in common interrupt dispatch code.  (i386 has not been modified to
58*0Sstevel@tonic-gate  * use this common layer.)  Further, on i386, the interrupted instruction
59*0Sstevel@tonic-gate  * appears as its own stack frame.  All of this means that we need to add one
60*0Sstevel@tonic-gate  * frame for amd64, and then take one away for both amd64 and i386.
61*0Sstevel@tonic-gate  *
62*0Sstevel@tonic-gate  * On SPARC, the picture is further complicated because the compiler
63*0Sstevel@tonic-gate  * optimizes away tail-calls -- so the following frames are optimized away:
64*0Sstevel@tonic-gate  *
65*0Sstevel@tonic-gate  * 	profile_fire
66*0Sstevel@tonic-gate  *	cyclic_expire
67*0Sstevel@tonic-gate  *
68*0Sstevel@tonic-gate  * This gives three frames.  However, on DEBUG kernels, the cyclic_expire
69*0Sstevel@tonic-gate  * frame cannot be tail-call eliminated, yielding four frames in this case.
70*0Sstevel@tonic-gate  *
71*0Sstevel@tonic-gate  * All of the above constraints lead to the mess below.  Yes, the profile
72*0Sstevel@tonic-gate  * provider should ideally figure this out on-the-fly by hiting one of its own
73*0Sstevel@tonic-gate  * probes and then walking its own stack trace.  This is complicated, however,
74*0Sstevel@tonic-gate  * and the static definition doesn't seem to be overly brittle.  Still, we
75*0Sstevel@tonic-gate  * allow for a manual override in case we get it completely wrong.
76*0Sstevel@tonic-gate  */
77*0Sstevel@tonic-gate #ifdef __amd64
78*0Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	7
79*0Sstevel@tonic-gate #else
80*0Sstevel@tonic-gate #ifdef __i386
81*0Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	6
82*0Sstevel@tonic-gate #else
83*0Sstevel@tonic-gate #ifdef __sparc
84*0Sstevel@tonic-gate #ifdef DEBUG
85*0Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	4
86*0Sstevel@tonic-gate #else
87*0Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	3
88*0Sstevel@tonic-gate #endif
89*0Sstevel@tonic-gate #endif
90*0Sstevel@tonic-gate #endif
91*0Sstevel@tonic-gate #endif
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate #define	PROF_NAMELEN		15
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate #define	PROF_PROFILE		0
96*0Sstevel@tonic-gate #define	PROF_TICK		1
97*0Sstevel@tonic-gate #define	PROF_PREFIX_PROFILE	"profile-"
98*0Sstevel@tonic-gate #define	PROF_PREFIX_TICK	"tick-"
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate typedef struct profile_probe {
101*0Sstevel@tonic-gate 	char		prof_name[PROF_NAMELEN];
102*0Sstevel@tonic-gate 	dtrace_id_t	prof_id;
103*0Sstevel@tonic-gate 	int		prof_kind;
104*0Sstevel@tonic-gate 	hrtime_t	prof_interval;
105*0Sstevel@tonic-gate 	cyclic_id_t	prof_cyclic;
106*0Sstevel@tonic-gate } profile_probe_t;
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate typedef struct profile_probe_percpu {
109*0Sstevel@tonic-gate 	hrtime_t	profc_expected;
110*0Sstevel@tonic-gate 	hrtime_t	profc_interval;
111*0Sstevel@tonic-gate 	profile_probe_t	*profc_probe;
112*0Sstevel@tonic-gate } profile_probe_percpu_t;
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate hrtime_t	profile_interval_min = NANOSEC / 5000;		/* 5000 hz */
115*0Sstevel@tonic-gate int		profile_aframes = 0;				/* override */
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate static int profile_rates[] = {
118*0Sstevel@tonic-gate     97, 199, 499, 997, 1999,
119*0Sstevel@tonic-gate     4001, 4999, 0, 0, 0,
120*0Sstevel@tonic-gate     0, 0, 0, 0, 0,
121*0Sstevel@tonic-gate     0, 0, 0, 0, 0
122*0Sstevel@tonic-gate };
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate static int profile_ticks[] = {
125*0Sstevel@tonic-gate     1, 10, 100, 500, 1000,
126*0Sstevel@tonic-gate     5000, 0, 0, 0, 0,
127*0Sstevel@tonic-gate     0, 0, 0, 0, 0
128*0Sstevel@tonic-gate };
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate /*
131*0Sstevel@tonic-gate  * profile_max defines the upper bound on the number of profile probes that
132*0Sstevel@tonic-gate  * can exist (this is to prevent malicious or clumsy users from exhausing
133*0Sstevel@tonic-gate  * system resources by creating a slew of profile probes). At mod load time,
134*0Sstevel@tonic-gate  * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
135*0Sstevel@tonic-gate  * present in the profile.conf file.
136*0Sstevel@tonic-gate  */
137*0Sstevel@tonic-gate #define	PROFILE_MAX_DEFAULT	1000	/* default max. number of probes */
138*0Sstevel@tonic-gate static uint32_t profile_max;		/* maximum number of profile probes */
139*0Sstevel@tonic-gate static uint32_t profile_total;	/* current number of profile probes */
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate static void
142*0Sstevel@tonic-gate profile_fire(void *arg)
143*0Sstevel@tonic-gate {
144*0Sstevel@tonic-gate 	profile_probe_percpu_t *pcpu = arg;
145*0Sstevel@tonic-gate 	profile_probe_t *prof = pcpu->profc_probe;
146*0Sstevel@tonic-gate 	hrtime_t late;
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 	late = dtrace_gethrtime() - pcpu->profc_expected;
149*0Sstevel@tonic-gate 	pcpu->profc_expected += pcpu->profc_interval;
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate 	dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
152*0Sstevel@tonic-gate 	    CPU->cpu_profile_upc, late, 0, 0);
153*0Sstevel@tonic-gate }
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate static void
156*0Sstevel@tonic-gate profile_tick(void *arg)
157*0Sstevel@tonic-gate {
158*0Sstevel@tonic-gate 	profile_probe_t *prof = arg;
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate 	dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
161*0Sstevel@tonic-gate 	    CPU->cpu_profile_upc, 0, 0, 0);
162*0Sstevel@tonic-gate }
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate static void
165*0Sstevel@tonic-gate profile_create(hrtime_t interval, const char *name, int kind)
166*0Sstevel@tonic-gate {
167*0Sstevel@tonic-gate 	profile_probe_t *prof;
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	if (interval < profile_interval_min)
170*0Sstevel@tonic-gate 		return;
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate 	if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
173*0Sstevel@tonic-gate 		return;
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	atomic_add_32(&profile_total, 1);
176*0Sstevel@tonic-gate 	if (profile_total > profile_max) {
177*0Sstevel@tonic-gate 		atomic_add_32(&profile_total, -1);
178*0Sstevel@tonic-gate 		return;
179*0Sstevel@tonic-gate 	}
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 	prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
182*0Sstevel@tonic-gate 	(void) strcpy(prof->prof_name, name);
183*0Sstevel@tonic-gate 	prof->prof_interval = interval;
184*0Sstevel@tonic-gate 	prof->prof_cyclic = CYCLIC_NONE;
185*0Sstevel@tonic-gate 	prof->prof_kind = kind;
186*0Sstevel@tonic-gate 	prof->prof_id = dtrace_probe_create(profile_id,
187*0Sstevel@tonic-gate 	    NULL, NULL, name,
188*0Sstevel@tonic-gate 	    profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
189*0Sstevel@tonic-gate }
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate /*ARGSUSED*/
192*0Sstevel@tonic-gate static void
193*0Sstevel@tonic-gate profile_provide(void *arg, const dtrace_probedesc_t *desc)
194*0Sstevel@tonic-gate {
195*0Sstevel@tonic-gate 	int i, j, rate, kind;
196*0Sstevel@tonic-gate 	hrtime_t val = 0, mult = 1, len;
197*0Sstevel@tonic-gate 	const char *name, *suffix = NULL;
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate 	const struct {
200*0Sstevel@tonic-gate 		char *prefix;
201*0Sstevel@tonic-gate 		int kind;
202*0Sstevel@tonic-gate 	} types[] = {
203*0Sstevel@tonic-gate 		{ PROF_PREFIX_PROFILE, PROF_PROFILE },
204*0Sstevel@tonic-gate 		{ PROF_PREFIX_TICK, PROF_TICK },
205*0Sstevel@tonic-gate 		{ NULL, NULL }
206*0Sstevel@tonic-gate 	};
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 	const struct {
209*0Sstevel@tonic-gate 		char *name;
210*0Sstevel@tonic-gate 		hrtime_t mult;
211*0Sstevel@tonic-gate 	} suffixes[] = {
212*0Sstevel@tonic-gate 		{ "ns", 	NANOSEC / NANOSEC },
213*0Sstevel@tonic-gate 		{ "nsec",	NANOSEC / NANOSEC },
214*0Sstevel@tonic-gate 		{ "us",		NANOSEC / MICROSEC },
215*0Sstevel@tonic-gate 		{ "usec",	NANOSEC / MICROSEC },
216*0Sstevel@tonic-gate 		{ "ms",		NANOSEC / MILLISEC },
217*0Sstevel@tonic-gate 		{ "msec",	NANOSEC / MILLISEC },
218*0Sstevel@tonic-gate 		{ "s",		NANOSEC / SEC },
219*0Sstevel@tonic-gate 		{ "sec",	NANOSEC / SEC },
220*0Sstevel@tonic-gate 		{ "m",		NANOSEC * (hrtime_t)60 },
221*0Sstevel@tonic-gate 		{ "min",	NANOSEC * (hrtime_t)60 },
222*0Sstevel@tonic-gate 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
223*0Sstevel@tonic-gate 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
224*0Sstevel@tonic-gate 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
225*0Sstevel@tonic-gate 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
226*0Sstevel@tonic-gate 		{ "hz",		0 },
227*0Sstevel@tonic-gate 		{ NULL }
228*0Sstevel@tonic-gate 	};
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 	if (desc == NULL) {
231*0Sstevel@tonic-gate 		char n[PROF_NAMELEN];
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 		/*
234*0Sstevel@tonic-gate 		 * If no description was provided, provide all of our probes.
235*0Sstevel@tonic-gate 		 */
236*0Sstevel@tonic-gate 		for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
237*0Sstevel@tonic-gate 			if ((rate = profile_rates[i]) == 0)
238*0Sstevel@tonic-gate 				continue;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
241*0Sstevel@tonic-gate 			    PROF_PREFIX_PROFILE, rate);
242*0Sstevel@tonic-gate 			profile_create(NANOSEC / rate, n, PROF_PROFILE);
243*0Sstevel@tonic-gate 		}
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate 		for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
246*0Sstevel@tonic-gate 			if ((rate = profile_ticks[i]) == 0)
247*0Sstevel@tonic-gate 				continue;
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
250*0Sstevel@tonic-gate 			    PROF_PREFIX_TICK, rate);
251*0Sstevel@tonic-gate 			profile_create(NANOSEC / rate, n, PROF_TICK);
252*0Sstevel@tonic-gate 		}
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 		return;
255*0Sstevel@tonic-gate 	}
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	name = desc->dtpd_name;
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	for (i = 0; types[i].prefix != NULL; i++) {
260*0Sstevel@tonic-gate 		len = strlen(types[i].prefix);
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate 		if (strncmp(name, types[i].prefix, len) != 0)
263*0Sstevel@tonic-gate 			continue;
264*0Sstevel@tonic-gate 		break;
265*0Sstevel@tonic-gate 	}
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	if (types[i].prefix == NULL)
268*0Sstevel@tonic-gate 		return;
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	kind = types[i].kind;
271*0Sstevel@tonic-gate 	j = strlen(name) - len;
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	/*
274*0Sstevel@tonic-gate 	 * We need to start before any time suffix.
275*0Sstevel@tonic-gate 	 */
276*0Sstevel@tonic-gate 	for (j = strlen(name); j >= len; j--) {
277*0Sstevel@tonic-gate 		if (name[j] >= '0' && name[j] <= '9')
278*0Sstevel@tonic-gate 			break;
279*0Sstevel@tonic-gate 		suffix = &name[j];
280*0Sstevel@tonic-gate 	}
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 	ASSERT(suffix != NULL);
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	/*
285*0Sstevel@tonic-gate 	 * Now determine the numerical value present in the probe name.
286*0Sstevel@tonic-gate 	 */
287*0Sstevel@tonic-gate 	for (; j >= len; j--) {
288*0Sstevel@tonic-gate 		if (name[j] < '0' || name[j] > '9')
289*0Sstevel@tonic-gate 			return;
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate 		val += (name[j] - '0') * mult;
292*0Sstevel@tonic-gate 		mult *= (hrtime_t)10;
293*0Sstevel@tonic-gate 	}
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate 	if (val == 0)
296*0Sstevel@tonic-gate 		return;
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 	/*
299*0Sstevel@tonic-gate 	 * Look-up the suffix to determine the multiplier.
300*0Sstevel@tonic-gate 	 */
301*0Sstevel@tonic-gate 	for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
302*0Sstevel@tonic-gate 		if (strcasecmp(suffixes[i].name, suffix) == 0) {
303*0Sstevel@tonic-gate 			mult = suffixes[i].mult;
304*0Sstevel@tonic-gate 			break;
305*0Sstevel@tonic-gate 		}
306*0Sstevel@tonic-gate 	}
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate 	if (suffixes[i].name == NULL && *suffix != '\0')
309*0Sstevel@tonic-gate 		return;
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate 	if (mult == 0) {
312*0Sstevel@tonic-gate 		/*
313*0Sstevel@tonic-gate 		 * The default is frequency-per-second.
314*0Sstevel@tonic-gate 		 */
315*0Sstevel@tonic-gate 		val = NANOSEC / val;
316*0Sstevel@tonic-gate 	} else {
317*0Sstevel@tonic-gate 		val *= mult;
318*0Sstevel@tonic-gate 	}
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 	profile_create(val, name, kind);
321*0Sstevel@tonic-gate }
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate /*ARGSUSED*/
324*0Sstevel@tonic-gate static void
325*0Sstevel@tonic-gate profile_destroy(void *arg, dtrace_id_t id, void *parg)
326*0Sstevel@tonic-gate {
327*0Sstevel@tonic-gate 	profile_probe_t *prof = parg;
328*0Sstevel@tonic-gate 
329*0Sstevel@tonic-gate 	ASSERT(prof->prof_cyclic == CYCLIC_NONE);
330*0Sstevel@tonic-gate 	kmem_free(prof, sizeof (profile_probe_t));
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 	ASSERT(profile_total >= 1);
333*0Sstevel@tonic-gate 	atomic_add_32(&profile_total, -1);
334*0Sstevel@tonic-gate }
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate /*ARGSUSED*/
337*0Sstevel@tonic-gate static void
338*0Sstevel@tonic-gate profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
339*0Sstevel@tonic-gate {
340*0Sstevel@tonic-gate 	profile_probe_t *prof = arg;
341*0Sstevel@tonic-gate 	profile_probe_percpu_t *pcpu;
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 	pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
344*0Sstevel@tonic-gate 	pcpu->profc_probe = prof;
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	hdlr->cyh_func = profile_fire;
347*0Sstevel@tonic-gate 	hdlr->cyh_arg = pcpu;
348*0Sstevel@tonic-gate 	hdlr->cyh_level = CY_HIGH_LEVEL;
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	when->cyt_interval = prof->prof_interval;
351*0Sstevel@tonic-gate 	when->cyt_when = dtrace_gethrtime() + when->cyt_interval;
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 	pcpu->profc_expected = when->cyt_when;
354*0Sstevel@tonic-gate 	pcpu->profc_interval = when->cyt_interval;
355*0Sstevel@tonic-gate }
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate /*ARGSUSED*/
358*0Sstevel@tonic-gate static void
359*0Sstevel@tonic-gate profile_offline(void *arg, cpu_t *cpu, void *oarg)
360*0Sstevel@tonic-gate {
361*0Sstevel@tonic-gate 	profile_probe_percpu_t *pcpu = oarg;
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate 	ASSERT(pcpu->profc_probe == arg);
364*0Sstevel@tonic-gate 	kmem_free(pcpu, sizeof (profile_probe_percpu_t));
365*0Sstevel@tonic-gate }
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate /*ARGSUSED*/
368*0Sstevel@tonic-gate static void
369*0Sstevel@tonic-gate profile_enable(void *arg, dtrace_id_t id, void *parg)
370*0Sstevel@tonic-gate {
371*0Sstevel@tonic-gate 	profile_probe_t *prof = parg;
372*0Sstevel@tonic-gate 	cyc_omni_handler_t omni;
373*0Sstevel@tonic-gate 	cyc_handler_t hdlr;
374*0Sstevel@tonic-gate 	cyc_time_t when;
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 	ASSERT(prof->prof_interval != 0);
377*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	if (prof->prof_kind == PROF_TICK) {
380*0Sstevel@tonic-gate 		hdlr.cyh_func = profile_tick;
381*0Sstevel@tonic-gate 		hdlr.cyh_arg = prof;
382*0Sstevel@tonic-gate 		hdlr.cyh_level = CY_HIGH_LEVEL;
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 		when.cyt_interval = prof->prof_interval;
385*0Sstevel@tonic-gate 		when.cyt_when = dtrace_gethrtime() + when.cyt_interval;
386*0Sstevel@tonic-gate 	} else {
387*0Sstevel@tonic-gate 		ASSERT(prof->prof_kind == PROF_PROFILE);
388*0Sstevel@tonic-gate 		omni.cyo_online = profile_online;
389*0Sstevel@tonic-gate 		omni.cyo_offline = profile_offline;
390*0Sstevel@tonic-gate 		omni.cyo_arg = prof;
391*0Sstevel@tonic-gate 	}
392*0Sstevel@tonic-gate 
393*0Sstevel@tonic-gate 	if (prof->prof_kind == PROF_TICK) {
394*0Sstevel@tonic-gate 		prof->prof_cyclic = cyclic_add(&hdlr, &when);
395*0Sstevel@tonic-gate 	} else {
396*0Sstevel@tonic-gate 		prof->prof_cyclic = cyclic_add_omni(&omni);
397*0Sstevel@tonic-gate 	}
398*0Sstevel@tonic-gate }
399*0Sstevel@tonic-gate 
400*0Sstevel@tonic-gate /*ARGSUSED*/
401*0Sstevel@tonic-gate static void
402*0Sstevel@tonic-gate profile_disable(void *arg, dtrace_id_t id, void *parg)
403*0Sstevel@tonic-gate {
404*0Sstevel@tonic-gate 	profile_probe_t *prof = parg;
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 	ASSERT(prof->prof_cyclic != CYCLIC_NONE);
407*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 	cyclic_remove(prof->prof_cyclic);
410*0Sstevel@tonic-gate 	prof->prof_cyclic = CYCLIC_NONE;
411*0Sstevel@tonic-gate }
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate /*ARGSUSED*/
414*0Sstevel@tonic-gate static int
415*0Sstevel@tonic-gate profile_usermode(void *arg, dtrace_id_t id, void *parg)
416*0Sstevel@tonic-gate {
417*0Sstevel@tonic-gate 	return (CPU->cpu_profile_pc == 0);
418*0Sstevel@tonic-gate }
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate static dtrace_pattr_t profile_attr = {
421*0Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
422*0Sstevel@tonic-gate { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_UNKNOWN },
423*0Sstevel@tonic-gate { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
424*0Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
425*0Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
426*0Sstevel@tonic-gate };
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate static dtrace_pops_t profile_pops = {
429*0Sstevel@tonic-gate 	profile_provide,
430*0Sstevel@tonic-gate 	NULL,
431*0Sstevel@tonic-gate 	profile_enable,
432*0Sstevel@tonic-gate 	profile_disable,
433*0Sstevel@tonic-gate 	NULL,
434*0Sstevel@tonic-gate 	NULL,
435*0Sstevel@tonic-gate 	NULL,
436*0Sstevel@tonic-gate 	NULL,
437*0Sstevel@tonic-gate 	profile_usermode,
438*0Sstevel@tonic-gate 	profile_destroy
439*0Sstevel@tonic-gate };
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate static int
442*0Sstevel@tonic-gate profile_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
443*0Sstevel@tonic-gate {
444*0Sstevel@tonic-gate 	switch (cmd) {
445*0Sstevel@tonic-gate 	case DDI_ATTACH:
446*0Sstevel@tonic-gate 		break;
447*0Sstevel@tonic-gate 	case DDI_RESUME:
448*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
449*0Sstevel@tonic-gate 	default:
450*0Sstevel@tonic-gate 		return (DDI_FAILURE);
451*0Sstevel@tonic-gate 	}
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
454*0Sstevel@tonic-gate 	    DDI_PSEUDO, NULL) == DDI_FAILURE ||
455*0Sstevel@tonic-gate 	    dtrace_register("profile", &profile_attr,
456*0Sstevel@tonic-gate 	    DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, 0,
457*0Sstevel@tonic-gate 	    &profile_pops, NULL, &profile_id) != 0) {
458*0Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
459*0Sstevel@tonic-gate 		return (DDI_FAILURE);
460*0Sstevel@tonic-gate 	}
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	profile_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
463*0Sstevel@tonic-gate 	    "profile-max-probes", PROFILE_MAX_DEFAULT);
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	ddi_report_dev(devi);
466*0Sstevel@tonic-gate 	profile_devi = devi;
467*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
468*0Sstevel@tonic-gate }
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate static int
471*0Sstevel@tonic-gate profile_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
472*0Sstevel@tonic-gate {
473*0Sstevel@tonic-gate 	switch (cmd) {
474*0Sstevel@tonic-gate 	case DDI_DETACH:
475*0Sstevel@tonic-gate 		break;
476*0Sstevel@tonic-gate 	case DDI_SUSPEND:
477*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
478*0Sstevel@tonic-gate 	default:
479*0Sstevel@tonic-gate 		return (DDI_FAILURE);
480*0Sstevel@tonic-gate 	}
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate 	if (dtrace_unregister(profile_id) != 0)
483*0Sstevel@tonic-gate 		return (DDI_FAILURE);
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
486*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
487*0Sstevel@tonic-gate }
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate /*ARGSUSED*/
490*0Sstevel@tonic-gate static int
491*0Sstevel@tonic-gate profile_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
492*0Sstevel@tonic-gate {
493*0Sstevel@tonic-gate 	int error;
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 	switch (infocmd) {
496*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
497*0Sstevel@tonic-gate 		*result = (void *)profile_devi;
498*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
499*0Sstevel@tonic-gate 		break;
500*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
501*0Sstevel@tonic-gate 		*result = (void *)0;
502*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
503*0Sstevel@tonic-gate 		break;
504*0Sstevel@tonic-gate 	default:
505*0Sstevel@tonic-gate 		error = DDI_FAILURE;
506*0Sstevel@tonic-gate 	}
507*0Sstevel@tonic-gate 	return (error);
508*0Sstevel@tonic-gate }
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate /*ARGSUSED*/
511*0Sstevel@tonic-gate static int
512*0Sstevel@tonic-gate profile_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
513*0Sstevel@tonic-gate {
514*0Sstevel@tonic-gate 	return (0);
515*0Sstevel@tonic-gate }
516*0Sstevel@tonic-gate 
517*0Sstevel@tonic-gate static struct cb_ops profile_cb_ops = {
518*0Sstevel@tonic-gate 	profile_open,		/* open */
519*0Sstevel@tonic-gate 	nodev,			/* close */
520*0Sstevel@tonic-gate 	nulldev,		/* strategy */
521*0Sstevel@tonic-gate 	nulldev,		/* print */
522*0Sstevel@tonic-gate 	nodev,			/* dump */
523*0Sstevel@tonic-gate 	nodev,			/* read */
524*0Sstevel@tonic-gate 	nodev,			/* write */
525*0Sstevel@tonic-gate 	nodev,			/* ioctl */
526*0Sstevel@tonic-gate 	nodev,			/* devmap */
527*0Sstevel@tonic-gate 	nodev,			/* mmap */
528*0Sstevel@tonic-gate 	nodev,			/* segmap */
529*0Sstevel@tonic-gate 	nochpoll,		/* poll */
530*0Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
531*0Sstevel@tonic-gate 	0,			/* streamtab  */
532*0Sstevel@tonic-gate 	D_NEW | D_MP		/* Driver compatibility flag */
533*0Sstevel@tonic-gate };
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate static struct dev_ops profile_ops = {
536*0Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
537*0Sstevel@tonic-gate 	0,			/* refcnt  */
538*0Sstevel@tonic-gate 	profile_info,		/* get_dev_info */
539*0Sstevel@tonic-gate 	nulldev,		/* identify */
540*0Sstevel@tonic-gate 	nulldev,		/* probe */
541*0Sstevel@tonic-gate 	profile_attach,		/* attach */
542*0Sstevel@tonic-gate 	profile_detach,		/* detach */
543*0Sstevel@tonic-gate 	nodev,			/* reset */
544*0Sstevel@tonic-gate 	&profile_cb_ops,	/* driver operations */
545*0Sstevel@tonic-gate 	NULL,			/* bus operations */
546*0Sstevel@tonic-gate 	nodev			/* dev power */
547*0Sstevel@tonic-gate };
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate /*
550*0Sstevel@tonic-gate  * Module linkage information for the kernel.
551*0Sstevel@tonic-gate  */
552*0Sstevel@tonic-gate static struct modldrv modldrv = {
553*0Sstevel@tonic-gate 	&mod_driverops,		/* module type (this is a pseudo driver) */
554*0Sstevel@tonic-gate 	"Profile Interrupt Tracing",	/* name of module */
555*0Sstevel@tonic-gate 	&profile_ops,		/* driver ops */
556*0Sstevel@tonic-gate };
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
559*0Sstevel@tonic-gate 	MODREV_1,
560*0Sstevel@tonic-gate 	(void *)&modldrv,
561*0Sstevel@tonic-gate 	NULL
562*0Sstevel@tonic-gate };
563*0Sstevel@tonic-gate 
564*0Sstevel@tonic-gate int
565*0Sstevel@tonic-gate _init(void)
566*0Sstevel@tonic-gate {
567*0Sstevel@tonic-gate 	return (mod_install(&modlinkage));
568*0Sstevel@tonic-gate }
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate int
571*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
572*0Sstevel@tonic-gate {
573*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
574*0Sstevel@tonic-gate }
575*0Sstevel@tonic-gate 
576*0Sstevel@tonic-gate int
577*0Sstevel@tonic-gate _fini(void)
578*0Sstevel@tonic-gate {
579*0Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
580*0Sstevel@tonic-gate }
581