1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <limits.h> 27 #include <sys/mdb_modapi.h> 28 #include <sys/sysinfo.h> 29 #include <sys/scsi/scsi.h> 30 #include <sys/scsi/adapters/pmcs/pmcs.h> 31 32 #define MDB_RD(a, b, c) mdb_vread(a, b, (uintptr_t)c) 33 #define NOREAD(a, b) mdb_warn("could not read " #a " at 0x%p", b) 34 35 static pmcs_hw_t ss; 36 static pmcs_xscsi_t **targets = NULL; 37 static int target_idx; 38 39 static uint32_t sas_phys, sata_phys, exp_phys, num_expanders, empty_phys; 40 41 static pmcs_phy_t *pmcs_next_sibling(pmcs_phy_t *phyp); 42 static void display_one_work(pmcwork_t *wp, int verbose, int idx); 43 44 static void 45 print_sas_address(pmcs_phy_t *phy) 46 { 47 int idx; 48 49 for (idx = 0; idx < 8; idx++) { 50 mdb_printf("%02x", phy->sas_address[idx]); 51 } 52 } 53 54 /*ARGSUSED*/ 55 static void 56 display_ic(struct pmcs_hw m, int verbose) 57 { 58 int msec_per_tick; 59 60 if (mdb_readvar(&msec_per_tick, "msec_per_tick") == -1) { 61 mdb_warn("can't read msec_per_tick"); 62 msec_per_tick = 0; 63 } 64 65 mdb_printf("\n"); 66 mdb_printf("Interrupt coalescing timer info\n"); 67 mdb_printf("-------------------------------\n"); 68 if (msec_per_tick == 0) { 69 mdb_printf("Quantum : ?? ms\n"); 70 } else { 71 mdb_printf("Quantum : %d ms\n", 72 m.io_intr_coal.quantum * msec_per_tick); 73 } 74 mdb_printf("Timer enabled : "); 75 if (m.io_intr_coal.timer_on) { 76 mdb_printf("Yes\n"); 77 mdb_printf("Coalescing timer value : %d us\n", 78 m.io_intr_coal.intr_coal_timer); 79 } else { 80 mdb_printf("No\n"); 81 } 82 mdb_printf("Total nsecs between interrupts: %ld\n", 83 m.io_intr_coal.nsecs_between_intrs); 84 mdb_printf("Time of last I/O interrupt : %ld\n", 85 m.io_intr_coal.last_io_comp); 86 mdb_printf("Number of I/O interrupts : %d\n", 87 m.io_intr_coal.num_intrs); 88 mdb_printf("Number of I/O completions : %d\n", 89 m.io_intr_coal.num_io_completions); 90 mdb_printf("Max I/O completion interrupts : %d\n", 91 m.io_intr_coal.max_io_completions); 92 mdb_printf("Measured ECHO int latency : %d ns\n", 93 m.io_intr_coal.intr_latency); 94 mdb_printf("Interrupt threshold : %d\n", 95 m.io_intr_coal.intr_threshold); 96 } 97 98 /*ARGSUSED*/ 99 static int 100 pmcs_iport_phy_walk_cb(uintptr_t addr, const void *wdata, void *priv) 101 { 102 struct pmcs_phy phy; 103 104 if (mdb_vread(&phy, sizeof (struct pmcs_phy), addr) != 105 sizeof (struct pmcs_phy)) { 106 return (DCMD_ERR); 107 } 108 109 mdb_printf("%16p %2d\n", addr, phy.phynum); 110 111 return (0); 112 } 113 114 /*ARGSUSED*/ 115 static int 116 pmcs_iport_walk_cb(uintptr_t addr, const void *wdata, void *priv) 117 { 118 struct pmcs_iport iport; 119 uintptr_t list_addr; 120 char *ua_state; 121 char portid[4]; 122 char unit_address[34]; 123 124 if (mdb_vread(&iport, sizeof (struct pmcs_iport), addr) != 125 sizeof (struct pmcs_iport)) { 126 return (DCMD_ERR); 127 } 128 129 if (mdb_readstr(unit_address, sizeof (unit_address), 130 (uintptr_t)(iport.ua)) == -1) { 131 strncpy(unit_address, "Unset", sizeof (unit_address)); 132 } 133 134 if (iport.portid == 0xffff) { 135 mdb_snprintf(portid, sizeof (portid), "%s", "-"); 136 } else { 137 mdb_snprintf(portid, sizeof (portid), "%d", iport.portid); 138 } 139 140 switch (iport.ua_state) { 141 case UA_INACTIVE: 142 ua_state = "Inactive"; 143 break; 144 case UA_PEND_ACTIVATE: 145 ua_state = "PendActivate"; 146 break; 147 case UA_ACTIVE: 148 ua_state = "Active"; 149 break; 150 case UA_PEND_DEACTIVATE: 151 ua_state = "PendDeactivate"; 152 break; 153 default: 154 ua_state = "Unknown"; 155 } 156 157 if (strlen(unit_address) < 3) { 158 /* Standard iport unit address */ 159 mdb_printf("UA %-16s %16s %8s %8s %16s", "Iport", "UA State", 160 "PortID", "NumPhys", "DIP\n"); 161 mdb_printf("%2s %16p %16s %8s %8d %16p\n", unit_address, addr, 162 ua_state, portid, iport.nphy, iport.dip); 163 } else { 164 /* Temporary iport unit address */ 165 mdb_printf("%-32s %16s %20s %8s %8s %16s", "UA", "Iport", 166 "UA State", "PortID", "NumPhys", "DIP\n"); 167 mdb_printf("%32s %16p %20s %8s %8d %16p\n", unit_address, addr, 168 ua_state, portid, iport.nphy, iport.dip); 169 } 170 171 if (iport.nphy > 0) { 172 mdb_inc_indent(4); 173 mdb_printf("%-18s %8s", "Phy", "PhyNum\n"); 174 mdb_inc_indent(2); 175 list_addr = 176 (uintptr_t)(addr + offsetof(struct pmcs_iport, phys)); 177 if (mdb_pwalk("list", pmcs_iport_phy_walk_cb, NULL, 178 list_addr) == -1) { 179 mdb_warn("pmcs iport walk failed"); 180 } 181 mdb_dec_indent(6); 182 mdb_printf("\n"); 183 } 184 185 return (0); 186 } 187 188 /*ARGSUSED*/ 189 static void 190 display_iport(struct pmcs_hw m, uintptr_t addr, int verbose) 191 { 192 uintptr_t list_addr; 193 194 if (m.iports_attached) { 195 mdb_printf("Iport information:\n"); 196 mdb_printf("-----------------\n"); 197 } else { 198 mdb_printf("No Iports found.\n\n"); 199 return; 200 } 201 202 list_addr = (uintptr_t)(addr + offsetof(struct pmcs_hw, iports)); 203 204 if (mdb_pwalk("list", pmcs_iport_walk_cb, NULL, list_addr) == -1) { 205 mdb_warn("pmcs iport walk failed"); 206 } 207 208 mdb_printf("\n"); 209 } 210 211 static void 212 display_completion_queue(struct pmcs_hw ss) 213 { 214 pmcs_iocomp_cb_t ccb, *ccbp; 215 pmcwork_t work; 216 217 if (ss.iocomp_cb_head == NULL) { 218 mdb_printf("Completion queue is empty.\n"); 219 return; 220 } 221 222 ccbp = ss.iocomp_cb_head; 223 mdb_printf("%8s %10s %20s %8s %8s O D\n", 224 "HTag", "State", "Phy Path", "Target", "Timer"); 225 226 while (ccbp) { 227 if (mdb_vread(&ccb, sizeof (pmcs_iocomp_cb_t), 228 (uintptr_t)ccbp) != sizeof (pmcs_iocomp_cb_t)) { 229 mdb_warn("Unable to read completion queue entry\n"); 230 return; 231 } 232 233 if (mdb_vread(&work, sizeof (pmcwork_t), (uintptr_t)ccb.pwrk) 234 != sizeof (pmcwork_t)) { 235 mdb_warn("Unable to read work structure\n"); 236 return; 237 } 238 239 /* 240 * Only print the work structure if it's still active. If 241 * it's not, it's been completed since we started looking at 242 * it. 243 */ 244 if (work.state != PMCS_WORK_STATE_NIL) { 245 display_one_work(&work, 0, 0); 246 } 247 ccbp = ccb.next; 248 } 249 } 250 251 /*ARGSUSED*/ 252 static void 253 display_hwinfo(struct pmcs_hw m, int verbose) 254 { 255 struct pmcs_hw *mp = &m; 256 char *fwsupport; 257 258 switch (PMCS_FW_TYPE(mp)) { 259 case PMCS_FW_TYPE_RELEASED: 260 fwsupport = "Released"; 261 break; 262 case PMCS_FW_TYPE_DEVELOPMENT: 263 fwsupport = "Development"; 264 break; 265 case PMCS_FW_TYPE_ALPHA: 266 fwsupport = "Alpha"; 267 break; 268 case PMCS_FW_TYPE_BETA: 269 fwsupport = "Beta"; 270 break; 271 default: 272 fwsupport = "Special"; 273 break; 274 } 275 276 mdb_printf("\nHardware information:\n"); 277 mdb_printf("---------------------\n"); 278 279 mdb_printf("Chip revision: %c\n", 'A' + m.chiprev); 280 mdb_printf("SAS WWID: %"PRIx64"\n", m.sas_wwns[0]); 281 mdb_printf("Firmware version: %x.%x.%x (%s)\n", 282 PMCS_FW_MAJOR(mp), PMCS_FW_MINOR(mp), PMCS_FW_MICRO(mp), 283 fwsupport); 284 285 mdb_printf("Number of PHYs: %d\n", m.nphy); 286 mdb_printf("Maximum commands: %d\n", m.max_cmd); 287 mdb_printf("Maximum devices: %d\n", m.max_dev); 288 mdb_printf("I/O queue depth: %d\n", m.ioq_depth); 289 if (m.fwlog == 0) { 290 mdb_printf("Firmware logging: Disabled\n"); 291 } else { 292 mdb_printf("Firmware logging: Enabled (%d)\n", m.fwlog); 293 } 294 } 295 296 static void 297 display_targets(struct pmcs_hw m, int verbose, int totals_only) 298 { 299 char *dtype; 300 pmcs_xscsi_t xs; 301 pmcs_phy_t phy; 302 uint16_t max_dev, idx; 303 uint32_t sas_targets = 0, smp_targets = 0, sata_targets = 0; 304 305 max_dev = m.max_dev; 306 307 if (targets == NULL) { 308 targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP); 309 } 310 311 if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) { 312 NOREAD(targets, m.targets); 313 return; 314 } 315 316 if (!totals_only) { 317 mdb_printf("\nTarget information:\n"); 318 mdb_printf("---------------------------------------\n"); 319 mdb_printf("VTGT %-16s %-16s %-5s %8s %s", "SAS Address", 320 "PHY Address", "DType", "Active", "DS"); 321 mdb_printf("\n"); 322 } 323 324 for (idx = 0; idx < max_dev; idx++) { 325 if (targets[idx] == NULL) { 326 continue; 327 } 328 329 if (MDB_RD(&xs, sizeof (xs), targets[idx]) == -1) { 330 NOREAD(pmcs_xscsi_t, targets[idx]); 331 continue; 332 } 333 334 /* 335 * It has to be new or assigned to be of interest. 336 */ 337 if (xs.new == 0 && xs.assigned == 0) { 338 continue; 339 } 340 341 switch (xs.dtype) { 342 case NOTHING: 343 dtype = "None"; 344 break; 345 case SATA: 346 dtype = "SATA"; 347 sata_targets++; 348 break; 349 case SAS: 350 dtype = "SAS"; 351 sas_targets++; 352 break; 353 case EXPANDER: 354 dtype = "SMP"; 355 smp_targets++; 356 break; 357 } 358 359 if (totals_only) { 360 continue; 361 } 362 363 if (xs.phy) { 364 if (MDB_RD(&phy, sizeof (phy), xs.phy) == -1) { 365 NOREAD(pmcs_phy_t, xs.phy); 366 continue; 367 } 368 mdb_printf("%4d ", idx); 369 print_sas_address(&phy); 370 mdb_printf(" %16p", xs.phy); 371 } else { 372 mdb_printf("%4d %16s", idx, "<no phy avail>"); 373 } 374 mdb_printf(" %5s", dtype); 375 mdb_printf(" %8d", xs.actv_cnt); 376 mdb_printf(" %2d", xs.dev_state); 377 378 if (verbose) { 379 if (xs.new) { 380 mdb_printf(" new"); 381 } 382 if (xs.assigned) { 383 mdb_printf(" assigned"); 384 } 385 if (xs.draining) { 386 mdb_printf(" draining"); 387 } 388 if (xs.reset_wait) { 389 mdb_printf(" reset_wait"); 390 } 391 if (xs.resetting) { 392 mdb_printf(" resetting"); 393 } 394 if (xs.recover_wait) { 395 mdb_printf(" recover_wait"); 396 } 397 if (xs.recovering) { 398 mdb_printf(" recovering"); 399 } 400 if (xs.event_recovery) { 401 mdb_printf(" event recovery"); 402 } 403 if (xs.special_running) { 404 mdb_printf(" special_active"); 405 } 406 if (xs.ncq) { 407 mdb_printf(" ncq_tagmap=0x%x qdepth=%d", 408 xs.tagmap, xs.qdepth); 409 } else if (xs.pio) { 410 mdb_printf(" pio"); 411 } 412 } 413 414 mdb_printf("\n"); 415 } 416 417 if (!totals_only) { 418 mdb_printf("\n"); 419 } 420 421 mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n", 422 "Configured targets:", (sas_targets + sata_targets + smp_targets), 423 sas_targets, sata_targets, smp_targets); 424 } 425 426 static char * 427 work_state_to_string(uint32_t state) 428 { 429 char *state_string; 430 431 switch (state) { 432 case PMCS_WORK_STATE_NIL: 433 state_string = "Free"; 434 break; 435 case PMCS_WORK_STATE_READY: 436 state_string = "Ready"; 437 break; 438 case PMCS_WORK_STATE_ONCHIP: 439 state_string = "On Chip"; 440 break; 441 case PMCS_WORK_STATE_INTR: 442 state_string = "In Intr"; 443 break; 444 case PMCS_WORK_STATE_IOCOMPQ: 445 state_string = "I/O Comp"; 446 break; 447 case PMCS_WORK_STATE_ABORTED: 448 state_string = "I/O Aborted"; 449 break; 450 case PMCS_WORK_STATE_TIMED_OUT: 451 state_string = "I/O Timed Out"; 452 break; 453 default: 454 state_string = "INVALID"; 455 break; 456 } 457 458 return (state_string); 459 } 460 461 static void 462 display_one_work(pmcwork_t *wp, int verbose, int idx) 463 { 464 char *state, *last_state; 465 char *path; 466 pmcs_xscsi_t xs; 467 pmcs_phy_t phy; 468 int tgt; 469 470 state = work_state_to_string(wp->state); 471 last_state = work_state_to_string(wp->last_state); 472 473 if (wp->ssp_event && wp->ssp_event != 0xffffffff) { 474 mdb_printf("SSP event 0x%x", wp->ssp_event); 475 } 476 477 tgt = -1; 478 if (wp->xp) { 479 if (MDB_RD(&xs, sizeof (xs), wp->xp) == -1) { 480 NOREAD(pmcs_xscsi_t, wp->xp); 481 } else { 482 tgt = xs.target_num; 483 } 484 } 485 if (wp->phy) { 486 if (MDB_RD(&phy, sizeof (phy), wp->phy) == -1) { 487 NOREAD(pmcs_phy_t, wp->phy); 488 } 489 path = phy.path; 490 } else { 491 path = "N/A"; 492 } 493 494 if (verbose) { 495 mdb_printf("%4d ", idx); 496 } 497 if (tgt == -1) { 498 mdb_printf("%08x %10s %20s N/A %8u %1d %1d ", 499 wp->htag, state, path, wp->timer, 500 wp->onwire, wp->dead); 501 } else { 502 mdb_printf("%08x %10s %20s %8d %8u %1d %1d ", 503 wp->htag, state, path, tgt, wp->timer, 504 wp->onwire, wp->dead); 505 } 506 if (verbose) { 507 mdb_printf("%08x %10s 0x%016p 0x%016p\n", 508 wp->last_htag, last_state, wp->last_phy, wp->last_xp); 509 } else { 510 mdb_printf("\n"); 511 } 512 } 513 514 static void 515 display_work(struct pmcs_hw m, int verbose) 516 { 517 int idx; 518 boolean_t header_printed = B_FALSE; 519 pmcwork_t work, *wp = &work; 520 uintptr_t _wp; 521 522 mdb_printf("\nActive Work structure information:\n"); 523 mdb_printf("----------------------------------\n"); 524 525 _wp = (uintptr_t)m.work; 526 527 for (idx = 0; idx < m.max_cmd; idx++, _wp += sizeof (pmcwork_t)) { 528 if (MDB_RD(&work, sizeof (pmcwork_t), _wp) == -1) { 529 NOREAD(pmcwork_t, _wp); 530 continue; 531 } 532 533 if (!verbose && (wp->htag == PMCS_TAG_TYPE_FREE)) { 534 continue; 535 } 536 537 if (header_printed == B_FALSE) { 538 if (verbose) { 539 mdb_printf("%4s ", "Idx"); 540 } 541 mdb_printf("%8s %10s %20s %8s %8s O D ", 542 "HTag", "State", "Phy Path", "Target", "Timer"); 543 if (verbose) { 544 mdb_printf("%8s %10s %18s %18s\n", "LastHTAG", 545 "LastState", "LastPHY", "LastTgt"); 546 } else { 547 mdb_printf("\n"); 548 } 549 header_printed = B_TRUE; 550 } 551 552 display_one_work(wp, verbose, idx); 553 } 554 } 555 556 static void 557 print_spcmd(pmcs_cmd_t *sp, void *kaddr, int printhdr, int verbose) 558 { 559 int cdb_size, idx; 560 struct scsi_pkt pkt; 561 uchar_t cdb[256]; 562 563 if (printhdr) { 564 if (verbose) { 565 mdb_printf("%16s %16s %16s %8s %s CDB\n", "Command", 566 "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag"); 567 } else { 568 mdb_printf("%16s %16s %16s %8s %s\n", "Command", 569 "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag"); 570 } 571 } 572 573 mdb_printf("%16p %16p %16p %08x %08x ", 574 kaddr, sp->cmd_pkt, sp->cmd_clist, sp->cmd_tag, sp->cmd_satltag); 575 576 /* 577 * If we're printing verbose, dump the CDB as well. 578 */ 579 if (verbose) { 580 if (sp->cmd_pkt) { 581 if (mdb_vread(&pkt, sizeof (struct scsi_pkt), 582 (uintptr_t)sp->cmd_pkt) != 583 sizeof (struct scsi_pkt)) { 584 mdb_warn("Unable to read SCSI pkt\n"); 585 return; 586 } 587 cdb_size = pkt.pkt_cdblen; 588 if (mdb_vread(&cdb[0], cdb_size, 589 (uintptr_t)pkt.pkt_cdbp) != cdb_size) { 590 mdb_warn("Unable to read CDB\n"); 591 return; 592 } 593 594 for (idx = 0; idx < cdb_size; idx++) { 595 mdb_printf("%02x ", cdb[idx]); 596 } 597 } else { 598 mdb_printf("N/A"); 599 } 600 601 mdb_printf("\n"); 602 } else { 603 mdb_printf("\n"); 604 } 605 } 606 607 /*ARGSUSED1*/ 608 static void 609 display_waitqs(struct pmcs_hw m, int verbose) 610 { 611 pmcs_cmd_t *sp, s; 612 pmcs_xscsi_t xs; 613 int first, i; 614 int max_dev = m.max_dev; 615 616 sp = m.dq.stqh_first; 617 first = 1; 618 while (sp) { 619 if (first) { 620 mdb_printf("\nDead Command Queue:\n"); 621 mdb_printf("---------------------------\n"); 622 } 623 if (MDB_RD(&s, sizeof (s), sp) == -1) { 624 NOREAD(pmcs_cmd_t, sp); 625 break; 626 } 627 print_spcmd(&s, sp, first, verbose); 628 sp = s.cmd_next.stqe_next; 629 first = 0; 630 } 631 632 sp = m.cq.stqh_first; 633 first = 1; 634 while (sp) { 635 if (first) { 636 mdb_printf("\nCompletion Command Queue:\n"); 637 mdb_printf("---------------------------\n"); 638 } 639 if (MDB_RD(&s, sizeof (s), sp) == -1) { 640 NOREAD(pmcs_cmd_t, sp); 641 break; 642 } 643 print_spcmd(&s, sp, first, verbose); 644 sp = s.cmd_next.stqe_next; 645 first = 0; 646 } 647 648 649 if (targets == NULL) { 650 targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP); 651 } 652 653 if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) { 654 NOREAD(targets, m.targets); 655 return; 656 } 657 658 for (i = 0; i < max_dev; i++) { 659 if (targets[i] == NULL) { 660 continue; 661 } 662 if (MDB_RD(&xs, sizeof (xs), targets[i]) == -1) { 663 NOREAD(pmcs_xscsi_t, targets[i]); 664 continue; 665 } 666 sp = xs.wq.stqh_first; 667 first = 1; 668 while (sp) { 669 if (first) { 670 mdb_printf("\nTarget %u Wait Queue:\n", 671 xs.target_num); 672 mdb_printf("---------------------------\n"); 673 } 674 if (MDB_RD(&s, sizeof (s), sp) == -1) { 675 NOREAD(pmcs_cmd_t, sp); 676 break; 677 } 678 print_spcmd(&s, sp, first, verbose); 679 sp = s.cmd_next.stqe_next; 680 first = 0; 681 } 682 sp = xs.aq.stqh_first; 683 first = 1; 684 while (sp) { 685 if (first) { 686 mdb_printf("\nTarget %u Active Queue:\n", 687 xs.target_num); 688 mdb_printf("---------------------------\n"); 689 } 690 if (MDB_RD(&s, sizeof (s), sp) == -1) { 691 NOREAD(pmcs_cmd_t, sp); 692 break; 693 } 694 print_spcmd(&s, sp, first, verbose); 695 sp = s.cmd_next.stqe_next; 696 first = 0; 697 } 698 sp = xs.sq.stqh_first; 699 first = 1; 700 while (sp) { 701 if (first) { 702 mdb_printf("\nTarget %u Special Queue:\n", 703 xs.target_num); 704 mdb_printf("---------------------------\n"); 705 } 706 if (MDB_RD(&s, sizeof (s), sp) == -1) { 707 NOREAD(pmcs_cmd_t, sp); 708 break; 709 } 710 print_spcmd(&s, sp, first, verbose); 711 sp = s.cmd_next.stqe_next; 712 first = 0; 713 } 714 } 715 } 716 717 static char * 718 ibq_type(int qnum) 719 { 720 if (qnum < 0 || qnum >= PMCS_NIQ) { 721 return ("UNKNOWN"); 722 } 723 724 if (qnum < PMCS_IQ_OTHER) { 725 return ("I/O"); 726 } 727 728 return ("Other"); 729 } 730 731 static char * 732 obq_type(int qnum) 733 { 734 switch (qnum) { 735 case PMCS_OQ_IODONE: 736 return ("I/O"); 737 break; 738 case PMCS_OQ_GENERAL: 739 return ("General"); 740 break; 741 case PMCS_OQ_EVENTS: 742 return ("Events"); 743 break; 744 default: 745 return ("UNKNOWN"); 746 } 747 } 748 749 static char * 750 iomb_cat(uint32_t cat) 751 { 752 switch (cat) { 753 case PMCS_IOMB_CAT_NET: 754 return ("NET"); 755 break; 756 case PMCS_IOMB_CAT_FC: 757 return ("FC"); 758 break; 759 case PMCS_IOMB_CAT_SAS: 760 return ("SAS"); 761 break; 762 case PMCS_IOMB_CAT_SCSI: 763 return ("SCSI"); 764 break; 765 default: 766 return ("???"); 767 } 768 } 769 770 static char * 771 inbound_iomb_opcode(uint32_t opcode) 772 { 773 switch (opcode) { 774 case PMCIN_ECHO: 775 return ("ECHO"); 776 break; 777 case PMCIN_GET_INFO: 778 return ("GET_INFO"); 779 break; 780 case PMCIN_GET_VPD: 781 return ("GET_VPD"); 782 break; 783 case PMCIN_PHY_START: 784 return ("PHY_START"); 785 break; 786 case PMCIN_PHY_STOP: 787 return ("PHY_STOP"); 788 break; 789 case PMCIN_SSP_INI_IO_START: 790 return ("INI_IO_START"); 791 break; 792 case PMCIN_SSP_INI_TM_START: 793 return ("INI_TM_START"); 794 break; 795 case PMCIN_SSP_INI_EXT_IO_START: 796 return ("INI_EXT_IO_START"); 797 break; 798 case PMCIN_DEVICE_HANDLE_ACCEPT: 799 return ("DEVICE_HANDLE_ACCEPT"); 800 break; 801 case PMCIN_SSP_TGT_IO_START: 802 return ("TGT_IO_START"); 803 break; 804 case PMCIN_SSP_TGT_RESPONSE_START: 805 return ("TGT_RESPONSE_START"); 806 break; 807 case PMCIN_SSP_INI_EDC_EXT_IO_START: 808 return ("INI_EDC_EXT_IO_START"); 809 break; 810 case PMCIN_SSP_INI_EDC_EXT_IO_START1: 811 return ("INI_EDC_EXT_IO_START1"); 812 break; 813 case PMCIN_SSP_TGT_EDC_IO_START: 814 return ("TGT_EDC_IO_START"); 815 break; 816 case PMCIN_SSP_ABORT: 817 return ("SSP_ABORT"); 818 break; 819 case PMCIN_DEREGISTER_DEVICE_HANDLE: 820 return ("DEREGISTER_DEVICE_HANDLE"); 821 break; 822 case PMCIN_GET_DEVICE_HANDLE: 823 return ("GET_DEVICE_HANDLE"); 824 break; 825 case PMCIN_SMP_REQUEST: 826 return ("SMP_REQUEST"); 827 break; 828 case PMCIN_SMP_RESPONSE: 829 return ("SMP_RESPONSE"); 830 break; 831 case PMCIN_SMP_ABORT: 832 return ("SMP_ABORT"); 833 break; 834 case PMCIN_ASSISTED_DISCOVERY: 835 return ("ASSISTED_DISCOVERY"); 836 break; 837 case PMCIN_REGISTER_DEVICE: 838 return ("REGISTER_DEVICE"); 839 break; 840 case PMCIN_SATA_HOST_IO_START: 841 return ("SATA_HOST_IO_START"); 842 break; 843 case PMCIN_SATA_ABORT: 844 return ("SATA_ABORT"); 845 break; 846 case PMCIN_LOCAL_PHY_CONTROL: 847 return ("LOCAL_PHY_CONTROL"); 848 break; 849 case PMCIN_GET_DEVICE_INFO: 850 return ("GET_DEVICE_INFO"); 851 break; 852 case PMCIN_TWI: 853 return ("TWI"); 854 break; 855 case PMCIN_FW_FLASH_UPDATE: 856 return ("FW_FLASH_UPDATE"); 857 break; 858 case PMCIN_SET_VPD: 859 return ("SET_VPD"); 860 break; 861 case PMCIN_GPIO: 862 return ("GPIO"); 863 break; 864 case PMCIN_SAS_DIAG_MODE_START_END: 865 return ("SAS_DIAG_MODE_START_END"); 866 break; 867 case PMCIN_SAS_DIAG_EXECUTE: 868 return ("SAS_DIAG_EXECUTE"); 869 break; 870 case PMCIN_SAW_HW_EVENT_ACK: 871 return ("SAS_HW_EVENT_ACK"); 872 break; 873 case PMCIN_GET_TIME_STAMP: 874 return ("GET_TIME_STAMP"); 875 break; 876 case PMCIN_PORT_CONTROL: 877 return ("PORT_CONTROL"); 878 break; 879 case PMCIN_GET_NVMD_DATA: 880 return ("GET_NVMD_DATA"); 881 break; 882 case PMCIN_SET_NVMD_DATA: 883 return ("SET_NVMD_DATA"); 884 break; 885 case PMCIN_SET_DEVICE_STATE: 886 return ("SET_DEVICE_STATE"); 887 break; 888 case PMCIN_GET_DEVICE_STATE: 889 return ("GET_DEVICE_STATE"); 890 break; 891 default: 892 return ("UNKNOWN"); 893 break; 894 } 895 } 896 897 static char * 898 outbound_iomb_opcode(uint32_t opcode) 899 { 900 switch (opcode) { 901 case PMCOUT_ECHO: 902 return ("ECHO"); 903 break; 904 case PMCOUT_GET_INFO: 905 return ("GET_INFO"); 906 break; 907 case PMCOUT_GET_VPD: 908 return ("GET_VPD"); 909 break; 910 case PMCOUT_SAS_HW_EVENT: 911 return ("SAS_HW_EVENT"); 912 break; 913 case PMCOUT_SSP_COMPLETION: 914 return ("SSP_COMPLETION"); 915 break; 916 case PMCOUT_SMP_COMPLETION: 917 return ("SMP_COMPLETION"); 918 break; 919 case PMCOUT_LOCAL_PHY_CONTROL: 920 return ("LOCAL_PHY_CONTROL"); 921 break; 922 case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT: 923 return ("SAS_ASSISTED_DISCOVERY_SENT"); 924 break; 925 case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT: 926 return ("SATA_ASSISTED_DISCOVERY_SENT"); 927 break; 928 case PMCOUT_DEVICE_REGISTRATION: 929 return ("DEVICE_REGISTRATION"); 930 break; 931 case PMCOUT_DEREGISTER_DEVICE_HANDLE: 932 return ("DEREGISTER_DEVICE_HANDLE"); 933 break; 934 case PMCOUT_GET_DEVICE_HANDLE: 935 return ("GET_DEVICE_HANDLE"); 936 break; 937 case PMCOUT_SATA_COMPLETION: 938 return ("SATA_COMPLETION"); 939 break; 940 case PMCOUT_SATA_EVENT: 941 return ("SATA_EVENT"); 942 break; 943 case PMCOUT_SSP_EVENT: 944 return ("SSP_EVENT"); 945 break; 946 case PMCOUT_DEVICE_HANDLE_ARRIVED: 947 return ("DEVICE_HANDLE_ARRIVED"); 948 break; 949 case PMCOUT_SMP_REQUEST_RECEIVED: 950 return ("SMP_REQUEST_RECEIVED"); 951 break; 952 case PMCOUT_SSP_REQUEST_RECEIVED: 953 return ("SSP_REQUEST_RECEIVED"); 954 break; 955 case PMCOUT_DEVICE_INFO: 956 return ("DEVICE_INFO"); 957 break; 958 case PMCOUT_FW_FLASH_UPDATE: 959 return ("FW_FLASH_UPDATE"); 960 break; 961 case PMCOUT_SET_VPD: 962 return ("SET_VPD"); 963 break; 964 case PMCOUT_GPIO: 965 return ("GPIO"); 966 break; 967 case PMCOUT_GPIO_EVENT: 968 return ("GPIO_EVENT"); 969 break; 970 case PMCOUT_GENERAL_EVENT: 971 return ("GENERAL_EVENT"); 972 break; 973 case PMCOUT_TWI: 974 return ("TWI"); 975 break; 976 case PMCOUT_SSP_ABORT: 977 return ("SSP_ABORT"); 978 break; 979 case PMCOUT_SATA_ABORT: 980 return ("SATA_ABORT"); 981 break; 982 case PMCOUT_SAS_DIAG_MODE_START_END: 983 return ("SAS_DIAG_MODE_START_END"); 984 break; 985 case PMCOUT_SAS_DIAG_EXECUTE: 986 return ("SAS_DIAG_EXECUTE"); 987 break; 988 case PMCOUT_GET_TIME_STAMP: 989 return ("GET_TIME_STAMP"); 990 break; 991 case PMCOUT_SAS_HW_EVENT_ACK_ACK: 992 return ("SAS_HW_EVENT_ACK_ACK"); 993 break; 994 case PMCOUT_PORT_CONTROL: 995 return ("PORT_CONTROL"); 996 break; 997 case PMCOUT_SKIP_ENTRIES: 998 return ("SKIP_ENTRIES"); 999 break; 1000 case PMCOUT_SMP_ABORT: 1001 return ("SMP_ABORT"); 1002 break; 1003 case PMCOUT_GET_NVMD_DATA: 1004 return ("GET_NVMD_DATA"); 1005 break; 1006 case PMCOUT_SET_NVMD_DATA: 1007 return ("SET_NVMD_DATA"); 1008 break; 1009 case PMCOUT_DEVICE_HANDLE_REMOVED: 1010 return ("DEVICE_HANDLE_REMOVED"); 1011 break; 1012 case PMCOUT_SET_DEVICE_STATE: 1013 return ("SET_DEVICE_STATE"); 1014 break; 1015 case PMCOUT_GET_DEVICE_STATE: 1016 return ("GET_DEVICE_STATE"); 1017 break; 1018 case PMCOUT_SET_DEVICE_INFO: 1019 return ("SET_DEVICE_INFO"); 1020 break; 1021 default: 1022 return ("UNKNOWN"); 1023 break; 1024 } 1025 } 1026 1027 static void 1028 dump_one_qentry_outbound(uint32_t *qentryp, int idx) 1029 { 1030 int qeidx; 1031 uint32_t word0 = LE_32(*qentryp); 1032 1033 mdb_printf("Entry #%02d\n", idx); 1034 mdb_inc_indent(2); 1035 1036 mdb_printf("Header: 0x%08x (", word0); 1037 if (word0 & PMCS_IOMB_VALID) { 1038 mdb_printf("VALID, "); 1039 } 1040 if (word0 & PMCS_IOMB_HIPRI) { 1041 mdb_printf("HIPRI, "); 1042 } 1043 mdb_printf("OBID=%d, ", 1044 (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT); 1045 mdb_printf("CAT=%s, ", 1046 iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT)); 1047 mdb_printf("OPCODE=%s", 1048 outbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK)); 1049 mdb_printf(")\n"); 1050 1051 mdb_printf("Remaining Payload:\n"); 1052 1053 mdb_inc_indent(2); 1054 for (qeidx = 1; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) { 1055 mdb_printf("%08x ", LE_32(*(qentryp + qeidx))); 1056 } 1057 mdb_printf("\n"); 1058 mdb_dec_indent(4); 1059 } 1060 1061 static void 1062 display_outbound_queues(struct pmcs_hw ss, uint_t verbose) 1063 { 1064 int idx, qidx; 1065 uintptr_t obqp; 1066 uint32_t *cip; 1067 uint32_t *qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP); 1068 uint32_t last_consumed, oqpi; 1069 1070 mdb_printf("\n"); 1071 mdb_printf("Outbound Queues\n"); 1072 mdb_printf("---------------\n"); 1073 1074 mdb_inc_indent(2); 1075 1076 for (qidx = 0; qidx < PMCS_NOQ; qidx++) { 1077 obqp = (uintptr_t)ss.oqp[qidx]; 1078 1079 if (obqp == NULL) { 1080 mdb_printf("No outbound queue ptr for queue #%d\n", 1081 qidx); 1082 continue; 1083 } 1084 1085 mdb_printf("Outbound Queue #%d (Queue Type = %s)\n", qidx, 1086 obq_type(qidx)); 1087 /* 1088 * Chip is the producer, so read the actual producer index 1089 * and not the driver's version 1090 */ 1091 cip = (uint32_t *)((void *)ss.cip); 1092 if (MDB_RD(&oqpi, 4, cip + OQPI_BASE_OFFSET + 1093 (qidx * 4)) == -1) { 1094 mdb_warn("Couldn't read oqpi\n"); 1095 break; 1096 } 1097 1098 mdb_printf("Producer index: %d Consumer index: %d\n\n", 1099 LE_32(oqpi), ss.oqci[qidx]); 1100 mdb_inc_indent(2); 1101 1102 if (ss.oqci[qidx] == 0) { 1103 last_consumed = ss.ioq_depth - 1; 1104 } else { 1105 last_consumed = ss.oqci[qidx] - 1; 1106 } 1107 1108 1109 if (!verbose) { 1110 mdb_printf("Last processed entry:\n"); 1111 if (MDB_RD(qentryp, PMCS_QENTRY_SIZE, 1112 (obqp + (PMCS_QENTRY_SIZE * last_consumed))) 1113 == -1) { 1114 mdb_warn("Couldn't read queue entry at 0x%p\n", 1115 (obqp + (PMCS_QENTRY_SIZE * 1116 last_consumed))); 1117 break; 1118 } 1119 dump_one_qentry_outbound(qentryp, last_consumed); 1120 mdb_printf("\n"); 1121 mdb_dec_indent(2); 1122 continue; 1123 } 1124 1125 for (idx = 0; idx < ss.ioq_depth; idx++) { 1126 if (MDB_RD(qentryp, PMCS_QENTRY_SIZE, 1127 (obqp + (PMCS_QENTRY_SIZE * idx))) == -1) { 1128 mdb_warn("Couldn't read queue entry at 0x%p\n", 1129 (obqp + (PMCS_QENTRY_SIZE * idx))); 1130 break; 1131 } 1132 dump_one_qentry_outbound(qentryp, idx); 1133 } 1134 1135 mdb_printf("\n"); 1136 mdb_dec_indent(2); 1137 } 1138 1139 mdb_dec_indent(2); 1140 mdb_free(qentryp, PMCS_QENTRY_SIZE); 1141 } 1142 1143 static void 1144 dump_one_qentry_inbound(uint32_t *qentryp, int idx) 1145 { 1146 int qeidx; 1147 uint32_t word0 = LE_32(*qentryp); 1148 1149 mdb_printf("Entry #%02d\n", idx); 1150 mdb_inc_indent(2); 1151 1152 mdb_printf("Header: 0x%08x (", word0); 1153 if (word0 & PMCS_IOMB_VALID) { 1154 mdb_printf("VALID, "); 1155 } 1156 if (word0 & PMCS_IOMB_HIPRI) { 1157 mdb_printf("HIPRI, "); 1158 } 1159 mdb_printf("OBID=%d, ", 1160 (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT); 1161 mdb_printf("CAT=%s, ", 1162 iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT)); 1163 mdb_printf("OPCODE=%s", 1164 inbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK)); 1165 mdb_printf(")\n"); 1166 1167 mdb_printf("HTAG: 0x%08x\n", LE_32(*(qentryp + 1))); 1168 mdb_printf("Remaining Payload:\n"); 1169 1170 mdb_inc_indent(2); 1171 for (qeidx = 2; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) { 1172 mdb_printf("%08x ", LE_32(*(qentryp + qeidx))); 1173 } 1174 mdb_printf("\n"); 1175 mdb_dec_indent(4); 1176 } 1177 1178 static void 1179 display_inbound_queues(struct pmcs_hw ss, uint_t verbose) 1180 { 1181 int idx, qidx, iqci, last_consumed; 1182 uintptr_t ibqp; 1183 uint32_t *qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP); 1184 uint32_t *cip; 1185 1186 mdb_printf("\n"); 1187 mdb_printf("Inbound Queues\n"); 1188 mdb_printf("--------------\n"); 1189 1190 mdb_inc_indent(2); 1191 1192 for (qidx = 0; qidx < PMCS_NIQ; qidx++) { 1193 ibqp = (uintptr_t)ss.iqp[qidx]; 1194 1195 if (ibqp == NULL) { 1196 mdb_printf("No inbound queue ptr for queue #%d\n", 1197 qidx); 1198 continue; 1199 } 1200 1201 mdb_printf("Inbound Queue #%d (Queue Type = %s)\n", qidx, 1202 ibq_type(qidx)); 1203 1204 cip = (uint32_t *)((void *)ss.cip); 1205 if (MDB_RD(&iqci, 4, cip + (qidx * 4)) == -1) { 1206 mdb_warn("Couldn't read iqci\n"); 1207 break; 1208 } 1209 iqci = LE_32(iqci); 1210 1211 mdb_printf("Producer index: %d Consumer index: %d\n\n", 1212 ss.shadow_iqpi[qidx], iqci); 1213 mdb_inc_indent(2); 1214 1215 if (iqci == 0) { 1216 last_consumed = ss.ioq_depth - 1; 1217 } else { 1218 last_consumed = iqci - 1; 1219 } 1220 1221 if (!verbose) { 1222 mdb_printf("Last processed entry:\n"); 1223 if (MDB_RD(qentryp, PMCS_QENTRY_SIZE, 1224 (ibqp + (PMCS_QENTRY_SIZE * last_consumed))) 1225 == -1) { 1226 mdb_warn("Couldn't read queue entry at 0x%p\n", 1227 (ibqp + (PMCS_QENTRY_SIZE * 1228 last_consumed))); 1229 break; 1230 } 1231 dump_one_qentry_inbound(qentryp, last_consumed); 1232 mdb_printf("\n"); 1233 mdb_dec_indent(2); 1234 continue; 1235 } 1236 1237 for (idx = 0; idx < ss.ioq_depth; idx++) { 1238 if (MDB_RD(qentryp, PMCS_QENTRY_SIZE, 1239 (ibqp + (PMCS_QENTRY_SIZE * idx))) == -1) { 1240 mdb_warn("Couldn't read queue entry at 0x%p\n", 1241 (ibqp + (PMCS_QENTRY_SIZE * idx))); 1242 break; 1243 } 1244 dump_one_qentry_inbound(qentryp, idx); 1245 } 1246 1247 mdb_printf("\n"); 1248 mdb_dec_indent(2); 1249 } 1250 1251 mdb_dec_indent(2); 1252 mdb_free(qentryp, PMCS_QENTRY_SIZE); 1253 } 1254 1255 static void 1256 display_phy(struct pmcs_phy phy, int verbose, int totals_only) 1257 { 1258 char *dtype, *speed; 1259 char *yes = "Yes"; 1260 char *no = "No"; 1261 char *cfgd = no; 1262 char *apend = no; 1263 char *asent = no; 1264 char *dead = no; 1265 char *changed = no; 1266 1267 switch (phy.dtype) { 1268 case NOTHING: 1269 dtype = "None"; 1270 break; 1271 case SATA: 1272 dtype = "SATA"; 1273 if (phy.configured) { 1274 ++sata_phys; 1275 } 1276 break; 1277 case SAS: 1278 dtype = "SAS"; 1279 if (phy.configured) { 1280 ++sas_phys; 1281 } 1282 break; 1283 case EXPANDER: 1284 dtype = "EXP"; 1285 if (phy.configured) { 1286 ++exp_phys; 1287 } 1288 break; 1289 } 1290 1291 if (phy.dtype == NOTHING) { 1292 empty_phys++; 1293 } else if ((phy.dtype == EXPANDER) && phy.configured) { 1294 num_expanders++; 1295 } 1296 1297 if (totals_only) { 1298 return; 1299 } 1300 1301 switch (phy.link_rate) { 1302 case SAS_LINK_RATE_1_5GBIT: 1303 speed = "1.5Gb/s"; 1304 break; 1305 case SAS_LINK_RATE_3GBIT: 1306 speed = "3 Gb/s"; 1307 break; 1308 case SAS_LINK_RATE_6GBIT: 1309 speed = "6 Gb/s"; 1310 break; 1311 default: 1312 speed = "N/A"; 1313 break; 1314 } 1315 1316 if ((phy.dtype != NOTHING) || verbose) { 1317 print_sas_address(&phy); 1318 1319 if (phy.device_id != PMCS_INVALID_DEVICE_ID) { 1320 mdb_printf(" %3d %4d %6s %4s ", 1321 phy.device_id, phy.phynum, speed, dtype); 1322 } else { 1323 mdb_printf(" N/A %4d %6s %4s ", 1324 phy.phynum, speed, dtype); 1325 } 1326 1327 if (verbose) { 1328 if (phy.abort_sent) { 1329 asent = yes; 1330 } 1331 if (phy.abort_pending) { 1332 apend = yes; 1333 } 1334 if (phy.configured) { 1335 cfgd = yes; 1336 } 1337 if (phy.dead) { 1338 dead = yes; 1339 } 1340 if (phy.changed) { 1341 changed = yes; 1342 } 1343 1344 mdb_printf("%-4s %-4s %-4s %-4s %-4s %3d " 1345 "0x%p ", cfgd, apend, asent, 1346 changed, dead, phy.ref_count, phy.phy_lock); 1347 } 1348 1349 mdb_printf("Path: %s\n", phy.path); 1350 } 1351 } 1352 1353 static void 1354 display_phys(struct pmcs_hw ss, int verbose, struct pmcs_phy *parent, int level, 1355 int totals_only) 1356 { 1357 pmcs_phy_t phy; 1358 pmcs_phy_t *pphy = parent; 1359 1360 mdb_inc_indent(3); 1361 1362 if (parent == NULL) { 1363 pphy = (pmcs_phy_t *)ss.root_phys; 1364 } else { 1365 pphy = (pmcs_phy_t *)parent; 1366 } 1367 1368 if (level == 0) { 1369 sas_phys = 0; 1370 sata_phys = 0; 1371 exp_phys = 0; 1372 num_expanders = 0; 1373 empty_phys = 0; 1374 } 1375 1376 if (!totals_only) { 1377 if (level == 0) { 1378 mdb_printf("PHY information\n"); 1379 } 1380 mdb_printf("--------\n"); 1381 mdb_printf("Level %2d\n", level); 1382 mdb_printf("--------\n"); 1383 mdb_printf("SAS Address Hdl Phy# Speed Type "); 1384 1385 if (verbose) { 1386 mdb_printf("Cfgd AbtP AbtS Chgd Dead Ref Lock\n"); 1387 } else { 1388 mdb_printf("\n"); 1389 } 1390 } 1391 1392 while (pphy) { 1393 if (MDB_RD(&phy, sizeof (phy), (uintptr_t)pphy) == -1) { 1394 NOREAD(pmcs_phy_t, phy); 1395 break; 1396 } 1397 1398 display_phy(phy, verbose, totals_only); 1399 1400 if (phy.children) { 1401 display_phys(ss, verbose, phy.children, level + 1, 1402 totals_only); 1403 if (!totals_only) { 1404 mdb_printf("\n"); 1405 } 1406 } 1407 1408 pphy = phy.sibling; 1409 } 1410 1411 mdb_dec_indent(3); 1412 1413 if (level == 0) { 1414 if (verbose) { 1415 mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP) " 1416 "(+%d subsidiary + %d empty)\n", "Occupied PHYs:", 1417 (sas_phys + sata_phys + num_expanders), 1418 sas_phys, sata_phys, num_expanders, 1419 (exp_phys - num_expanders), empty_phys); 1420 } else { 1421 mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n", 1422 "Occupied PHYs:", 1423 (sas_phys + sata_phys + num_expanders), 1424 sas_phys, sata_phys, num_expanders); 1425 } 1426 } 1427 } 1428 1429 /* 1430 * MAX_INST_STRLEN is the largest string size from which we will attempt 1431 * to convert to an instance number. The string will be formed up as 1432 * "0t<inst>\0" so that mdb_strtoull can parse it properly. 1433 */ 1434 #define MAX_INST_STRLEN 8 1435 1436 static int 1437 pmcs_dump_tracelog(boolean_t filter, int instance) 1438 { 1439 pmcs_tbuf_t *tbuf_addr; 1440 uint_t tbuf_idx; 1441 pmcs_tbuf_t tbuf; 1442 boolean_t wrap, elem_filtered; 1443 uint_t start_idx, elems_to_print, idx, tbuf_num_elems; 1444 char *bufp; 1445 char elem_inst[MAX_INST_STRLEN], ei_idx; 1446 1447 /* Get the address of the first element */ 1448 if (mdb_readvar(&tbuf_addr, "pmcs_tbuf") == -1) { 1449 mdb_warn("can't read pmcs_tbuf"); 1450 return (DCMD_ERR); 1451 } 1452 1453 /* Get the total number */ 1454 if (mdb_readvar(&tbuf_num_elems, "pmcs_tbuf_num_elems") == -1) { 1455 mdb_warn("can't read pmcs_tbuf_num_elems"); 1456 return (DCMD_ERR); 1457 } 1458 1459 /* Get the current index */ 1460 if (mdb_readvar(&tbuf_idx, "pmcs_tbuf_idx") == -1) { 1461 mdb_warn("can't read pmcs_tbuf_idx"); 1462 return (DCMD_ERR); 1463 } 1464 1465 /* Indicator as to whether the buffer has wrapped */ 1466 if (mdb_readvar(&wrap, "pmcs_tbuf_wrap") == -1) { 1467 mdb_warn("can't read pmcs_tbuf_wrap"); 1468 return (DCMD_ERR); 1469 } 1470 1471 /* Figure out where we start and stop */ 1472 if (wrap) { 1473 start_idx = tbuf_idx; 1474 elems_to_print = tbuf_num_elems; 1475 } else { 1476 start_idx = 0; 1477 elems_to_print = tbuf_idx; 1478 } 1479 1480 idx = start_idx; 1481 1482 /* Dump the buffer contents */ 1483 while (elems_to_print != 0) { 1484 if (MDB_RD(&tbuf, sizeof (pmcs_tbuf_t), (tbuf_addr + idx)) 1485 == -1) { 1486 NOREAD(tbuf, (tbuf_addr + idx)); 1487 return (DCMD_ERR); 1488 } 1489 1490 elem_filtered = B_FALSE; 1491 1492 if (filter) { 1493 bufp = tbuf.buf; 1494 /* Skip the driver name */ 1495 while (*bufp < '0' || *bufp > '9') { 1496 bufp++; 1497 } 1498 1499 ei_idx = 0; 1500 elem_inst[ei_idx++] = '0'; 1501 elem_inst[ei_idx++] = 't'; 1502 while (*bufp != ':' && ei_idx < (MAX_INST_STRLEN - 1)) { 1503 elem_inst[ei_idx++] = *bufp; 1504 bufp++; 1505 } 1506 elem_inst[ei_idx] = 0; 1507 1508 /* Get the instance */ 1509 if ((int)mdb_strtoull(elem_inst) != instance) { 1510 elem_filtered = B_TRUE; 1511 } 1512 } 1513 1514 if (!elem_filtered) { 1515 mdb_printf("%Y.%09ld %s\n", tbuf.timestamp, tbuf.buf); 1516 } 1517 1518 --elems_to_print; 1519 if (++idx == tbuf_num_elems) { 1520 idx = 0; 1521 } 1522 } 1523 1524 return (DCMD_OK); 1525 } 1526 1527 /* 1528 * Walkers 1529 */ 1530 static int 1531 targets_walk_i(mdb_walk_state_t *wsp) 1532 { 1533 if (wsp->walk_addr == NULL) { 1534 mdb_warn("Can not perform global walk\n"); 1535 return (WALK_ERR); 1536 } 1537 1538 /* 1539 * Address provided belongs to HBA softstate. Get the targets pointer 1540 * to begin the walk. 1541 */ 1542 if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) != 1543 sizeof (pmcs_hw_t)) { 1544 mdb_warn("Unable to read HBA softstate\n"); 1545 return (WALK_ERR); 1546 } 1547 1548 if (targets == NULL) { 1549 targets = mdb_alloc(sizeof (targets) * ss.max_dev, UM_SLEEP); 1550 } 1551 1552 if (MDB_RD(targets, sizeof (targets) * ss.max_dev, ss.targets) == -1) { 1553 NOREAD(targets, ss.targets); 1554 return (WALK_ERR); 1555 } 1556 1557 target_idx = 0; 1558 wsp->walk_addr = (uintptr_t)(targets[0]); 1559 wsp->walk_data = mdb_alloc(sizeof (pmcs_xscsi_t), UM_SLEEP); 1560 1561 return (WALK_NEXT); 1562 } 1563 1564 static int 1565 targets_walk_s(mdb_walk_state_t *wsp) 1566 { 1567 int status; 1568 1569 if (target_idx == ss.max_dev) { 1570 return (WALK_DONE); 1571 } 1572 1573 if (mdb_vread(wsp->walk_data, sizeof (pmcs_xscsi_t), 1574 wsp->walk_addr) == -1) { 1575 mdb_warn("Failed to read target at %p", (void *)wsp->walk_addr); 1576 return (WALK_DONE); 1577 } 1578 1579 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, 1580 wsp->walk_cbdata); 1581 1582 do { 1583 wsp->walk_addr = (uintptr_t)(targets[++target_idx]); 1584 } while ((wsp->walk_addr == NULL) && (target_idx < ss.max_dev)); 1585 1586 if (target_idx == ss.max_dev) { 1587 return (WALK_DONE); 1588 } 1589 1590 return (status); 1591 } 1592 1593 static void 1594 targets_walk_f(mdb_walk_state_t *wsp) 1595 { 1596 mdb_free(wsp->walk_data, sizeof (pmcs_xscsi_t)); 1597 } 1598 1599 1600 static pmcs_phy_t * 1601 pmcs_next_sibling(pmcs_phy_t *phyp) 1602 { 1603 pmcs_phy_t parent; 1604 1605 /* 1606 * First, if this is a root PHY, there are no more siblings 1607 */ 1608 if (phyp->level == 0) { 1609 return (NULL); 1610 } 1611 1612 /* 1613 * Otherwise, next sibling is the parent's sibling 1614 */ 1615 while (phyp->level > 0) { 1616 if (mdb_vread(&parent, sizeof (pmcs_phy_t), 1617 (uintptr_t)phyp->parent) == -1) { 1618 mdb_warn("pmcs_next_sibling: Failed to read PHY at %p", 1619 (void *)phyp->parent); 1620 return (NULL); 1621 } 1622 1623 if (parent.sibling != NULL) { 1624 break; 1625 } 1626 1627 phyp = phyp->parent; 1628 } 1629 1630 return (parent.sibling); 1631 } 1632 1633 static int 1634 phy_walk_i(mdb_walk_state_t *wsp) 1635 { 1636 if (wsp->walk_addr == NULL) { 1637 mdb_warn("Can not perform global walk\n"); 1638 return (WALK_ERR); 1639 } 1640 1641 /* 1642 * Address provided belongs to HBA softstate. Get the targets pointer 1643 * to begin the walk. 1644 */ 1645 if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) != 1646 sizeof (pmcs_hw_t)) { 1647 mdb_warn("Unable to read HBA softstate\n"); 1648 return (WALK_ERR); 1649 } 1650 1651 wsp->walk_addr = (uintptr_t)(ss.root_phys); 1652 wsp->walk_data = mdb_alloc(sizeof (pmcs_phy_t), UM_SLEEP); 1653 1654 return (WALK_NEXT); 1655 } 1656 1657 static int 1658 phy_walk_s(mdb_walk_state_t *wsp) 1659 { 1660 pmcs_phy_t *phyp, *nphyp; 1661 int status; 1662 1663 if (mdb_vread(wsp->walk_data, sizeof (pmcs_phy_t), 1664 wsp->walk_addr) == -1) { 1665 mdb_warn("phy_walk_s: Failed to read PHY at %p", 1666 (void *)wsp->walk_addr); 1667 return (WALK_DONE); 1668 } 1669 1670 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, 1671 wsp->walk_cbdata); 1672 1673 phyp = (pmcs_phy_t *)wsp->walk_data; 1674 if (phyp->children) { 1675 wsp->walk_addr = (uintptr_t)(phyp->children); 1676 } else { 1677 wsp->walk_addr = (uintptr_t)(phyp->sibling); 1678 } 1679 1680 if (wsp->walk_addr == NULL) { 1681 /* 1682 * We reached the end of this sibling list. Trudge back up 1683 * to the parent and find the next sibling after the expander 1684 * we just finished traversing, if there is one. 1685 */ 1686 nphyp = pmcs_next_sibling(phyp); 1687 1688 if (nphyp == NULL) { 1689 return (WALK_DONE); 1690 } 1691 1692 wsp->walk_addr = (uintptr_t)nphyp; 1693 } 1694 1695 return (status); 1696 } 1697 1698 static void 1699 phy_walk_f(mdb_walk_state_t *wsp) 1700 { 1701 mdb_free(wsp->walk_data, sizeof (pmcs_phy_t)); 1702 } 1703 1704 static void 1705 display_matching_work(struct pmcs_hw ss, uintmax_t index, uintmax_t snum, 1706 uintmax_t tag_type) 1707 { 1708 int idx; 1709 pmcwork_t work, *wp = &work; 1710 uintptr_t _wp; 1711 boolean_t printed_header = B_FALSE; 1712 uint32_t mask, mask_val, match_val; 1713 char *match_type; 1714 1715 if (index != UINT_MAX) { 1716 match_type = "index"; 1717 mask = PMCS_TAG_INDEX_MASK; 1718 mask_val = index << PMCS_TAG_INDEX_SHIFT; 1719 match_val = index; 1720 } else if (snum != UINT_MAX) { 1721 match_type = "serial number"; 1722 mask = PMCS_TAG_SERNO_MASK; 1723 mask_val = snum << PMCS_TAG_SERNO_SHIFT; 1724 match_val = snum; 1725 } else { 1726 switch (tag_type) { 1727 case PMCS_TAG_TYPE_NONE: 1728 match_type = "tag type NONE"; 1729 break; 1730 case PMCS_TAG_TYPE_CBACK: 1731 match_type = "tag type CBACK"; 1732 break; 1733 case PMCS_TAG_TYPE_WAIT: 1734 match_type = "tag type WAIT"; 1735 break; 1736 } 1737 mask = PMCS_TAG_TYPE_MASK; 1738 mask_val = tag_type << PMCS_TAG_TYPE_SHIFT; 1739 match_val = tag_type; 1740 } 1741 1742 _wp = (uintptr_t)ss.work; 1743 1744 for (idx = 0; idx < ss.max_cmd; idx++, _wp += sizeof (pmcwork_t)) { 1745 if (MDB_RD(&work, sizeof (pmcwork_t), _wp) == -1) { 1746 NOREAD(pmcwork_t, _wp); 1747 continue; 1748 } 1749 1750 if ((work.htag & mask) != mask_val) { 1751 continue; 1752 } 1753 1754 if (printed_header == B_FALSE) { 1755 if (tag_type) { 1756 mdb_printf("\nWork structures matching %s\n\n", 1757 match_type, match_val); 1758 } else { 1759 mdb_printf("\nWork structures matching %s of " 1760 "0x%x\n\n", match_type, match_val); 1761 } 1762 mdb_printf("%8s %10s %20s %8s %8s O D\n", 1763 "HTag", "State", "Phy Path", "Target", "Timer"); 1764 printed_header = B_TRUE; 1765 } 1766 1767 display_one_work(wp, 0, 0); 1768 } 1769 1770 if (!printed_header) { 1771 mdb_printf("No work structure matches found\n"); 1772 } 1773 } 1774 1775 static int 1776 pmcs_tag(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1777 { 1778 struct pmcs_hw ss; 1779 uintmax_t tag_type = UINT_MAX; 1780 uintmax_t snum = UINT_MAX; 1781 uintmax_t index = UINT_MAX; 1782 int args = 0; 1783 void *pmcs_state; 1784 char *state_str; 1785 struct dev_info dip; 1786 1787 if (!(flags & DCMD_ADDRSPEC)) { 1788 pmcs_state = NULL; 1789 if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) { 1790 mdb_warn("can't read pmcs_softc_state"); 1791 return (DCMD_ERR); 1792 } 1793 if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs_tag", argc, 1794 argv, (uintptr_t)pmcs_state) == -1) { 1795 mdb_warn("mdb_pwalk_dcmd failed"); 1796 return (DCMD_ERR); 1797 } 1798 return (DCMD_OK); 1799 } 1800 1801 if (mdb_getopts(argc, argv, 1802 'i', MDB_OPT_UINT64, &index, 1803 's', MDB_OPT_UINT64, &snum, 1804 't', MDB_OPT_UINT64, &tag_type) != argc) 1805 return (DCMD_USAGE); 1806 1807 /* 1808 * Count the number of supplied options and make sure they are 1809 * within appropriate ranges. If they're set to UINT_MAX, that means 1810 * they were not supplied, in which case reset them to 0. 1811 */ 1812 if (index != UINT_MAX) { 1813 args++; 1814 if (index > PMCS_TAG_INDEX_MASK) { 1815 mdb_warn("Index is out of range\n"); 1816 return (DCMD_USAGE); 1817 } 1818 } 1819 1820 if (tag_type != UINT_MAX) { 1821 args++; 1822 switch (tag_type) { 1823 case PMCS_TAG_TYPE_NONE: 1824 case PMCS_TAG_TYPE_CBACK: 1825 case PMCS_TAG_TYPE_WAIT: 1826 break; 1827 default: 1828 mdb_warn("Invalid tag type\n"); 1829 return (DCMD_USAGE); 1830 } 1831 } 1832 1833 if (snum != UINT_MAX) { 1834 args++; 1835 if (snum > (PMCS_TAG_SERNO_MASK >> PMCS_TAG_SERNO_SHIFT)) { 1836 mdb_warn("Serial number is out of range\n"); 1837 return (DCMD_USAGE); 1838 } 1839 } 1840 1841 /* 1842 * Make sure 1 and only 1 option is specified 1843 */ 1844 if ((args == 0) || (args > 1)) { 1845 mdb_warn("Exactly one of -i, -s and -t must be specified\n"); 1846 return (DCMD_USAGE); 1847 } 1848 1849 if (MDB_RD(&ss, sizeof (ss), addr) == -1) { 1850 NOREAD(pmcs_hw_t, addr); 1851 return (DCMD_ERR); 1852 } 1853 1854 if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) { 1855 NOREAD(pmcs_hw_t, addr); 1856 return (DCMD_ERR); 1857 } 1858 1859 /* processing completed */ 1860 1861 if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) || 1862 (flags & DCMD_LOOPFIRST)) { 1863 if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST)) 1864 mdb_printf("\n"); 1865 mdb_printf("%16s %9s %4s B C WorkFlags wserno DbgMsk %16s\n", 1866 "Address", "State", "Inst", "DIP"); 1867 mdb_printf("=================================" 1868 "============================================\n"); 1869 } 1870 1871 switch (ss.state) { 1872 case STATE_NIL: 1873 state_str = "Invalid"; 1874 break; 1875 case STATE_PROBING: 1876 state_str = "Probing"; 1877 break; 1878 case STATE_RUNNING: 1879 state_str = "Running"; 1880 break; 1881 case STATE_UNPROBING: 1882 state_str = "Unprobing"; 1883 break; 1884 case STATE_DEAD: 1885 state_str = "Dead"; 1886 break; 1887 } 1888 1889 mdb_printf("%16p %9s %4d %1d %1d 0x%08x 0x%04x 0x%04x %16p\n", addr, 1890 state_str, dip.devi_instance, ss.blocked, ss.configuring, 1891 ss.work_flags, ss.wserno, ss.debug_mask, ss.dip); 1892 mdb_printf("\n"); 1893 1894 mdb_inc_indent(4); 1895 display_matching_work(ss, index, snum, tag_type); 1896 mdb_dec_indent(4); 1897 mdb_printf("\n"); 1898 1899 return (DCMD_OK); 1900 } 1901 1902 static int 1903 pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1904 { 1905 struct pmcs_hw ss; 1906 uint_t verbose = FALSE; 1907 uint_t phy_info = FALSE; 1908 uint_t hw_info = FALSE; 1909 uint_t target_info = FALSE; 1910 uint_t work_info = FALSE; 1911 uint_t ic_info = FALSE; 1912 uint_t iport_info = FALSE; 1913 uint_t waitqs_info = FALSE; 1914 uint_t tracelog = FALSE; 1915 uint_t ibq = FALSE; 1916 uint_t obq = FALSE; 1917 uint_t tgt_phy_count = FALSE; 1918 uint_t compq = FALSE; 1919 int rv = DCMD_OK; 1920 void *pmcs_state; 1921 char *state_str; 1922 struct dev_info dip; 1923 1924 if (!(flags & DCMD_ADDRSPEC)) { 1925 pmcs_state = NULL; 1926 if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) { 1927 mdb_warn("can't read pmcs_softc_state"); 1928 return (DCMD_ERR); 1929 } 1930 if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs", argc, argv, 1931 (uintptr_t)pmcs_state) == -1) { 1932 mdb_warn("mdb_pwalk_dcmd failed"); 1933 return (DCMD_ERR); 1934 } 1935 return (DCMD_OK); 1936 } 1937 1938 if (mdb_getopts(argc, argv, 1939 'c', MDB_OPT_SETBITS, TRUE, &compq, 1940 'h', MDB_OPT_SETBITS, TRUE, &hw_info, 1941 'i', MDB_OPT_SETBITS, TRUE, &ic_info, 1942 'I', MDB_OPT_SETBITS, TRUE, &iport_info, 1943 'l', MDB_OPT_SETBITS, TRUE, &tracelog, 1944 'p', MDB_OPT_SETBITS, TRUE, &phy_info, 1945 'q', MDB_OPT_SETBITS, TRUE, &ibq, 1946 'Q', MDB_OPT_SETBITS, TRUE, &obq, 1947 't', MDB_OPT_SETBITS, TRUE, &target_info, 1948 'T', MDB_OPT_SETBITS, TRUE, &tgt_phy_count, 1949 'v', MDB_OPT_SETBITS, TRUE, &verbose, 1950 'w', MDB_OPT_SETBITS, TRUE, &work_info, 1951 'W', MDB_OPT_SETBITS, TRUE, &waitqs_info, 1952 NULL) != argc) 1953 return (DCMD_USAGE); 1954 1955 if (MDB_RD(&ss, sizeof (ss), addr) == -1) { 1956 NOREAD(pmcs_hw_t, addr); 1957 return (DCMD_ERR); 1958 } 1959 1960 if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) { 1961 NOREAD(pmcs_hw_t, addr); 1962 return (DCMD_ERR); 1963 } 1964 1965 /* 1966 * Dumping the trace log is special. It's global, not per-HBA. 1967 * Thus, a provided address is ignored. In addition, other options 1968 * cannot be specified at the same time. 1969 */ 1970 if (tracelog) { 1971 if (hw_info || ic_info || iport_info || phy_info || work_info || 1972 target_info || waitqs_info || ibq || obq || tgt_phy_count || 1973 compq) { 1974 return (DCMD_USAGE); 1975 } 1976 1977 if ((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) { 1978 return (pmcs_dump_tracelog(B_TRUE, dip.devi_instance)); 1979 } else if (flags & DCMD_LOOPFIRST) { 1980 return (pmcs_dump_tracelog(B_FALSE, 0)); 1981 } else { 1982 return (DCMD_OK); 1983 } 1984 } 1985 1986 /* processing completed */ 1987 1988 if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) || 1989 (flags & DCMD_LOOPFIRST) || phy_info || target_info || hw_info || 1990 work_info || waitqs_info || ibq || obq || tgt_phy_count || compq) { 1991 if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST)) 1992 mdb_printf("\n"); 1993 mdb_printf("%16s %9s %4s B C WorkFlags wserno DbgMsk %16s\n", 1994 "Address", "State", "Inst", "DIP"); 1995 mdb_printf("=================================" 1996 "============================================\n"); 1997 } 1998 1999 switch (ss.state) { 2000 case STATE_NIL: 2001 state_str = "Invalid"; 2002 break; 2003 case STATE_PROBING: 2004 state_str = "Probing"; 2005 break; 2006 case STATE_RUNNING: 2007 state_str = "Running"; 2008 break; 2009 case STATE_UNPROBING: 2010 state_str = "Unprobing"; 2011 break; 2012 case STATE_DEAD: 2013 state_str = "Dead"; 2014 break; 2015 } 2016 2017 mdb_printf("%16p %9s %4d %1d %1d 0x%08x 0x%04x 0x%04x %16p\n", addr, 2018 state_str, dip.devi_instance, ss.blocked, ss.configuring, 2019 ss.work_flags, ss.wserno, ss.debug_mask, ss.dip); 2020 mdb_printf("\n"); 2021 2022 mdb_inc_indent(4); 2023 2024 if (waitqs_info) 2025 display_waitqs(ss, verbose); 2026 2027 if (hw_info) 2028 display_hwinfo(ss, verbose); 2029 2030 if (phy_info || tgt_phy_count) 2031 display_phys(ss, verbose, NULL, 0, tgt_phy_count); 2032 2033 if (target_info || tgt_phy_count) 2034 display_targets(ss, verbose, tgt_phy_count); 2035 2036 if (work_info) 2037 display_work(ss, verbose); 2038 2039 if (ic_info) 2040 display_ic(ss, verbose); 2041 2042 if (ibq) 2043 display_inbound_queues(ss, verbose); 2044 2045 if (obq) 2046 display_outbound_queues(ss, verbose); 2047 2048 if (iport_info) 2049 display_iport(ss, addr, verbose); 2050 2051 if (compq) 2052 display_completion_queue(ss); 2053 2054 mdb_dec_indent(4); 2055 2056 return (rv); 2057 } 2058 2059 void 2060 pmcs_help() 2061 { 2062 mdb_printf("Prints summary information about each pmcs instance.\n" 2063 " -c: Dump the completion queue\n" 2064 " -h: Print more detailed hardware information\n" 2065 " -i: Print interrupt coalescing information\n" 2066 " -I: Print information about each iport\n" 2067 " -l: Dump the trace log (cannot be used with other options)\n" 2068 " -p: Print information about each attached PHY\n" 2069 " -q: Dump inbound queues\n" 2070 " -Q: Dump outbound queues\n" 2071 " -t: Print information about each known target\n" 2072 " -T: Print target and PHY count summary\n" 2073 " -w: Dump work structures\n" 2074 " -W: List pmcs cmds waiting on various queues\n" 2075 " -v: Add verbosity to the above options\n"); 2076 } 2077 2078 void 2079 pmcs_tag_help() 2080 { 2081 mdb_printf("Print all work structures by matching the tag.\n" 2082 " -i index: Match tag index (0x000 - 0xfff)\n" 2083 " -s serialnumber: Match serial number (0x0000 - 0xffff)\n" 2084 " -t tagtype: Match tag type [NONE(1), CBACK(2), " 2085 "WAIT(3)]\n"); 2086 } 2087 2088 static const mdb_dcmd_t dcmds[] = { 2089 { "pmcs", "?[-chiIpQqtTwWv] | -l", "print pmcs information", 2090 pmcs_dcmd, pmcs_help 2091 }, 2092 { "pmcs_tag", "?[-t tagtype|-s serialnum|-i index]", 2093 "Find work structures by tag type, serial number or index", 2094 pmcs_tag, pmcs_tag_help 2095 }, 2096 { NULL } 2097 }; 2098 2099 static const mdb_walker_t walkers[] = { 2100 { "pmcs_targets", "walk target structures", 2101 targets_walk_i, targets_walk_s, targets_walk_f }, 2102 { "pmcs_phys", "walk PHY structures", 2103 phy_walk_i, phy_walk_s, phy_walk_f }, 2104 { NULL } 2105 }; 2106 2107 static const mdb_modinfo_t modinfo = { 2108 MDB_API_VERSION, dcmds, walkers 2109 }; 2110 2111 const mdb_modinfo_t * 2112 _mdb_init(void) 2113 { 2114 return (&modinfo); 2115 } 2116