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
60static int	dtnfsclient_unload(void);
61static void	dtnfsclient_getargdesc(void *, dtrace_id_t, void *,
62		    dtrace_argdesc_t *);
63static void	dtnfsclient_provide(void *, dtrace_probedesc_t *);
64static void	dtnfsclient_destroy(void *, dtrace_id_t, void *);
65static void	dtnfsclient_enable(void *, dtrace_id_t, void *);
66static void	dtnfsclient_disable(void *, dtrace_id_t, void *);
67static void	dtnfsclient_load(void *);
68
69static 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 */
80struct 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 */
97static 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 */
145static char	*dtnfsclient_accesscache_str = "accesscache";
146static char	*dtnfsclient_attrcache_str = "attrcache";
147static char	*dtnfsclient_nfs2_str = "nfs2";
148static char	*dtnfsclient_nfs3_str = "nfs3";
149static char	*dtnfsclient_nfs4_str = "nfs4";
150
151/*
152 * Function name strings.
153 */
154static char	*dtnfsclient_flush_str = "flush";
155static char	*dtnfsclient_load_str = "load";
156static char	*dtnfsclient_get_str = "get";
157
158/*
159 * Name strings.
160 */
161static char	*dtnfsclient_done_str = "done";
162static char	*dtnfsclient_hit_str = "hit";
163static char	*dtnfsclient_miss_str = "miss";
164static char	*dtnfsclient_start_str = "start";
165
166static 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
179static 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 */
189extern uint32_t			nfscl_nfs2_start_probes[NFSV41_NPROCS + 1];
190extern uint32_t			nfscl_nfs2_done_probes[NFSV41_NPROCS + 1];
191
192extern uint32_t			nfscl_nfs3_start_probes[NFSV41_NPROCS + 1];
193extern uint32_t			nfscl_nfs3_done_probes[NFSV41_NPROCS + 1];
194
195extern uint32_t			nfscl_nfs4_start_probes[NFSV41_NPROCS + 1];
196extern 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 */
202static int
203dtnfs234_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
216static void
217dtnfsclient_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
323static void
324dtnfsclient_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
472static void
473dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg)
474{
475}
476
477static void
478dtnfsclient_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
503static void
504dtnfsclient_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
528static void
529dtnfsclient_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
544static int
545dtnfsclient_unload()
546{
547
548	dtrace_nfscl_nfs234_start_probe = NULL;
549	dtrace_nfscl_nfs234_done_probe = NULL;
550
551	return (dtrace_unregister(dtnfsclient_id));
552}
553
554static int
555dtnfsclient_modevent(module_t mod __unused, int type, void *data __unused)
556{
557	int error = 0;
558
559	switch (type) {
560	case MOD_LOAD:
561		break;
562
563	case MOD_UNLOAD:
564		break;
565
566	case MOD_SHUTDOWN:
567		break;
568
569	default:
570		error = EOPNOTSUPP;
571		break;
572	}
573
574	return (error);
575}
576
577SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
578    dtnfsclient_load, NULL);
579SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
580    dtnfsclient_unload, NULL);
581
582DEV_MODULE(dtnfscl, dtnfsclient_modevent, NULL);
583MODULE_VERSION(dtnfscl, 1);
584MODULE_DEPEND(dtnfscl, dtrace, 1, 1, 1);
585MODULE_DEPEND(dtnfscl, opensolaris, 1, 1, 1);
586MODULE_DEPEND(dtnfscl, nfscl, 1, 1, 1);
587MODULE_DEPEND(dtnfscl, nfscommon, 1, 1, 1);
588