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