1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 33 #include <sys/systm.h> 34 #include <sys/conf.h> 35 #include <sys/kernel.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 39 #include <sys/dtrace.h> 40 #include <sys/dtrace_bsd.h> 41 42 #include <fs/nfs/nfsproto.h> 43 44 #include <fs/nfsclient/nfs_kdtrace.h> 45 46 /* 47 * dtnfscl is a DTrace provider that tracks the intent to perform RPCs 48 * in the NFS client, as well as access to and maintenance of the access and 49 * attribute caches. This is not quite the same as RPCs, because NFS may 50 * issue multiple RPC transactions in the event that authentication fails, 51 * there's a jukebox error, or none at all if the access or attribute cache 52 * hits. However, it cleanly represents the logical layer between RPC 53 * transmission and vnode/vfs operations, providing access to state linking 54 * the two. 55 */ 56 57 static int dtnfsclient_unload(void); 58 static void dtnfsclient_getargdesc(void *, dtrace_id_t, void *, 59 dtrace_argdesc_t *); 60 static void dtnfsclient_provide(void *, dtrace_probedesc_t *); 61 static void dtnfsclient_destroy(void *, dtrace_id_t, void *); 62 static void dtnfsclient_enable(void *, dtrace_id_t, void *); 63 static void dtnfsclient_disable(void *, dtrace_id_t, void *); 64 static void dtnfsclient_load(void *); 65 66 static dtrace_pattr_t dtnfsclient_attr = { 67 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, 68 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 69 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 70 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, 71 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, 72 }; 73 74 /* 75 * Description of NFSv4, NFSv3 and (optional) NFSv2 probes for a procedure. 76 */ 77 struct dtnfsclient_rpc { 78 char *nr_v4_name; 79 char *nr_v3_name; /* Or NULL if none. */ 80 char *nr_v2_name; /* Or NULL if none. */ 81 82 /* 83 * IDs for the start and done cases, for NFSv2, NFSv3 and NFSv4. 84 */ 85 uint32_t nr_v2_id_start, nr_v2_id_done; 86 uint32_t nr_v3_id_start, nr_v3_id_done; 87 uint32_t nr_v4_id_start, nr_v4_id_done; 88 }; 89 90 /* 91 * This table is indexed by NFSv3 procedure number, but also used for NFSv2 92 * procedure names and NFSv4 operations. 93 */ 94 static struct dtnfsclient_rpc dtnfsclient_rpcs[NFSV41_NPROCS + 1] = { 95 { "null", "null", "null" }, 96 { "getattr", "getattr", "getattr" }, 97 { "setattr", "setattr", "setattr" }, 98 { "lookup", "lookup", "lookup" }, 99 { "access", "access", "noop" }, 100 { "readlink", "readlink", "readlink" }, 101 { "read", "read", "read" }, 102 { "write", "write", "write" }, 103 { "create", "create", "create" }, 104 { "mkdir", "mkdir", "mkdir" }, 105 { "symlink", "symlink", "symlink" }, 106 { "mknod", "mknod" }, 107 { "remove", "remove", "remove" }, 108 { "rmdir", "rmdir", "rmdir" }, 109 { "rename", "rename", "rename" }, 110 { "link", "link", "link" }, 111 { "readdir", "readdir", "readdir" }, 112 { "readdirplus", "readdirplus" }, 113 { "fsstat", "fsstat", "statfs" }, 114 { "fsinfo", "fsinfo" }, 115 { "pathconf", "pathconf" }, 116 { "commit", "commit" }, 117 { "lookupp" }, 118 { "setclientid" }, 119 { "setclientidcfrm" }, 120 { "lock" }, 121 { "locku" }, 122 { "open" }, 123 { "close" }, 124 { "openconfirm" }, 125 { "lockt" }, 126 { "opendowngrade" }, 127 { "renew" }, 128 { "putrootfh" }, 129 { "releaselckown" }, 130 { "delegreturn" }, 131 { "retdelegremove" }, 132 { "retdelegrename1" }, 133 { "retdelegrename2" }, 134 { "getacl" }, 135 { "setacl" }, 136 { "noop", "noop", "noop" } 137 }; 138 139 /* 140 * Module name strings. 141 */ 142 static char *dtnfsclient_accesscache_str = "accesscache"; 143 static char *dtnfsclient_attrcache_str = "attrcache"; 144 static char *dtnfsclient_nfs2_str = "nfs2"; 145 static char *dtnfsclient_nfs3_str = "nfs3"; 146 static char *dtnfsclient_nfs4_str = "nfs4"; 147 148 /* 149 * Function name strings. 150 */ 151 static char *dtnfsclient_flush_str = "flush"; 152 static char *dtnfsclient_load_str = "load"; 153 static char *dtnfsclient_get_str = "get"; 154 155 /* 156 * Name strings. 157 */ 158 static char *dtnfsclient_done_str = "done"; 159 static char *dtnfsclient_hit_str = "hit"; 160 static char *dtnfsclient_miss_str = "miss"; 161 static char *dtnfsclient_start_str = "start"; 162 163 static dtrace_pops_t dtnfsclient_pops = { 164 .dtps_provide = dtnfsclient_provide, 165 .dtps_provide_module = NULL, 166 .dtps_enable = dtnfsclient_enable, 167 .dtps_disable = dtnfsclient_disable, 168 .dtps_suspend = NULL, 169 .dtps_resume = NULL, 170 .dtps_getargdesc = dtnfsclient_getargdesc, 171 .dtps_getargval = NULL, 172 .dtps_usermode = NULL, 173 .dtps_destroy = dtnfsclient_destroy 174 }; 175 176 static dtrace_provider_id_t dtnfsclient_id; 177 178 /* 179 * When tracing on a procedure is enabled, the DTrace ID for an RPC event is 180 * stored in one of these two NFS client-allocated arrays; 0 indicates that 181 * the event is not being traced so probes should not be called. 182 * 183 * For simplicity, we allocate both v2, v3 and v4 arrays as NFSV41_NPROCS + 1, 184 * and the v2, v3 arrays are simply sparse. 185 */ 186 extern uint32_t nfscl_nfs2_start_probes[NFSV41_NPROCS + 1]; 187 extern uint32_t nfscl_nfs2_done_probes[NFSV41_NPROCS + 1]; 188 189 extern uint32_t nfscl_nfs3_start_probes[NFSV41_NPROCS + 1]; 190 extern uint32_t nfscl_nfs3_done_probes[NFSV41_NPROCS + 1]; 191 192 extern uint32_t nfscl_nfs4_start_probes[NFSV41_NPROCS + 1]; 193 extern uint32_t nfscl_nfs4_done_probes[NFSV41_NPROCS + 1]; 194 195 /* 196 * Look up a DTrace probe ID to see if it's associated with a "done" event -- 197 * if so, we will return a fourth argument type of "int". 198 */ 199 static int 200 dtnfs234_isdoneprobe(dtrace_id_t id) 201 { 202 int i; 203 204 for (i = 0; i < NFSV41_NPROCS + 1; i++) { 205 if (dtnfsclient_rpcs[i].nr_v4_id_done == id || 206 dtnfsclient_rpcs[i].nr_v3_id_done == id || 207 dtnfsclient_rpcs[i].nr_v2_id_done == id) 208 return (1); 209 } 210 return (0); 211 } 212 213 static void 214 dtnfsclient_getargdesc(void *arg, dtrace_id_t id, void *parg, 215 dtrace_argdesc_t *desc) 216 { 217 const char *p = NULL; 218 219 if (id == nfscl_accesscache_flush_done_id || 220 id == nfscl_attrcache_flush_done_id || 221 id == nfscl_attrcache_get_miss_id) { 222 switch (desc->dtargd_ndx) { 223 case 0: 224 p = "struct vnode *"; 225 break; 226 default: 227 desc->dtargd_ndx = DTRACE_ARGNONE; 228 break; 229 } 230 } else if (id == nfscl_accesscache_get_hit_id || 231 id == nfscl_accesscache_get_miss_id) { 232 switch (desc->dtargd_ndx) { 233 case 0: 234 p = "struct vnode *"; 235 break; 236 case 1: 237 p = "uid_t"; 238 break; 239 case 2: 240 p = "uint32_t"; 241 break; 242 default: 243 desc->dtargd_ndx = DTRACE_ARGNONE; 244 break; 245 } 246 } else if (id == nfscl_accesscache_load_done_id) { 247 switch (desc->dtargd_ndx) { 248 case 0: 249 p = "struct vnode *"; 250 break; 251 case 1: 252 p = "uid_t"; 253 break; 254 case 2: 255 p = "uint32_t"; 256 break; 257 case 3: 258 p = "int"; 259 break; 260 default: 261 desc->dtargd_ndx = DTRACE_ARGNONE; 262 break; 263 } 264 } else if (id == nfscl_attrcache_get_hit_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 default: 273 desc->dtargd_ndx = DTRACE_ARGNONE; 274 break; 275 } 276 } else if (id == nfscl_attrcache_load_done_id) { 277 switch (desc->dtargd_ndx) { 278 case 0: 279 p = "struct vnode *"; 280 break; 281 case 1: 282 p = "struct vattr *"; 283 break; 284 case 2: 285 p = "int"; 286 break; 287 default: 288 desc->dtargd_ndx = DTRACE_ARGNONE; 289 break; 290 } 291 } else { 292 switch (desc->dtargd_ndx) { 293 case 0: 294 p = "struct vnode *"; 295 break; 296 case 1: 297 p = "struct mbuf *"; 298 break; 299 case 2: 300 p = "struct ucred *"; 301 break; 302 case 3: 303 p = "int"; 304 break; 305 case 4: 306 if (dtnfs234_isdoneprobe(id)) { 307 p = "int"; 308 break; 309 } 310 /* FALLSTHROUGH */ 311 default: 312 desc->dtargd_ndx = DTRACE_ARGNONE; 313 break; 314 } 315 } 316 if (p != NULL) 317 strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native)); 318 } 319 320 static void 321 dtnfsclient_provide(void *arg, dtrace_probedesc_t *desc) 322 { 323 int i; 324 325 if (desc != NULL) 326 return; 327 328 /* 329 * Register access cache probes. 330 */ 331 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str, 332 dtnfsclient_flush_str, dtnfsclient_done_str) == 0) { 333 nfscl_accesscache_flush_done_id = dtrace_probe_create( 334 dtnfsclient_id, dtnfsclient_accesscache_str, 335 dtnfsclient_flush_str, dtnfsclient_done_str, 0, NULL); 336 } 337 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str, 338 dtnfsclient_get_str, dtnfsclient_hit_str) == 0) { 339 nfscl_accesscache_get_hit_id = dtrace_probe_create( 340 dtnfsclient_id, dtnfsclient_accesscache_str, 341 dtnfsclient_get_str, dtnfsclient_hit_str, 0, NULL); 342 } 343 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str, 344 dtnfsclient_get_str, dtnfsclient_miss_str) == 0) { 345 nfscl_accesscache_get_miss_id = dtrace_probe_create( 346 dtnfsclient_id, dtnfsclient_accesscache_str, 347 dtnfsclient_get_str, dtnfsclient_miss_str, 0, NULL); 348 } 349 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_accesscache_str, 350 dtnfsclient_load_str, dtnfsclient_done_str) == 0) { 351 nfscl_accesscache_load_done_id = dtrace_probe_create( 352 dtnfsclient_id, dtnfsclient_accesscache_str, 353 dtnfsclient_load_str, dtnfsclient_done_str, 0, NULL); 354 } 355 356 /* 357 * Register attribute cache probes. 358 */ 359 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str, 360 dtnfsclient_flush_str, dtnfsclient_done_str) == 0) { 361 nfscl_attrcache_flush_done_id = dtrace_probe_create( 362 dtnfsclient_id, dtnfsclient_attrcache_str, 363 dtnfsclient_flush_str, dtnfsclient_done_str, 0, NULL); 364 } 365 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str, 366 dtnfsclient_get_str, dtnfsclient_hit_str) == 0) { 367 nfscl_attrcache_get_hit_id = dtrace_probe_create( 368 dtnfsclient_id, dtnfsclient_attrcache_str, 369 dtnfsclient_get_str, dtnfsclient_hit_str, 0, NULL); 370 } 371 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str, 372 dtnfsclient_get_str, dtnfsclient_miss_str) == 0) { 373 nfscl_attrcache_get_miss_id = dtrace_probe_create( 374 dtnfsclient_id, dtnfsclient_attrcache_str, 375 dtnfsclient_get_str, dtnfsclient_miss_str, 0, NULL); 376 } 377 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_attrcache_str, 378 dtnfsclient_load_str, dtnfsclient_done_str) == 0) { 379 nfscl_attrcache_load_done_id = dtrace_probe_create( 380 dtnfsclient_id, dtnfsclient_attrcache_str, 381 dtnfsclient_load_str, dtnfsclient_done_str, 0, NULL); 382 } 383 384 /* 385 * Register NFSv2 RPC procedures; note sparseness check for each slot 386 * in the NFSv3, NFSv4 procnum-indexed array. 387 */ 388 for (i = 0; i < NFSV41_NPROCS + 1; i++) { 389 if (dtnfsclient_rpcs[i].nr_v2_name != NULL && 390 dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs2_str, 391 dtnfsclient_rpcs[i].nr_v2_name, dtnfsclient_start_str) == 392 0) { 393 dtnfsclient_rpcs[i].nr_v2_id_start = 394 dtrace_probe_create(dtnfsclient_id, 395 dtnfsclient_nfs2_str, 396 dtnfsclient_rpcs[i].nr_v2_name, 397 dtnfsclient_start_str, 0, 398 &nfscl_nfs2_start_probes[i]); 399 } 400 if (dtnfsclient_rpcs[i].nr_v2_name != NULL && 401 dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs2_str, 402 dtnfsclient_rpcs[i].nr_v2_name, dtnfsclient_done_str) == 403 0) { 404 dtnfsclient_rpcs[i].nr_v2_id_done = 405 dtrace_probe_create(dtnfsclient_id, 406 dtnfsclient_nfs2_str, 407 dtnfsclient_rpcs[i].nr_v2_name, 408 dtnfsclient_done_str, 0, 409 &nfscl_nfs2_done_probes[i]); 410 } 411 } 412 413 /* 414 * Register NFSv3 RPC procedures; note sparseness check for each slot 415 * in the NFSv4 procnum-indexed array. 416 */ 417 for (i = 0; i < NFSV41_NPROCS + 1; i++) { 418 if (dtnfsclient_rpcs[i].nr_v3_name != NULL && 419 dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs3_str, 420 dtnfsclient_rpcs[i].nr_v3_name, dtnfsclient_start_str) == 421 0) { 422 dtnfsclient_rpcs[i].nr_v3_id_start = 423 dtrace_probe_create(dtnfsclient_id, 424 dtnfsclient_nfs3_str, 425 dtnfsclient_rpcs[i].nr_v3_name, 426 dtnfsclient_start_str, 0, 427 &nfscl_nfs3_start_probes[i]); 428 } 429 if (dtnfsclient_rpcs[i].nr_v3_name != NULL && 430 dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs3_str, 431 dtnfsclient_rpcs[i].nr_v3_name, dtnfsclient_done_str) == 432 0) { 433 dtnfsclient_rpcs[i].nr_v3_id_done = 434 dtrace_probe_create(dtnfsclient_id, 435 dtnfsclient_nfs3_str, 436 dtnfsclient_rpcs[i].nr_v3_name, 437 dtnfsclient_done_str, 0, 438 &nfscl_nfs3_done_probes[i]); 439 } 440 } 441 442 /* 443 * Register NFSv4 RPC procedures. 444 */ 445 for (i = 0; i < NFSV41_NPROCS + 1; i++) { 446 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs4_str, 447 dtnfsclient_rpcs[i].nr_v4_name, dtnfsclient_start_str) == 448 0) { 449 dtnfsclient_rpcs[i].nr_v4_id_start = 450 dtrace_probe_create(dtnfsclient_id, 451 dtnfsclient_nfs4_str, 452 dtnfsclient_rpcs[i].nr_v4_name, 453 dtnfsclient_start_str, 0, 454 &nfscl_nfs4_start_probes[i]); 455 } 456 if (dtrace_probe_lookup(dtnfsclient_id, dtnfsclient_nfs4_str, 457 dtnfsclient_rpcs[i].nr_v4_name, dtnfsclient_done_str) == 458 0) { 459 dtnfsclient_rpcs[i].nr_v4_id_done = 460 dtrace_probe_create(dtnfsclient_id, 461 dtnfsclient_nfs4_str, 462 dtnfsclient_rpcs[i].nr_v4_name, 463 dtnfsclient_done_str, 0, 464 &nfscl_nfs4_done_probes[i]); 465 } 466 } 467 } 468 469 static void 470 dtnfsclient_destroy(void *arg, dtrace_id_t id, void *parg) 471 { 472 } 473 474 static void 475 dtnfsclient_enable(void *arg, dtrace_id_t id, void *parg) 476 { 477 uint32_t *p = parg; 478 void *f = dtrace_probe; 479 480 if (id == nfscl_accesscache_flush_done_id) 481 dtrace_nfscl_accesscache_flush_done_probe = f; 482 else if (id == nfscl_accesscache_get_hit_id) 483 dtrace_nfscl_accesscache_get_hit_probe = f; 484 else if (id == nfscl_accesscache_get_miss_id) 485 dtrace_nfscl_accesscache_get_miss_probe = f; 486 else if (id == nfscl_accesscache_load_done_id) 487 dtrace_nfscl_accesscache_load_done_probe = f; 488 else if (id == nfscl_attrcache_flush_done_id) 489 dtrace_nfscl_attrcache_flush_done_probe = f; 490 else if (id == nfscl_attrcache_get_hit_id) 491 dtrace_nfscl_attrcache_get_hit_probe = f; 492 else if (id == nfscl_attrcache_get_miss_id) 493 dtrace_nfscl_attrcache_get_miss_probe = f; 494 else if (id == nfscl_attrcache_load_done_id) 495 dtrace_nfscl_attrcache_load_done_probe = f; 496 else 497 *p = id; 498 } 499 500 static void 501 dtnfsclient_disable(void *arg, dtrace_id_t id, void *parg) 502 { 503 uint32_t *p = parg; 504 505 if (id == nfscl_accesscache_flush_done_id) 506 dtrace_nfscl_accesscache_flush_done_probe = NULL; 507 else if (id == nfscl_accesscache_get_hit_id) 508 dtrace_nfscl_accesscache_get_hit_probe = NULL; 509 else if (id == nfscl_accesscache_get_miss_id) 510 dtrace_nfscl_accesscache_get_miss_probe = NULL; 511 else if (id == nfscl_accesscache_load_done_id) 512 dtrace_nfscl_accesscache_load_done_probe = NULL; 513 else if (id == nfscl_attrcache_flush_done_id) 514 dtrace_nfscl_attrcache_flush_done_probe = NULL; 515 else if (id == nfscl_attrcache_get_hit_id) 516 dtrace_nfscl_attrcache_get_hit_probe = NULL; 517 else if (id == nfscl_attrcache_get_miss_id) 518 dtrace_nfscl_attrcache_get_miss_probe = NULL; 519 else if (id == nfscl_attrcache_load_done_id) 520 dtrace_nfscl_attrcache_load_done_probe = NULL; 521 else 522 *p = 0; 523 } 524 525 static void 526 dtnfsclient_load(void *dummy) 527 { 528 529 if (dtrace_register("nfscl", &dtnfsclient_attr, 530 DTRACE_PRIV_USER, NULL, &dtnfsclient_pops, NULL, 531 &dtnfsclient_id) != 0) 532 return; 533 534 dtrace_nfscl_nfs234_start_probe = 535 (dtrace_nfsclient_nfs23_start_probe_func_t)dtrace_probe; 536 dtrace_nfscl_nfs234_done_probe = 537 (dtrace_nfsclient_nfs23_done_probe_func_t)dtrace_probe; 538 } 539 540 static int 541 dtnfsclient_unload(void) 542 { 543 544 dtrace_nfscl_nfs234_start_probe = NULL; 545 dtrace_nfscl_nfs234_done_probe = NULL; 546 547 return (dtrace_unregister(dtnfsclient_id)); 548 } 549 550 static int 551 dtnfsclient_modevent(module_t mod __unused, int type, void *data __unused) 552 { 553 int error = 0; 554 555 switch (type) { 556 case MOD_LOAD: 557 break; 558 559 case MOD_UNLOAD: 560 break; 561 562 case MOD_SHUTDOWN: 563 break; 564 565 default: 566 error = EOPNOTSUPP; 567 break; 568 } 569 570 return (error); 571 } 572 573 SYSINIT(dtnfsclient_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, 574 dtnfsclient_load, NULL); 575 SYSUNINIT(dtnfsclient_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, 576 dtnfsclient_unload, NULL); 577 578 DEV_MODULE(dtnfscl, dtnfsclient_modevent, NULL); 579 MODULE_VERSION(dtnfscl, 1); 580 MODULE_DEPEND(dtnfscl, dtrace, 1, 1, 1); 581 MODULE_DEPEND(dtnfscl, opensolaris, 1, 1, 1); 582 MODULE_DEPEND(dtnfscl, nfscl, 1, 1, 1); 583 MODULE_DEPEND(dtnfscl, nfscommon, 1, 1, 1); 584