]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/nfsclient/nfs_kdtrace.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / nfsclient / nfs_kdtrace.c
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 <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);