2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2009 Robert N. M. Watson
7 * This software was developed at the University of Cambridge Computer
8 * Laboratory with support from a grant from Google, Inc.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
42 #include <sys/dtrace.h>
43 #include <sys/dtrace_bsd.h>
45 #include <fs/nfs/nfsproto.h>
47 #include <fs/nfsclient/nfs_kdtrace.h>
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
60 static int dtnfsclient_unload(void);
61 static void dtnfsclient_getargdesc(void *, dtrace_id_t, void *,
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 *);
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 },
78 * Description of NFSv4, NFSv3 and (optional) NFSv2 probes for a procedure.
80 struct dtnfsclient_rpc {
82 char *nr_v3_name; /* Or NULL if none. */
83 char *nr_v2_name; /* Or NULL if none. */
86 * IDs for the start and done cases, for NFSv2, NFSv3 and NFSv4.
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;
94 * This table is indexed by NFSv3 procedure number, but also used for NFSv2
95 * procedure names and NFSv4 operations.
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" },
122 { "setclientidcfrm" },
134 { "retdelegremove" },
135 { "retdelegrename1" },
136 { "retdelegrename2" },
139 { "noop", "noop", "noop" }
143 * Module name strings.
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";
152 * Function name strings.
154 static char *dtnfsclient_flush_str = "flush";
155 static char *dtnfsclient_load_str = "load";
156 static char *dtnfsclient_get_str = "get";
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";
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,
173 .dtps_getargdesc = dtnfsclient_getargdesc,
174 .dtps_getargval = NULL,
175 .dtps_usermode = NULL,
176 .dtps_destroy = dtnfsclient_destroy
179 static dtrace_provider_id_t dtnfsclient_id;
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.
186 * For simplicity, we allocate both v2, v3 and v4 arrays as NFSV41_NPROCS + 1,
187 * and the v2, v3 arrays are simply sparse.
189 extern uint32_t nfscl_nfs2_start_probes[NFSV41_NPROCS + 1];
190 extern uint32_t nfscl_nfs2_done_probes[NFSV41_NPROCS + 1];
192 extern uint32_t nfscl_nfs3_start_probes[NFSV41_NPROCS + 1];
193 extern uint32_t nfscl_nfs3_done_probes[NFSV41_NPROCS + 1];
195 extern uint32_t nfscl_nfs4_start_probes[NFSV41_NPROCS + 1];
196 extern uint32_t nfscl_nfs4_done_probes[NFSV41_NPROCS + 1];
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".
203 dtnfs234_isdoneprobe(dtrace_id_t id)
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)
217 dtnfsclient_getargdesc(void *arg, dtrace_id_t id, void *parg,
218 dtrace_argdesc_t *desc)
220 const char *p = NULL;
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) {
227 p = "struct vnode *";
230 desc->dtargd_ndx = DTRACE_ARGNONE;
233 } else if (id == nfscl_accesscache_get_hit_id ||
234 id == nfscl_accesscache_get_miss_id) {
235 switch (desc->dtargd_ndx) {
237 p = "struct vnode *";
246 desc->dtargd_ndx = DTRACE_ARGNONE;
249 } else if (id == nfscl_accesscache_load_done_id) {
250 switch (desc->dtargd_ndx) {
252 p = "struct vnode *";
264 desc->dtargd_ndx = DTRACE_ARGNONE;
267 } else if (id == nfscl_attrcache_get_hit_id) {
268 switch (desc->dtargd_ndx) {
270 p = "struct vnode *";
273 p = "struct vattr *";
276 desc->dtargd_ndx = DTRACE_ARGNONE;
279 } else if (id == nfscl_attrcache_load_done_id) {
280 switch (desc->dtargd_ndx) {
282 p = "struct vnode *";
285 p = "struct vattr *";
291 desc->dtargd_ndx = DTRACE_ARGNONE;
295 switch (desc->dtargd_ndx) {
297 p = "struct vnode *";
303 p = "struct ucred *";
309 if (dtnfs234_isdoneprobe(id)) {
315 desc->dtargd_ndx = DTRACE_ARGNONE;
320 strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
324 dtnfsclient_provide(void *arg, dtrace_probedesc_t *desc)
332 * Register access cache probes.
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);
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);
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);
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);
360 * Register attribute cache probes.
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);
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);
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);
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);
388 * Register NFSv2 RPC procedures; note sparseness check for each slot
389 * in the NFSv3, NFSv4 procnum-indexed array.
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) ==
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]);
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) ==
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]);
417 * Register NFSv3 RPC procedures; note sparseness check for each slot
418 * in the NFSv4 procnum-indexed array.
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) ==
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]);
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) ==
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]);
446 * Register NFSv4 RPC procedures.
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) ==
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]);
459 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs4_str,
460 dtnfsclient_rpcs[i].nr_v4_name, dtnfsclient_done_str) ==
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]);
473 dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg)
478 dtnfsclient_enable(void *arg, dtrace_id_t id, void *parg)
481 void *f = dtrace_probe;
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;
504 dtnfsclient_disable(void *arg, dtrace_id_t id, void *parg)
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;
529 dtnfsclient_load(void *dummy)
532 if (dtrace_register("nfscl", &dtnfsclient_attr,
533 DTRACE_PRIV_USER, NULL, &dtnfsclient_pops, NULL,
534 &dtnfsclient_id) != 0)
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;
548 dtrace_nfscl_nfs234_start_probe = NULL;
549 dtrace_nfscl_nfs234_done_probe = NULL;
551 return (dtrace_unregister(dtnfsclient_id));
555 dtnfsclient_modevent(module_t mod __unused, int type, void *data __unused)
577 SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
578 dtnfsclient_load, NULL);
579 SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
580 dtnfsclient_unload, NULL);
582 DEV_MODULE(dtnfscl, dtnfsclient_modevent, NULL);
583 MODULE_VERSION(dtnfscl, 1);
584 MODULE_DEPEND(dtnfscl, dtrace, 1, 1, 1);
585 MODULE_DEPEND(dtnfscl, opensolaris, 1, 1, 1);
586 MODULE_DEPEND(dtnfscl, nfscl, 1, 1, 1);
587 MODULE_DEPEND(dtnfscl, nfscommon, 1, 1, 1);