1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2021 Tintri by DDN, Inc. All rights reserved. 14 */ 15 16 #include <sys/mdb_modapi.h> 17 #include <mdb/mdb_ctf.h> 18 #include <sys/vnode.h> 19 #include <stddef.h> 20 #include <nfs/rnode.h> 21 #include <limits.h> 22 #include <nfs/lm.h> 23 #include <sys/flock_impl.h> 24 #include <mdb/mdb_ks.h> 25 26 #include <rpcsvc/nlm_prot.h> 27 #include <rpcsvc/sm_inter.h> 28 #include <rpcsvc/nsm_addr.h> 29 30 #include "klm/nlm_impl.h" 31 32 #define NLM_MAXNAMELEN 256 33 #define NLM_MAXADDRSTR 64 34 35 /* 36 * **************************************************************** 37 * Helper functions 38 */ 39 40 /* 41 * Helper to get printable IP address into a buffer. 42 * Used by nlm_host_dcmd 43 */ 44 static int 45 nlm_netbuf_str(char *buf, size_t bufsz, const struct netbuf *nb) 46 { 47 struct sockaddr_storage sa; 48 struct sockaddr_in *s_in; 49 struct sockaddr_in6 *s_in6; 50 uint_t salen = nb->len; 51 in_port_t port; 52 53 if (salen < sizeof (sa_family_t)) 54 return (-1); 55 if (salen > sizeof (sa)) 56 salen = sizeof (sa); 57 if (mdb_vread(&sa, salen, (uintptr_t)nb->buf) < 0) 58 return (-1); 59 60 switch (sa.ss_family) { 61 case AF_INET: 62 s_in = (struct sockaddr_in *)(void *)&sa; 63 mdb_nhconvert(&port, &s_in->sin_port, sizeof (port)); 64 mdb_snprintf(buf, bufsz, "%I/%d", 65 s_in->sin_addr.s_addr, port); 66 break; 67 68 case AF_INET6: 69 s_in6 = (struct sockaddr_in6 *)(void *)&sa; 70 mdb_nhconvert(&port, &s_in6->sin6_port, sizeof (port)); 71 mdb_snprintf(buf, bufsz, "%N/%d", 72 &(s_in6->sin6_addr), port); 73 break; 74 75 default: 76 mdb_printf("AF_%d", sa.ss_family); 77 break; 78 } 79 80 return (0); 81 } 82 83 /* 84 * Get the name for an enum value 85 */ 86 static void 87 get_enum(char *obuf, size_t size, const char *type_str, int val, 88 const char *prefix) 89 { 90 mdb_ctf_id_t type_id; 91 const char *cp; 92 93 if (mdb_ctf_lookup_by_name(type_str, &type_id) != 0) 94 goto errout; 95 if (mdb_ctf_type_resolve(type_id, &type_id) != 0) 96 goto errout; 97 if ((cp = mdb_ctf_enum_name(type_id, val)) == NULL) 98 goto errout; 99 if (prefix != NULL) { 100 size_t len = strlen(prefix); 101 if (strncmp(cp, prefix, len) == 0) 102 cp += len; 103 } 104 (void) strlcpy(obuf, cp, size); 105 return; 106 107 errout: 108 mdb_snprintf(obuf, size, "? (%d)", val); 109 } 110 111 static const mdb_bitmask_t 112 host_flag_bits[] = { 113 { 114 "MONITORED", 115 NLM_NH_MONITORED, 116 NLM_NH_MONITORED }, 117 { 118 "RECLAIM", 119 NLM_NH_RECLAIM, 120 NLM_NH_RECLAIM }, 121 { 122 "INIDLE", 123 NLM_NH_INIDLE, 124 NLM_NH_INIDLE }, 125 { 126 "SUSPEND", 127 NLM_NH_SUSPEND, 128 NLM_NH_SUSPEND }, 129 { 130 NULL, 0, 0 } 131 }; 132 133 /* 134 * **************************************************************** 135 * NLM zones (top level) 136 */ 137 138 /* 139 * nlm_zone walker implementation 140 */ 141 142 int 143 nlm_zone_walk_init(mdb_walk_state_t *wsp) 144 { 145 146 /* 147 * Technically, this is "cheating" with the knowledge that 148 * the TAILQ_HEAD link is at the beginning of this object. 149 */ 150 if (wsp->walk_addr == 0 && mdb_readsym(&wsp->walk_addr, 151 sizeof (wsp->walk_addr), "nlm_zones_list") == -1) { 152 mdb_warn("failed to read 'nlm_zones_list'"); 153 return (WALK_ERR); 154 } 155 156 return (WALK_NEXT); 157 } 158 159 int 160 nlm_zone_walk_step(mdb_walk_state_t *wsp) 161 { 162 struct nlm_globals g; 163 uintptr_t addr = wsp->walk_addr; 164 165 if (addr == 0) 166 return (WALK_DONE); 167 168 if (mdb_vread(&g, sizeof (g), addr) < 0) { 169 mdb_warn("failed to read nlm_globals at %p", addr); 170 return (WALK_ERR); 171 } 172 173 wsp->walk_addr = (uintptr_t)TAILQ_NEXT(&g, nlm_link); 174 return (wsp->walk_callback(addr, &g, wsp->walk_cbdata)); 175 } 176 177 /* 178 * nlm_zone dcmd implementation 179 */ 180 181 static void nlm_zone_print(uintptr_t, const struct nlm_globals *, uint_t); 182 183 void 184 nlm_zone_help(void) 185 { 186 mdb_printf("-v verbose information\n"); 187 } 188 189 int 190 nlm_zone_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 191 { 192 struct nlm_globals g; 193 char enum_val[32]; 194 uint_t opt_v = FALSE; 195 196 if (mdb_getopts(argc, argv, 197 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 198 return (DCMD_USAGE); 199 200 if ((flags & DCMD_ADDRSPEC) == 0) { 201 mdb_warn("requires addr of nlm_zone"); 202 return (DCMD_ERR); 203 } 204 205 if (mdb_vread(&g, sizeof (g), addr) == -1) { 206 mdb_warn("failed to read nlm_globals at %p", addr); 207 return (DCMD_ERR); 208 } 209 210 if (opt_v == FALSE) { 211 nlm_zone_print(addr, &g, flags); 212 return (DCMD_OK); 213 } 214 215 /* 216 * Print verbose format 217 */ 218 mdb_printf("%<b>%<u>NLM zone globals (%p):%</u>%</b>\n", addr); 219 mdb_printf(" Lockd PID: %u\n", g.lockd_pid); 220 get_enum(enum_val, sizeof (enum_val), 221 "nlm_run_status_t", g.run_status, "NLM_S_"); 222 mdb_printf("Run status: %d (%s)\n", g.run_status, enum_val); 223 mdb_printf(" NSM state: %d\n", g.nsm_state); 224 225 return (DCMD_OK); 226 } 227 228 /* 229 * Shared by nlm_zone_dcmd and nlm_list_zone_cb 230 * Print a zone (nlm_globals) summary line. 231 */ 232 static void 233 nlm_zone_print(uintptr_t addr, const struct nlm_globals *g, uint_t flags) 234 { 235 236 if (DCMD_HDRSPEC(flags)) { 237 mdb_printf( 238 "%<b>%<u>%?-s %-16s %</u>%</b>\n", 239 "nlm_globals", "pid"); 240 } 241 242 mdb_printf("%-?p %6d\n", addr, (int)g->lockd_pid); 243 } 244 245 /* 246 * **************************************************************** 247 * NLM hosts (under zones) 248 */ 249 250 /* 251 * nlm_host walker implementation 252 */ 253 254 int 255 nlm_host_walk_init(mdb_walk_state_t *wsp) 256 { 257 static int avl_off = -1; 258 259 if (wsp->walk_addr == 0) { 260 mdb_printf("requires address of struct nlm_globals\n"); 261 return (WALK_ERR); 262 } 263 264 /* 265 * Need the address of the nlm_hosts_tree AVL head 266 * within the nlm_globals, for the AVL walker. 267 */ 268 if (avl_off < 0) { 269 avl_off = mdb_ctf_offsetof_by_name( 270 "struct nlm_globals", "nlm_hosts_tree"); 271 } 272 if (avl_off < 0) { 273 mdb_warn("cannot lookup: nlm_globals .nlm_hosts_tree"); 274 return (WALK_ERR); 275 } 276 wsp->walk_addr += avl_off; 277 278 if (mdb_layered_walk("avl", wsp) == -1) { 279 mdb_warn("failed to walk nlm_globals .nlm_hosts_tree"); 280 return (WALK_ERR); 281 } 282 283 return (WALK_NEXT); 284 } 285 286 int 287 nlm_host_walk_step(mdb_walk_state_t *wsp) 288 { 289 struct nlm_host nh; 290 uintptr_t addr = wsp->walk_addr; 291 292 if (mdb_vread(&nh, sizeof (nh), addr) < 0) { 293 mdb_warn("failed to read nlm_host at %p", addr); 294 return (WALK_ERR); 295 } 296 297 /* layered walk avl */ 298 return (wsp->walk_callback(wsp->walk_addr, &nh, 299 wsp->walk_cbdata)); 300 } 301 302 /* 303 * nlm_host dcmd implementation 304 */ 305 306 static void nlm_host_print(uintptr_t, const struct nlm_host *, 307 char *, char *, uint_t); 308 309 void 310 nlm_host_help(void) 311 { 312 mdb_printf("-v verbose information\n"); 313 } 314 315 int 316 nlm_host_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 317 { 318 struct nlm_host nh; 319 char hname[NLM_MAXNAMELEN]; 320 char haddr[NLM_MAXADDRSTR]; 321 uint_t opt_v = FALSE; 322 323 if (mdb_getopts(argc, argv, 324 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 325 return (DCMD_USAGE); 326 327 if ((flags & DCMD_ADDRSPEC) == 0) { 328 mdb_warn("requires addr of nlm_host"); 329 return (DCMD_ERR); 330 } 331 332 /* Get the nlm_host */ 333 if (mdb_vread(&nh, sizeof (nh), addr) == -1) { 334 mdb_warn("failed to read nlm_host at %p", addr); 335 return (DCMD_ERR); 336 } 337 338 /* Get its name and address */ 339 if (mdb_readstr(hname, sizeof (hname), 340 (uintptr_t)nh.nh_name) < 0) 341 strlcpy(hname, "?", sizeof (hname)); 342 if (nlm_netbuf_str(haddr, sizeof (haddr), &nh.nh_addr) < 0) 343 strlcpy(haddr, "?", sizeof (haddr)); 344 345 if (opt_v == FALSE) { 346 nlm_host_print(addr, &nh, hname, haddr, flags); 347 return (DCMD_OK); 348 } 349 350 /* 351 * Print verbose format 352 */ 353 354 mdb_printf("%<b>%<u>NLM host (%p):%</u>%</b>\n", addr); 355 356 mdb_printf("Refcnt: %u\n", nh.nh_refs); 357 mdb_printf(" Sysid: %d\n", (int)nh.nh_sysid); 358 mdb_printf(" Name: %s\n", hname); 359 mdb_printf(" Addr: %s\n", haddr); 360 mdb_printf(" State: %d\n", nh.nh_state); 361 mdb_printf(" Flags: 0x%x <%b>\n", 362 nh.nh_flags, nh.nh_flags, host_flag_bits); 363 mdb_printf("Vholds: %?p\n", nh.nh_vholds_list.tqh_first); 364 365 return (DCMD_OK); 366 } 367 368 /* 369 * Shared by nlm_host_dcmd and nlm_list_host_cb 370 * Print an nlm_host summary line. 371 */ 372 static void 373 nlm_host_print(uintptr_t addr, const struct nlm_host *nh, 374 char *hname, char *haddr, uint_t flags) 375 { 376 int hname_width = 20; 377 378 if (DCMD_HDRSPEC(flags)) { 379 mdb_printf("%<b>%<u>%-?s %-*s%10s %6s ", "nlm_host", 380 hname_width, "name", "refs", "sysid"); 381 mdb_printf("%s%</u>%</b>\n", "net_addr"); 382 } 383 384 mdb_printf("%?p %-*s%10i %6hi %s\n", 385 addr, hname_width, hname, 386 nh->nh_refs, nh->nh_sysid, haddr); 387 } 388 389 /* 390 * **************************************************************** 391 * NLM vholds (under hosts) 392 */ 393 394 /* 395 * nlm_vhold walker implementation 396 */ 397 398 int 399 nlm_vhold_walk_init(mdb_walk_state_t *wsp) 400 { 401 struct nlm_vhold_list head; 402 uintptr_t addr; 403 static int head_off = -1; 404 405 if (wsp->walk_addr == 0) { 406 mdb_printf("requires address of struct nlm_host\n"); 407 return (WALK_ERR); 408 } 409 410 /* Get offset of the list head and read it. */ 411 if (head_off < 0) { 412 head_off = mdb_ctf_offsetof_by_name( 413 "struct nlm_host", "nh_vholds_list"); 414 } 415 if (head_off < 0) { 416 mdb_warn("cannot lookup: nlm_host .nh_vholds_list"); 417 return (WALK_ERR); 418 } 419 420 addr = wsp->walk_addr + head_off; 421 if (mdb_vread(&head, sizeof (head), addr) < 0) { 422 mdb_warn("cannot read nlm_host at %p", wsp->walk_addr); 423 return (WALK_ERR); 424 } 425 426 wsp->walk_addr = (uintptr_t)head.tqh_first; 427 return (WALK_NEXT); 428 } 429 430 int 431 nlm_vhold_walk_step(mdb_walk_state_t *wsp) 432 { 433 struct nlm_vhold nv; 434 uintptr_t addr = wsp->walk_addr; 435 436 if (addr == 0) 437 return (WALK_DONE); 438 439 if (mdb_vread(&nv, sizeof (nv), addr) < 0) { 440 mdb_warn("failed to read nlm_vhold at %p", addr); 441 return (WALK_ERR); 442 } 443 444 wsp->walk_addr = (uintptr_t)nv.nv_link.tqe_next; 445 return (wsp->walk_callback(addr, &nv, wsp->walk_cbdata)); 446 } 447 448 /* 449 * nlm_vhold dcmd implementation 450 */ 451 452 static void nlm_vhold_print(uintptr_t, const struct nlm_vhold *, uint_t); 453 454 void 455 nlm_vhold_help(void) 456 { 457 mdb_printf("-v verbose information\n"); 458 } 459 460 int 461 nlm_vhold_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 462 { 463 struct nlm_vhold nv; 464 char path_buf[MAXPATHLEN]; 465 uint_t opt_v = FALSE; 466 467 if (mdb_getopts(argc, argv, 468 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 469 return (DCMD_USAGE); 470 471 if ((flags & DCMD_ADDRSPEC) == 0) { 472 mdb_warn("requires addr of nlm_vhold"); 473 return (DCMD_ERR); 474 } 475 476 if (mdb_vread(&nv, sizeof (nv), addr) == -1) { 477 mdb_warn("failed to read nlm_vhold at %p", addr); 478 return (DCMD_ERR); 479 } 480 481 if (opt_v == FALSE) { 482 nlm_vhold_print(addr, &nv, flags); 483 return (DCMD_OK); 484 } 485 486 /* 487 * Print verbose format 488 */ 489 490 if (nv.nv_vp == NULL || mdb_vnode2path((uintptr_t)nv.nv_vp, 491 path_buf, sizeof (path_buf)) != 0) 492 strlcpy(path_buf, "?", sizeof (path_buf)); 493 494 mdb_printf("%<b>%<u>NLM vhold (%p):%</u>%</b>\n", addr); 495 496 mdb_printf("Refcnt: %u\n", nv.nv_refcnt); 497 mdb_printf(" Vnode: %?p (%s)\n", nv.nv_vp, path_buf); 498 mdb_printf(" Slreq: %?p\n", nv.nv_slreqs.tqh_first); 499 500 return (DCMD_OK); 501 } 502 503 /* 504 * Shared by nlm_vhold_dcmd and nlm_list_vnode_cb 505 * Print an nlm_vhold summary line. 506 */ 507 static void 508 nlm_vhold_print(uintptr_t addr, const struct nlm_vhold *nv, uint_t flags) 509 { 510 511 if (DCMD_HDRSPEC(flags)) { 512 mdb_printf("%<b>%<u>%-?s %10s %-?s %-?s%</u>%</b>\n", 513 "nlm_vhold", "refcnt", "vnode", "slreq"); 514 } 515 516 mdb_printf("%?p %10i %?p %?-p\n", 517 addr, nv->nv_refcnt, nv->nv_vp, 518 nv->nv_slreqs.tqh_first); 519 } 520 521 /* 522 * **************************************************************** 523 * NLM slreqs (under vhold) 524 */ 525 526 /* 527 * nlm_slreq walker implementation 528 */ 529 530 int 531 nlm_slreq_walk_init(mdb_walk_state_t *wsp) 532 { 533 struct nlm_slreq_list head; 534 uintptr_t addr; 535 static int head_off = -1; 536 537 if (wsp->walk_addr == 0) { 538 mdb_printf("requires address of struct nlm_vhold\n"); 539 return (WALK_ERR); 540 } 541 542 /* Get offset of the list head and read it. */ 543 if (head_off < 0) { 544 head_off = mdb_ctf_offsetof_by_name( 545 "struct nlm_vhold", "nv_slreqs"); 546 } 547 if (head_off < 0) { 548 mdb_warn("cannot lookup: nlm_vhold .nv_slreqs"); 549 return (WALK_ERR); 550 } 551 552 addr = wsp->walk_addr + head_off; 553 if (mdb_vread(&head, sizeof (head), addr) < 0) { 554 mdb_warn("cannot read nlm_vhold at %p", wsp->walk_addr); 555 return (WALK_ERR); 556 } 557 558 wsp->walk_addr = (uintptr_t)head.tqh_first; 559 return (WALK_NEXT); 560 } 561 562 int 563 nlm_slreq_walk_step(mdb_walk_state_t *wsp) 564 { 565 struct nlm_slreq nsr; 566 uintptr_t addr = wsp->walk_addr; 567 568 if (addr == 0) 569 return (WALK_DONE); 570 571 if (mdb_vread(&nsr, sizeof (nsr), addr) < 0) { 572 mdb_warn("failed to read nlm_slreq at %p", addr); 573 return (WALK_ERR); 574 } 575 576 wsp->walk_addr = (uintptr_t)nsr.nsr_link.tqe_next; 577 return (wsp->walk_callback(addr, &nsr, wsp->walk_cbdata)); 578 } 579 580 /* 581 * nlm_slreq dcmd implementation 582 */ 583 584 static void nlm_slreq_print(uintptr_t, const struct nlm_slreq *, uint_t); 585 586 void 587 nlm_slreq_help(void) 588 { 589 mdb_printf("-v verbose information\n"); 590 } 591 592 int 593 nlm_slreq_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 594 { 595 struct nlm_slreq nsr; 596 uint_t opt_v = FALSE; 597 598 if (mdb_getopts(argc, argv, 599 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 600 return (DCMD_USAGE); 601 602 if ((flags & DCMD_ADDRSPEC) == 0) { 603 mdb_warn("requires addr of nlm_slreq"); 604 return (DCMD_ERR); 605 } 606 607 if (mdb_vread(&nsr, sizeof (nsr), addr) == -1) { 608 mdb_warn("failed to read nlm_slreq at %p", addr); 609 return (DCMD_ERR); 610 } 611 612 if (opt_v == FALSE) { 613 nlm_slreq_print(addr, &nsr, flags); 614 return (DCMD_OK); 615 } 616 617 /* 618 * Print verbose format 619 */ 620 621 mdb_printf("%<b>%<u>NLM slreq (%p):%</u>%</b>\n", addr); 622 623 mdb_printf(" type: %d (%s)\n", nsr.nsr_fl.l_type, 624 (nsr.nsr_fl.l_type == F_RDLCK) ? "RD" : 625 (nsr.nsr_fl.l_type == F_WRLCK) ? "WR" : "??"); 626 mdb_printf("sysid: %d\n", nsr.nsr_fl.l_sysid); 627 mdb_printf(" pid: %d\n", nsr.nsr_fl.l_pid); 628 mdb_printf("start: %lld\n", nsr.nsr_fl.l_start); 629 mdb_printf(" len: %lld\n", nsr.nsr_fl.l_len); 630 631 return (DCMD_OK); 632 } 633 634 /* 635 * Shared by nlm_slreq_dcmd and nlm_list_slreq_cb 636 * Print an nlm_slreq summary line. 637 */ 638 static void 639 nlm_slreq_print(uintptr_t addr, const struct nlm_slreq *nsr, uint_t flags) 640 { 641 642 if (DCMD_HDRSPEC(flags)) { 643 mdb_printf("%<b>%<u>%-?s %4s %5s %3s %6s %6s%</u>%</b>\n", 644 "nlm_slreq", "type", "sysid", "pid", "start", "len"); 645 } 646 647 mdb_printf( 648 "%?p %4d %5d %3d %6lld %6lld\n", 649 addr, 650 nsr->nsr_fl.l_type, 651 nsr->nsr_fl.l_sysid, 652 nsr->nsr_fl.l_pid, 653 nsr->nsr_fl.l_start, 654 nsr->nsr_fl.l_len); 655 } 656 657 /* 658 * **************************************************************** 659 */ 660 661 /* 662 * nlm_list dcmd implementation 663 * 664 * This is a fancy command command to walk the whole NLM 665 * data hierarchy, skipping uninteresting elements. 666 */ 667 668 #define NLM_LIST_DEPTH_HOSTS 1 /* just hosts */ 669 #define NLM_LIST_DEPTH_VHOLDS 2 /* host and vholds */ 670 #define NLM_LIST_DEPTH_SLREQS 3 /* sleeping lock requests */ 671 #define NLM_LIST_DEPTH_DEFAULT 3 /* default: show all */ 672 673 struct nlm_list_arg { 674 uint_t opt_v; 675 uint_t opt_a; 676 uint_t depth; 677 int sysid; 678 char *host; 679 uint_t zone_flags; 680 uint_t host_flags; 681 uint_t vhold_flags; 682 uint_t slreq_flags; 683 char namebuf[NLM_MAXNAMELEN]; 684 char addrbuf[NLM_MAXADDRSTR]; 685 }; 686 687 static int nlm_list_zone_cb(uintptr_t, const void *, void *); 688 static int nlm_list_host_cb(uintptr_t, const void *, void *); 689 static int nlm_list_vhold_cb(uintptr_t, const void *, void *); 690 static int nlm_list_slreq_cb(uintptr_t, const void *, void *); 691 692 void 693 nlm_list_help(void) 694 { 695 mdb_printf("-v verbose information\n"); 696 mdb_printf("-a include idle hosts\n"); 697 mdb_printf("-d depth recursion depth (zones, hosts, ...)\n"); 698 mdb_printf("-h host filter by host name\n"); 699 mdb_printf("-s sysid filter by sysid (0tnnn for decimal)\n"); 700 } 701 702 int 703 nlm_list_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 704 { 705 struct nlm_list_arg *arg; 706 uintptr_t depth = NLM_LIST_DEPTH_DEFAULT; 707 char *host = NULL; 708 char *sysid = NULL; 709 710 if ((flags & DCMD_ADDRSPEC) != 0) 711 return (DCMD_USAGE); 712 713 arg = mdb_zalloc(sizeof (*arg), UM_SLEEP | UM_GC); 714 715 if (mdb_getopts(argc, argv, 716 'v', MDB_OPT_SETBITS, TRUE, &arg->opt_v, 717 'a', MDB_OPT_SETBITS, TRUE, &arg->opt_a, 718 'd', MDB_OPT_UINTPTR, &depth, 719 'h', MDB_OPT_STR, &host, 720 's', MDB_OPT_STR, &sysid, 721 NULL) != argc) 722 return (DCMD_USAGE); 723 724 arg->depth = (uint_t)depth; 725 arg->sysid = -1; 726 if (host != NULL) 727 arg->host = host; 728 if (sysid != NULL) { 729 arg->sysid = (int)mdb_strtoull(sysid); 730 if (arg->sysid < 1) { 731 mdb_warn("invalid sysid"); 732 arg->sysid = -1; 733 } 734 } 735 736 /* Specifying host or sysid id implies -a */ 737 if (arg->host != NULL || arg->sysid >= 0) 738 arg->opt_a = TRUE; 739 740 arg->zone_flags = (DCMD_LOOP | DCMD_LOOPFIRST); 741 if (mdb_pwalk("nlm_zone", nlm_list_zone_cb, arg, 0)) { 742 mdb_warn("cannot walk nlm_zone list"); 743 return (DCMD_ERR); 744 } 745 746 return (DCMD_OK); 747 } 748 749 /* Called for each zone's nlm_globals */ 750 static int 751 nlm_list_zone_cb(uintptr_t addr, const void *data, void *cb_data) 752 { 753 struct nlm_list_arg *arg = cb_data; 754 const struct nlm_globals *g = data; 755 756 /* Add zone filtering? */ 757 758 /* 759 * Summary line for a struct nlm_globals 760 */ 761 nlm_zone_print(addr, g, 0); 762 arg->zone_flags &= ~DCMD_LOOPFIRST; 763 764 if (arg->depth >= NLM_LIST_DEPTH_HOSTS) { 765 (void) mdb_inc_indent(2); 766 arg->host_flags = (DCMD_LOOP | DCMD_LOOPFIRST); 767 if (mdb_pwalk("nlm_host", nlm_list_host_cb, arg, addr) != 0) { 768 mdb_warn("failed to walk hosts for zone %p", addr); 769 /* keep going */ 770 } 771 (void) mdb_dec_indent(2); 772 } 773 774 return (WALK_NEXT); 775 } 776 777 /* Called for each nlm_host */ 778 static int 779 nlm_list_host_cb(uintptr_t addr, const void *data, void *cb_data) 780 { 781 struct nlm_list_arg *arg = cb_data; 782 const struct nlm_host *nh = data; 783 784 /* Get the host name and net addr. */ 785 if (mdb_readstr(arg->namebuf, NLM_MAXNAMELEN, 786 (uintptr_t)nh->nh_name) < 0) 787 (void) strlcpy(arg->namebuf, "?", sizeof (char)); 788 if (nlm_netbuf_str(arg->addrbuf, NLM_MAXADDRSTR, &nh->nh_addr) < 0) 789 (void) strlcpy(arg->addrbuf, "?", sizeof (char)); 790 791 /* Filter out uninteresting hosts */ 792 if (arg->opt_a == 0 && nh->nh_refs == 0) 793 return (WALK_NEXT); 794 if (arg->sysid != -1 && arg->sysid != (nh->nh_sysid & LM_SYSID_MAX)) 795 return (WALK_NEXT); 796 if (arg->host != NULL && strcmp(arg->host, arg->namebuf) != 0) 797 return (WALK_NEXT); 798 799 /* 800 * Summary line for struct nlm_host 801 */ 802 nlm_host_print(addr, nh, arg->namebuf, arg->addrbuf, 803 arg->host_flags); 804 arg->host_flags &= ~DCMD_LOOPFIRST; 805 806 if (arg->depth >= NLM_LIST_DEPTH_VHOLDS) { 807 (void) mdb_inc_indent(2); 808 arg->vhold_flags = (DCMD_LOOP | DCMD_LOOPFIRST); 809 if (mdb_pwalk("nlm_vhold", nlm_list_vhold_cb, arg, addr)) { 810 mdb_warn("failed to walk vholds for host %p", addr); 811 /* keep going */ 812 } 813 (void) mdb_dec_indent(2); 814 } 815 816 /* 817 * We printed some hosts, so tell nlm_list_zone_cb to 818 * print its header line again. 819 */ 820 arg->zone_flags |= DCMD_LOOPFIRST; 821 822 return (WALK_NEXT); 823 } 824 825 /* Called for each nlm_vhold */ 826 static int 827 nlm_list_vhold_cb(uintptr_t addr, const void *data, void *cb_data) 828 { 829 struct nlm_list_arg *arg = cb_data; 830 const struct nlm_vhold *nv = data; 831 832 /* Filter out uninteresting vholds */ 833 if (arg->opt_a == 0 && nv->nv_refcnt == 0) 834 return (WALK_NEXT); 835 836 /* 837 * Summary line for struct nlm_vhold 838 */ 839 nlm_vhold_print(addr, nv, arg->vhold_flags); 840 arg->vhold_flags &= ~DCMD_LOOPFIRST; 841 842 if (arg->depth >= NLM_LIST_DEPTH_SLREQS) { 843 (void) mdb_inc_indent(2); 844 arg->slreq_flags = (DCMD_LOOP | DCMD_LOOPFIRST); 845 if (mdb_pwalk("nlm_slreq", nlm_list_slreq_cb, arg, addr)) { 846 mdb_warn("failed to walk slreqs for vhold %p", addr); 847 /* keep going */ 848 } 849 (void) mdb_dec_indent(2); 850 } 851 852 /* 853 * We printed some vholds, so tell nlm_list_host_cb to 854 * print its header line again. 855 */ 856 arg->host_flags |= DCMD_LOOPFIRST; 857 858 return (WALK_NEXT); 859 } 860 861 /* Called for each nlm_slreq */ 862 static int 863 nlm_list_slreq_cb(uintptr_t addr, const void *data, void *cb_data) 864 { 865 struct nlm_list_arg *arg = cb_data; 866 const struct nlm_slreq *nv = data; 867 868 /* 869 * Summary line for struct nlm_slreq 870 */ 871 nlm_slreq_print(addr, nv, arg->slreq_flags); 872 arg->slreq_flags &= ~DCMD_LOOPFIRST; 873 874 /* 875 * We printed some slreqs, so tell nlm_list_vhold_cb to 876 * print its header line again. 877 */ 878 arg->vhold_flags |= DCMD_LOOPFIRST; 879 880 return (WALK_NEXT); 881 } 882 883 /* 884 * **************************************************************** 885 */ 886 887 /* 888 * nlm_lockson dcmd implementation 889 * Walk the lock_graph, filtered by sysid 890 */ 891 892 struct nlm_locks_arg { 893 /* dcmd options */ 894 uint_t opt_v; 895 int sysid; 896 char *host; 897 /* callback vars */ 898 uint_t flags; 899 int lg_sysid; 900 char namebuf[NLM_MAXNAMELEN]; 901 char addrbuf[NLM_MAXADDRSTR]; 902 char pathbuf[PATH_MAX]; 903 }; 904 905 static int nlm_locks_zone_cb(uintptr_t, const void *, void *); 906 static int nlm_locks_host_cb(uintptr_t, const void *, void *); 907 static int nlm_lockson_cb(uintptr_t, const void *, void *c); 908 909 void 910 nlm_lockson_help(void) 911 { 912 mdb_printf("-v verbose information\n"); 913 mdb_printf("-h host filter by host name\n"); 914 mdb_printf("-s sysid filter by sysid (0tnnn for decimal)\n"); 915 } 916 917 int 918 nlm_lockson_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 919 { 920 struct nlm_locks_arg *arg; 921 char *host = NULL; 922 char *sysid = NULL; 923 924 if ((flags & DCMD_ADDRSPEC) != 0) 925 return (DCMD_USAGE); 926 927 arg = mdb_zalloc(sizeof (*arg), UM_SLEEP | UM_GC); 928 929 if (mdb_getopts(argc, argv, 930 'v', MDB_OPT_SETBITS, TRUE, &arg->opt_v, 931 'h', MDB_OPT_STR, &host, 932 's', MDB_OPT_STR, &sysid, 933 NULL) != argc) 934 return (DCMD_USAGE); 935 936 arg->sysid = -1; 937 if (host != NULL) 938 arg->host = host; 939 if (sysid != NULL) { 940 arg->sysid = (int)mdb_strtoull(sysid); 941 if (arg->sysid < 1) { 942 mdb_warn("invalid sysid"); 943 arg->sysid = -1; 944 } 945 } 946 947 if (mdb_pwalk("nlm_zone", nlm_locks_zone_cb, arg, 0)) { 948 mdb_warn("cannot walk nlm_zone list"); 949 return (DCMD_ERR); 950 } 951 952 return (DCMD_OK); 953 } 954 955 /* Called for each zone's nlm_globals */ 956 static int 957 nlm_locks_zone_cb(uintptr_t addr, const void *data, void *cb_data) 958 { 959 struct nlm_locks_arg *arg = cb_data; 960 (void) data; 961 962 /* 963 * No filtering here. Don't even print zone addr. 964 * Just run the host list walker. 965 */ 966 if (mdb_pwalk("nlm_host", nlm_locks_host_cb, arg, addr) != 0) { 967 mdb_warn("failed to walk hosts for zone %p", addr); 968 /* keep going */ 969 } 970 971 return (WALK_NEXT); 972 } 973 974 /* Called for each nlm_host */ 975 static int 976 nlm_locks_host_cb(uintptr_t addr, const void *data, void *cb_data) 977 { 978 struct nlm_locks_arg *arg = cb_data; 979 const struct nlm_host *nh = data; 980 981 /* Get the host name and net addr. */ 982 if (mdb_readstr(arg->namebuf, NLM_MAXNAMELEN, 983 (uintptr_t)nh->nh_name) < 0) 984 (void) strlcpy(arg->namebuf, "?", sizeof (char)); 985 if (nlm_netbuf_str(arg->addrbuf, NLM_MAXADDRSTR, &nh->nh_addr) < 0) 986 (void) strlcpy(arg->addrbuf, "?", sizeof (char)); 987 988 /* Filter out uninteresting hosts */ 989 if (arg->sysid != -1 && arg->sysid != (nh->nh_sysid & LM_SYSID_MAX)) 990 return (WALK_NEXT); 991 if (arg->host != NULL && strcmp(arg->host, arg->namebuf) != 0) 992 return (WALK_NEXT); 993 994 /* 995 * Summary line for struct nlm_host 996 */ 997 nlm_host_print(addr, nh, arg->namebuf, arg->addrbuf, 0); 998 999 /* 1000 * We run the lock_graph walker for every sysid, and the callback 1001 * uses arg->lg_sysid to filter graph elements. Set that from 1002 * the host we're visiting now. 1003 */ 1004 arg->lg_sysid = (int)nh->nh_sysid; 1005 arg->flags = (DCMD_LOOP | DCMD_LOOPFIRST); 1006 if (mdb_pwalk("lock_graph", nlm_lockson_cb, arg, 0) < 0) { 1007 mdb_warn("failed to walk lock_graph"); 1008 return (WALK_ERR); 1009 } 1010 1011 return (WALK_NEXT); 1012 } 1013 1014 static int 1015 nlm_lockson_cb(uintptr_t addr, const void *data, void *cb_data) 1016 { 1017 struct nlm_locks_arg *arg = cb_data; 1018 const lock_descriptor_t *ld = data; 1019 proc_t p; 1020 int local, sysid; 1021 int host_width = 16; 1022 char *s; 1023 1024 local = ld->l_flock.l_sysid & LM_SYSID_CLIENT; 1025 sysid = ld->l_flock.l_sysid & LM_SYSID_MAX; 1026 1027 if (arg->lg_sysid != sysid) 1028 return (WALK_NEXT); 1029 1030 if (DCMD_HDRSPEC(arg->flags)) { 1031 mdb_printf("%<b>%<u>%-?s %-*s %5s(x) %-?s %-6s %-*s %-*s type", 1032 "lock_addr", host_width, "host", "sysid", "vnode", "pid", 1033 MAXCOMLEN, "cmd", arg->opt_v ? 9 : 5, "state"); 1034 1035 if (arg->opt_v) 1036 mdb_printf("%-11s srvstat %-10s", "(width)", "path"); 1037 1038 mdb_printf("%</u>%</b>\n"); 1039 } 1040 arg->flags &= ~DCMD_LOOPFIRST; 1041 1042 mdb_printf("%?p %-*s %5hi(%c) %?p %-6i %-*s ", 1043 addr, host_width, arg->namebuf, 1044 sysid, local ? 'L' : 'R', ld->l_vnode, 1045 ld->l_flock.l_pid, MAXCOMLEN, 1046 ld->l_flock.l_pid == 0 ? "<kernel>" 1047 : !local ? "<remote>" 1048 : mdb_pid2proc(ld->l_flock.l_pid, &p) == 0 ? "<defunct>" 1049 : p.p_user.u_comm); 1050 1051 if (arg->opt_v) { 1052 switch (ld->l_status) { 1053 case FLK_INITIAL_STATE: 1054 s = "init"; 1055 break; 1056 case FLK_START_STATE: 1057 s = "execute"; 1058 break; 1059 case FLK_ACTIVE_STATE: 1060 s = "active"; 1061 break; 1062 case FLK_SLEEPING_STATE: 1063 s = "blocked"; 1064 break; 1065 case FLK_GRANTED_STATE: 1066 s = "granted"; 1067 break; 1068 case FLK_INTERRUPTED_STATE: 1069 s = "interrupt"; 1070 break; 1071 case FLK_CANCELLED_STATE: 1072 s = "cancel"; 1073 break; 1074 case FLK_DEAD_STATE: 1075 s = "done"; 1076 break; 1077 default: 1078 s = "??"; 1079 break; 1080 } 1081 mdb_printf("%-9s", s); 1082 } else { 1083 mdb_printf("%-5i", ld->l_status); 1084 } 1085 1086 mdb_printf(" %-2s", ld->l_type == F_RDLCK ? "RD" 1087 : ld->l_type == F_WRLCK ? "WR" : "??"); 1088 1089 1090 if (!arg->opt_v) { 1091 mdb_printf("\n"); 1092 return (WALK_NEXT); 1093 } 1094 1095 switch (GET_NLM_STATE(ld)) { 1096 case FLK_NLM_UP: 1097 s = "up"; 1098 break; 1099 case FLK_NLM_SHUTTING_DOWN: 1100 s = "halting"; 1101 break; 1102 case FLK_NLM_DOWN: 1103 s = "down"; 1104 break; 1105 case FLK_NLM_UNKNOWN: 1106 s = "unknown"; 1107 break; 1108 default: 1109 s = "??"; 1110 break; 1111 } 1112 1113 mdb_printf("(%5i:%-5i) %-7s ", ld->l_start, ld->l_len, s); 1114 if (mdb_vnode2path((uintptr_t)ld->l_vnode, 1115 arg->pathbuf, PATH_MAX) == -1) 1116 strlcpy(arg->pathbuf, "??", PATH_MAX); 1117 mdb_printf("%s\n", arg->pathbuf); 1118 1119 return (WALK_NEXT); 1120 } 1121 1122 1123 static const mdb_walker_t walkers[] = { 1124 { 1125 "nlm_zone", "nlm_zone walker", 1126 nlm_zone_walk_init, nlm_zone_walk_step 1127 }, 1128 { 1129 "nlm_host", "nlm_host walker", 1130 nlm_host_walk_init, nlm_host_walk_step 1131 }, 1132 { 1133 "nlm_vhold", "nlm_vhold walker", 1134 nlm_vhold_walk_init, nlm_vhold_walk_step 1135 }, 1136 { 1137 "nlm_slreq", "nlm_slreq walker", 1138 nlm_slreq_walk_init, nlm_slreq_walk_step 1139 }, 1140 {NULL, NULL, NULL, NULL} 1141 }; 1142 1143 static const mdb_dcmd_t dcmds[] = { 1144 { 1145 "nlm_zone", "?[-v]", 1146 "dump per-zone nlm_globals", 1147 nlm_zone_dcmd, nlm_zone_help 1148 }, 1149 { 1150 "nlm_host", "?[-v]", 1151 "dump nlm_host structures (hosts/sysids)", 1152 nlm_host_dcmd, nlm_host_help 1153 }, 1154 { 1155 "nlm_vhold", "?[-v]", 1156 "dump nlm_vhold structures (vnode holds)", 1157 nlm_vhold_dcmd, nlm_vhold_help 1158 }, 1159 { 1160 "nlm_slreq", "?[-v]", 1161 "dump nlm_slreq structures (sleeping lock requests)", 1162 nlm_slreq_dcmd, nlm_slreq_help 1163 }, 1164 { 1165 "nlm_list", "[-v][-a][-d depth][-h host][-s 0tSysID]", 1166 "list all zones, optionally filter hosts ", 1167 nlm_list_dcmd, nlm_list_help 1168 }, 1169 { 1170 "nlm_lockson", "[-v] [-h host] [-s 0tSysID]", 1171 "dump NLM locks from host (or sysid)", 1172 nlm_lockson_dcmd, nlm_lockson_help 1173 }, 1174 {NULL, NULL, NULL, NULL} 1175 }; 1176 1177 static const mdb_modinfo_t modinfo = { 1178 MDB_API_VERSION, 1179 dcmds, 1180 walkers 1181 }; 1182 1183 const mdb_modinfo_t * 1184 _mdb_init(void) 1185 { 1186 return (&modinfo); 1187 } 1188