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