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
58static int	dtnfsclient_unload(void);
59static void	dtnfsclient_getargdesc(void *, dtrace_id_t, void *,
60		    dtrace_argdesc_t *);
61static void	dtnfsclient_provide(void *, dtrace_probedesc_t *);
62static void	dtnfsclient_destroy(void *, dtrace_id_t, void *);
63static void	dtnfsclient_enable(void *, dtrace_id_t, void *);
64static void	dtnfsclient_disable(void *, dtrace_id_t, void *);
65static void	dtnfsclient_load(void *);
66
67static 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 */
78struct 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 */
95static struct dtnfsclient_rpc	dtnfsclient_rpcs[NFSV41_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 */
143static char	*dtnfsclient_accesscache_str = "accesscache";
144static char	*dtnfsclient_attrcache_str = "attrcache";
145static char	*dtnfsclient_nfs2_str = "nfs2";
146static char	*dtnfsclient_nfs3_str = "nfs3";
147static char	*dtnfsclient_nfs4_str = "nfs4";
148
149/*
150 * Function name strings.
151 */
152static char	*dtnfsclient_flush_str = "flush";
153static char	*dtnfsclient_load_str = "load";
154static char	*dtnfsclient_get_str = "get";
155
156/*
157 * Name strings.
158 */
159static char	*dtnfsclient_done_str = "done";
160static char	*dtnfsclient_hit_str = "hit";
161static char	*dtnfsclient_miss_str = "miss";
162static char	*dtnfsclient_start_str = "start";
163
164static 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
177static 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 */
184extern uint32_t	nfscl_accesscache_flush_done_id;
185extern uint32_t	nfscl_accesscache_get_hit_id;
186extern uint32_t	nfscl_accesscache_get_miss_id;
187extern uint32_t	nfscl_accesscache_load_done_id;
188
189extern uint32_t	nfscl_attrcache_flush_done_id;
190extern uint32_t	nfscl_attrcache_get_hit_id;
191extern uint32_t	nfscl_attrcache_get_miss_id;
192extern 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 NFSV41_NPROCS + 1,
200 * and the v2, v3 arrays are simply sparse.
201 */
202extern uint32_t			nfscl_nfs2_start_probes[NFSV41_NPROCS + 1];
203extern uint32_t			nfscl_nfs2_done_probes[NFSV41_NPROCS + 1];
204
205extern uint32_t			nfscl_nfs3_start_probes[NFSV41_NPROCS + 1];
206extern uint32_t			nfscl_nfs3_done_probes[NFSV41_NPROCS + 1];
207
208extern uint32_t			nfscl_nfs4_start_probes[NFSV41_NPROCS + 1];
209extern uint32_t			nfscl_nfs4_done_probes[NFSV41_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 */
215static int
216dtnfs234_isdoneprobe(dtrace_id_t id)
217{
218	int i;
219
220	for (i = 0; i < NFSV41_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
229static void
230dtnfsclient_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
336static void
337dtnfsclient_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 < NFSV41_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 < NFSV41_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 < NFSV41_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
485static void
486dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg)
487{
488}
489
490static void
491dtnfsclient_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
516static void
517dtnfsclient_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
541static void
542dtnfsclient_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
557static int
558dtnfsclient_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
567static int
568dtnfsclient_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
590SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
591    dtnfsclient_load, NULL);
592SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
593    dtnfsclient_unload, NULL);
594
595DEV_MODULE(dtnfscl, dtnfsclient_modevent, NULL);
596MODULE_VERSION(dtnfscl, 1);
597MODULE_DEPEND(dtnfscl, dtrace, 1, 1, 1);
598MODULE_DEPEND(dtnfscl, opensolaris, 1, 1, 1);
599MODULE_DEPEND(dtnfscl, nfscl, 1, 1, 1);
600MODULE_DEPEND(dtnfscl, nfscommon, 1, 1, 1);
601