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 * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22 * Copyright 2024 Mark Johnston <markj@FreeBSD.org>
23 */
24
25 /*
26 * This file contains a reimplementation of the statically-defined tracing (SDT)
27 * framework for DTrace. Probes and SDT providers are defined using the macros
28 * in sys/sdt.h, which append all the needed structures to linker sets. When
29 * this module is loaded, it iterates over all of the loaded modules and
30 * registers probes and providers with the DTrace framework based on the
31 * contents of these linker sets.
32 *
33 * A list of SDT providers is maintained here since a provider may span multiple
34 * modules. When a kernel module is unloaded, a provider defined in that module
35 * is unregistered only if no other modules refer to it. The DTrace framework is
36 * responsible for destroying individual probes when a kernel module is
37 * unloaded; in particular, probes may not span multiple kernel modules.
38 */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42
43 #include <sys/conf.h>
44 #include <sys/endian.h>
45 #include <sys/eventhandler.h>
46 #include <sys/kernel.h>
47 #include <sys/limits.h>
48 #include <sys/linker.h>
49 #include <sys/linker_set.h>
50 #include <sys/lock.h>
51 #include <sys/lockstat.h>
52 #include <sys/malloc.h>
53 #include <sys/module.h>
54 #include <sys/mutex.h>
55 #include <sys/queue.h>
56 #include <sys/sdt.h>
57
58 #include <sys/dtrace.h>
59 #include <sys/dtrace_bsd.h>
60
61 #include <cddl/dev/dtrace/dtrace_cddl.h>
62
63 _Static_assert(sizeof((struct sdt_probe *)NULL)->id == sizeof(dtrace_id_t),
64 "sdt_probe.id and dtrace_id_t size mismatch");
65
66 /* DTrace methods. */
67 static void sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
68 static uint64_t sdt_getargval(void *, dtrace_id_t, void *, int, int);
69 static void sdt_provide_probes(void *, dtrace_probedesc_t *);
70 static void sdt_destroy(void *, dtrace_id_t, void *);
71 static void sdt_enable(void *, dtrace_id_t, void *);
72 static void sdt_disable(void *, dtrace_id_t, void *);
73
74 static void sdt_load(void);
75 static int sdt_unload(void);
76 static void sdt_create_provider(struct sdt_provider *);
77 static void sdt_create_probe(struct sdt_probe *);
78 static void sdt_init_probe(struct sdt_probe *, linker_file_t);
79 static void sdt_kld_load(void *, struct linker_file *);
80 static void sdt_kld_unload_try(void *, struct linker_file *, int *);
81
82 static MALLOC_DEFINE(M_SDT, "SDT", "DTrace SDT providers");
83
84 static int sdt_probes_enabled_count;
85 static int lockstat_enabled_count;
86
87 static dtrace_pattr_t sdt_attr = {
88 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
89 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
90 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
91 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
92 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
93 };
94
95 static dtrace_pops_t sdt_pops = {
96 .dtps_provide = sdt_provide_probes,
97 .dtps_provide_module = NULL,
98 .dtps_enable = sdt_enable,
99 .dtps_disable = sdt_disable,
100 .dtps_suspend = NULL,
101 .dtps_resume = NULL,
102 .dtps_getargdesc = sdt_getargdesc,
103 .dtps_getargval = sdt_getargval,
104 .dtps_usermode = NULL,
105 .dtps_destroy = sdt_destroy,
106 };
107
108 static TAILQ_HEAD(, sdt_provider) sdt_prov_list;
109
110 static eventhandler_tag sdt_kld_load_tag;
111 static eventhandler_tag sdt_kld_unload_try_tag;
112
113 static void
sdt_create_provider(struct sdt_provider * prov)114 sdt_create_provider(struct sdt_provider *prov)
115 {
116 struct sdt_provider *curr, *newprov;
117
118 TAILQ_FOREACH(curr, &sdt_prov_list, prov_entry)
119 if (strcmp(prov->name, curr->name) == 0) {
120 /* The provider has already been defined. */
121 curr->sdt_refs++;
122 return;
123 }
124
125 /*
126 * Make a copy of prov so that we don't lose fields if its module is
127 * unloaded but the provider isn't destroyed. This could happen with
128 * a provider that spans multiple modules.
129 */
130 newprov = malloc(sizeof(*newprov), M_SDT, M_WAITOK | M_ZERO);
131 newprov->name = strdup(prov->name, M_SDT);
132 prov->sdt_refs = newprov->sdt_refs = 1;
133
134 TAILQ_INSERT_TAIL(&sdt_prov_list, newprov, prov_entry);
135
136 (void)dtrace_register(newprov->name, &sdt_attr, DTRACE_PRIV_USER, NULL,
137 &sdt_pops, NULL, (dtrace_provider_id_t *)&newprov->id);
138 prov->id = newprov->id;
139 }
140
141 static void
sdt_create_probe(struct sdt_probe * probe)142 sdt_create_probe(struct sdt_probe *probe)
143 {
144 struct sdt_provider *prov;
145 char mod[DTRACE_MODNAMELEN];
146 char func[DTRACE_FUNCNAMELEN];
147 char name[DTRACE_NAMELEN];
148 const char *from;
149 char *to;
150 size_t len;
151 int aframes;
152
153 if (probe->version != (int)sizeof(*probe)) {
154 printf("ignoring probe %p, version %u expected %u\n",
155 probe, probe->version, (int)sizeof(*probe));
156 return;
157 }
158
159 TAILQ_FOREACH(prov, &sdt_prov_list, prov_entry)
160 if (strcmp(prov->name, probe->prov->name) == 0)
161 break;
162
163 KASSERT(prov != NULL, ("probe defined without a provider"));
164
165 /* If no module name was specified, use the module filename. */
166 if (*probe->mod == 0) {
167 len = strlcpy(mod, probe->sdtp_lf->filename, sizeof(mod));
168 if (len > 3 && strcmp(mod + len - 3, ".ko") == 0)
169 mod[len - 3] = '\0';
170 } else
171 strlcpy(mod, probe->mod, sizeof(mod));
172
173 /*
174 * Unfortunately this is necessary because the Solaris DTrace
175 * code mixes consts and non-consts with casts to override
176 * the incompatibilies. On FreeBSD, we use strict warnings
177 * in the C compiler, so we have to respect const vs non-const.
178 */
179 strlcpy(func, probe->func, sizeof(func));
180 if (func[0] == '\0')
181 strcpy(func, "none");
182
183 from = probe->name;
184 to = name;
185 for (len = 0; len < (sizeof(name) - 1) && *from != '\0';
186 len++, from++, to++) {
187 if (from[0] == '_' && from[1] == '_') {
188 *to = '-';
189 from++;
190 } else
191 *to = *from;
192 }
193 *to = '\0';
194
195 if (dtrace_probe_lookup(prov->id, mod, func, name) != DTRACE_IDNONE)
196 return;
197
198 aframes = 1; /* unwind past sdt_probe() */
199 if (strcmp(prov->name, "lockstat") == 0) {
200 /*
201 * Locking primitives instrumented by lockstat automatically
202 * disable inlining. Step forward an extra frame so that DTrace
203 * variables like "caller" provide the function trying to
204 * acquire or release the lock rather than an internal function.
205 */
206 aframes++;
207 }
208 (void)dtrace_probe_create(prov->id, mod, func, name, aframes, probe);
209 }
210
211 static void
sdt_init_probe(struct sdt_probe * probe,linker_file_t lf)212 sdt_init_probe(struct sdt_probe *probe, linker_file_t lf)
213 {
214 probe->sdtp_lf = lf;
215 TAILQ_INIT(&probe->argtype_list);
216 STAILQ_INIT(&probe->tracepoint_list);
217 }
218
219 /*
220 * Probes are created through the SDT module load/unload hook, so this function
221 * has nothing to do. It only exists because the DTrace provider framework
222 * requires one of provide_probes and provide_module to be defined.
223 */
224 static void
sdt_provide_probes(void * arg,dtrace_probedesc_t * desc)225 sdt_provide_probes(void *arg, dtrace_probedesc_t *desc)
226 {
227 }
228
229 struct sdt_enable_cb_arg {
230 struct sdt_probe *probe;
231 int cpu;
232 int arrived;
233 int done;
234 bool enable;
235 };
236
237 static void
sdt_probe_update_cb(void * _arg)238 sdt_probe_update_cb(void *_arg)
239 {
240 struct sdt_enable_cb_arg *arg;
241 struct sdt_tracepoint *tp;
242
243 arg = _arg;
244 if (arg->cpu != curcpu) {
245 atomic_add_rel_int(&arg->arrived, 1);
246 while (atomic_load_acq_int(&arg->done) == 0)
247 cpu_spinwait();
248 return;
249 } else {
250 while (atomic_load_acq_int(&arg->arrived) != mp_ncpus - 1)
251 cpu_spinwait();
252 }
253
254 STAILQ_FOREACH(tp, &arg->probe->tracepoint_list, tracepoint_entry) {
255 if (arg->enable)
256 sdt_tracepoint_patch(tp->patchpoint, tp->target);
257 else
258 sdt_tracepoint_restore(tp->patchpoint);
259 }
260
261 atomic_store_rel_int(&arg->done, 1);
262 }
263
264 static void
sdt_probe_update(struct sdt_probe * probe,bool enable)265 sdt_probe_update(struct sdt_probe *probe, bool enable)
266 {
267 struct sdt_enable_cb_arg cbarg;
268
269 sched_pin();
270 cbarg.probe = probe;
271 cbarg.cpu = curcpu;
272 atomic_store_rel_int(&cbarg.arrived, 0);
273 atomic_store_rel_int(&cbarg.done, 0);
274 cbarg.enable = enable;
275 smp_rendezvous(NULL, sdt_probe_update_cb, NULL, &cbarg);
276 sched_unpin();
277 }
278
279 static void
sdt_enable(void * arg __unused,dtrace_id_t id,void * parg)280 sdt_enable(void *arg __unused, dtrace_id_t id, void *parg)
281 {
282 struct sdt_probe *probe;
283
284 probe = parg;
285
286 probe->id = id;
287 probe->sdtp_lf->nenabled++;
288 if (strcmp(probe->prov->name, "lockstat") == 0) {
289 lockstat_enabled_count++;
290 if (lockstat_enabled_count == 1)
291 lockstat_enabled = true;
292 }
293 sdt_probes_enabled_count++;
294 if (sdt_probes_enabled_count == 1)
295 sdt_probes_enabled = true;
296
297 sdt_probe_update(probe, true);
298 }
299
300 static void
sdt_disable(void * arg __unused,dtrace_id_t id,void * parg)301 sdt_disable(void *arg __unused, dtrace_id_t id, void *parg)
302 {
303 struct sdt_probe *probe;
304
305 probe = parg;
306 KASSERT(probe->sdtp_lf->nenabled > 0, ("no probes enabled"));
307
308 sdt_probe_update(probe, false);
309
310 sdt_probes_enabled_count--;
311 if (sdt_probes_enabled_count == 0)
312 sdt_probes_enabled = false;
313 if (strcmp(probe->prov->name, "lockstat") == 0) {
314 lockstat_enabled_count--;
315 if (lockstat_enabled_count == 0)
316 lockstat_enabled = false;
317 }
318 probe->id = 0;
319 probe->sdtp_lf->nenabled--;
320 }
321
322 static void
sdt_getargdesc(void * arg,dtrace_id_t id,void * parg,dtrace_argdesc_t * desc)323 sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
324 {
325 struct sdt_argtype *argtype;
326 struct sdt_probe *probe = parg;
327
328 if (desc->dtargd_ndx >= probe->n_args) {
329 desc->dtargd_ndx = DTRACE_ARGNONE;
330 return;
331 }
332
333 TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) {
334 if (desc->dtargd_ndx == argtype->ndx) {
335 desc->dtargd_mapping = desc->dtargd_ndx;
336 if (argtype->type == NULL) {
337 desc->dtargd_native[0] = '\0';
338 desc->dtargd_xlate[0] = '\0';
339 continue;
340 }
341 strlcpy(desc->dtargd_native, argtype->type,
342 sizeof(desc->dtargd_native));
343 if (argtype->xtype != NULL)
344 strlcpy(desc->dtargd_xlate, argtype->xtype,
345 sizeof(desc->dtargd_xlate));
346 }
347 }
348 }
349
350 /*
351 * Fetch arguments beyond the first five passed directly to dtrace_probe().
352 * FreeBSD's SDT implement currently only supports up to 6 arguments, so we just
353 * need to handle arg5 here.
354 */
355 static uint64_t
sdt_getargval(void * arg __unused,dtrace_id_t id __unused,void * parg __unused,int argno,int aframes __unused)356 sdt_getargval(void *arg __unused, dtrace_id_t id __unused,
357 void *parg __unused, int argno, int aframes __unused)
358 {
359 if (argno != 5) {
360 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
361 return (0);
362 } else {
363 return (curthread->t_dtrace_sdt_arg[argno - 5]);
364 }
365 }
366
367 static void
sdt_destroy(void * arg,dtrace_id_t id,void * parg)368 sdt_destroy(void *arg, dtrace_id_t id, void *parg)
369 {
370 }
371
372 static void
sdt_kld_load_providers(struct linker_file * lf)373 sdt_kld_load_providers(struct linker_file *lf)
374 {
375 struct sdt_provider **prov, **begin, **end;
376 struct sdt_probe **p_begin, **p_end;
377
378 if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end,
379 NULL) == 0) {
380 for (prov = begin; prov < end; prov++)
381 sdt_create_provider(*prov);
382 }
383
384 if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
385 NULL) == 0) {
386 for (struct sdt_probe **probe = p_begin; probe < p_end; probe++)
387 sdt_init_probe(*probe, lf);
388 }
389 }
390
391 static void
sdt_kld_load_probes(struct linker_file * lf)392 sdt_kld_load_probes(struct linker_file *lf)
393 {
394 struct sdt_probe **p_begin, **p_end;
395 struct sdt_argtype **a_begin, **a_end;
396 struct sdt_tracepoint *tp_begin, *tp_end;
397
398 if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
399 NULL) == 0) {
400 for (struct sdt_probe **probe = p_begin; probe < p_end; probe++)
401 sdt_create_probe(*probe);
402 }
403
404 if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end,
405 NULL) == 0) {
406 for (struct sdt_argtype **argtype = a_begin; argtype < a_end;
407 argtype++) {
408 (*argtype)->probe->n_args++;
409 TAILQ_INSERT_TAIL(&(*argtype)->probe->argtype_list,
410 *argtype, argtype_entry);
411 }
412 }
413
414 if (linker_file_lookup_set(lf, __XSTRING(_SDT_TRACEPOINT_SET),
415 &tp_begin, &tp_end, NULL) == 0) {
416 for (struct sdt_tracepoint *tp = tp_begin; tp < tp_end; tp++) {
417 if (!sdt_tracepoint_valid(tp->patchpoint, tp->target)) {
418 printf(
419 "invalid tracepoint %#jx->%#jx for %s:%s:%s:%s\n",
420 (uintmax_t)tp->patchpoint,
421 (uintmax_t)tp->target,
422 tp->probe->prov->name, tp->probe->mod,
423 tp->probe->func, tp->probe->name);
424 continue;
425 }
426 STAILQ_INSERT_TAIL(&tp->probe->tracepoint_list, tp,
427 tracepoint_entry);
428 }
429 }
430 }
431
432 /*
433 * Called from the kernel linker when a module is loaded, before
434 * dtrace_module_loaded() is called. This is done so that it's possible to
435 * register new providers when modules are loaded. The DTrace framework
436 * explicitly disallows calling into the framework from the provide_module
437 * provider method, so we cannot do this there.
438 */
439 static void
sdt_kld_load(void * arg __unused,struct linker_file * lf)440 sdt_kld_load(void *arg __unused, struct linker_file *lf)
441 {
442 sdt_kld_load_providers(lf);
443 sdt_kld_load_probes(lf);
444 }
445
446 static bool
sdt_kld_unload_providers(struct linker_file * lf)447 sdt_kld_unload_providers(struct linker_file *lf)
448 {
449 struct sdt_provider *prov, **curr, **begin, **end, *tmp;
450
451 if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end,
452 NULL))
453 /* No DTrace providers are declared in this file. */
454 return (true);
455
456 /*
457 * Go through all the providers declared in this linker file and
458 * unregister any that aren't declared in another loaded file.
459 */
460 for (curr = begin; curr < end; curr++) {
461 TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
462 if (strcmp(prov->name, (*curr)->name) != 0)
463 continue;
464
465 if (prov->sdt_refs == 1) {
466 if (dtrace_unregister(prov->id) != 0) {
467 return (false);
468 }
469 TAILQ_REMOVE(&sdt_prov_list, prov, prov_entry);
470 free(prov->name, M_SDT);
471 free(prov, M_SDT);
472 } else
473 prov->sdt_refs--;
474 break;
475 }
476 }
477
478 return (true);
479 }
480
481 static bool
sdt_kld_unload_probes(struct linker_file * lf)482 sdt_kld_unload_probes(struct linker_file *lf)
483 {
484 struct sdt_probe **p_begin, **p_end;
485 struct sdt_argtype **a_begin, **a_end;
486 struct sdt_tracepoint *tp_begin, *tp_end;
487
488 if (linker_file_lookup_set(lf, __XSTRING(_SDT_TRACEPOINT_SET),
489 &tp_begin, &tp_end, NULL) == 0) {
490 for (struct sdt_tracepoint *tp = tp_begin; tp < tp_end; tp++) {
491 struct sdt_tracepoint *tp2;
492
493 if (!sdt_tracepoint_valid(tp->patchpoint, tp->target))
494 continue;
495
496 /* Only remove the entry if it is in the list. */
497 tp2 = STAILQ_FIRST(&tp->probe->tracepoint_list);
498 if (tp2 == tp) {
499 STAILQ_REMOVE_HEAD(&tp->probe->tracepoint_list,
500 tracepoint_entry);
501 } else if (tp2 != NULL) {
502 struct sdt_tracepoint *tp3;
503
504 for (;;) {
505 tp3 = STAILQ_NEXT(tp2,
506 tracepoint_entry);
507 if (tp3 == NULL)
508 break;
509 if (tp3 == tp) {
510 STAILQ_REMOVE_AFTER(
511 &tp->probe->tracepoint_list,
512 tp2, tracepoint_entry);
513 break;
514 }
515 tp2 = tp3;
516 }
517 }
518 }
519 }
520
521 if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end,
522 NULL) == 0) {
523 for (struct sdt_argtype **argtype = a_begin; argtype < a_end;
524 argtype++) {
525 struct sdt_argtype *argtype2;
526
527 /* Only remove the entry if it is in the list. */
528 TAILQ_FOREACH(argtype2,
529 &(*argtype)->probe->argtype_list, argtype_entry) {
530 if (argtype2 == *argtype) {
531 (*argtype)->probe->n_args--;
532 TAILQ_REMOVE(
533 &(*argtype)->probe->argtype_list,
534 *argtype, argtype_entry);
535 break;
536 }
537 }
538 }
539 }
540
541 if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end,
542 NULL) == 0) {
543 for (struct sdt_probe **probe = p_begin; probe < p_end;
544 probe++) {
545 if ((*probe)->sdtp_lf == lf) {
546 if (!TAILQ_EMPTY(&(*probe)->argtype_list))
547 return (false);
548 if (!STAILQ_EMPTY(&(*probe)->tracepoint_list))
549 return (false);
550
551 /*
552 * Don't destroy the probe as there
553 * might be multiple instances of the
554 * same probe in different modules.
555 */
556 }
557 }
558 }
559
560 return (true);
561 }
562
563 static void
sdt_kld_unload_try(void * arg __unused,struct linker_file * lf,int * error)564 sdt_kld_unload_try(void *arg __unused, struct linker_file *lf, int *error)
565 {
566 if (*error != 0)
567 /* We already have an error, so don't do anything. */
568 return;
569
570 if (!sdt_kld_unload_probes(lf))
571 *error = 1;
572 else if (!sdt_kld_unload_providers(lf))
573 *error = 1;
574 }
575
576 static int
sdt_load_providers_cb(linker_file_t lf,void * arg __unused)577 sdt_load_providers_cb(linker_file_t lf, void *arg __unused)
578 {
579 sdt_kld_load_providers(lf);
580 return (0);
581 }
582
583 static int
sdt_load_probes_cb(linker_file_t lf,void * arg __unused)584 sdt_load_probes_cb(linker_file_t lf, void *arg __unused)
585 {
586 sdt_kld_load_probes(lf);
587 return (0);
588 }
589
590 static void
sdt_dtrace_probe(dtrace_id_t id,uintptr_t arg0,uintptr_t arg1,uintptr_t arg2,uintptr_t arg3,uintptr_t arg4,uintptr_t arg5)591 sdt_dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
592 uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
593 {
594 curthread->t_dtrace_sdt_arg[0] = arg5;
595 dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
596 }
597
598 static void
sdt_load(void)599 sdt_load(void)
600 {
601
602 TAILQ_INIT(&sdt_prov_list);
603
604 sdt_probe_func = sdt_dtrace_probe;
605
606 sdt_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, sdt_kld_load, NULL,
607 EVENTHANDLER_PRI_ANY);
608 sdt_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try,
609 sdt_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY);
610
611 /*
612 * Pick up probes from the kernel and already-loaded linker files.
613 * Define providers in a separate pass since a linker file may be using
614 * providers defined in a file that appears later in the list.
615 */
616 linker_file_foreach(sdt_load_providers_cb, NULL);
617 linker_file_foreach(sdt_load_probes_cb, NULL);
618 }
619
620 static int
sdt_unload(void)621 sdt_unload(void)
622 {
623 struct sdt_provider *prov, *tmp;
624 int ret;
625
626 EVENTHANDLER_DEREGISTER(kld_load, sdt_kld_load_tag);
627 EVENTHANDLER_DEREGISTER(kld_unload_try, sdt_kld_unload_try_tag);
628
629 sdt_probe_func = sdt_probe_stub;
630
631 TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) {
632 ret = dtrace_unregister(prov->id);
633 if (ret != 0)
634 return (ret);
635 TAILQ_REMOVE(&sdt_prov_list, prov, prov_entry);
636 free(prov->name, M_SDT);
637 free(prov, M_SDT);
638 }
639
640 return (0);
641 }
642
643 static int
sdt_modevent(module_t mod __unused,int type,void * data __unused)644 sdt_modevent(module_t mod __unused, int type, void *data __unused)
645 {
646 switch (type) {
647 case MOD_LOAD:
648 case MOD_UNLOAD:
649 case MOD_SHUTDOWN:
650 return (0);
651 default:
652 return (EOPNOTSUPP);
653 }
654 }
655
656 SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL);
657 SYSUNINIT(sdt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_unload, NULL);
658
659 DEV_MODULE(sdt, sdt_modevent, NULL);
660 MODULE_VERSION(sdt, 1);
661 MODULE_DEPEND(sdt, dtrace, 1, 1, 1);
662 MODULE_DEPEND(sdt, opensolaris, 1, 1, 1);
663