xref: /freebsd/sys/cddl/dev/dtrace/dtrace_ioctl.c (revision dda5b39711dab90ae1c5624bdd6ff7453177df31)
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  * $FreeBSD$
22  *
23  */
24 
25 static int dtrace_verbose_ioctl;
26 SYSCTL_INT(_debug_dtrace, OID_AUTO, verbose_ioctl, CTLFLAG_RW,
27     &dtrace_verbose_ioctl, 0, "log DTrace ioctls");
28 
29 #define DTRACE_IOCTL_PRINTF(fmt, ...)	if (dtrace_verbose_ioctl) printf(fmt, ## __VA_ARGS__ )
30 
31 static int
32 dtrace_ioctl_helper(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
33     struct thread *td)
34 {
35 	int rval;
36 	dof_helper_t *dhp = NULL;
37 	dof_hdr_t *dof = NULL;
38 
39 	switch (cmd) {
40 	case DTRACEHIOC_ADDDOF:
41 		dhp = (dof_helper_t *)addr;
42 		/* XXX all because dofhp_dof is 64 bit */
43 		addr = (caddr_t)(vm_offset_t)dhp->dofhp_dof;
44 		/* FALLTHROUGH */
45 	case DTRACEHIOC_ADD:
46 		dof = dtrace_dof_copyin((intptr_t)addr, &rval);
47 
48 		if (dof == NULL)
49 			return (rval);
50 
51 		mutex_enter(&dtrace_lock);
52 		if ((rval = dtrace_helper_slurp((dof_hdr_t *)dof, dhp)) != -1) {
53 			if (dhp) {
54 				dhp->gen = rval;
55 				copyout(dhp, addr, sizeof(*dhp));
56 			}
57 			rval = 0;
58 		} else {
59 			rval = EINVAL;
60 		}
61 		mutex_exit(&dtrace_lock);
62 		return (rval);
63 	case DTRACEHIOC_REMOVE:
64 		mutex_enter(&dtrace_lock);
65 		rval = dtrace_helper_destroygen((int)*addr);
66 		mutex_exit(&dtrace_lock);
67 
68 		return (rval);
69 	default:
70 		break;
71 	}
72 
73 	return (ENOTTY);
74 }
75 
76 /* ARGSUSED */
77 static int
78 dtrace_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
79     int flags __unused, struct thread *td)
80 {
81 #if __FreeBSD_version < 800039
82 	dtrace_state_t *state = dev->si_drv1;
83 #else
84 	dtrace_state_t *state;
85 	devfs_get_cdevpriv((void **) &state);
86 #endif
87 	int error = 0;
88 	if (state == NULL)
89 		return (EINVAL);
90 
91 	if (state->dts_anon) {
92 		ASSERT(dtrace_anon.dta_state == NULL);
93 		state = state->dts_anon;
94 	}
95 
96 	switch (cmd) {
97 	case DTRACEIOC_AGGDESC: {
98 		dtrace_aggdesc_t **paggdesc = (dtrace_aggdesc_t **) addr;
99 		dtrace_aggdesc_t aggdesc;
100 		dtrace_action_t *act;
101 		dtrace_aggregation_t *agg;
102 		int nrecs;
103 		uint32_t offs;
104 		dtrace_recdesc_t *lrec;
105 		void *buf;
106 		size_t size;
107 		uintptr_t dest;
108 
109 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_AGGDESC\n",__func__,__LINE__);
110 
111 		if (copyin((void *) *paggdesc, &aggdesc, sizeof (aggdesc)) != 0)
112 			return (EFAULT);
113 
114 		mutex_enter(&dtrace_lock);
115 
116 		if ((agg = dtrace_aggid2agg(state, aggdesc.dtagd_id)) == NULL) {
117 			mutex_exit(&dtrace_lock);
118 			return (EINVAL);
119 		}
120 
121 		aggdesc.dtagd_epid = agg->dtag_ecb->dte_epid;
122 
123 		nrecs = aggdesc.dtagd_nrecs;
124 		aggdesc.dtagd_nrecs = 0;
125 
126 		offs = agg->dtag_base;
127 		lrec = &agg->dtag_action.dta_rec;
128 		aggdesc.dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - offs;
129 
130 		for (act = agg->dtag_first; ; act = act->dta_next) {
131 			ASSERT(act->dta_intuple ||
132 			    DTRACEACT_ISAGG(act->dta_kind));
133 
134 			/*
135 			 * If this action has a record size of zero, it
136 			 * denotes an argument to the aggregating action.
137 			 * Because the presence of this record doesn't (or
138 			 * shouldn't) affect the way the data is interpreted,
139 			 * we don't copy it out to save user-level the
140 			 * confusion of dealing with a zero-length record.
141 			 */
142 			if (act->dta_rec.dtrd_size == 0) {
143 				ASSERT(agg->dtag_hasarg);
144 				continue;
145 			}
146 
147 			aggdesc.dtagd_nrecs++;
148 
149 			if (act == &agg->dtag_action)
150 				break;
151 		}
152 
153 		/*
154 		 * Now that we have the size, we need to allocate a temporary
155 		 * buffer in which to store the complete description.  We need
156 		 * the temporary buffer to be able to drop dtrace_lock()
157 		 * across the copyout(), below.
158 		 */
159 		size = sizeof (dtrace_aggdesc_t) +
160 		    (aggdesc.dtagd_nrecs * sizeof (dtrace_recdesc_t));
161 
162 		buf = kmem_alloc(size, KM_SLEEP);
163 		dest = (uintptr_t)buf;
164 
165 		bcopy(&aggdesc, (void *)dest, sizeof (aggdesc));
166 		dest += offsetof(dtrace_aggdesc_t, dtagd_rec[0]);
167 
168 		for (act = agg->dtag_first; ; act = act->dta_next) {
169 			dtrace_recdesc_t rec = act->dta_rec;
170 
171 			/*
172 			 * See the comment in the above loop for why we pass
173 			 * over zero-length records.
174 			 */
175 			if (rec.dtrd_size == 0) {
176 				ASSERT(agg->dtag_hasarg);
177 				continue;
178 			}
179 
180 			if (nrecs-- == 0)
181 				break;
182 
183 			rec.dtrd_offset -= offs;
184 			bcopy(&rec, (void *)dest, sizeof (rec));
185 			dest += sizeof (dtrace_recdesc_t);
186 
187 			if (act == &agg->dtag_action)
188 				break;
189 		}
190 
191 		mutex_exit(&dtrace_lock);
192 
193 		if (copyout(buf, (void *) *paggdesc, dest - (uintptr_t)buf) != 0) {
194 			kmem_free(buf, size);
195 			return (EFAULT);
196 		}
197 
198 		kmem_free(buf, size);
199 		return (0);
200 	}
201 	case DTRACEIOC_AGGSNAP:
202 	case DTRACEIOC_BUFSNAP: {
203 		dtrace_bufdesc_t **pdesc = (dtrace_bufdesc_t **) addr;
204 		dtrace_bufdesc_t desc;
205 		caddr_t cached;
206 		dtrace_buffer_t *buf;
207 
208 		dtrace_debug_output();
209 
210 		if (copyin((void *) *pdesc, &desc, sizeof (desc)) != 0)
211 			return (EFAULT);
212 
213 		DTRACE_IOCTL_PRINTF("%s(%d): %s curcpu %d cpu %d\n",
214 		    __func__,__LINE__,
215 		    cmd == DTRACEIOC_AGGSNAP ?
216 		    "DTRACEIOC_AGGSNAP":"DTRACEIOC_BUFSNAP",
217 		    curcpu, desc.dtbd_cpu);
218 
219 		if (desc.dtbd_cpu >= NCPU)
220 			return (ENOENT);
221 		if (pcpu_find(desc.dtbd_cpu) == NULL)
222 			return (ENOENT);
223 
224 		mutex_enter(&dtrace_lock);
225 
226 		if (cmd == DTRACEIOC_BUFSNAP) {
227 			buf = &state->dts_buffer[desc.dtbd_cpu];
228 		} else {
229 			buf = &state->dts_aggbuffer[desc.dtbd_cpu];
230 		}
231 
232 		if (buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL)) {
233 			size_t sz = buf->dtb_offset;
234 
235 			if (state->dts_activity != DTRACE_ACTIVITY_STOPPED) {
236 				mutex_exit(&dtrace_lock);
237 				return (EBUSY);
238 			}
239 
240 			/*
241 			 * If this buffer has already been consumed, we're
242 			 * going to indicate that there's nothing left here
243 			 * to consume.
244 			 */
245 			if (buf->dtb_flags & DTRACEBUF_CONSUMED) {
246 				mutex_exit(&dtrace_lock);
247 
248 				desc.dtbd_size = 0;
249 				desc.dtbd_drops = 0;
250 				desc.dtbd_errors = 0;
251 				desc.dtbd_oldest = 0;
252 				sz = sizeof (desc);
253 
254 				if (copyout(&desc, (void *) *pdesc, sz) != 0)
255 					return (EFAULT);
256 
257 				return (0);
258 			}
259 
260 			/*
261 			 * If this is a ring buffer that has wrapped, we want
262 			 * to copy the whole thing out.
263 			 */
264 			if (buf->dtb_flags & DTRACEBUF_WRAPPED) {
265 				dtrace_buffer_polish(buf);
266 				sz = buf->dtb_size;
267 			}
268 
269 			if (copyout(buf->dtb_tomax, desc.dtbd_data, sz) != 0) {
270 				mutex_exit(&dtrace_lock);
271 				return (EFAULT);
272 			}
273 
274 			desc.dtbd_size = sz;
275 			desc.dtbd_drops = buf->dtb_drops;
276 			desc.dtbd_errors = buf->dtb_errors;
277 			desc.dtbd_oldest = buf->dtb_xamot_offset;
278 			desc.dtbd_timestamp = dtrace_gethrtime();
279 
280 			mutex_exit(&dtrace_lock);
281 
282 			if (copyout(&desc, (void *) *pdesc, sizeof (desc)) != 0)
283 				return (EFAULT);
284 
285 			buf->dtb_flags |= DTRACEBUF_CONSUMED;
286 
287 			return (0);
288 		}
289 
290 		if (buf->dtb_tomax == NULL) {
291 			ASSERT(buf->dtb_xamot == NULL);
292 			mutex_exit(&dtrace_lock);
293 			return (ENOENT);
294 		}
295 
296 		cached = buf->dtb_tomax;
297 		ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
298 
299 		dtrace_xcall(desc.dtbd_cpu,
300 		    (dtrace_xcall_t)dtrace_buffer_switch, buf);
301 
302 		state->dts_errors += buf->dtb_xamot_errors;
303 
304 		/*
305 		 * If the buffers did not actually switch, then the cross call
306 		 * did not take place -- presumably because the given CPU is
307 		 * not in the ready set.  If this is the case, we'll return
308 		 * ENOENT.
309 		 */
310 		if (buf->dtb_tomax == cached) {
311 			ASSERT(buf->dtb_xamot != cached);
312 			mutex_exit(&dtrace_lock);
313 			return (ENOENT);
314 		}
315 
316 		ASSERT(cached == buf->dtb_xamot);
317 
318 		DTRACE_IOCTL_PRINTF("%s(%d): copyout the buffer snapshot\n",__func__,__LINE__);
319 
320 		/*
321 		 * We have our snapshot; now copy it out.
322 		 */
323 		if (copyout(buf->dtb_xamot, desc.dtbd_data,
324 		    buf->dtb_xamot_offset) != 0) {
325 			mutex_exit(&dtrace_lock);
326 			return (EFAULT);
327 		}
328 
329 		desc.dtbd_size = buf->dtb_xamot_offset;
330 		desc.dtbd_drops = buf->dtb_xamot_drops;
331 		desc.dtbd_errors = buf->dtb_xamot_errors;
332 		desc.dtbd_oldest = 0;
333 		desc.dtbd_timestamp = buf->dtb_switched;
334 
335 		mutex_exit(&dtrace_lock);
336 
337 		DTRACE_IOCTL_PRINTF("%s(%d): copyout buffer desc: size %zd drops %lu errors %lu\n",__func__,__LINE__,(size_t) desc.dtbd_size,(u_long) desc.dtbd_drops,(u_long) desc.dtbd_errors);
338 
339 		/*
340 		 * Finally, copy out the buffer description.
341 		 */
342 		if (copyout(&desc, (void *) *pdesc, sizeof (desc)) != 0)
343 			return (EFAULT);
344 
345 		return (0);
346 	}
347 	case DTRACEIOC_CONF: {
348 		dtrace_conf_t conf;
349 
350 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_CONF\n",__func__,__LINE__);
351 
352 		bzero(&conf, sizeof (conf));
353 		conf.dtc_difversion = DIF_VERSION;
354 		conf.dtc_difintregs = DIF_DIR_NREGS;
355 		conf.dtc_diftupregs = DIF_DTR_NREGS;
356 		conf.dtc_ctfmodel = CTF_MODEL_NATIVE;
357 
358 		*((dtrace_conf_t *) addr) = conf;
359 
360 		return (0);
361 	}
362 	case DTRACEIOC_DOFGET: {
363 		dof_hdr_t **pdof = (dof_hdr_t **) addr;
364 		dof_hdr_t hdr, *dof = *pdof;
365 		int rval;
366 		uint64_t len;
367 
368 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_DOFGET\n",__func__,__LINE__);
369 
370 		if (copyin((void *)dof, &hdr, sizeof (hdr)) != 0)
371 			return (EFAULT);
372 
373 		mutex_enter(&dtrace_lock);
374 		dof = dtrace_dof_create(state);
375 		mutex_exit(&dtrace_lock);
376 
377 		len = MIN(hdr.dofh_loadsz, dof->dofh_loadsz);
378 		rval = copyout(dof, (void *) *pdof, len);
379 		dtrace_dof_destroy(dof);
380 
381 		return (rval == 0 ? 0 : EFAULT);
382 	}
383 	case DTRACEIOC_ENABLE: {
384 		dof_hdr_t *dof = NULL;
385 		dtrace_enabling_t *enab = NULL;
386 		dtrace_vstate_t *vstate;
387 		int err = 0;
388 		int rval;
389 		dtrace_enable_io_t *p = (dtrace_enable_io_t *) addr;
390 
391 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_ENABLE\n",__func__,__LINE__);
392 
393 		/*
394 		 * If a NULL argument has been passed, we take this as our
395 		 * cue to reevaluate our enablings.
396 		 */
397 		if (p->dof == NULL) {
398 			dtrace_enabling_matchall();
399 
400 			return (0);
401 		}
402 
403 		if ((dof = dtrace_dof_copyin((uintptr_t) p->dof, &rval)) == NULL)
404 			return (EINVAL);
405 
406 		mutex_enter(&cpu_lock);
407 		mutex_enter(&dtrace_lock);
408 		vstate = &state->dts_vstate;
409 
410 		if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) {
411 			mutex_exit(&dtrace_lock);
412 			mutex_exit(&cpu_lock);
413 			dtrace_dof_destroy(dof);
414 			return (EBUSY);
415 		}
416 
417 		if (dtrace_dof_slurp(dof, vstate, td->td_ucred, &enab, 0, B_TRUE) != 0) {
418 			mutex_exit(&dtrace_lock);
419 			mutex_exit(&cpu_lock);
420 			dtrace_dof_destroy(dof);
421 			return (EINVAL);
422 		}
423 
424 		if ((rval = dtrace_dof_options(dof, state)) != 0) {
425 			dtrace_enabling_destroy(enab);
426 			mutex_exit(&dtrace_lock);
427 			mutex_exit(&cpu_lock);
428 			dtrace_dof_destroy(dof);
429 			return (rval);
430 		}
431 
432 		if ((err = dtrace_enabling_match(enab, &p->n_matched)) == 0) {
433 			err = dtrace_enabling_retain(enab);
434 		} else {
435 			dtrace_enabling_destroy(enab);
436 		}
437 
438 		mutex_exit(&cpu_lock);
439 		mutex_exit(&dtrace_lock);
440 		dtrace_dof_destroy(dof);
441 
442 		return (err);
443 	}
444 	case DTRACEIOC_EPROBE: {
445 		dtrace_eprobedesc_t **pepdesc = (dtrace_eprobedesc_t **) addr;
446 		dtrace_eprobedesc_t epdesc;
447 		dtrace_ecb_t *ecb;
448 		dtrace_action_t *act;
449 		void *buf;
450 		size_t size;
451 		uintptr_t dest;
452 		int nrecs;
453 
454 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_EPROBE\n",__func__,__LINE__);
455 
456 		if (copyin((void *)*pepdesc, &epdesc, sizeof (epdesc)) != 0)
457 			return (EFAULT);
458 
459 		mutex_enter(&dtrace_lock);
460 
461 		if ((ecb = dtrace_epid2ecb(state, epdesc.dtepd_epid)) == NULL) {
462 			mutex_exit(&dtrace_lock);
463 			return (EINVAL);
464 		}
465 
466 		if (ecb->dte_probe == NULL) {
467 			mutex_exit(&dtrace_lock);
468 			return (EINVAL);
469 		}
470 
471 		epdesc.dtepd_probeid = ecb->dte_probe->dtpr_id;
472 		epdesc.dtepd_uarg = ecb->dte_uarg;
473 		epdesc.dtepd_size = ecb->dte_size;
474 
475 		nrecs = epdesc.dtepd_nrecs;
476 		epdesc.dtepd_nrecs = 0;
477 		for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
478 			if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
479 				continue;
480 
481 			epdesc.dtepd_nrecs++;
482 		}
483 
484 		/*
485 		 * Now that we have the size, we need to allocate a temporary
486 		 * buffer in which to store the complete description.  We need
487 		 * the temporary buffer to be able to drop dtrace_lock()
488 		 * across the copyout(), below.
489 		 */
490 		size = sizeof (dtrace_eprobedesc_t) +
491 		    (epdesc.dtepd_nrecs * sizeof (dtrace_recdesc_t));
492 
493 		buf = kmem_alloc(size, KM_SLEEP);
494 		dest = (uintptr_t)buf;
495 
496 		bcopy(&epdesc, (void *)dest, sizeof (epdesc));
497 		dest += offsetof(dtrace_eprobedesc_t, dtepd_rec[0]);
498 
499 		for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
500 			if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
501 				continue;
502 
503 			if (nrecs-- == 0)
504 				break;
505 
506 			bcopy(&act->dta_rec, (void *)dest,
507 			    sizeof (dtrace_recdesc_t));
508 			dest += sizeof (dtrace_recdesc_t);
509 		}
510 
511 		mutex_exit(&dtrace_lock);
512 
513 		if (copyout(buf, (void *) *pepdesc, dest - (uintptr_t)buf) != 0) {
514 			kmem_free(buf, size);
515 			return (EFAULT);
516 		}
517 
518 		kmem_free(buf, size);
519 		return (0);
520 	}
521 	case DTRACEIOC_FORMAT: {
522 		dtrace_fmtdesc_t *fmt = (dtrace_fmtdesc_t *) addr;
523 		char *str;
524 		int len;
525 
526 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_FORMAT\n",__func__,__LINE__);
527 
528 		mutex_enter(&dtrace_lock);
529 
530 		if (fmt->dtfd_format == 0 ||
531 		    fmt->dtfd_format > state->dts_nformats) {
532 			mutex_exit(&dtrace_lock);
533 			return (EINVAL);
534 		}
535 
536 		/*
537 		 * Format strings are allocated contiguously and they are
538 		 * never freed; if a format index is less than the number
539 		 * of formats, we can assert that the format map is non-NULL
540 		 * and that the format for the specified index is non-NULL.
541 		 */
542 		ASSERT(state->dts_formats != NULL);
543 		str = state->dts_formats[fmt->dtfd_format - 1];
544 		ASSERT(str != NULL);
545 
546 		len = strlen(str) + 1;
547 
548 		if (len > fmt->dtfd_length) {
549 			fmt->dtfd_length = len;
550 		} else {
551 			if (copyout(str, fmt->dtfd_string, len) != 0) {
552 				mutex_exit(&dtrace_lock);
553 				return (EINVAL);
554 			}
555 		}
556 
557 		mutex_exit(&dtrace_lock);
558 		return (0);
559 	}
560 	case DTRACEIOC_GO: {
561 		int rval;
562 		processorid_t *cpuid = (processorid_t *) addr;
563 
564 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_GO\n",__func__,__LINE__);
565 
566 		rval = dtrace_state_go(state, cpuid);
567 
568 		return (rval);
569 	}
570 	case DTRACEIOC_PROBEARG: {
571 		dtrace_argdesc_t *desc = (dtrace_argdesc_t *) addr;
572 		dtrace_probe_t *probe;
573 		dtrace_provider_t *prov;
574 
575 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_PROBEARG\n",__func__,__LINE__);
576 
577 		if (desc->dtargd_id == DTRACE_IDNONE)
578 			return (EINVAL);
579 
580 		if (desc->dtargd_ndx == DTRACE_ARGNONE)
581 			return (EINVAL);
582 
583 		mutex_enter(&dtrace_provider_lock);
584 #if defined(sun)
585 		mutex_enter(&mod_lock);
586 #endif
587 		mutex_enter(&dtrace_lock);
588 
589 		if (desc->dtargd_id > dtrace_nprobes) {
590 			mutex_exit(&dtrace_lock);
591 #if defined(sun)
592 			mutex_exit(&mod_lock);
593 #endif
594 			mutex_exit(&dtrace_provider_lock);
595 			return (EINVAL);
596 		}
597 
598 		if ((probe = dtrace_probes[desc->dtargd_id - 1]) == NULL) {
599 			mutex_exit(&dtrace_lock);
600 #if defined(sun)
601 			mutex_exit(&mod_lock);
602 #endif
603 			mutex_exit(&dtrace_provider_lock);
604 			return (EINVAL);
605 		}
606 
607 		mutex_exit(&dtrace_lock);
608 
609 		prov = probe->dtpr_provider;
610 
611 		if (prov->dtpv_pops.dtps_getargdesc == NULL) {
612 			/*
613 			 * There isn't any typed information for this probe.
614 			 * Set the argument number to DTRACE_ARGNONE.
615 			 */
616 			desc->dtargd_ndx = DTRACE_ARGNONE;
617 		} else {
618 			desc->dtargd_native[0] = '\0';
619 			desc->dtargd_xlate[0] = '\0';
620 			desc->dtargd_mapping = desc->dtargd_ndx;
621 
622 			prov->dtpv_pops.dtps_getargdesc(prov->dtpv_arg,
623 			    probe->dtpr_id, probe->dtpr_arg, desc);
624 		}
625 
626 #if defined(sun)
627 		mutex_exit(&mod_lock);
628 #endif
629 		mutex_exit(&dtrace_provider_lock);
630 
631 		return (0);
632 	}
633 	case DTRACEIOC_PROBEMATCH:
634 	case DTRACEIOC_PROBES: {
635 		dtrace_probedesc_t *p_desc = (dtrace_probedesc_t *) addr;
636 		dtrace_probe_t *probe = NULL;
637 		dtrace_probekey_t pkey;
638 		dtrace_id_t i;
639 		int m = 0;
640 		uint32_t priv = 0;
641 		uid_t uid = 0;
642 		zoneid_t zoneid = 0;
643 
644 		DTRACE_IOCTL_PRINTF("%s(%d): %s\n",__func__,__LINE__,
645 		    cmd == DTRACEIOC_PROBEMATCH ?
646 		    "DTRACEIOC_PROBEMATCH":"DTRACEIOC_PROBES");
647 
648 		p_desc->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
649 		p_desc->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
650 		p_desc->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
651 		p_desc->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
652 
653 		/*
654 		 * Before we attempt to match this probe, we want to give
655 		 * all providers the opportunity to provide it.
656 		 */
657 		if (p_desc->dtpd_id == DTRACE_IDNONE) {
658 			mutex_enter(&dtrace_provider_lock);
659 			dtrace_probe_provide(p_desc, NULL);
660 			mutex_exit(&dtrace_provider_lock);
661 			p_desc->dtpd_id++;
662 		}
663 
664 		if (cmd == DTRACEIOC_PROBEMATCH)  {
665 			dtrace_probekey(p_desc, &pkey);
666 			pkey.dtpk_id = DTRACE_IDNONE;
667 		}
668 
669 		dtrace_cred2priv(td->td_ucred, &priv, &uid, &zoneid);
670 
671 		mutex_enter(&dtrace_lock);
672 
673 		if (cmd == DTRACEIOC_PROBEMATCH) {
674 			for (i = p_desc->dtpd_id; i <= dtrace_nprobes; i++) {
675 				if ((probe = dtrace_probes[i - 1]) != NULL &&
676 				    (m = dtrace_match_probe(probe, &pkey,
677 				    priv, uid, zoneid)) != 0)
678 					break;
679 			}
680 
681 			if (m < 0) {
682 				mutex_exit(&dtrace_lock);
683 				return (EINVAL);
684 			}
685 
686 		} else {
687 			for (i = p_desc->dtpd_id; i <= dtrace_nprobes; i++) {
688 				if ((probe = dtrace_probes[i - 1]) != NULL &&
689 				    dtrace_match_priv(probe, priv, uid, zoneid))
690 					break;
691 			}
692 		}
693 
694 		if (probe == NULL) {
695 			mutex_exit(&dtrace_lock);
696 			return (ESRCH);
697 		}
698 
699 		dtrace_probe_description(probe, p_desc);
700 		mutex_exit(&dtrace_lock);
701 
702 		return (0);
703 	}
704 	case DTRACEIOC_PROVIDER: {
705 		dtrace_providerdesc_t *pvd = (dtrace_providerdesc_t *) addr;
706 		dtrace_provider_t *pvp;
707 
708 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_PROVIDER\n",__func__,__LINE__);
709 
710 		pvd->dtvd_name[DTRACE_PROVNAMELEN - 1] = '\0';
711 		mutex_enter(&dtrace_provider_lock);
712 
713 		for (pvp = dtrace_provider; pvp != NULL; pvp = pvp->dtpv_next) {
714 			if (strcmp(pvp->dtpv_name, pvd->dtvd_name) == 0)
715 				break;
716 		}
717 
718 		mutex_exit(&dtrace_provider_lock);
719 
720 		if (pvp == NULL)
721 			return (ESRCH);
722 
723 		bcopy(&pvp->dtpv_priv, &pvd->dtvd_priv, sizeof (dtrace_ppriv_t));
724 		bcopy(&pvp->dtpv_attr, &pvd->dtvd_attr, sizeof (dtrace_pattr_t));
725 
726 		return (0);
727 	}
728 	case DTRACEIOC_REPLICATE: {
729 		dtrace_repldesc_t *desc = (dtrace_repldesc_t *) addr;
730 		dtrace_probedesc_t *match = &desc->dtrpd_match;
731 		dtrace_probedesc_t *create = &desc->dtrpd_create;
732 		int err;
733 
734 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_REPLICATE\n",__func__,__LINE__);
735 
736 		match->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
737 		match->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
738 		match->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
739 		match->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
740 
741 		create->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
742 		create->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
743 		create->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
744 		create->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
745 
746 		mutex_enter(&dtrace_lock);
747 		err = dtrace_enabling_replicate(state, match, create);
748 		mutex_exit(&dtrace_lock);
749 
750 		return (err);
751 	}
752 	case DTRACEIOC_STATUS: {
753 		dtrace_status_t *stat = (dtrace_status_t *) addr;
754 		dtrace_dstate_t *dstate;
755 		int i, j;
756 		uint64_t nerrs;
757 
758 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_STATUS\n",__func__,__LINE__);
759 
760 		/*
761 		 * See the comment in dtrace_state_deadman() for the reason
762 		 * for setting dts_laststatus to INT64_MAX before setting
763 		 * it to the correct value.
764 		 */
765 		state->dts_laststatus = INT64_MAX;
766 		dtrace_membar_producer();
767 		state->dts_laststatus = dtrace_gethrtime();
768 
769 		bzero(stat, sizeof (*stat));
770 
771 		mutex_enter(&dtrace_lock);
772 
773 		if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) {
774 			mutex_exit(&dtrace_lock);
775 			return (ENOENT);
776 		}
777 
778 		if (state->dts_activity == DTRACE_ACTIVITY_DRAINING)
779 			stat->dtst_exiting = 1;
780 
781 		nerrs = state->dts_errors;
782 		dstate = &state->dts_vstate.dtvs_dynvars;
783 
784 		for (i = 0; i < NCPU; i++) {
785 #if !defined(sun)
786 			if (pcpu_find(i) == NULL)
787 				continue;
788 #endif
789 			dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[i];
790 
791 			stat->dtst_dyndrops += dcpu->dtdsc_drops;
792 			stat->dtst_dyndrops_dirty += dcpu->dtdsc_dirty_drops;
793 			stat->dtst_dyndrops_rinsing += dcpu->dtdsc_rinsing_drops;
794 
795 			if (state->dts_buffer[i].dtb_flags & DTRACEBUF_FULL)
796 				stat->dtst_filled++;
797 
798 			nerrs += state->dts_buffer[i].dtb_errors;
799 
800 			for (j = 0; j < state->dts_nspeculations; j++) {
801 				dtrace_speculation_t *spec;
802 				dtrace_buffer_t *buf;
803 
804 				spec = &state->dts_speculations[j];
805 				buf = &spec->dtsp_buffer[i];
806 				stat->dtst_specdrops += buf->dtb_xamot_drops;
807 			}
808 		}
809 
810 		stat->dtst_specdrops_busy = state->dts_speculations_busy;
811 		stat->dtst_specdrops_unavail = state->dts_speculations_unavail;
812 		stat->dtst_stkstroverflows = state->dts_stkstroverflows;
813 		stat->dtst_dblerrors = state->dts_dblerrors;
814 		stat->dtst_killed =
815 		    (state->dts_activity == DTRACE_ACTIVITY_KILLED);
816 		stat->dtst_errors = nerrs;
817 
818 		mutex_exit(&dtrace_lock);
819 
820 		return (0);
821 	}
822 	case DTRACEIOC_STOP: {
823 		int rval;
824 		processorid_t *cpuid = (processorid_t *) addr;
825 
826 		DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_STOP\n",__func__,__LINE__);
827 
828 		mutex_enter(&dtrace_lock);
829 		rval = dtrace_state_stop(state, cpuid);
830 		mutex_exit(&dtrace_lock);
831 
832 		return (rval);
833 	}
834 	default:
835 		error = ENOTTY;
836 	}
837 	return (error);
838 }
839