xref: /freebsd/sys/fs/nfsclient/nfs_clkdtrace.c (revision aa772005)
1 /*-
2  * Copyright (c) 2009 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * This software was developed at the University of Cambridge Computer
6  * Laboratory with support from a grant from Google, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/conf.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 
40 #include <sys/dtrace.h>
41 #include <sys/dtrace_bsd.h>
42 
43 #include <fs/nfs/nfsproto.h>
44 
45 #include <fs/nfsclient/nfs_kdtrace.h>
46 
47 /*
48  * dtnfscl is a DTrace provider that tracks the intent to perform RPCs
49  * in the NFS client, as well as acess to and maintenance of the access and
50  * attribute caches.  This is not quite the same as RPCs, because NFS may
51  * issue multiple RPC transactions in the event that authentication fails,
52  * there's a jukebox error, or none at all if the access or attribute cache
53  * hits.  However, it cleanly represents the logical layer between RPC
54  * transmission and vnode/vfs operations, providing access to state linking
55  * the two.
56  */
57 
58 static int	dtnfsclient_unload(void);
59 static void	dtnfsclient_getargdesc(void *, dtrace_id_t, void *,
60 		    dtrace_argdesc_t *);
61 static void	dtnfsclient_provide(void *, dtrace_probedesc_t *);
62 static void	dtnfsclient_destroy(void *, dtrace_id_t, void *);
63 static void	dtnfsclient_enable(void *, dtrace_id_t, void *);
64 static void	dtnfsclient_disable(void *, dtrace_id_t, void *);
65 static void	dtnfsclient_load(void *);
66 
67 static dtrace_pattr_t dtnfsclient_attr = {
68 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
69 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
70 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
71 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
72 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
73 };
74 
75 /*
76  * Description of NFSv4, NFSv3 and (optional) NFSv2 probes for a procedure.
77  */
78 struct dtnfsclient_rpc {
79 	char		*nr_v4_name;
80 	char		*nr_v3_name;	/* Or NULL if none. */
81 	char		*nr_v2_name;	/* Or NULL if none. */
82 
83 	/*
84 	 * IDs for the start and done cases, for NFSv2, NFSv3 and NFSv4.
85 	 */
86 	uint32_t	 nr_v2_id_start, nr_v2_id_done;
87 	uint32_t	 nr_v3_id_start, nr_v3_id_done;
88 	uint32_t	 nr_v4_id_start, nr_v4_id_done;
89 };
90 
91 /*
92  * This table is indexed by NFSv3 procedure number, but also used for NFSv2
93  * procedure names and NFSv4 operations.
94  */
95 static struct dtnfsclient_rpc	dtnfsclient_rpcs[NFS_NPROCS + 1] = {
96 	{ "null", "null", "null" },
97 	{ "getattr", "getattr", "getattr" },
98 	{ "setattr", "setattr", "setattr" },
99 	{ "lookup", "lookup", "lookup" },
100 	{ "access", "access", "noop" },
101 	{ "readlink", "readlink", "readlink" },
102 	{ "read", "read", "read" },
103 	{ "write", "write", "write" },
104 	{ "create", "create", "create" },
105 	{ "mkdir", "mkdir", "mkdir" },
106 	{ "symlink", "symlink", "symlink" },
107 	{ "mknod", "mknod" },
108 	{ "remove", "remove", "remove" },
109 	{ "rmdir", "rmdir", "rmdir" },
110 	{ "rename", "rename", "rename" },
111 	{ "link", "link", "link" },
112 	{ "readdir", "readdir", "readdir" },
113 	{ "readdirplus", "readdirplus" },
114 	{ "fsstat", "fsstat", "statfs" },
115 	{ "fsinfo", "fsinfo" },
116 	{ "pathconf", "pathconf" },
117 	{ "commit", "commit" },
118 	{ "lookupp" },
119 	{ "setclientid" },
120 	{ "setclientidcfrm" },
121 	{ "lock" },
122 	{ "locku" },
123 	{ "open" },
124 	{ "close" },
125 	{ "openconfirm" },
126 	{ "lockt" },
127 	{ "opendowngrade" },
128 	{ "renew" },
129 	{ "putrootfh" },
130 	{ "releaselckown" },
131 	{ "delegreturn" },
132 	{ "retdelegremove" },
133 	{ "retdelegrename1" },
134 	{ "retdelegrename2" },
135 	{ "getacl" },
136 	{ "setacl" },
137 	{ "noop", "noop", "noop" }
138 };
139 
140 /*
141  * Module name strings.
142  */
143 static char	*dtnfsclient_accesscache_str = "accesscache";
144 static char	*dtnfsclient_attrcache_str = "attrcache";
145 static char	*dtnfsclient_nfs2_str = "nfs2";
146 static char	*dtnfsclient_nfs3_str = "nfs3";
147 static char	*dtnfsclient_nfs4_str = "nfs4";
148 
149 /*
150  * Function name strings.
151  */
152 static char	*dtnfsclient_flush_str = "flush";
153 static char	*dtnfsclient_load_str = "load";
154 static char	*dtnfsclient_get_str = "get";
155 
156 /*
157  * Name strings.
158  */
159 static char	*dtnfsclient_done_str = "done";
160 static char	*dtnfsclient_hit_str = "hit";
161 static char	*dtnfsclient_miss_str = "miss";
162 static char	*dtnfsclient_start_str = "start";
163 
164 static dtrace_pops_t dtnfsclient_pops = {
165 	dtnfsclient_provide,
166 	NULL,
167 	dtnfsclient_enable,
168 	dtnfsclient_disable,
169 	NULL,
170 	NULL,
171 	dtnfsclient_getargdesc,
172 	NULL,
173 	NULL,
174 	dtnfsclient_destroy
175 };
176 
177 static dtrace_provider_id_t	dtnfsclient_id;
178 
179 /*
180  * Most probes are generated from the above RPC table, but for access and
181  * attribute caches, we have specific IDs we recognize and handle specially
182  * in various spots.
183  */
184 extern uint32_t	nfscl_accesscache_flush_done_id;
185 extern uint32_t	nfscl_accesscache_get_hit_id;
186 extern uint32_t	nfscl_accesscache_get_miss_id;
187 extern uint32_t	nfscl_accesscache_load_done_id;
188 
189 extern uint32_t	nfscl_attrcache_flush_done_id;
190 extern uint32_t	nfscl_attrcache_get_hit_id;
191 extern uint32_t	nfscl_attrcache_get_miss_id;
192 extern uint32_t	nfscl_attrcache_load_done_id;
193 
194 /*
195  * When tracing on a procedure is enabled, the DTrace ID for an RPC event is
196  * stored in one of these two NFS client-allocated arrays; 0 indicates that
197  * the event is not being traced so probes should not be called.
198  *
199  * For simplicity, we allocate both v2, v3 and v4 arrays as NFS_NPROCS + 1, and
200  * the v2, v3 arrays are simply sparse.
201  */
202 extern uint32_t			nfscl_nfs2_start_probes[NFS_NPROCS + 1];
203 extern uint32_t			nfscl_nfs2_done_probes[NFS_NPROCS + 1];
204 
205 extern uint32_t			nfscl_nfs3_start_probes[NFS_NPROCS + 1];
206 extern uint32_t			nfscl_nfs3_done_probes[NFS_NPROCS + 1];
207 
208 extern uint32_t			nfscl_nfs4_start_probes[NFS_NPROCS + 1];
209 extern uint32_t			nfscl_nfs4_done_probes[NFS_NPROCS + 1];
210 
211 /*
212  * Look up a DTrace probe ID to see if it's associated with a "done" event --
213  * if so, we will return a fourth argument type of "int".
214  */
215 static int
216 dtnfs234_isdoneprobe(dtrace_id_t id)
217 {
218 	int i;
219 
220 	for (i = 0; i < NFS_NPROCS + 1; i++) {
221 		if (dtnfsclient_rpcs[i].nr_v4_id_done == id ||
222 		    dtnfsclient_rpcs[i].nr_v3_id_done == id ||
223 		    dtnfsclient_rpcs[i].nr_v2_id_done == id)
224 			return (1);
225 	}
226 	return (0);
227 }
228 
229 static void
230 dtnfsclient_getargdesc(void *arg, dtrace_id_t id, void *parg,
231     dtrace_argdesc_t *desc)
232 {
233 	const char *p = NULL;
234 
235 	if (id == nfscl_accesscache_flush_done_id ||
236 	    id == nfscl_attrcache_flush_done_id ||
237 	    id == nfscl_attrcache_get_miss_id) {
238 		switch (desc->dtargd_ndx) {
239 		case 0:
240 			p = "struct vnode *";
241 			break;
242 		default:
243 			desc->dtargd_ndx = DTRACE_ARGNONE;
244 			break;
245 		}
246 	} else if (id == nfscl_accesscache_get_hit_id ||
247 	    id == nfscl_accesscache_get_miss_id) {
248 		switch (desc->dtargd_ndx) {
249 		case 0:
250 			p = "struct vnode *";
251 			break;
252 		case 1:
253 			p = "uid_t";
254 			break;
255 		case 2:
256 			p = "uint32_t";
257 			break;
258 		default:
259 			desc->dtargd_ndx = DTRACE_ARGNONE;
260 			break;
261 		}
262 	} else if (id == nfscl_accesscache_load_done_id) {
263 		switch (desc->dtargd_ndx) {
264 		case 0:
265 			p = "struct vnode *";
266 			break;
267 		case 1:
268 			p = "uid_t";
269 			break;
270 		case 2:
271 			p = "uint32_t";
272 			break;
273 		case 3:
274 			p = "int";
275 			break;
276 		default:
277 			desc->dtargd_ndx = DTRACE_ARGNONE;
278 			break;
279 		}
280 	} else if (id == nfscl_attrcache_get_hit_id) {
281 		switch (desc->dtargd_ndx) {
282 		case 0:
283 			p = "struct vnode *";
284 			break;
285 		case 1:
286 			p = "struct vattr *";
287 			break;
288 		default:
289 			desc->dtargd_ndx = DTRACE_ARGNONE;
290 			break;
291 		}
292 	} else if (id == nfscl_attrcache_load_done_id) {
293 		switch (desc->dtargd_ndx) {
294 		case 0:
295 			p = "struct vnode *";
296 			break;
297 		case 1:
298 			p = "struct vattr *";
299 			break;
300 		case 2:
301 			p = "int";
302 			break;
303 		default:
304 			desc->dtargd_ndx = DTRACE_ARGNONE;
305 			break;
306 		}
307 	} else {
308 		switch (desc->dtargd_ndx) {
309 		case 0:
310 			p = "struct vnode *";
311 			break;
312 		case 1:
313 			p = "struct mbuf *";
314 			break;
315 		case 2:
316 			p = "struct ucred *";
317 			break;
318 		case 3:
319 			p = "int";
320 			break;
321 		case 4:
322 			if (dtnfs234_isdoneprobe(id)) {
323 				p = "int";
324 				break;
325 			}
326 			/* FALLSTHROUGH */
327 		default:
328 			desc->dtargd_ndx = DTRACE_ARGNONE;
329 			break;
330 		}
331 	}
332 	if (p != NULL)
333 		strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
334 }
335 
336 static void
337 dtnfsclient_provide(void *arg, dtrace_probedesc_t *desc)
338 {
339 	int i;
340 
341 	if (desc != NULL)
342 		return;
343 
344 	/*
345 	 * Register access cache probes.
346 	 */
347 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
348 	    dtnfsclient_flush_str, dtnfsclient_done_str) == 0) {
349 		nfscl_accesscache_flush_done_id = dtrace_probe_create(
350 		    dtnfsclient_id, dtnfsclient_accesscache_str,
351 		    dtnfsclient_flush_str, dtnfsclient_done_str, 0, NULL);
352 	}
353 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
354 	    dtnfsclient_get_str, dtnfsclient_hit_str) == 0) {
355 		nfscl_accesscache_get_hit_id = dtrace_probe_create(
356 		    dtnfsclient_id, dtnfsclient_accesscache_str,
357 		    dtnfsclient_get_str, dtnfsclient_hit_str, 0, NULL);
358 	}
359 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
360 	    dtnfsclient_get_str, dtnfsclient_miss_str) == 0) {
361 		nfscl_accesscache_get_miss_id = dtrace_probe_create(
362 		    dtnfsclient_id, dtnfsclient_accesscache_str,
363 		    dtnfsclient_get_str, dtnfsclient_miss_str, 0, NULL);
364 	}
365 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
366 	    dtnfsclient_load_str, dtnfsclient_done_str) == 0) {
367 		nfscl_accesscache_load_done_id = dtrace_probe_create(
368 		    dtnfsclient_id, dtnfsclient_accesscache_str,
369 		    dtnfsclient_load_str, dtnfsclient_done_str, 0, NULL);
370 	}
371 
372 	/*
373 	 * Register attribute cache probes.
374 	 */
375 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
376 	    dtnfsclient_flush_str, dtnfsclient_done_str) == 0) {
377 		nfscl_attrcache_flush_done_id = dtrace_probe_create(
378 		    dtnfsclient_id, dtnfsclient_attrcache_str,
379 		    dtnfsclient_flush_str, dtnfsclient_done_str, 0, NULL);
380 	}
381 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
382 	    dtnfsclient_get_str, dtnfsclient_hit_str) == 0) {
383 		nfscl_attrcache_get_hit_id = dtrace_probe_create(
384 		    dtnfsclient_id, dtnfsclient_attrcache_str,
385 		    dtnfsclient_get_str, dtnfsclient_hit_str, 0, NULL);
386 	}
387 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
388 	    dtnfsclient_get_str, dtnfsclient_miss_str) == 0) {
389 		nfscl_attrcache_get_miss_id = dtrace_probe_create(
390 		    dtnfsclient_id, dtnfsclient_attrcache_str,
391 		    dtnfsclient_get_str, dtnfsclient_miss_str, 0, NULL);
392 	}
393 	if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
394 	    dtnfsclient_load_str, dtnfsclient_done_str) == 0) {
395 		nfscl_attrcache_load_done_id = dtrace_probe_create(
396 		    dtnfsclient_id, dtnfsclient_attrcache_str,
397 		    dtnfsclient_load_str, dtnfsclient_done_str, 0, NULL);
398 	}
399 
400 	/*
401 	 * Register NFSv2 RPC procedures; note sparseness check for each slot
402 	 * in the NFSv3, NFSv4 procnum-indexed array.
403 	 */
404 	for (i = 0; i < NFS_NPROCS + 1; i++) {
405 		if (dtnfsclient_rpcs[i].nr_v2_name != NULL &&
406 		    dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs2_str,
407 		    dtnfsclient_rpcs[i].nr_v2_name, dtnfsclient_start_str) ==
408 		    0) {
409 			dtnfsclient_rpcs[i].nr_v2_id_start =
410 			    dtrace_probe_create(dtnfsclient_id,
411 			    dtnfsclient_nfs2_str,
412 			    dtnfsclient_rpcs[i].nr_v2_name,
413 			    dtnfsclient_start_str, 0,
414 			    &nfscl_nfs2_start_probes[i]);
415 		}
416 		if (dtnfsclient_rpcs[i].nr_v2_name != NULL &&
417 		    dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs2_str,
418 		    dtnfsclient_rpcs[i].nr_v2_name, dtnfsclient_done_str) ==
419 		    0) {
420 			dtnfsclient_rpcs[i].nr_v2_id_done =
421 			    dtrace_probe_create(dtnfsclient_id,
422 			    dtnfsclient_nfs2_str,
423 			    dtnfsclient_rpcs[i].nr_v2_name,
424 			    dtnfsclient_done_str, 0,
425 			    &nfscl_nfs2_done_probes[i]);
426 		}
427 	}
428 
429 	/*
430 	 * Register NFSv3 RPC procedures; note sparseness check for each slot
431 	 * in the NFSv4 procnum-indexed array.
432 	 */
433 	for (i = 0; i < NFS_NPROCS + 1; i++) {
434 		if (dtnfsclient_rpcs[i].nr_v3_name != NULL &&
435 		    dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs3_str,
436 		    dtnfsclient_rpcs[i].nr_v3_name, dtnfsclient_start_str) ==
437 		    0) {
438 			dtnfsclient_rpcs[i].nr_v3_id_start =
439 			    dtrace_probe_create(dtnfsclient_id,
440 			    dtnfsclient_nfs3_str,
441 			    dtnfsclient_rpcs[i].nr_v3_name,
442 			    dtnfsclient_start_str, 0,
443 			    &nfscl_nfs3_start_probes[i]);
444 		}
445 		if (dtnfsclient_rpcs[i].nr_v3_name != NULL &&
446 		    dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs3_str,
447 		    dtnfsclient_rpcs[i].nr_v3_name, dtnfsclient_done_str) ==
448 		    0) {
449 			dtnfsclient_rpcs[i].nr_v3_id_done =
450 			    dtrace_probe_create(dtnfsclient_id,
451 			    dtnfsclient_nfs3_str,
452 			    dtnfsclient_rpcs[i].nr_v3_name,
453 			    dtnfsclient_done_str, 0,
454 			    &nfscl_nfs3_done_probes[i]);
455 		}
456 	}
457 
458 	/*
459 	 * Register NFSv4 RPC procedures.
460 	 */
461 	for (i = 0; i < NFS_NPROCS + 1; i++) {
462 		if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs4_str,
463 		    dtnfsclient_rpcs[i].nr_v4_name, dtnfsclient_start_str) ==
464 		    0) {
465 			dtnfsclient_rpcs[i].nr_v4_id_start =
466 			    dtrace_probe_create(dtnfsclient_id,
467 			    dtnfsclient_nfs4_str,
468 			    dtnfsclient_rpcs[i].nr_v4_name,
469 			    dtnfsclient_start_str, 0,
470 			    &nfscl_nfs4_start_probes[i]);
471 		}
472 		if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs4_str,
473 		    dtnfsclient_rpcs[i].nr_v4_name, dtnfsclient_done_str) ==
474 		    0) {
475 			dtnfsclient_rpcs[i].nr_v4_id_done =
476 			    dtrace_probe_create(dtnfsclient_id,
477 			    dtnfsclient_nfs4_str,
478 			    dtnfsclient_rpcs[i].nr_v4_name,
479 			    dtnfsclient_done_str, 0,
480 			    &nfscl_nfs4_done_probes[i]);
481 		}
482 	}
483 }
484 
485 static void
486 dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg)
487 {
488 }
489 
490 static void
491 dtnfsclient_enable(void *arg, dtrace_id_t id, void *parg)
492 {
493 	uint32_t *p = parg;
494 	void *f = dtrace_probe;
495 
496 	if (id == nfscl_accesscache_flush_done_id)
497 		dtrace_nfscl_accesscache_flush_done_probe = f;
498 	else if (id == nfscl_accesscache_get_hit_id)
499 		dtrace_nfscl_accesscache_get_hit_probe = f;
500 	else if (id == nfscl_accesscache_get_miss_id)
501 		dtrace_nfscl_accesscache_get_miss_probe = f;
502 	else if (id == nfscl_accesscache_load_done_id)
503 		dtrace_nfscl_accesscache_load_done_probe = f;
504 	else if (id == nfscl_attrcache_flush_done_id)
505 		dtrace_nfscl_attrcache_flush_done_probe = f;
506 	else if (id == nfscl_attrcache_get_hit_id)
507 		dtrace_nfscl_attrcache_get_hit_probe = f;
508 	else if (id == nfscl_attrcache_get_miss_id)
509 		dtrace_nfscl_attrcache_get_miss_probe = f;
510 	else if (id == nfscl_attrcache_load_done_id)
511 		dtrace_nfscl_attrcache_load_done_probe = f;
512 	else
513 		*p = id;
514 }
515 
516 static void
517 dtnfsclient_disable(void *arg, dtrace_id_t id, void *parg)
518 {
519 	uint32_t *p = parg;
520 
521 	if (id == nfscl_accesscache_flush_done_id)
522 		dtrace_nfscl_accesscache_flush_done_probe = NULL;
523 	else if (id == nfscl_accesscache_get_hit_id)
524 		dtrace_nfscl_accesscache_get_hit_probe = NULL;
525 	else if (id == nfscl_accesscache_get_miss_id)
526 		dtrace_nfscl_accesscache_get_miss_probe = NULL;
527 	else if (id == nfscl_accesscache_load_done_id)
528 		dtrace_nfscl_accesscache_load_done_probe = NULL;
529 	else if (id == nfscl_attrcache_flush_done_id)
530 		dtrace_nfscl_attrcache_flush_done_probe = NULL;
531 	else if (id == nfscl_attrcache_get_hit_id)
532 		dtrace_nfscl_attrcache_get_hit_probe = NULL;
533 	else if (id == nfscl_attrcache_get_miss_id)
534 		dtrace_nfscl_attrcache_get_miss_probe = NULL;
535 	else if (id == nfscl_attrcache_load_done_id)
536 		dtrace_nfscl_attrcache_load_done_probe = NULL;
537 	else
538 		*p = 0;
539 }
540 
541 static void
542 dtnfsclient_load(void *dummy)
543 {
544 
545 	if (dtrace_register("nfscl", &dtnfsclient_attr,
546 	    DTRACE_PRIV_USER, NULL, &dtnfsclient_pops, NULL,
547 	    &dtnfsclient_id) != 0)
548 		return;
549 
550 	dtrace_nfscl_nfs234_start_probe =
551 	    (dtrace_nfsclient_nfs23_start_probe_func_t)dtrace_probe;
552 	dtrace_nfscl_nfs234_done_probe =
553 	    (dtrace_nfsclient_nfs23_done_probe_func_t)dtrace_probe;
554 }
555 
556 
557 static int
558 dtnfsclient_unload()
559 {
560 
561 	dtrace_nfscl_nfs234_start_probe = NULL;
562 	dtrace_nfscl_nfs234_done_probe = NULL;
563 
564 	return (dtrace_unregister(dtnfsclient_id));
565 }
566 
567 static int
568 dtnfsclient_modevent(module_t mod __unused, int type, void *data __unused)
569 {
570 	int error = 0;
571 
572 	switch (type) {
573 	case MOD_LOAD:
574 		break;
575 
576 	case MOD_UNLOAD:
577 		break;
578 
579 	case MOD_SHUTDOWN:
580 		break;
581 
582 	default:
583 		error = EOPNOTSUPP;
584 		break;
585 	}
586 
587 	return (error);
588 }
589 
590 SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
591     dtnfsclient_load, NULL);
592 SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
593     dtnfsclient_unload, NULL);
594 
595 DEV_MODULE(dtnfscl, dtnfsclient_modevent, NULL);
596 MODULE_VERSION(dtnfscl, 1);
597 MODULE_DEPEND(dtnfscl, dtrace, 1, 1, 1);
598 MODULE_DEPEND(dtnfscl, opensolaris, 1, 1, 1);
599 MODULE_DEPEND(dtnfscl, nfscl, 1, 1, 1);
600 MODULE_DEPEND(dtnfscl, nfscommon, 1, 1, 1);
601