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: releng/9.2/sys/nfsclient/nfs_kdtrace.c 221542 2011-05-06 19:55:15Z rmacklem $");
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 <nfs/nfsproto.h>
44
45 /*
46 * dtnfsclient is a DTrace provider that tracks the intent to perform RPCs
47 * in the NFS client, as well as acess to and maintenance of the access and
48 * attribute caches. This is not quite the same as RPCs, because NFS may
49 * issue multiple RPC transactions in the event that authentication fails,
50 * there's a jukebox error, or none at all if the access or attribute cache
51 * hits. However, it cleanly represents the logical layer between RPC
52 * transmission and vnode/vfs operations, providing access to state linking
53 * the two.
54 */
55
56 static int dtnfsclient_unload(void);
57 static void dtnfsclient_getargdesc(void *, dtrace_id_t, void *,
58 dtrace_argdesc_t *);
59 static void dtnfsclient_provide(void *, dtrace_probedesc_t *);
60 static void dtnfsclient_destroy(void *, dtrace_id_t, void *);
61 static void dtnfsclient_enable(void *, dtrace_id_t, void *);
62 static void dtnfsclient_disable(void *, dtrace_id_t, void *);
63 static void dtnfsclient_load(void *);
64
65 static dtrace_pattr_t dtnfsclient_attr = {
66 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
67 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
68 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
69 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
70 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
71 };
72
73 /*
74 * Description of NFSv3 and (optional) NFSv2 probes for a procedure.
75 */
76 struct dtnfsclient_rpc {
77 char *nr_v3_name;
78 char *nr_v2_name; /* Or NULL if none. */
79
80 /*
81 * IDs for the start and done cases, for both NFSv2 and NFSv3.
82 */
83 uint32_t nr_v2_id_start, nr_v2_id_done;
84 uint32_t nr_v3_id_start, nr_v3_id_done;
85 };
86
87 /*
88 * This table is indexed by NFSv3 procedure number, but also used for NFSv2
89 * procedure names.
90 */
91 static struct dtnfsclient_rpc dtnfsclient_rpcs[NFS_NPROCS] = {
92 { "null", "null" },
93 { "getattr", "getattr" },
94 { "setattr", "setattr" },
95 { "lookup", "lookup" },
96 { "access" },
97 { "readlink", "readlink" },
98 { "read", "read" },
99 { "write", "write" },
100 { "create", "create" },
101 { "mkdir", "mkdir" },
102 { "symlink", "symlink" },
103 { "mknod" },
104 { "remove", "remove" },
105 { "rmdir", "rmdir" },
106 { "rename", "rename" },
107 { "link", "link" },
108 { "readdir", "readdir" },
109 { "readdirplus" },
110 { "fsstat", "statfs" },
111 { "fsinfo" },
112 { "pathconf" },
113 { "commit" },
114 { "noop" },
115 };
116
117 /*
118 * Module name strings.
119 */
120 static char *dtnfsclient_accesscache_str = "accesscache";
121 static char *dtnfsclient_attrcache_str = "attrcache";
122 static char *dtnfsclient_nfs2_str = "nfs2";
123 static char *dtnfsclient_nfs3_str = "nfs3";
124
125 /*
126 * Function name strings.
127 */
128 static char *dtnfsclient_flush_str = "flush";
129 static char *dtnfsclient_load_str = "load";
130 static char *dtnfsclient_get_str = "get";
131
132 /*
133 * Name strings.
134 */
135 static char *dtnfsclient_done_str = "done";
136 static char *dtnfsclient_hit_str = "hit";
137 static char *dtnfsclient_miss_str = "miss";
138 static char *dtnfsclient_start_str = "start";
139
140 static dtrace_pops_t dtnfsclient_pops = {
141 dtnfsclient_provide,
142 NULL,
143 dtnfsclient_enable,
144 dtnfsclient_disable,
145 NULL,
146 NULL,
147 dtnfsclient_getargdesc,
148 NULL,
149 NULL,
150 dtnfsclient_destroy
151 };
152
153 static dtrace_provider_id_t dtnfsclient_id;
154
155 /*
156 * Most probes are generated from the above RPC table, but for access and
157 * attribute caches, we have specific IDs we recognize and handle specially
158 * in various spots.
159 */
160 extern uint32_t nfsclient_accesscache_flush_done_id;
161 extern uint32_t nfsclient_accesscache_get_hit_id;
162 extern uint32_t nfsclient_accesscache_get_miss_id;
163 extern uint32_t nfsclient_accesscache_load_done_id;
164
165 extern uint32_t nfsclient_attrcache_flush_done_id;
166 extern uint32_t nfsclient_attrcache_get_hit_id;
167 extern uint32_t nfsclient_attrcache_get_miss_id;
168 extern uint32_t nfsclient_attrcache_load_done_id;
169
170 /*
171 * When tracing on a procedure is enabled, the DTrace ID for an RPC event is
172 * stored in one of these two NFS client-allocated arrays; 0 indicates that
173 * the event is not being traced so probes should not be called.
174 *
175 * For simplicity, we allocate both v2 and v3 arrays as NFS_NPROCS, and the
176 * v2 array is simply sparse.
177 */
178 extern uint32_t nfsclient_nfs2_start_probes[NFS_NPROCS];
179 extern uint32_t nfsclient_nfs2_done_probes[NFS_NPROCS];
180
181 extern uint32_t nfsclient_nfs3_start_probes[NFS_NPROCS];
182 extern uint32_t nfsclient_nfs3_done_probes[NFS_NPROCS];
183
184 /*
185 * Look up a DTrace probe ID to see if it's associated with a "done" event --
186 * if so, we will return a fourth argument type of "int".
187 */
188 static int
189 dtnfs23_isdoneprobe(dtrace_id_t id)
190 {
191 int i;
192
193 for (i = 0; i < NFS_NPROCS; i++) {
194 if (dtnfsclient_rpcs[i].nr_v3_id_done == id ||
195 dtnfsclient_rpcs[i].nr_v2_id_done == id)
196 return (1);
197 }
198 return (0);
199 }
200
201 static void
202 dtnfsclient_getargdesc(void *arg, dtrace_id_t id, void *parg,
203 dtrace_argdesc_t *desc)
204 {
205 const char *p = NULL;
206
207 if (id == nfsclient_accesscache_flush_done_id ||
208 id == nfsclient_attrcache_flush_done_id ||
209 id == nfsclient_attrcache_get_miss_id) {
210 switch (desc->dtargd_ndx) {
211 case 0:
212 p = "struct vnode *";
213 break;
214 default:
215 desc->dtargd_ndx = DTRACE_ARGNONE;
216 break;
217 }
218 } else if (id == nfsclient_accesscache_get_hit_id ||
219 id == nfsclient_accesscache_get_miss_id) {
220 switch (desc->dtargd_ndx) {
221 case 0:
222 p = "struct vnode *";
223 break;
224 case 1:
225 p = "uid_t";
226 break;
227 case 2:
228 p = "uint32_t";
229 break;
230 default:
231 desc->dtargd_ndx = DTRACE_ARGNONE;
232 break;
233 }
234 } else if (id == nfsclient_accesscache_load_done_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 case 3:
246 p = "int";
247 break;
248 default:
249 desc->dtargd_ndx = DTRACE_ARGNONE;
250 break;
251 }
252 } else if (id == nfsclient_attrcache_get_hit_id) {
253 switch (desc->dtargd_ndx) {
254 case 0:
255 p = "struct vnode *";
256 break;
257 case 1:
258 p = "struct vattr *";
259 break;
260 default:
261 desc->dtargd_ndx = DTRACE_ARGNONE;
262 break;
263 }
264 } else if (id == nfsclient_attrcache_load_done_id) {
265 switch (desc->dtargd_ndx) {
266 case 0:
267 p = "struct vnode *";
268 break;
269 case 1:
270 p = "struct vattr *";
271 break;
272 case 2:
273 p = "int";
274 break;
275 default:
276 desc->dtargd_ndx = DTRACE_ARGNONE;
277 break;
278 }
279 } else {
280 switch (desc->dtargd_ndx) {
281 case 0:
282 p = "struct vnode *";
283 break;
284 case 1:
285 p = "struct mbuf *";
286 break;
287 case 2:
288 p = "struct ucred *";
289 break;
290 case 3:
291 p = "int";
292 break;
293 case 4:
294 if (dtnfs23_isdoneprobe(id)) {
295 p = "int";
296 break;
297 }
298 /* FALLSTHROUGH */
299 default:
300 desc->dtargd_ndx = DTRACE_ARGNONE;
301 break;
302 }
303 }
304 if (p != NULL)
305 strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
306 }
307
308 static void
309 dtnfsclient_provide(void *arg, dtrace_probedesc_t *desc)
310 {
311 int i;
312
313 if (desc != NULL)
314 return;
315
316 /*
317 * Register access cache probes.
318 */
319 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
320 dtnfsclient_flush_str, dtnfsclient_done_str) == 0) {
321 nfsclient_accesscache_flush_done_id = dtrace_probe_create(
322 dtnfsclient_id, dtnfsclient_accesscache_str,
323 dtnfsclient_flush_str, dtnfsclient_done_str, 0, NULL);
324 }
325 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
326 dtnfsclient_get_str, dtnfsclient_hit_str) == 0) {
327 nfsclient_accesscache_get_hit_id = dtrace_probe_create(
328 dtnfsclient_id, dtnfsclient_accesscache_str,
329 dtnfsclient_get_str, dtnfsclient_hit_str, 0, NULL);
330 }
331 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
332 dtnfsclient_get_str, dtnfsclient_miss_str) == 0) {
333 nfsclient_accesscache_get_miss_id = dtrace_probe_create(
334 dtnfsclient_id, dtnfsclient_accesscache_str,
335 dtnfsclient_get_str, dtnfsclient_miss_str, 0, NULL);
336 }
337 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str,
338 dtnfsclient_load_str, dtnfsclient_done_str) == 0) {
339 nfsclient_accesscache_load_done_id = dtrace_probe_create(
340 dtnfsclient_id, dtnfsclient_accesscache_str,
341 dtnfsclient_load_str, dtnfsclient_done_str, 0, NULL);
342 }
343
344 /*
345 * Register attribute cache probes.
346 */
347 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
348 dtnfsclient_flush_str, dtnfsclient_done_str) == 0) {
349 nfsclient_attrcache_flush_done_id = dtrace_probe_create(
350 dtnfsclient_id, dtnfsclient_attrcache_str,
351 dtnfsclient_flush_str, dtnfsclient_done_str, 0, NULL);
352 }
353 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
354 dtnfsclient_get_str, dtnfsclient_hit_str) == 0) {
355 nfsclient_attrcache_get_hit_id = dtrace_probe_create(
356 dtnfsclient_id, dtnfsclient_attrcache_str,
357 dtnfsclient_get_str, dtnfsclient_hit_str, 0, NULL);
358 }
359 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
360 dtnfsclient_get_str, dtnfsclient_miss_str) == 0) {
361 nfsclient_attrcache_get_miss_id = dtrace_probe_create(
362 dtnfsclient_id, dtnfsclient_attrcache_str,
363 dtnfsclient_get_str, dtnfsclient_miss_str, 0, NULL);
364 }
365 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str,
366 dtnfsclient_load_str, dtnfsclient_done_str) == 0) {
367 nfsclient_attrcache_load_done_id = dtrace_probe_create(
368 dtnfsclient_id, dtnfsclient_attrcache_str,
369 dtnfsclient_load_str, dtnfsclient_done_str, 0, NULL);
370 }
371
372 /*
373 * Register NFSv2 RPC procedures; note sparseness check for each slot
374 * in the NFSv3 procnum-indexed array.
375 */
376 for (i = 0; i < NFS_NPROCS; i++) {
377 if (dtnfsclient_rpcs[i].nr_v2_name != NULL &&
378 dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs2_str,
379 dtnfsclient_rpcs[i].nr_v2_name, dtnfsclient_start_str) ==
380 0) {
381 dtnfsclient_rpcs[i].nr_v2_id_start =
382 dtrace_probe_create(dtnfsclient_id,
383 dtnfsclient_nfs2_str,
384 dtnfsclient_rpcs[i].nr_v2_name,
385 dtnfsclient_start_str, 0,
386 &nfsclient_nfs2_start_probes[i]);
387 }
388 if (dtnfsclient_rpcs[i].nr_v2_name != NULL &&
389 dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs2_str,
390 dtnfsclient_rpcs[i].nr_v2_name, dtnfsclient_done_str) ==
391 0) {
392 dtnfsclient_rpcs[i].nr_v2_id_done =
393 dtrace_probe_create(dtnfsclient_id,
394 dtnfsclient_nfs2_str,
395 dtnfsclient_rpcs[i].nr_v2_name,
396 dtnfsclient_done_str, 0,
397 &nfsclient_nfs2_done_probes[i]);
398 }
399 }
400
401 /*
402 * Register NFSv3 RPC procedures.
403 */
404 for (i = 0; i < NFS_NPROCS; i++) {
405 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs3_str,
406 dtnfsclient_rpcs[i].nr_v3_name, dtnfsclient_start_str) ==
407 0) {
408 dtnfsclient_rpcs[i].nr_v3_id_start =
409 dtrace_probe_create(dtnfsclient_id,
410 dtnfsclient_nfs3_str,
411 dtnfsclient_rpcs[i].nr_v3_name,
412 dtnfsclient_start_str, 0,
413 &nfsclient_nfs3_start_probes[i]);
414 }
415 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs3_str,
416 dtnfsclient_rpcs[i].nr_v3_name, dtnfsclient_done_str) ==
417 0) {
418 dtnfsclient_rpcs[i].nr_v3_id_done =
419 dtrace_probe_create(dtnfsclient_id,
420 dtnfsclient_nfs3_str,
421 dtnfsclient_rpcs[i].nr_v3_name,
422 dtnfsclient_done_str, 0,
423 &nfsclient_nfs3_done_probes[i]);
424 }
425 }
426 }
427
428 static void
429 dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg)
430 {
431 }
432
433 static void
434 dtnfsclient_enable(void *arg, dtrace_id_t id, void *parg)
435 {
436 uint32_t *p = parg;
437 void *f = dtrace_probe;
438
439 if (id == nfsclient_accesscache_flush_done_id)
440 dtrace_nfsclient_accesscache_flush_done_probe = f;
441 else if (id == nfsclient_accesscache_get_hit_id)
442 dtrace_nfsclient_accesscache_get_hit_probe = f;
443 else if (id == nfsclient_accesscache_get_miss_id)
444 dtrace_nfsclient_accesscache_get_miss_probe = f;
445 else if (id == nfsclient_accesscache_load_done_id)
446 dtrace_nfsclient_accesscache_load_done_probe = f;
447 else if (id == nfsclient_attrcache_flush_done_id)
448 dtrace_nfsclient_attrcache_flush_done_probe = f;
449 else if (id == nfsclient_attrcache_get_hit_id)
450 dtrace_nfsclient_attrcache_get_hit_probe = f;
451 else if (id == nfsclient_attrcache_get_miss_id)
452 dtrace_nfsclient_attrcache_get_miss_probe = f;
453 else if (id == nfsclient_attrcache_load_done_id)
454 dtrace_nfsclient_attrcache_load_done_probe = f;
455 else
456 *p = id;
457 }
458
459 static void
460 dtnfsclient_disable(void *arg, dtrace_id_t id, void *parg)
461 {
462 uint32_t *p = parg;
463
464 if (id == nfsclient_accesscache_flush_done_id)
465 dtrace_nfsclient_accesscache_flush_done_probe = NULL;
466 else if (id == nfsclient_accesscache_get_hit_id)
467 dtrace_nfsclient_accesscache_get_hit_probe = NULL;
468 else if (id == nfsclient_accesscache_get_miss_id)
469 dtrace_nfsclient_accesscache_get_miss_probe = NULL;
470 else if (id == nfsclient_accesscache_load_done_id)
471 dtrace_nfsclient_accesscache_load_done_probe = NULL;
472 else if (id == nfsclient_attrcache_flush_done_id)
473 dtrace_nfsclient_attrcache_flush_done_probe = NULL;
474 else if (id == nfsclient_attrcache_get_hit_id)
475 dtrace_nfsclient_attrcache_get_hit_probe = NULL;
476 else if (id == nfsclient_attrcache_get_miss_id)
477 dtrace_nfsclient_attrcache_get_miss_probe = NULL;
478 else if (id == nfsclient_attrcache_load_done_id)
479 dtrace_nfsclient_attrcache_load_done_probe = NULL;
480 else
481 *p = 0;
482 }
483
484 static void
485 dtnfsclient_load(void *dummy)
486 {
487
488 if (dtrace_register("nfsclient", &dtnfsclient_attr,
489 DTRACE_PRIV_USER, NULL, &dtnfsclient_pops, NULL,
490 &dtnfsclient_id) != 0)
491 return;
492
493 dtrace_nfsclient_nfs23_start_probe =
494 (dtrace_nfsclient_nfs23_start_probe_func_t)dtrace_probe;
495 dtrace_nfsclient_nfs23_done_probe =
496 (dtrace_nfsclient_nfs23_done_probe_func_t)dtrace_probe;
497 }
498
499
500 static int
501 dtnfsclient_unload()
502 {
503
504 dtrace_nfsclient_nfs23_start_probe = NULL;
505 dtrace_nfsclient_nfs23_done_probe = NULL;
506
507 return (dtrace_unregister(dtnfsclient_id));
508 }
509
510 static int
511 dtnfsclient_modevent(module_t mod __unused, int type, void *data __unused)
512 {
513 int error = 0;
514
515 switch (type) {
516 case MOD_LOAD:
517 break;
518
519 case MOD_UNLOAD:
520 break;
521
522 case MOD_SHUTDOWN:
523 break;
524
525 default:
526 error = EOPNOTSUPP;
527 break;
528 }
529
530 return (error);
531 }
532
533 SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
534 dtnfsclient_load, NULL);
535 SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
536 dtnfsclient_unload, NULL);
537
538 DEV_MODULE(dtnfsclient, dtnfsclient_modevent, NULL);
539 MODULE_VERSION(dtnfsclient, 1);
540 MODULE_DEPEND(dtnfsclient, dtrace, 1, 1, 1);
541 MODULE_DEPEND(dtnfsclient, opensolaris, 1, 1, 1);
542 MODULE_DEPEND(dtnfsclient, oldnfs, 1, 1, 1);
Cache object: abbc422b5b19f9046a6e591c465d0d0c
|