Mercurial > illumos > illumos-gate
changeset 10743:9bd3b79547a5
6888611 Improve data collection in pmcs mdb module
author | dh142964 <David.Hollister@Sun.COM> |
---|---|
date | Thu, 08 Oct 2009 10:44:50 -0600 |
parents | 98b03a712f28 |
children | 674514c28935 |
files | usr/src/cmd/mdb/common/modules/pmcs/pmcs.c usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_def.h |
diffstat | 3 files changed, 418 insertions(+), 81 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c Thu Oct 08 09:47:38 2009 +0100 +++ b/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c Thu Oct 08 10:44:50 2009 -0600 @@ -39,6 +39,7 @@ static uint32_t sas_phys, sata_phys, exp_phys, num_expanders, empty_phys; static pmcs_phy_t *pmcs_next_sibling(pmcs_phy_t *phyp); +static void display_one_work(pmcwork_t *wp, int verbose, int idx); static void print_sas_address(pmcs_phy_t *phy) @@ -207,6 +208,46 @@ mdb_printf("\n"); } +static void +display_completion_queue(struct pmcs_hw ss) +{ + pmcs_iocomp_cb_t ccb, *ccbp; + pmcwork_t work; + + if (ss.iocomp_cb_head == NULL) { + mdb_printf("Completion queue is empty.\n"); + return; + } + + ccbp = ss.iocomp_cb_head; + mdb_printf("%8s %10s %20s %8s %8s O D\n", + "HTag", "State", "Phy Path", "Target", "Timer"); + + while (ccbp) { + if (mdb_vread(&ccb, sizeof (pmcs_iocomp_cb_t), + (uintptr_t)ccbp) != sizeof (pmcs_iocomp_cb_t)) { + mdb_warn("Unable to read completion queue entry\n"); + return; + } + + if (mdb_vread(&work, sizeof (pmcwork_t), (uintptr_t)ccb.pwrk) + != sizeof (pmcwork_t)) { + mdb_warn("Unable to read work structure\n"); + return; + } + + /* + * Only print the work structure if it's still active. If + * it's not, it's been completed since we started looking at + * it. + */ + if (work.state != PMCS_WORK_STATE_NIL) { + display_one_work(&work, 0, 0); + } + ccbp = ccb.next; + } +} + /*ARGSUSED*/ static void display_hwinfo(struct pmcs_hw m, int verbose) @@ -383,20 +424,101 @@ sas_targets, sata_targets, smp_targets); } -/*ARGSUSED1*/ +static char * +work_state_to_string(uint32_t state) +{ + char *state_string; + + switch (state) { + case PMCS_WORK_STATE_NIL: + state_string = "Free"; + break; + case PMCS_WORK_STATE_READY: + state_string = "Ready"; + break; + case PMCS_WORK_STATE_ONCHIP: + state_string = "On Chip"; + break; + case PMCS_WORK_STATE_INTR: + state_string = "In Intr"; + break; + case PMCS_WORK_STATE_IOCOMPQ: + state_string = "I/O Comp"; + break; + case PMCS_WORK_STATE_ABORTED: + state_string = "I/O Aborted"; + break; + case PMCS_WORK_STATE_TIMED_OUT: + state_string = "I/O Timed Out"; + break; + default: + state_string = "INVALID"; + break; + } + + return (state_string); +} + +static void +display_one_work(pmcwork_t *wp, int verbose, int idx) +{ + char *state, *last_state; + char *path; + pmcs_xscsi_t xs; + pmcs_phy_t phy; + int tgt; + + state = work_state_to_string(wp->state); + last_state = work_state_to_string(wp->last_state); + + if (wp->ssp_event && wp->ssp_event != 0xffffffff) { + mdb_printf("SSP event 0x%x", wp->ssp_event); + } + + tgt = -1; + if (wp->xp) { + if (MDB_RD(&xs, sizeof (xs), wp->xp) == -1) { + NOREAD(pmcs_xscsi_t, wp->xp); + } else { + tgt = xs.target_num; + } + } + if (wp->phy) { + if (MDB_RD(&phy, sizeof (phy), wp->phy) == -1) { + NOREAD(pmcs_phy_t, wp->phy); + } + path = phy.path; + } else { + path = "N/A"; + } + + if (verbose) { + mdb_printf("%4d ", idx); + } + if (tgt == -1) { + mdb_printf("%08x %10s %20s N/A %8u %1d %1d ", + wp->htag, state, path, wp->timer, + wp->onwire, wp->dead); + } else { + mdb_printf("%08x %10s %20s %8d %8u %1d %1d ", + wp->htag, state, path, tgt, wp->timer, + wp->onwire, wp->dead); + } + if (verbose) { + mdb_printf("%08x %10s 0x%016p 0x%016p\n", + wp->last_htag, last_state, wp->last_phy, wp->last_xp); + } else { + mdb_printf("\n"); + } +} + static void display_work(struct pmcs_hw m, int verbose) { int idx; - int tgt; - int hdrp = 0; - pmcs_xscsi_t xs; - pmcs_phy_t phy; - char buf[16]; + boolean_t header_printed = B_FALSE; pmcwork_t work, *wp = &work; uintptr_t _wp; - char *state; - char *path; mdb_printf("\nActive Work structure information:\n"); mdb_printf("----------------------------------\n"); @@ -408,79 +530,79 @@ NOREAD(pmcwork_t, _wp); continue; } - if (wp->htag == PMCS_TAG_TYPE_FREE) { + + if (!verbose && (wp->htag == PMCS_TAG_TYPE_FREE)) { continue; } - if (hdrp++ == 0) { - mdb_printf("%8s %10s %20s %8s %8s O D\n", + + if (header_printed == B_FALSE) { + if (verbose) { + mdb_printf("%4s ", "Idx"); + } + mdb_printf("%8s %10s %20s %8s %8s O D ", "HTag", "State", "Phy Path", "Target", "Timer"); + if (verbose) { + mdb_printf("%8s %10s %18s %18s\n", "LastHTAG", + "LastState", "LastPHY", "LastTgt"); + } else { + mdb_printf("\n"); + } + header_printed = B_TRUE; } - switch (wp->state) { - case PMCS_WORK_STATE_NIL: - state = "N/A"; - break; - case PMCS_WORK_STATE_READY: - state = "Ready"; - break; - case PMCS_WORK_STATE_ONCHIP: - state = "On Chip"; - break; - case PMCS_WORK_STATE_INTR: - state = "In Intr"; - break; - case PMCS_WORK_STATE_IOCOMPQ: - state = "I/O Comp"; - break; - case PMCS_WORK_STATE_ABORTED: - state = "I/O Aborted"; - break; - case PMCS_WORK_STATE_TIMED_OUT: - state = "I/O Timed Out"; - break; - default: - mdb_snprintf(buf, sizeof (buf), "STATE=%d", wp->state); - state = buf; - break; - } - if (wp->ssp_event && wp->ssp_event != 0xffffffff) { - mdb_printf("SSP event 0x%x", wp->ssp_event); - } - tgt = -1; - if (wp->xp) { - if (MDB_RD(&xs, sizeof (xs), wp->xp) == -1) { - NOREAD(pmcs_xscsi_t, wp->xp); - } else { - tgt = xs.target_num; - } - } - if (wp->phy) { - if (MDB_RD(&phy, sizeof (phy), wp->phy) == -1) { - NOREAD(pmcs_phy_t, wp->phy); - continue; - } - path = phy.path; - } else { - path = "????"; - } - mdb_printf("%08x %10s %20s %8d %8u %1d %1d\n", - wp->htag, state, path, tgt, wp->timer, - wp->onwire, wp->dead); + + display_one_work(wp, verbose, idx); } } static void -print_spcmd(pmcs_cmd_t *sp, void *kaddr, int printhdr, int indent) +print_spcmd(pmcs_cmd_t *sp, void *kaddr, int printhdr, int verbose) { - if (indent) - mdb_inc_indent(4); + int cdb_size, idx; + struct scsi_pkt pkt; + uchar_t cdb[256]; + if (printhdr) { - mdb_printf("%16s %16s %16s %8s %s\n", - "Command", "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag"); + if (verbose) { + mdb_printf("%16s %16s %16s %8s %s CDB\n", "Command", + "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag"); + } else { + mdb_printf("%16s %16s %16s %8s %s\n", "Command", + "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag"); + } } - mdb_printf("%16p %16p %16p %08x %08x\n", + + mdb_printf("%16p %16p %16p %08x %08x ", kaddr, sp->cmd_pkt, sp->cmd_clist, sp->cmd_tag, sp->cmd_satltag); - if (indent) - mdb_dec_indent(4); + + /* + * If we're printing verbose, dump the CDB as well. + */ + if (verbose) { + if (sp->cmd_pkt) { + if (mdb_vread(&pkt, sizeof (struct scsi_pkt), + (uintptr_t)sp->cmd_pkt) != + sizeof (struct scsi_pkt)) { + mdb_warn("Unable to read SCSI pkt\n"); + return; + } + cdb_size = pkt.pkt_cdblen; + if (mdb_vread(&cdb[0], cdb_size, + (uintptr_t)pkt.pkt_cdbp) != cdb_size) { + mdb_warn("Unable to read CDB\n"); + return; + } + + for (idx = 0; idx < cdb_size; idx++) { + mdb_printf("%02x ", cdb[idx]); + } + } else { + mdb_printf("N/A"); + } + + mdb_printf("\n"); + } else { + mdb_printf("\n"); + } } /*ARGSUSED1*/ @@ -503,7 +625,7 @@ NOREAD(pmcs_cmd_t, sp); break; } - print_spcmd(&s, sp, first, 0); + print_spcmd(&s, sp, first, verbose); sp = s.cmd_next.stqe_next; first = 0; } @@ -519,7 +641,7 @@ NOREAD(pmcs_cmd_t, sp); break; } - print_spcmd(&s, sp, first, 0); + print_spcmd(&s, sp, first, verbose); sp = s.cmd_next.stqe_next; first = 0; } @@ -554,7 +676,7 @@ NOREAD(pmcs_cmd_t, sp); break; } - print_spcmd(&s, sp, first, 0); + print_spcmd(&s, sp, first, verbose); sp = s.cmd_next.stqe_next; first = 0; } @@ -570,7 +692,7 @@ NOREAD(pmcs_cmd_t, sp); break; } - print_spcmd(&s, sp, first, 0); + print_spcmd(&s, sp, first, verbose); sp = s.cmd_next.stqe_next; first = 0; } @@ -586,7 +708,7 @@ NOREAD(pmcs_cmd_t, sp); break; } - print_spcmd(&s, sp, first, 0); + print_spcmd(&s, sp, first, verbose); sp = s.cmd_next.stqe_next; first = 0; } @@ -1580,6 +1702,204 @@ mdb_free(wsp->walk_data, sizeof (pmcs_phy_t)); } +static void +display_matching_work(struct pmcs_hw ss, uintmax_t index, uintmax_t snum, + uintmax_t tag_type) +{ + int idx; + pmcwork_t work, *wp = &work; + uintptr_t _wp; + boolean_t printed_header = B_FALSE; + uint32_t mask, mask_val, match_val; + char *match_type; + + if (index != UINT_MAX) { + match_type = "index"; + mask = PMCS_TAG_INDEX_MASK; + mask_val = index << PMCS_TAG_INDEX_SHIFT; + match_val = index; + } else if (snum != UINT_MAX) { + match_type = "serial number"; + mask = PMCS_TAG_SERNO_MASK; + mask_val = snum << PMCS_TAG_SERNO_SHIFT; + match_val = snum; + } else { + switch (tag_type) { + case PMCS_TAG_TYPE_NONE: + match_type = "tag type NONE"; + break; + case PMCS_TAG_TYPE_CBACK: + match_type = "tag type CBACK"; + break; + case PMCS_TAG_TYPE_WAIT: + match_type = "tag type WAIT"; + break; + } + mask = PMCS_TAG_TYPE_MASK; + mask_val = tag_type << PMCS_TAG_TYPE_SHIFT; + match_val = tag_type; + } + + _wp = (uintptr_t)ss.work; + + for (idx = 0; idx < ss.max_cmd; idx++, _wp += sizeof (pmcwork_t)) { + if (MDB_RD(&work, sizeof (pmcwork_t), _wp) == -1) { + NOREAD(pmcwork_t, _wp); + continue; + } + + if ((work.htag & mask) != mask_val) { + continue; + } + + if (printed_header == B_FALSE) { + if (tag_type) { + mdb_printf("\nWork structures matching %s\n\n", + match_type, match_val); + } else { + mdb_printf("\nWork structures matching %s of " + "0x%x\n\n", match_type, match_val); + } + mdb_printf("%8s %10s %20s %8s %8s O D\n", + "HTag", "State", "Phy Path", "Target", "Timer"); + printed_header = B_TRUE; + } + + display_one_work(wp, 0, 0); + } + + if (!printed_header) { + mdb_printf("No work structure matches found\n"); + } +} + +static int +pmcs_tag(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + struct pmcs_hw ss; + uintmax_t tag_type = UINT_MAX; + uintmax_t snum = UINT_MAX; + uintmax_t index = UINT_MAX; + int args = 0; + void *pmcs_state; + char *state_str; + struct dev_info dip; + + if (!(flags & DCMD_ADDRSPEC)) { + pmcs_state = NULL; + if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) { + mdb_warn("can't read pmcs_softc_state"); + return (DCMD_ERR); + } + if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs_tag", argc, + argv, (uintptr_t)pmcs_state) == -1) { + mdb_warn("mdb_pwalk_dcmd failed"); + return (DCMD_ERR); + } + return (DCMD_OK); + } + + if (mdb_getopts(argc, argv, + 'i', MDB_OPT_UINT64, &index, + 's', MDB_OPT_UINT64, &snum, + 't', MDB_OPT_UINT64, &tag_type) != argc) + return (DCMD_USAGE); + + /* + * Count the number of supplied options and make sure they are + * within appropriate ranges. If they're set to UINT_MAX, that means + * they were not supplied, in which case reset them to 0. + */ + if (index != UINT_MAX) { + args++; + if (index > PMCS_TAG_INDEX_MASK) { + mdb_warn("Index is out of range\n"); + return (DCMD_USAGE); + } + } + + if (tag_type != UINT_MAX) { + args++; + switch (tag_type) { + case PMCS_TAG_TYPE_NONE: + case PMCS_TAG_TYPE_CBACK: + case PMCS_TAG_TYPE_WAIT: + break; + default: + mdb_warn("Invalid tag type\n"); + return (DCMD_USAGE); + } + } + + if (snum != UINT_MAX) { + args++; + if (snum > (PMCS_TAG_SERNO_MASK >> PMCS_TAG_SERNO_SHIFT)) { + mdb_warn("Serial number is out of range\n"); + return (DCMD_USAGE); + } + } + + /* + * Make sure 1 and only 1 option is specified + */ + if ((args == 0) || (args > 1)) { + mdb_warn("Exactly one of -i, -s and -t must be specified\n"); + return (DCMD_USAGE); + } + + if (MDB_RD(&ss, sizeof (ss), addr) == -1) { + NOREAD(pmcs_hw_t, addr); + return (DCMD_ERR); + } + + if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) { + NOREAD(pmcs_hw_t, addr); + return (DCMD_ERR); + } + + /* processing completed */ + + if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) || + (flags & DCMD_LOOPFIRST)) { + if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST)) + mdb_printf("\n"); + mdb_printf("%16s %9s %4s B C WorkFlags wserno DbgMsk %16s\n", + "Address", "State", "Inst", "DIP"); + mdb_printf("=================================" + "============================================\n"); + } + + switch (ss.state) { + case STATE_NIL: + state_str = "Invalid"; + break; + case STATE_PROBING: + state_str = "Probing"; + break; + case STATE_RUNNING: + state_str = "Running"; + break; + case STATE_UNPROBING: + state_str = "Unprobing"; + break; + case STATE_DEAD: + state_str = "Dead"; + break; + } + + mdb_printf("%16p %9s %4d %1d %1d 0x%08x 0x%04x 0x%04x %16p\n", addr, + state_str, dip.devi_instance, ss.blocked, ss.configuring, + ss.work_flags, ss.wserno, ss.debug_mask, ss.dip); + mdb_printf("\n"); + + mdb_inc_indent(4); + display_matching_work(ss, index, snum, tag_type); + mdb_dec_indent(4); + mdb_printf("\n"); + + return (DCMD_OK); +} + static int pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { @@ -1596,6 +1916,7 @@ uint_t ibq = FALSE; uint_t obq = FALSE; uint_t tgt_phy_count = FALSE; + uint_t compq = FALSE; int rv = DCMD_OK; void *pmcs_state; char *state_str; @@ -1616,6 +1937,7 @@ } if (mdb_getopts(argc, argv, + 'c', MDB_OPT_SETBITS, TRUE, &compq, 'h', MDB_OPT_SETBITS, TRUE, &hw_info, 'i', MDB_OPT_SETBITS, TRUE, &ic_info, 'I', MDB_OPT_SETBITS, TRUE, &iport_info, @@ -1646,10 +1968,10 @@ * Thus, a provided address is ignored. In addition, other options * cannot be specified at the same time. */ - if (tracelog) { if (hw_info || ic_info || iport_info || phy_info || work_info || - target_info || waitqs_info || ibq || obq || tgt_phy_count) { + target_info || waitqs_info || ibq || obq || tgt_phy_count || + compq) { return (DCMD_USAGE); } @@ -1666,7 +1988,7 @@ if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) || (flags & DCMD_LOOPFIRST) || phy_info || target_info || hw_info || - work_info || waitqs_info || ibq || obq || tgt_phy_count) { + work_info || waitqs_info || ibq || obq || tgt_phy_count || compq) { if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST)) mdb_printf("\n"); mdb_printf("%16s %9s %4s B C WorkFlags wserno DbgMsk %16s\n", @@ -1727,6 +2049,9 @@ if (iport_info) display_iport(ss, addr, verbose); + if (compq) + display_completion_queue(ss); + mdb_dec_indent(4); return (rv); @@ -1736,6 +2061,7 @@ pmcs_help() { mdb_printf("Prints summary information about each pmcs instance.\n" + " -c: Dump the completion queue\n" " -h: Print more detailed hardware information\n" " -i: Print interrupt coalescing information\n" " -I: Print information about each iport\n" @@ -1750,10 +2076,24 @@ " -v: Add verbosity to the above options\n"); } +void +pmcs_tag_help() +{ + mdb_printf("Print all work structures by matching the tag.\n" + " -i index: Match tag index (0x000 - 0xfff)\n" + " -s serialnumber: Match serial number (0x0000 - 0xffff)\n" + " -t tagtype: Match tag type [NONE(1), CBACK(2), " + "WAIT(3)]\n"); +} + static const mdb_dcmd_t dcmds[] = { - { "pmcs", "?[-hiIpQqtTwWv] | -l", "print pmcs information", + { "pmcs", "?[-chiIpQqtTwWv] | -l", "print pmcs information", pmcs_dcmd, pmcs_help }, + { "pmcs_tag", "?[-t tagtype|-s serialnum|-i index]", + "Find work structures by tag type, serial number or index", + pmcs_tag, pmcs_tag_help + }, { NULL } };
--- a/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c Thu Oct 08 09:47:38 2009 +0100 +++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c Thu Oct 08 10:44:50 2009 -0600 @@ -4340,14 +4340,12 @@ ASSERT(p != NULL); ASSERT(mutex_owned(&p->lock)); -#ifdef DEBUG p->last_ptr = p->ptr; p->last_arg = p->arg; p->last_phy = p->phy; p->last_xp = p->xp; p->last_htag = p->htag; p->last_state = p->state; -#endif p->finish = gethrtime(); if (p->phy) {
--- a/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_def.h Thu Oct 08 09:47:38 2009 +0100 +++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_def.h Thu Oct 08 10:44:50 2009 -0600 @@ -201,7 +201,6 @@ uint32_t ssp_event; /* ssp event */ pmcs_dtype_t dtype; /* stash, incase phy gets cleared */ - /* DEBUG-only fields from here on */ void *last_ptr; void *last_arg; pmcs_phy_t *last_phy;