1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2009 Robert N. M. Watson 5 * All rights reserved. 6 * 7 * This software was developed at the University of Cambridge Computer 8 * Laboratory with support from a grant from Google, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 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. 18 * 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 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/conf.h> 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #include <sys/module.h> 41 42 #include <sys/dtrace.h> 43 #include <sys/dtrace_bsd.h> 44 45 #include <fs/nfs/nfsproto.h> 46 47 #include <fs/nfsclient/nfs_kdtrace.h> 48 49 /* 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 57 * the two. 58 */ 59 60 static int dtnfsclient_unload(void); 61 static void dtnfsclient_getargdesc(void *, dtrace_id_t, void *, 62 dtrace_argdesc_t *); 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 *); 68 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 }, 75 }; 76 77 /* 78 * Description of NFSv4, NFSv3 and (optional) NFSv2 probes for a procedure. 79 */ 80 struct dtnfsclient_rpc { 81 char *nr_v4_name; 82 char *nr_v3_name; /* Or NULL if none. */ 83 char *nr_v2_name; /* Or NULL if none. */ 84 85 /* 86 * IDs for the start and done cases, for NFSv2, NFSv3 and NFSv4. 87 */ 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; 91 }; 92 93 /* 94 * This table is indexed by NFSv3 procedure number, but also used for NFSv2 95 * procedure names and NFSv4 operations. 96 */ 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" }, 120 { "lookupp" }, 121 { "setclientid" }, 122 { "setclientidcfrm" }, 123 { "lock" }, 124 { "locku" }, 125 { "open" }, 126 { "close" }, 127 { "openconfirm" }, 128 { "lockt" }, 129 { "opendowngrade" }, 130 { "renew" }, 131 { "putrootfh" }, 132 { "releaselckown" }, 133 { "delegreturn" }, 134 { "retdelegremove" }, 135 { "retdelegrename1" }, 136 { "retdelegrename2" }, 137 { "getacl" }, 138 { "setacl" }, 139 { "noop", "noop", "noop" } 140 }; 141 142 /* 143 * Module name strings. 144 */ 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"; 150 151 /* 152 * Function name strings. 153 */ 154 static char *dtnfsclient_flush_str = "flush"; 155 static char *dtnfsclient_load_str = "load"; 156 static char *dtnfsclient_get_str = "get"; 157 158 /* 159 * Name strings. 160 */ 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"; 165 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, 172 .dtps_resume = NULL, 173 .dtps_getargdesc = dtnfsclient_getargdesc, 174 .dtps_getargval = NULL, 175 .dtps_usermode = NULL, 176 .dtps_destroy = dtnfsclient_destroy 177 }; 178 179 static dtrace_provider_id_t dtnfsclient_id; 180 181 /* 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. 185 * 186 * For simplicity, we allocate both v2, v3 and v4 arrays as NFSV41_NPROCS + 1, 187 * and the v2, v3 arrays are simply sparse. 188 */ 189 extern uint32_t nfscl_nfs2_start_probes[NFSV41_NPROCS + 1]; 190 extern uint32_t nfscl_nfs2_done_probes[NFSV41_NPROCS + 1]; 191 192 extern uint32_t nfscl_nfs3_start_probes[NFSV41_NPROCS + 1]; 193 extern uint32_t nfscl_nfs3_done_probes[NFSV41_NPROCS + 1]; 194 195 extern uint32_t nfscl_nfs4_start_probes[NFSV41_NPROCS + 1]; 196 extern uint32_t nfscl_nfs4_done_probes[NFSV41_NPROCS + 1]; 197 198 /* 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". 201 */ 202 static int 203 dtnfs234_isdoneprobe(dtrace_id_t id) 204 { 205 int i; 206 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) 211 return (1); 212 } 213 return (0); 214 } 215 216 static void 217 dtnfsclient_getargdesc(void *arg, dtrace_id_t id, void *parg, 218 dtrace_argdesc_t *desc) 219 { 220 const char *p = NULL; 221 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) { 226 case 0: 227 p = "struct vnode *"; 228 break; 229 default: 230 desc->dtargd_ndx = DTRACE_ARGNONE; 231 break; 232 } 233 } else if (id == nfscl_accesscache_get_hit_id || 234 id == nfscl_accesscache_get_miss_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 default: 246 desc->dtargd_ndx = DTRACE_ARGNONE; 247 break; 248 } 249 } else if (id == nfscl_accesscache_load_done_id) { 250 switch (desc->dtargd_ndx) { 251 case 0: 252 p = "struct vnode *"; 253 break; 254 case 1: 255 p = "uid_t"; 256 break; 257 case 2: 258 p = "uint32_t"; 259 break; 260 case 3: 261 p = "int"; 262 break; 263 default: 264 desc->dtargd_ndx = DTRACE_ARGNONE; 265 break; 266 } 267 } else if (id == nfscl_attrcache_get_hit_id) { 268 switch (desc->dtargd_ndx) { 269 case 0: 270 p = "struct vnode *"; 271 break; 272 case 1: 273 p = "struct vattr *"; 274 break; 275 default: 276 desc->dtargd_ndx = DTRACE_ARGNONE; 277 break; 278 } 279 } else if (id == nfscl_attrcache_load_done_id) { 280 switch (desc->dtargd_ndx) { 281 case 0: 282 p = "struct vnode *"; 283 break; 284 case 1: 285 p = "struct vattr *"; 286 break; 287 case 2: 288 p = "int"; 289 break; 290 default: 291 desc->dtargd_ndx = DTRACE_ARGNONE; 292 break; 293 } 294 } else { 295 switch (desc->dtargd_ndx) { 296 case 0: 297 p = "struct vnode *"; 298 break; 299 case 1: 300 p = "struct mbuf *"; 301 break; 302 case 2: 303 p = "struct ucred *"; 304 break; 305 case 3: 306 p = "int"; 307 break; 308 case 4: 309 if (dtnfs234_isdoneprobe(id)) { 310 p = "int"; 311 break; 312 } 313 /* FALLSTHROUGH */ 314 default: 315 desc->dtargd_ndx = DTRACE_ARGNONE; 316 break; 317 } 318 } 319 if (p != NULL) 320 strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native)); 321 } 322 323 static void 324 dtnfsclient_provide(void *arg, dtrace_probedesc_t *desc) 325 { 326 int i; 327 328 if (desc != NULL) 329 return; 330 331 /* 332 * Register access cache probes. 333 */ 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); 339 } 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); 345 } 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); 351 } 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); 357 } 358 359 /* 360 * Register attribute cache probes. 361 */ 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); 367 } 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); 373 } 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); 379 } 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); 385 } 386 387 /* 388 * Register NFSv2 RPC procedures; note sparseness check for each slot 389 * in the NFSv3, NFSv4 procnum-indexed array. 390 */ 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) == 395 0) { 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]); 402 } 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) == 406 0) { 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]); 413 } 414 } 415 416 /* 417 * Register NFSv3 RPC procedures; note sparseness check for each slot 418 * in the NFSv4 procnum-indexed array. 419 */ 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) == 424 0) { 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]); 431 } 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) == 435 0) { 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]); 442 } 443 } 444 445 /* 446 * Register NFSv4 RPC procedures. 447 */ 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) == 451 0) { 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]); 458 } 459 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs4_str, 460 dtnfsclient_rpcs[i].nr_v4_name, dtnfsclient_done_str) == 461 0) { 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]); 468 } 469 } 470 } 471 472 static void 473 dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg) 474 { 475 } 476 477 static void 478 dtnfsclient_enable(void *arg, dtrace_id_t id, void *parg) 479 { 480 uint32_t *p = parg; 481 void *f = dtrace_probe; 482 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; 499 else 500 *p = id; 501 } 502 503 static void 504 dtnfsclient_disable(void *arg, dtrace_id_t id, void *parg) 505 { 506 uint32_t *p = parg; 507 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; 524 else 525 *p = 0; 526 } 527 528 static void 529 dtnfsclient_load(void *dummy) 530 { 531 532 if (dtrace_register("nfscl", &dtnfsclient_attr, 533 DTRACE_PRIV_USER, NULL, &dtnfsclient_pops, NULL, 534 &dtnfsclient_id) != 0) 535 return; 536 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; 541 } 542 543 static int 544 dtnfsclient_unload(void) 545 { 546 547 dtrace_nfscl_nfs234_start_probe = NULL; 548 dtrace_nfscl_nfs234_done_probe = NULL; 549 550 return (dtrace_unregister(dtnfsclient_id)); 551 } 552 553 static int 554 dtnfsclient_modevent(module_t mod __unused, int type, void *data __unused) 555 { 556 int error = 0; 557 558 switch (type) { 559 case MOD_LOAD: 560 break; 561 562 case MOD_UNLOAD: 563 break; 564 565 case MOD_SHUTDOWN: 566 break; 567 568 default: 569 error = EOPNOTSUPP; 570 break; 571 } 572 573 return (error); 574 } 575 576 SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, 577 dtnfsclient_load, NULL); 578 SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, 579 dtnfsclient_unload, NULL); 580 581 DEV_MODULE(dtnfscl, dtnfsclient_modevent, NULL); 582 MODULE_VERSION(dtnfscl, 1); 583 MODULE_DEPEND(dtnfscl, dtrace, 1, 1, 1); 584 MODULE_DEPEND(dtnfscl, opensolaris, 1, 1, 1); 585 MODULE_DEPEND(dtnfscl, nfscl, 1, 1, 1); 586 MODULE_DEPEND(dtnfscl, nfscommon, 1, 1, 1); 587