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 56static int dtnfsclient_unload(void); 57static void dtnfsclient_getargdesc(void *, dtrace_id_t, void *, 58 dtrace_argdesc_t *); 59static void dtnfsclient_provide(void *, dtrace_probedesc_t *); 60static void dtnfsclient_destroy(void *, dtrace_id_t, void *); 61static void dtnfsclient_enable(void *, dtrace_id_t, void *); 62static void dtnfsclient_disable(void *, dtrace_id_t, void *); 63static void dtnfsclient_load(void *); 64 65static 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 */ 76struct 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 */ 91static 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 */ 120static char *dtnfsclient_accesscache_str = "accesscache"; 121static char *dtnfsclient_attrcache_str = "attrcache"; 122static char *dtnfsclient_nfs2_str = "nfs2"; 123static char *dtnfsclient_nfs3_str = "nfs3"; 124 125/* 126 * Function name strings. 127 */ 128static char *dtnfsclient_flush_str = "flush"; 129static char *dtnfsclient_load_str = "load"; 130static char *dtnfsclient_get_str = "get"; 131 132/* 133 * Name strings. 134 */ 135static char *dtnfsclient_done_str = "done"; 136static char *dtnfsclient_hit_str = "hit"; 137static char *dtnfsclient_miss_str = "miss"; 138static char *dtnfsclient_start_str = "start"; 139 140static 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 153static 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 */ 160extern uint32_t nfsclient_accesscache_flush_done_id; 161extern uint32_t nfsclient_accesscache_get_hit_id; 162extern uint32_t nfsclient_accesscache_get_miss_id; 163extern uint32_t nfsclient_accesscache_load_done_id; 164 165extern uint32_t nfsclient_attrcache_flush_done_id; 166extern uint32_t nfsclient_attrcache_get_hit_id; 167extern uint32_t nfsclient_attrcache_get_miss_id; 168extern 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 */ 178extern uint32_t nfsclient_nfs2_start_probes[NFS_NPROCS]; 179extern uint32_t nfsclient_nfs2_done_probes[NFS_NPROCS]; 180 181extern uint32_t nfsclient_nfs3_start_probes[NFS_NPROCS]; 182extern 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 */ 188static int 189dtnfs23_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 201static void 202dtnfsclient_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 308static void 309dtnfsclient_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 428static void 429dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg) 430{ 431} 432 433static void 434dtnfsclient_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 459static void 460dtnfsclient_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 484static void 485dtnfsclient_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 500static int 501dtnfsclient_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 510static int 511dtnfsclient_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 533SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, 534 dtnfsclient_load, NULL); 535SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, 536 dtnfsclient_unload, NULL); 537 538DEV_MODULE(dtnfsclient, dtnfsclient_modevent, NULL); 539MODULE_VERSION(dtnfsclient, 1); 540MODULE_DEPEND(dtnfsclient, dtrace, 1, 1, 1); 541MODULE_DEPEND(dtnfsclient, opensolaris, 1, 1, 1); 542MODULE_DEPEND(dtnfsclient, oldnfs, 1, 1, 1); 543