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