1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/* #pragma ident	"@(#)profile.c	1.7	07/01/10 SMI" */
27
28#if !defined(__APPLE__)
29#include <sys/errno.h>
30#include <sys/stat.h>
31#include <sys/modctl.h>
32#include <sys/conf.h>
33#include <sys/systm.h>
34#include <sys/ddi.h>
35#include <sys/sunddi.h>
36#include <sys/cpuvar.h>
37#include <sys/kmem.h>
38#include <sys/strsubr.h>
39#include <sys/dtrace.h>
40#include <sys/cyclic.h>
41#include <sys/atomic.h>
42#else
43#ifdef KERNEL
44#ifndef _KERNEL
45#define _KERNEL /* Solaris vs. Darwin */
46#endif
47#endif
48
49#include <kern/cpu_data.h>
50#include <kern/thread.h>
51#include <kern/assert.h>
52#include <mach/thread_status.h>
53
54#include <sys/param.h>
55#include <sys/systm.h>
56#include <sys/errno.h>
57#include <sys/stat.h>
58#include <sys/ioctl.h>
59#include <sys/conf.h>
60#include <sys/fcntl.h>
61#include <miscfs/devfs/devfs.h>
62
63#include <sys/dtrace.h>
64#include <sys/dtrace_impl.h>
65
66#include <sys/dtrace_glue.h>
67
68#include <machine/pal_routines.h>
69
70#if defined(__x86_64__)
71extern x86_saved_state_t *find_kern_regs(thread_t);
72#else
73#error Unknown architecture
74#endif
75
76#undef ASSERT
77#define ASSERT(x) do {} while(0)
78
79extern void profile_init(void);
80#endif /* __APPLE__ */
81
82static dev_info_t *profile_devi;
83static dtrace_provider_id_t profile_id;
84
85/*
86 * Regardless of platform, the stack frames look like this in the case of the
87 * profile provider:
88 *
89 *	profile_fire
90 *	cyclic_expire
91 *	cyclic_fire
92 *	[ cbe ]
93 *	[ interrupt code ]
94 *
95 * On x86, there are five frames from the generic interrupt code; further, the
96 * interrupted instruction appears as its own stack frame, giving us a total of
97 * 10.
98 *
99 * On SPARC, the picture is further complicated because the compiler
100 * optimizes away tail-calls -- so the following frames are optimized away:
101 *
102 * 	profile_fire
103 *	cyclic_expire
104 *
105 * This gives three frames.  However, on DEBUG kernels, the cyclic_expire
106 * frame cannot be tail-call eliminated, yielding four frames in this case.
107 *
108 * All of the above constraints lead to the mess below.  Yes, the profile
109 * provider should ideally figure this out on-the-fly by hitting one of its own
110 * probes and then walking its own stack trace.  This is complicated, however,
111 * and the static definition doesn't seem to be overly brittle.  Still, we
112 * allow for a manual override in case we get it completely wrong.
113 */
114
115#if defined(__x86_64__)
116#define PROF_ARTIFICIAL_FRAMES  9
117#else
118#error Unknown architecture
119#endif
120
121#define	PROF_NAMELEN		15
122
123#define	PROF_PROFILE		0
124#define	PROF_TICK		1
125#define	PROF_PREFIX_PROFILE	"profile-"
126#define	PROF_PREFIX_TICK	"tick-"
127
128typedef struct profile_probe {
129	char		prof_name[PROF_NAMELEN];
130	dtrace_id_t	prof_id;
131	int		prof_kind;
132	hrtime_t	prof_interval;
133	cyclic_id_t	prof_cyclic;
134} profile_probe_t;
135
136typedef struct profile_probe_percpu {
137	hrtime_t	profc_expected;
138	hrtime_t	profc_interval;
139	profile_probe_t	*profc_probe;
140} profile_probe_percpu_t;
141
142hrtime_t	profile_interval_min = NANOSEC / 5000;		/* 5000 hz */
143int		profile_aframes = 0;				/* override */
144
145static int profile_rates[] = {
146    97, 199, 499, 997, 1999,
147    4001, 4999, 0, 0, 0,
148    0, 0, 0, 0, 0,
149    0, 0, 0, 0, 0
150};
151
152static int profile_ticks[] = {
153    1, 10, 100, 500, 1000,
154    5000, 0, 0, 0, 0,
155    0, 0, 0, 0, 0
156};
157
158/*
159 * profile_max defines the upper bound on the number of profile probes that
160 * can exist (this is to prevent malicious or clumsy users from exhausing
161 * system resources by creating a slew of profile probes). At mod load time,
162 * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
163 * present in the profile.conf file.
164 */
165#define	PROFILE_MAX_DEFAULT	1000	/* default max. number of probes */
166static uint32_t profile_max;		/* maximum number of profile probes */
167static uint32_t profile_total;	/* current number of profile probes */
168
169static void
170profile_fire(void *arg)
171{
172	profile_probe_percpu_t *pcpu = arg;
173	profile_probe_t *prof = pcpu->profc_probe;
174	hrtime_t late;
175
176	late = dtrace_gethrtime() - pcpu->profc_expected;
177	pcpu->profc_expected += pcpu->profc_interval;
178
179#if !defined(__APPLE__)
180	dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
181	    CPU->cpu_profile_upc, late, 0, 0);
182#else
183#if defined(__x86_64__)
184	x86_saved_state_t *kern_regs = find_kern_regs(current_thread());
185
186	if (NULL != kern_regs) {
187		/* Kernel was interrupted. */
188		dtrace_probe(prof->prof_id, saved_state64(kern_regs)->isf.rip,  0x0, 0, 0, 0);
189
190	} else {
191		pal_register_cache_state(current_thread(), VALID);
192		/* Possibly a user interrupt */
193		x86_saved_state_t   *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
194
195		if (NULL == tagged_regs) {
196			/* Too bad, so sad, no useful interrupt state. */
197			dtrace_probe(prof->prof_id, 0xcafebabe,
198	    		0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
199		} else if (is_saved_state64(tagged_regs)) {
200			x86_saved_state64_t *regs = saved_state64(tagged_regs);
201
202			dtrace_probe(prof->prof_id, 0x0, regs->isf.rip, 0, 0, 0);
203		} else {
204			x86_saved_state32_t *regs = saved_state32(tagged_regs);
205
206			dtrace_probe(prof->prof_id, 0x0, regs->eip, 0, 0, 0);
207		}
208	}
209#else
210#error Unknown architecture
211#endif
212#endif /* __APPLE__ */
213}
214
215static void
216profile_tick(void *arg)
217{
218	profile_probe_t *prof = arg;
219
220#if !defined(__APPLE__)
221	dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
222	    CPU->cpu_profile_upc, 0, 0, 0);
223#else
224#if defined(__x86_64__)
225	x86_saved_state_t *kern_regs = find_kern_regs(current_thread());
226
227	if (NULL != kern_regs) {
228		/* Kernel was interrupted. */
229		dtrace_probe(prof->prof_id, saved_state64(kern_regs)->isf.rip,  0x0, 0, 0, 0);
230	} else {
231		pal_register_cache_state(current_thread(), VALID);
232		/* Possibly a user interrupt */
233		x86_saved_state_t   *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
234
235		if (NULL == tagged_regs) {
236			/* Too bad, so sad, no useful interrupt state. */
237			dtrace_probe(prof->prof_id, 0xcafebabe,
238	    		0x0, 0, 0, 0); /* XXX_BOGUS also see profile_usermode() below. */
239		} else if (is_saved_state64(tagged_regs)) {
240			x86_saved_state64_t *regs = saved_state64(tagged_regs);
241
242			dtrace_probe(prof->prof_id, 0x0, regs->isf.rip, 0, 0, 0);
243		} else {
244			x86_saved_state32_t *regs = saved_state32(tagged_regs);
245
246			dtrace_probe(prof->prof_id, 0x0, regs->eip, 0, 0, 0);
247		}
248	}
249#else
250#error Unknown architecture
251#endif
252#endif /* __APPLE__ */
253}
254
255static void
256profile_create(hrtime_t interval, const char *name, int kind)
257{
258	profile_probe_t *prof;
259
260	if (interval < profile_interval_min)
261		return;
262
263	if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
264		return;
265
266	atomic_add_32(&profile_total, 1);
267	if (profile_total > profile_max) {
268		atomic_add_32(&profile_total, -1);
269		return;
270	}
271
272#if !defined(__APPLE__)
273	prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
274#else
275	if (PROF_TICK == kind)
276		prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
277	else
278		prof = kmem_zalloc(sizeof (profile_probe_t) + NCPU*sizeof(profile_probe_percpu_t), KM_SLEEP);
279#endif /* __APPLE__ */
280	(void) strlcpy(prof->prof_name, name, sizeof(prof->prof_name));
281	prof->prof_interval = interval;
282	prof->prof_cyclic = CYCLIC_NONE;
283	prof->prof_kind = kind;
284	prof->prof_id = dtrace_probe_create(profile_id,
285	    NULL, NULL, name,
286	    profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
287}
288
289/*ARGSUSED*/
290static void
291profile_provide(void *arg, const dtrace_probedesc_t *desc)
292{
293#pragma unused(arg) /* __APPLE__ */
294	int i, j, rate, kind;
295	hrtime_t val = 0, mult = 1, len;
296	const char *name, *suffix = NULL;
297
298#if !defined(__APPLE__)
299	const struct {
300		char *prefix;
301		int kind;
302	} types[] = {
303		{ PROF_PREFIX_PROFILE, PROF_PROFILE },
304		{ PROF_PREFIX_TICK, PROF_TICK },
305		{ NULL, NULL }
306	};
307
308	const struct {
309		char *name;
310		hrtime_t mult;
311	} suffixes[] = {
312		{ "ns", 	NANOSEC / NANOSEC },
313		{ "nsec",	NANOSEC / NANOSEC },
314		{ "us",		NANOSEC / MICROSEC },
315		{ "usec",	NANOSEC / MICROSEC },
316		{ "ms",		NANOSEC / MILLISEC },
317		{ "msec",	NANOSEC / MILLISEC },
318		{ "s",		NANOSEC / SEC },
319		{ "sec",	NANOSEC / SEC },
320		{ "m",		NANOSEC * (hrtime_t)60 },
321		{ "min",	NANOSEC * (hrtime_t)60 },
322		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
323		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
324		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
325		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
326		{ "hz",		0 },
327		{ NULL }
328	};
329#else
330	const struct {
331		const char *prefix;
332		int kind;
333	} types[] = {
334		{ PROF_PREFIX_PROFILE, PROF_PROFILE },
335		{ PROF_PREFIX_TICK, PROF_TICK },
336		{ NULL, 0 }
337	};
338
339	const struct {
340		const char *name;
341		hrtime_t mult;
342	} suffixes[] = {
343		{ "ns", 	NANOSEC / NANOSEC },
344		{ "nsec",	NANOSEC / NANOSEC },
345		{ "us",		NANOSEC / MICROSEC },
346		{ "usec",	NANOSEC / MICROSEC },
347		{ "ms",		NANOSEC / MILLISEC },
348		{ "msec",	NANOSEC / MILLISEC },
349		{ "s",		NANOSEC / SEC },
350		{ "sec",	NANOSEC / SEC },
351		{ "m",		NANOSEC * (hrtime_t)60 },
352		{ "min",	NANOSEC * (hrtime_t)60 },
353		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
354		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
355		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
356		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
357		{ "hz",		0 },
358		{ NULL, 0 }
359	};
360#endif /* __APPLE__ */
361
362
363	if (desc == NULL) {
364		char n[PROF_NAMELEN];
365
366		/*
367		 * If no description was provided, provide all of our probes.
368		 */
369#if !defined(__APPLE__)
370		for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
371#else
372		for (i = 0; i < (int)(sizeof (profile_rates) / sizeof (int)); i++) {
373#endif /* __APPLE__ */
374			if ((rate = profile_rates[i]) == 0)
375				continue;
376
377			(void) snprintf(n, PROF_NAMELEN, "%s%d",
378			    PROF_PREFIX_PROFILE, rate);
379			profile_create(NANOSEC / rate, n, PROF_PROFILE);
380		}
381
382#if !defined(__APPLE__)
383		for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
384#else
385		for (i = 0; i < (int)(sizeof (profile_ticks) / sizeof (int)); i++) {
386#endif /* __APPLE__ */
387			if ((rate = profile_ticks[i]) == 0)
388				continue;
389
390			(void) snprintf(n, PROF_NAMELEN, "%s%d",
391			    PROF_PREFIX_TICK, rate);
392			profile_create(NANOSEC / rate, n, PROF_TICK);
393		}
394
395		return;
396	}
397
398	name = desc->dtpd_name;
399
400	for (i = 0; types[i].prefix != NULL; i++) {
401		len = strlen(types[i].prefix);
402
403		if (strncmp(name, types[i].prefix, len) != 0)
404			continue;
405		break;
406	}
407
408	if (types[i].prefix == NULL)
409		return;
410
411	kind = types[i].kind;
412	j = strlen(name) - len;
413
414	/*
415	 * We need to start before any time suffix.
416	 */
417	for (j = strlen(name); j >= len; j--) {
418		if (name[j] >= '0' && name[j] <= '9')
419			break;
420		suffix = &name[j];
421	}
422
423	ASSERT(suffix != NULL);
424
425	/*
426	 * Now determine the numerical value present in the probe name.
427	 */
428	for (; j >= len; j--) {
429		if (name[j] < '0' || name[j] > '9')
430			return;
431
432		val += (name[j] - '0') * mult;
433		mult *= (hrtime_t)10;
434	}
435
436	if (val == 0)
437		return;
438
439	/*
440	 * Look-up the suffix to determine the multiplier.
441	 */
442	for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
443#if !defined(__APPLE__)
444		if (strcasecmp(suffixes[i].name, suffix) == 0) {
445			mult = suffixes[i].mult;
446			break;
447		}
448#else
449		if (strncasecmp(suffixes[i].name, suffix, strlen(suffixes[i].name) + 1) == 0) {
450			mult = suffixes[i].mult;
451			break;
452		}
453#endif /* __APPLE__ */
454	}
455
456	if (suffixes[i].name == NULL && *suffix != '\0')
457		return;
458
459	if (mult == 0) {
460		/*
461		 * The default is frequency-per-second.
462		 */
463		val = NANOSEC / val;
464	} else {
465		val *= mult;
466	}
467
468	profile_create(val, name, kind);
469}
470
471/*ARGSUSED*/
472static void
473profile_destroy(void *arg, dtrace_id_t id, void *parg)
474{
475#pragma unused(arg,id) /* __APPLE__ */
476	profile_probe_t *prof = parg;
477
478	ASSERT(prof->prof_cyclic == CYCLIC_NONE);
479#if !defined(__APPLE__)
480	kmem_free(prof, sizeof (profile_probe_t));
481#else
482	if (prof->prof_kind == PROF_TICK)
483		kmem_free(prof, sizeof (profile_probe_t));
484	else
485		kmem_free(prof, sizeof (profile_probe_t) + NCPU*sizeof(profile_probe_percpu_t));
486#endif /* __APPLE__ */
487
488	ASSERT(profile_total >= 1);
489	atomic_add_32(&profile_total, -1);
490}
491
492/*ARGSUSED*/
493static void
494profile_online(void *arg, dtrace_cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
495{
496#pragma unused(cpu) /* __APPLE__ */
497	profile_probe_t *prof = arg;
498	profile_probe_percpu_t *pcpu;
499
500#if !defined(__APPLE__)
501	pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
502#else
503	pcpu = ((profile_probe_percpu_t *)(&(prof[1]))) + cpu_number();
504#endif /* __APPLE__ */
505	pcpu->profc_probe = prof;
506
507	hdlr->cyh_func = profile_fire;
508	hdlr->cyh_arg = pcpu;
509	hdlr->cyh_level = CY_HIGH_LEVEL;
510
511	when->cyt_interval = prof->prof_interval;
512#if !defined(__APPLE__)
513	when->cyt_when = dtrace_gethrtime() + when->cyt_interval;
514#else
515	when->cyt_when = 0;
516#endif /* __APPLE__ */
517
518	pcpu->profc_expected = when->cyt_when;
519	pcpu->profc_interval = when->cyt_interval;
520}
521
522/*ARGSUSED*/
523static void
524profile_offline(void *arg, dtrace_cpu_t *cpu, void *oarg)
525{
526	profile_probe_percpu_t *pcpu = oarg;
527
528	ASSERT(pcpu->profc_probe == arg);
529#if !defined(__APPLE__)
530	kmem_free(pcpu, sizeof (profile_probe_percpu_t));
531#else
532#pragma unused(pcpu,arg,cpu) /* __APPLE__ */
533#endif /* __APPLE__ */
534}
535
536/*ARGSUSED*/
537static int
538profile_enable(void *arg, dtrace_id_t id, void *parg)
539{
540#pragma unused(arg,id) /* __APPLE__ */
541	profile_probe_t *prof = parg;
542	cyc_omni_handler_t omni;
543	cyc_handler_t hdlr;
544	cyc_time_t when;
545
546	ASSERT(prof->prof_interval != 0);
547	ASSERT(MUTEX_HELD(&cpu_lock));
548
549	if (prof->prof_kind == PROF_TICK) {
550		hdlr.cyh_func = profile_tick;
551		hdlr.cyh_arg = prof;
552		hdlr.cyh_level = CY_HIGH_LEVEL;
553
554		when.cyt_interval = prof->prof_interval;
555#if !defined(__APPLE__)
556		when.cyt_when = dtrace_gethrtime() + when.cyt_interval;
557#else
558		when.cyt_when = 0;
559#endif /* __APPLE__ */
560	} else {
561		ASSERT(prof->prof_kind == PROF_PROFILE);
562		omni.cyo_online = profile_online;
563		omni.cyo_offline = profile_offline;
564		omni.cyo_arg = prof;
565	}
566
567#if !defined(__APPLE__)
568	if (prof->prof_kind == PROF_TICK) {
569		prof->prof_cyclic = cyclic_add(&hdlr, &when);
570	} else {
571		prof->prof_cyclic = cyclic_add_omni(&omni);
572	}
573#else
574	if (prof->prof_kind == PROF_TICK) {
575		prof->prof_cyclic = cyclic_timer_add(&hdlr, &when);
576	} else {
577		prof->prof_cyclic = (cyclic_id_t)cyclic_add_omni(&omni); /* cast puns cyclic_id_list_t with cyclic_id_t */
578	}
579#endif /* __APPLE__ */
580	return(0);
581}
582
583/*ARGSUSED*/
584static void
585profile_disable(void *arg, dtrace_id_t id, void *parg)
586{
587	profile_probe_t *prof = parg;
588
589	ASSERT(prof->prof_cyclic != CYCLIC_NONE);
590	ASSERT(MUTEX_HELD(&cpu_lock));
591
592#if !defined(__APPLE__)
593	cyclic_remove(prof->prof_cyclic);
594#else
595#pragma unused(arg,id)
596	if (prof->prof_kind == PROF_TICK) {
597		cyclic_timer_remove(prof->prof_cyclic);
598	} else {
599		cyclic_remove_omni((cyclic_id_list_t)prof->prof_cyclic); /* cast puns cyclic_id_list_t with cyclic_id_t */
600	}
601#endif /* __APPLE__ */
602	prof->prof_cyclic = CYCLIC_NONE;
603}
604
605#if !defined(__APPLE__)
606/*ARGSUSED*/
607static int
608profile_usermode(void *arg, dtrace_id_t id, void *parg)
609{
610	return (CPU->cpu_profile_pc == 0);
611}
612#else
613static int
614profile_usermode(void *arg, dtrace_id_t id, void *parg)
615{
616#pragma unused(arg,id,parg)
617	return 1; /* XXX_BOGUS */
618}
619#endif /* __APPLE__ */
620
621static dtrace_pattr_t profile_attr = {
622{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
623{ DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_UNKNOWN },
624{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
625{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
626{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
627};
628
629static dtrace_pops_t profile_pops = {
630	profile_provide,
631	NULL,
632	profile_enable,
633	profile_disable,
634	NULL,
635	NULL,
636	NULL,
637	NULL,
638	profile_usermode,
639	profile_destroy
640};
641
642static int
643profile_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
644{
645	switch (cmd) {
646	case DDI_ATTACH:
647		break;
648	case DDI_RESUME:
649		return (DDI_SUCCESS);
650	default:
651		return (DDI_FAILURE);
652	}
653
654#if !defined(__APPLE__)
655	if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
656	    DDI_PSEUDO, NULL) == DDI_FAILURE ||
657	    dtrace_register("profile", &profile_attr,
658	    DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, NULL,
659	    &profile_pops, NULL, &profile_id) != 0) {
660		ddi_remove_minor_node(devi, NULL);
661		return (DDI_FAILURE);
662	}
663
664	profile_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
665	    "profile-max-probes", PROFILE_MAX_DEFAULT);
666#else
667	if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
668	    DDI_PSEUDO, 0) == DDI_FAILURE ||
669	    dtrace_register("profile", &profile_attr,
670	    DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, NULL,
671	    &profile_pops, NULL, &profile_id) != 0) {
672		ddi_remove_minor_node(devi, NULL);
673		return (DDI_FAILURE);
674	}
675
676	profile_max = PROFILE_MAX_DEFAULT;
677#endif /* __APPLE__ */
678
679	ddi_report_dev(devi);
680	profile_devi = devi;
681	return (DDI_SUCCESS);
682}
683
684#if !defined(__APPLE__)
685static int
686profile_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
687{
688	switch (cmd) {
689	case DDI_DETACH:
690		break;
691	case DDI_SUSPEND:
692		return (DDI_SUCCESS);
693	default:
694		return (DDI_FAILURE);
695	}
696
697	if (dtrace_unregister(profile_id) != 0)
698		return (DDI_FAILURE);
699
700	ddi_remove_minor_node(devi, NULL);
701	return (DDI_SUCCESS);
702}
703
704/*ARGSUSED*/
705static int
706profile_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
707{
708	int error;
709
710	switch (infocmd) {
711	case DDI_INFO_DEVT2DEVINFO:
712		*result = (void *)profile_devi;
713		error = DDI_SUCCESS;
714		break;
715	case DDI_INFO_DEVT2INSTANCE:
716		*result = (void *)0;
717		error = DDI_SUCCESS;
718		break;
719	default:
720		error = DDI_FAILURE;
721	}
722	return (error);
723}
724
725/*ARGSUSED*/
726static int
727profile_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
728{
729	return (0);
730}
731
732static struct cb_ops profile_cb_ops = {
733	profile_open,		/* open */
734	nodev,			/* close */
735	nulldev,		/* strategy */
736	nulldev,		/* print */
737	nodev,			/* dump */
738	nodev,			/* read */
739	nodev,			/* write */
740	nodev,			/* ioctl */
741	nodev,			/* devmap */
742	nodev,			/* mmap */
743	nodev,			/* segmap */
744	nochpoll,		/* poll */
745	ddi_prop_op,		/* cb_prop_op */
746	0,			/* streamtab  */
747	D_NEW | D_MP		/* Driver compatibility flag */
748};
749
750static struct dev_ops profile_ops = {
751	DEVO_REV,		/* devo_rev, */
752	0,			/* refcnt  */
753	profile_info,		/* get_dev_info */
754	nulldev,		/* identify */
755	nulldev,		/* probe */
756	profile_attach,		/* attach */
757	profile_detach,		/* detach */
758	nodev,			/* reset */
759	&profile_cb_ops,	/* driver operations */
760	NULL,			/* bus operations */
761	nodev			/* dev power */
762};
763
764/*
765 * Module linkage information for the kernel.
766 */
767static struct modldrv modldrv = {
768	&mod_driverops,		/* module type (this is a pseudo driver) */
769	"Profile Interrupt Tracing",	/* name of module */
770	&profile_ops,		/* driver ops */
771};
772
773static struct modlinkage modlinkage = {
774	MODREV_1,
775	(void *)&modldrv,
776	NULL
777};
778
779int
780_init(void)
781{
782	return (mod_install(&modlinkage));
783}
784
785int
786_info(struct modinfo *modinfop)
787{
788	return (mod_info(&modlinkage, modinfop));
789}
790
791int
792_fini(void)
793{
794	return (mod_remove(&modlinkage));
795}
796#else
797d_open_t _profile_open;
798
799int _profile_open(dev_t dev, int flags, int devtype, struct proc *p)
800{
801#pragma unused(dev,flags,devtype,p)
802	return 0;
803}
804
805#define PROFILE_MAJOR  -24 /* let the kernel pick the device number */
806
807/*
808 * A struct describing which functions will get invoked for certain
809 * actions.
810 */
811static struct cdevsw profile_cdevsw =
812{
813	_profile_open,		/* open */
814	eno_opcl,			/* close */
815	eno_rdwrt,			/* read */
816	eno_rdwrt,			/* write */
817	eno_ioctl,			/* ioctl */
818	(stop_fcn_t *)nulldev, /* stop */
819	(reset_fcn_t *)nulldev, /* reset */
820	NULL,				/* tty's */
821	eno_select,			/* select */
822	eno_mmap,			/* mmap */
823	eno_strat,			/* strategy */
824	eno_getc,			/* getc */
825	eno_putc,			/* putc */
826	0					/* type */
827};
828
829static int gProfileInited = 0;
830
831void profile_init( void )
832{
833	if (0 == gProfileInited)
834	{
835		int majdevno = cdevsw_add(PROFILE_MAJOR, &profile_cdevsw);
836
837		if (majdevno < 0) {
838			printf("profile_init: failed to allocate a major number!\n");
839			gProfileInited = 0;
840			return;
841		}
842
843		profile_attach( (dev_info_t	*)(uintptr_t)majdevno, DDI_ATTACH );
844
845		gProfileInited = 1;
846	} else
847		panic("profile_init: called twice!\n");
848}
849#undef PROFILE_MAJOR
850#endif /* __APPLE__ */
851