--- /dev/null	2014-06-12 17:42:14.000000000 +0100
+++ new/usr/src/cmd/mdb/common/modules/mpt_sas3/mpt_sas3.c	2014-06-12 17:42:14.143870790 +0100
@@ -0,0 +1,912 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ * Copyright (c) 2014, Tegile Systems Inc. All rights reserved.
+ */
+
+#include <limits.h>
+#include <sys/mdb_modapi.h>
+#include <sys/sysinfo.h>
+#include <sys/sunmdi.h>
+#include <sys/list.h>
+#include <sys/scsi/scsi.h>
+
+#pragma pack(1)
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2_type.h>
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2.h>
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2_cnfg.h>
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2_init.h>
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2_ioc.h>
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2_sas.h>
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2_raid.h>
+#include <sys/scsi/adapters/mpt_sas3/mpi/mpi2_tool.h>
+#pragma pack()
+
+#include <sys/scsi/adapters/mpt_sas3/mptsas3_var.h>
+#include <sys/scsi/adapters/mpt_sas3/mptsas3_hash.h>
+
+struct {
+	int	value;
+	char	*text;
+} devinfo_array[] = {
+	{ MPI2_SAS_DEVICE_INFO_SEP,		"SEP" },
+	{ MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE,	"ATAPI device" },
+	{ MPI2_SAS_DEVICE_INFO_LSI_DEVICE,	"LSI device" },
+	{ MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH,	"direct attach" },
+	{ MPI2_SAS_DEVICE_INFO_SSP_TARGET,	"SSP tgt" },
+	{ MPI2_SAS_DEVICE_INFO_STP_TARGET,	"STP tgt" },
+	{ MPI2_SAS_DEVICE_INFO_SMP_TARGET,	"SMP tgt" },
+	{ MPI2_SAS_DEVICE_INFO_SATA_DEVICE,	"SATA dev" },
+	{ MPI2_SAS_DEVICE_INFO_SSP_INITIATOR,	"SSP init" },
+	{ MPI2_SAS_DEVICE_INFO_STP_INITIATOR,	"STP init" },
+	{ MPI2_SAS_DEVICE_INFO_SMP_INITIATOR,	"SMP init" },
+	{ MPI2_SAS_DEVICE_INFO_SATA_HOST,	"SATA host" }
+};
+
+int
+construct_path(uintptr_t addr, char *result)
+{
+	struct	dev_info	d;
+	char	devi_node[PATH_MAX];
+	char	devi_addr[PATH_MAX];
+
+	if (mdb_vread(&d, sizeof (d), addr) == -1) {
+		mdb_warn("couldn't read dev_info");
+		return (DCMD_ERR);
+	}
+
+	if (d.devi_parent) {
+		construct_path((uintptr_t)d.devi_parent, result);
+		mdb_readstr(devi_node, sizeof (devi_node),
+		    (uintptr_t)d.devi_node_name);
+		mdb_readstr(devi_addr, sizeof (devi_addr),
+		    (uintptr_t)d.devi_addr);
+		mdb_snprintf(result+strlen(result),
+		    PATH_MAX-strlen(result),
+		    "/%s%s%s", devi_node, (*devi_addr ? "@" : ""),
+		    devi_addr);
+	}
+	return (DCMD_OK);
+}
+
+/* ARGSUSED */
+int
+mdi_info_cb(uintptr_t addr, const void *data, void *cbdata)
+{
+	struct	mdi_pathinfo	pi;
+	struct	mdi_client	c;
+	char	dev_path[PATH_MAX];
+	char	string[PATH_MAX];
+	int	mdi_target = 0, mdi_lun = 0;
+	int	target = *(int *)cbdata;
+
+	if (mdb_vread(&pi, sizeof (pi), addr) == -1) {
+		mdb_warn("couldn't read mdi_pathinfo");
+		return (DCMD_ERR);
+	}
+	mdb_readstr(string, sizeof (string), (uintptr_t)pi.pi_addr);
+	mdi_target = (int)mdb_strtoull(string);
+	mdi_lun = (int)mdb_strtoull(strchr(string, ',') + 1);
+	if (target != mdi_target)
+		return (0);
+
+	if (mdb_vread(&c, sizeof (c), (uintptr_t)pi.pi_client) == -1) {
+		mdb_warn("couldn't read mdi_client");
+		return (-1);
+	}
+
+	*dev_path = NULL;
+	if (construct_path((uintptr_t)c.ct_dip, dev_path) != DCMD_OK)
+		strcpy(dev_path, "unknown");
+
+	mdb_printf("LUN %d: %s\n", mdi_lun, dev_path);
+	mdb_printf("       dip: %p %s path", c.ct_dip,
+	    (pi.pi_preferred ? "preferred" : ""));
+	switch (pi.pi_state & MDI_PATHINFO_STATE_MASK) {
+		case MDI_PATHINFO_STATE_INIT:
+			mdb_printf(" initializing");
+			break;
+		case MDI_PATHINFO_STATE_ONLINE:
+			mdb_printf(" online");
+			break;
+		case MDI_PATHINFO_STATE_STANDBY:
+			mdb_printf(" standby");
+			break;
+		case MDI_PATHINFO_STATE_FAULT:
+			mdb_printf(" fault");
+			break;
+		case MDI_PATHINFO_STATE_OFFLINE:
+			mdb_printf(" offline");
+			break;
+		default:
+			mdb_printf(" invalid state");
+			break;
+	}
+	mdb_printf("\n");
+	return (0);
+}
+
+void
+mdi_info(struct mptsas *mp, int target)
+{
+	struct	dev_info	d;
+	struct	mdi_phci	p;
+
+	if (mdb_vread(&d, sizeof (d), (uintptr_t)mp->m_dip) == -1) {
+		mdb_warn("couldn't read m_dip");
+		return;
+	}
+
+	if (MDI_PHCI(&d)) {
+		if (mdb_vread(&p, sizeof (p), (uintptr_t)d.devi_mdi_xhci)
+		    == -1) {
+			mdb_warn("couldn't read m_dip.devi_mdi_xhci");
+			return;
+		}
+		if (p.ph_path_head)
+			mdb_pwalk("mdipi_phci_list", (mdb_walk_cb_t)mdi_info_cb,
+			    &target, (uintptr_t)p.ph_path_head);
+		return;
+	}
+}
+
+void
+print_cdb(mptsas_cmd_t *m)
+{
+	struct	scsi_pkt	pkt;
+	uchar_t	cdb[512];	/* an arbitrarily large number */
+	int	j;
+
+	if (mdb_vread(&pkt, sizeof (pkt), (uintptr_t)m->cmd_pkt) == -1) {
+		mdb_warn("couldn't read cmd_pkt");
+		return;
+	}
+
+	/*
+	 * We use cmd_cdblen here because 5.10 doesn't
+	 * have the cdb length in the pkt
+	 */
+	if (mdb_vread(&cdb, m->cmd_cdblen, (uintptr_t)pkt.pkt_cdbp) == -1) {
+		mdb_warn("couldn't read pkt_cdbp");
+		return;
+	}
+
+	mdb_printf("%3d,%-3d [ ",
+	    pkt.pkt_address.a_target, pkt.pkt_address.a_lun);
+
+	for (j = 0; j < m->cmd_cdblen; j++)
+		mdb_printf("%02x ", cdb[j]);
+
+	mdb_printf("]\n");
+}
+
+
+void
+display_ports(struct mptsas *mp)
+{
+	int i;
+	mdb_printf("\n");
+	mdb_printf("phy number and port mapping table\n");
+	for (i = 0; i < MPTSAS_MAX_PHYS; i++) {
+		if (mp->m_phy_info[i].attached_devhdl) {
+			mdb_printf("phy %x --> port %x, phymask %x,"
+			"attached_devhdl %x\n", i, mp->m_phy_info[i].port_num,
+			    mp->m_phy_info[i].phy_mask,
+			    mp->m_phy_info[i].attached_devhdl);
+		}
+	}
+	mdb_printf("\n");
+}
+
+static uintptr_t
+klist_head(list_t *lp, uintptr_t klp)
+{
+	if ((uintptr_t)lp->list_head.list_next ==
+	    klp + offsetof(struct list, list_head))
+		return (NULL);
+
+	return ((uintptr_t)(((char *)lp->list_head.list_next) -
+	    lp->list_offset));
+}
+
+static uintptr_t
+klist_next(list_t *lp, uintptr_t klp, void *op)
+{
+	/* LINTED E_BAD_PTR_CAST_ALIG */
+	struct list_node *np = (struct list_node *)(((char *)op) +
+	    lp->list_offset);
+
+	if ((uintptr_t)np->list_next == klp + offsetof(struct list, list_head))
+		return (NULL);
+
+	return (((uintptr_t)(np->list_next)) - lp->list_offset);
+}
+
+static void *
+krefhash_first(uintptr_t khp, uintptr_t *addr)
+{
+	refhash_t mh;
+	uintptr_t klp;
+	uintptr_t kop;
+	void *rp;
+
+	mdb_vread(&mh, sizeof (mh), khp);
+	klp = klist_head(&mh.rh_objs, khp + offsetof(refhash_t, rh_objs));
+	if (klp == 0)
+		return (NULL);
+
+	kop = klp - mh.rh_link_off;
+	if (addr)
+		*addr = kop;
+	rp = mdb_alloc(mh.rh_obj_size, UM_SLEEP);
+	mdb_vread(rp, mh.rh_obj_size, kop);
+
+	return (rp);
+}
+
+static void *
+krefhash_next(uintptr_t khp, void *op, uintptr_t *addr)
+{
+	refhash_t mh;
+	void *prev = op;
+	refhash_link_t *lp;
+	uintptr_t klp;
+	uintptr_t kop;
+	refhash_link_t ml;
+	void *rp;
+
+	mdb_vread(&mh, sizeof (mh), khp);
+	/* LINTED E_BAD_PTR_CAST_ALIG */
+	lp = (refhash_link_t *)(((char *)(op)) + mh.rh_link_off);
+	ml = *lp;
+	while ((klp = klist_next(&mh.rh_objs,
+	    khp + offsetof(refhash_t, rh_objs), &ml)) != NULL) {
+		mdb_vread(&ml, sizeof (ml), klp);
+		if (!(ml.rhl_flags & RHL_F_DEAD))
+			break;
+	}
+
+	if (klp == 0) {
+		mdb_free(prev, mh.rh_obj_size);
+		return (NULL);
+	}
+
+	kop = klp - mh.rh_link_off;
+	if (addr)
+		*addr = kop;
+	rp = mdb_alloc(mh.rh_obj_size, UM_SLEEP);
+	mdb_vread(rp, mh.rh_obj_size, kop);
+
+	mdb_free(prev, mh.rh_obj_size);
+	return (rp);
+}
+
+void
+display_targets(struct mptsas *mp, uint_t verbose)
+{
+	mptsas_target_t *ptgt;
+	mptsas_smp_t *psmp;
+	int loop, comma;
+	uintptr_t p_addr;
+
+	mdb_printf("\n");
+	mdb_printf(" mptsas_target_t slot devhdl      wwn     ncmds throttle   "
+	    "dr_flag dups\n");
+	mdb_printf("---------------------------------------"
+	    "-------------------------------\n");
+	for (ptgt = (mptsas_target_t *)krefhash_first(
+		(uintptr_t)mp->m_targets, &p_addr);
+	    ptgt != NULL;
+	    ptgt = krefhash_next((uintptr_t)mp->m_targets, ptgt, &p_addr)) {
+		if (ptgt->m_addr.mta_wwn ||
+		    ptgt->m_deviceinfo) {
+			mdb_printf("%16p ", p_addr);
+			mdb_printf("%4d ", ptgt->m_slot_num);
+			mdb_printf("%4d ", ptgt->m_devhdl);
+			if (ptgt->m_addr.mta_wwn)
+				mdb_printf("%"PRIx64" ",
+				    ptgt->m_addr.mta_wwn);
+			mdb_printf("%3d", ptgt->m_t_ncmds);
+			switch (ptgt->m_t_throttle) {
+				case QFULL_THROTTLE:
+					mdb_printf("   QFULL ");
+					break;
+				case DRAIN_THROTTLE:
+					mdb_printf("   DRAIN ");
+					break;
+				case HOLD_THROTTLE:
+					mdb_printf("    HOLD ");
+					break;
+				case MAX_THROTTLE:
+					mdb_printf("     MAX ");
+					break;
+				default:
+					mdb_printf("%8d ",
+					    ptgt->m_t_throttle);
+			}
+			switch (ptgt->m_dr_flag) {
+				case MPTSAS_DR_INACTIVE:
+					mdb_printf("  INACTIVE ");
+					break;
+				case MPTSAS_DR_INTRANSITION:
+					mdb_printf("TRANSITION ");
+					break;
+				default:
+					mdb_printf("   UNKNOWN ");
+					break;
+				}
+			mdb_printf("%d\n",
+			    ptgt->m_dups);
+
+			if (verbose) {
+				mdb_inc_indent(5);
+				if ((ptgt->m_deviceinfo &
+				    MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+				    MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
+					mdb_printf("Fanout expander: ");
+				if ((ptgt->m_deviceinfo &
+				    MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+				    MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER)
+					mdb_printf("Edge expander: ");
+				if ((ptgt->m_deviceinfo &
+				    MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+				    MPI2_SAS_DEVICE_INFO_END_DEVICE)
+					mdb_printf("End device: ");
+				if ((ptgt->m_deviceinfo &
+				    MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+				    MPI2_SAS_DEVICE_INFO_NO_DEVICE)
+					mdb_printf("No device ");
+
+				for (loop = 0, comma = 0;
+				    loop < (sizeof (devinfo_array) /
+				    sizeof (devinfo_array[0])); loop++) {
+					if (ptgt->m_deviceinfo &
+					    devinfo_array[loop].value) {
+						mdb_printf("%s%s",
+						    (comma ? ", " : ""),
+						    devinfo_array[loop].text);
+						comma++;
+					}
+				}
+				mdb_printf("\n");
+				mdi_info(mp, ptgt->m_slot_num);
+				mdb_dec_indent(5);
+			}
+		}
+	}
+
+	mdb_printf("\n");
+	mdb_printf("    mptsas_smp_t devhdl      wwn          phymask\n");
+	mdb_printf("---------------------------------------"
+	    "------------------\n");
+	for (psmp = (mptsas_smp_t *)krefhash_first(
+	    (uintptr_t)mp->m_smp_targets, &p_addr);
+	    psmp != NULL;
+	    psmp = krefhash_next((uintptr_t)mp->m_smp_targets, psmp,
+	    &p_addr)) {
+		mdb_printf("%16p   ", p_addr);
+		mdb_printf("%4d  %"PRIx64"    %04x\n",
+		    psmp->m_devhdl, psmp->m_addr.mta_wwn,
+		    psmp->m_addr.mta_phymask);
+		if (verbose) {
+			mdb_inc_indent(5);
+			if ((psmp->m_deviceinfo &
+				MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+			    MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
+				mdb_printf("Fanout expander: ");
+			if ((psmp->m_deviceinfo &
+				MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+			    MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER)
+				mdb_printf("Edge expander: ");
+			if ((psmp->m_deviceinfo &
+				MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+			    MPI2_SAS_DEVICE_INFO_END_DEVICE)
+				mdb_printf("End device: ");
+			if ((psmp->m_deviceinfo &
+				MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
+			    MPI2_SAS_DEVICE_INFO_NO_DEVICE)
+				mdb_printf("No device ");
+
+			for (loop = 0, comma = 0;
+			     loop < (sizeof (devinfo_array) /
+				 sizeof (devinfo_array[0])); loop++) {
+				if (psmp->m_deviceinfo &
+				    devinfo_array[loop].value) {
+					mdb_printf("%s%s",
+					    (comma ? ", " : ""),
+					    devinfo_array[loop].text);
+					comma++;
+				}
+			}
+			mdb_printf("\n");
+			mdb_dec_indent(5);
+		}
+	}
+}
+
+int
+display_slotinfo(struct mptsas *mp, struct mptsas_slots *s)
+{
+	int			i, nslots;
+	struct mptsas_cmd	c, *q, *slots;
+	mptsas_target_t		*ptgt;
+	int			header_output = 0;
+	int			rv = DCMD_OK;
+	int			slots_in_use = 0;
+	int			tcmds = 0;
+	int			mismatch = 0;
+	int			wq, dq;
+	int			ncmds = 0;
+	ulong_t			saved_indent;
+
+	nslots = s->m_n_normal;
+	slots = mdb_alloc(sizeof (mptsas_cmd_t) * nslots, UM_SLEEP);
+
+	for (i = 0; i < nslots; i++)
+		if (s->m_slot[i]) {
+			slots_in_use++;
+			if (mdb_vread(&slots[i], sizeof (mptsas_cmd_t),
+			    (uintptr_t)s->m_slot[i]) == -1) {
+				mdb_warn("couldn't read slot");
+				s->m_slot[i] = NULL;
+			}
+			if ((slots[i].cmd_flags & CFLAG_CMDIOC) == 0)
+				tcmds++;
+			if (i != slots[i].cmd_slot)
+				mismatch++;
+		}
+
+	for (q = mp->m_waitq, wq = 0; q; q = c.cmd_linkp, wq++)
+		if (mdb_vread(&c, sizeof (mptsas_cmd_t), (uintptr_t)q) == -1) {
+			mdb_warn("couldn't follow m_waitq");
+			rv = DCMD_ERR;
+			goto exit;
+		}
+
+	for (q = mp->m_dlist.dl_q, dq = 0; q; q = c.cmd_linkp, dq++)
+		if (mdb_vread(&c, sizeof (mptsas_cmd_t), (uintptr_t)q) == -1) {
+			mdb_warn("couldn't follow m_doneq");
+			rv = DCMD_ERR;
+			goto exit;
+		}
+
+	for (ptgt = (mptsas_target_t *)krefhash_first(
+		(uintptr_t)mp->m_targets, NULL);
+	    ptgt != NULL;
+	     ptgt = krefhash_next((uintptr_t)mp->m_targets, ptgt, NULL)) {
+		if (ptgt->m_addr.mta_wwn ||
+		    ptgt->m_deviceinfo) {
+			ncmds += ptgt->m_t_ncmds;
+		}
+	}
+
+	mdb_printf("\n");
+	mdb_printf("   mpt.  slot               mptsas_slots     slot");
+	mdb_printf("\n");
+	mdb_printf("m_ncmds total"
+	    " targ throttle m_t_ncmds targ_tot wq dq");
+	mdb_printf("\n");
+	mdb_printf("----------------------------------------------------");
+	mdb_printf("\n");
+
+	mdb_printf("%7d ", mp->m_ncmds);
+	mdb_printf("%s", (mp->m_ncmds == slots_in_use ? "  " : "!="));
+	mdb_printf("%3d               total %3d ", slots_in_use, ncmds);
+	mdb_printf("%s", (tcmds == ncmds ? "     " : "   !="));
+	mdb_printf("%3d %2d %2d\n", tcmds, wq, dq);
+
+	saved_indent = mdb_dec_indent(0);
+	mdb_dec_indent(saved_indent);
+
+	for (i = 0; i < s->m_n_normal; i++)
+		if (s->m_slot[i]) {
+			if (!header_output) {
+				mdb_printf("\n");
+				mdb_printf("mptsas_cmd          slot cmd_slot "
+				    "cmd_flags cmd_pkt_flags scsi_pkt      "
+				    "  targ,lun [ pkt_cdbp ...\n");
+				mdb_printf("-------------------------------"
+				    "--------------------------------------"
+				    "--------------------------------------"
+				    "------\n");
+				header_output = 1;
+			}
+			mdb_printf("%16p %4d %s %4d  %8x      %8x %16p ",
+			    s->m_slot[i], i,
+			    (i == slots[i].cmd_slot?"   ":"BAD"),
+			    slots[i].cmd_slot,
+			    slots[i].cmd_flags,
+			    slots[i].cmd_pkt_flags,
+			    slots[i].cmd_pkt);
+			(void) print_cdb(&slots[i]);
+		}
+
+	/* print the wait queue */
+
+	for (q = mp->m_waitq; q; q = c.cmd_linkp) {
+		if (q == mp->m_waitq)
+			mdb_printf("\n");
+		if (mdb_vread(&c, sizeof (mptsas_cmd_t), (uintptr_t)q)
+		    == -1) {
+			mdb_warn("couldn't follow m_waitq");
+			rv = DCMD_ERR;
+			goto exit;
+		}
+		mdb_printf("%16p wait n/a %4d  %8x      %8x %16p ",
+		    q, c.cmd_slot, c.cmd_flags, c.cmd_pkt_flags,
+		    c.cmd_pkt);
+		print_cdb(&c);
+	}
+
+	/* print the done queue */
+
+	for (q = mp->m_dlist.dl_q; q; q = c.cmd_linkp) {
+		if (q == mp->m_dlist.dl_q)
+			mdb_printf("\n");
+		if (mdb_vread(&c, sizeof (mptsas_cmd_t), (uintptr_t)q)
+		    == -1) {
+			mdb_warn("couldn't follow m_doneq");
+			rv = DCMD_ERR;
+			goto exit;
+		}
+		mdb_printf("%16p done  n/a %4d  %8x      %8x %16p ",
+		    q, c.cmd_slot, c.cmd_flags, c.cmd_pkt_flags,
+		    c.cmd_pkt);
+		print_cdb(&c);
+	}
+
+	mdb_inc_indent(saved_indent);
+
+	if (mp->m_ncmds != slots_in_use)
+		mdb_printf("WARNING: mpt.m_ncmds does not match the number of "
+		    "slots in use\n");
+
+	if (tcmds != ncmds)
+		mdb_printf("WARNING: the total of m_target[].m_t_ncmds does "
+		    "not match the slots in use\n");
+
+	if (mismatch)
+		mdb_printf("WARNING: corruption in slot table, "
+		    "m_slot[].cmd_slot incorrect\n");
+
+	/* now check for corruptions */
+
+	for (q = mp->m_waitq; q; q = c.cmd_linkp) {
+		for (i = 0; i < nslots; i++)
+			if (s->m_slot[i] == q)
+				mdb_printf("WARNING: m_waitq entry"
+				    "(mptsas_cmd_t) %p is in m_slot[%i]\n",
+				    q, i);
+
+		if (mdb_vread(&c, sizeof (mptsas_cmd_t), (uintptr_t)q) == -1) {
+			mdb_warn("couldn't follow m_waitq");
+			rv = DCMD_ERR;
+			goto exit;
+		}
+	}
+
+	for (q = mp->m_dlist.dl_q; q; q = c.cmd_linkp) {
+		for (i = 0; i < nslots; i++)
+			if (s->m_slot[i] == q)
+				mdb_printf("WARNING: m_doneq entry "
+				"(mptsas_cmd_t) %p is in m_slot[%i]\n", q, i);
+
+		if (mdb_vread(&c, sizeof (mptsas_cmd_t), (uintptr_t)q) == -1) {
+			mdb_warn("couldn't follow m_doneq");
+			rv = DCMD_ERR;
+			goto exit;
+		}
+		if ((c.cmd_flags & CFLAG_FINISHED) == 0)
+			mdb_printf("WARNING: m_doneq entry (mptsas_cmd_t) %p "
+			    "should have CFLAG_FINISHED set\n", q);
+		if (c.cmd_flags & CFLAG_IN_TRANSPORT)
+			mdb_printf("WARNING: m_doneq entry (mptsas_cmd_t) %p "
+			    "should not have CFLAG_IN_TRANSPORT set\n", q);
+		if (c.cmd_flags & CFLAG_CMDARQ)
+			mdb_printf("WARNING: m_doneq entry (mptsas_cmd_t) %p "
+			    "should not have CFLAG_CMDARQ set\n", q);
+		if (c.cmd_flags & CFLAG_COMPLETED)
+			mdb_printf("WARNING: m_doneq entry (mptsas_cmd_t) %p "
+			    "should not have CFLAG_COMPLETED set\n", q);
+	}
+
+exit:
+	mdb_free(slots, sizeof (mptsas_cmd_t) * nslots);
+	return (rv);
+}
+
+void
+display_deviceinfo(struct mptsas *mp)
+{
+	char	device_path[PATH_MAX];
+
+	*device_path = 0;
+	if (construct_path((uintptr_t)mp->m_dip, device_path) != DCMD_OK) {
+		strcpy(device_path, "couldn't determine device path");
+	}
+
+	mdb_printf("\n");
+	mdb_printf("base_wwid          phys "
+	    " prodid  devid          revid   ssid\n");
+	mdb_printf("-----------------------------"
+	    "----------------------------------\n");
+	mdb_printf("%"PRIx64"     %2d  "
+	    "0x%04x 0x%04x ", mp->un.m_base_wwid, mp->m_num_phys,
+	    mp->m_productid, mp->m_devid);
+	switch (mp->m_devid) {
+		case MPI2_MFGPAGE_DEVID_SAS2004:
+			mdb_printf("(SAS2004) ");
+			break;
+		case MPI2_MFGPAGE_DEVID_SAS2008:
+			mdb_printf("(SAS2008) ");
+			break;
+		case MPI2_MFGPAGE_DEVID_SAS2108_1:
+		case MPI2_MFGPAGE_DEVID_SAS2108_2:
+		case MPI2_MFGPAGE_DEVID_SAS2108_3:
+			mdb_printf("(SAS2108) ");
+			break;
+		case MPI2_MFGPAGE_DEVID_SAS2116_1:
+		case MPI2_MFGPAGE_DEVID_SAS2116_2:
+			mdb_printf("(SAS2116) ");
+			break;
+		case MPI2_MFGPAGE_DEVID_SSS6200:
+			mdb_printf("(SSS6200) ");
+			break;
+		case MPI2_MFGPAGE_DEVID_SAS2208_1:
+		case MPI2_MFGPAGE_DEVID_SAS2208_2:
+		case MPI2_MFGPAGE_DEVID_SAS2208_3:
+		case MPI2_MFGPAGE_DEVID_SAS2208_4:
+		case MPI2_MFGPAGE_DEVID_SAS2208_5:
+		case MPI2_MFGPAGE_DEVID_SAS2208_6:
+#if 0
+		/* Same as 2308_1/2 ?? */
+		case MPI2_MFGPAGE_DEVID_SAS2208_7:
+		case MPI2_MFGPAGE_DEVID_SAS2208_8:
+#endif
+			mdb_printf("(SAS2208) ");
+			break;
+		case MPI2_MFGPAGE_DEVID_SAS2308_1:
+		case MPI2_MFGPAGE_DEVID_SAS2308_2:
+		case MPI2_MFGPAGE_DEVID_SAS2308_3:
+			mdb_printf("(SAS2308) ");
+			break;
+		case MPI25_MFGPAGE_DEVID_SAS3004:
+			mdb_printf("(SAS3004) ");
+			break;
+		case MPI25_MFGPAGE_DEVID_SAS3008:
+			mdb_printf("(SAS3008) ");
+			break;
+		case MPI25_MFGPAGE_DEVID_SAS3108_1:
+		case MPI25_MFGPAGE_DEVID_SAS3108_2:
+		case MPI25_MFGPAGE_DEVID_SAS3108_3:
+		case MPI25_MFGPAGE_DEVID_SAS3108_4:
+		case MPI25_MFGPAGE_DEVID_SAS3108_5:
+		case MPI25_MFGPAGE_DEVID_SAS3108_6:
+			mdb_printf("(SAS3108) ");
+			break;
+		default:
+			mdb_printf("(SAS????) ");
+			break;
+	}
+	mdb_printf("0x%02x 0x%04x\n", mp->m_revid, mp->m_ssid);
+	mdb_printf("%s\n", device_path);
+}
+
+void
+dump_debug_log(struct mptsas *mp)
+{
+	uint_t	idx;
+	char	*logbuf;
+	int	i;
+
+	if (mdb_readsym(&idx, sizeof (uint_t),
+		"mptsas_dbglog_idx") == -1) {
+		mdb_warn("No debug log buffer present");
+		return;
+	}
+	logbuf = mdb_alloc(32*256, UM_SLEEP);
+	if (idx == 0) {
+		mdb_warn("Logging turned off");
+		return;
+	}
+
+	if (mdb_readsym(logbuf, 32*256,
+		"mptsas_dbglog_bufs") == -1) {
+		mdb_warn("No debug log buffer present");
+		return;
+	}
+	mdb_printf("\n");
+	idx &= 0x1f;
+	for (i = 0; i < 32; i++) {
+		mdb_printf("%s\n", &logbuf[idx*256]);
+		idx = (idx+1) & 0x1f;
+	}
+	mdb_free(logbuf, 32*256);
+}
+
+static int
+mptsas_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	struct mptsas		m;
+	struct mptsas_slots	*s;
+
+	int			nslots;
+	int			slot_size = 0;
+	uint_t			verbose = FALSE;
+	uint_t			target_info = FALSE;
+	uint_t			slot_info = FALSE;
+	uint_t			device_info = FALSE;
+	uint_t			port_info = FALSE;
+	uint_t			debug_log = FALSE;
+	int			rv = DCMD_OK;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		void		*mptsas3_state = NULL;
+
+		if (mdb_readvar(&mptsas3_state, "mptsas3_state") == -1) {
+			mdb_warn("can't read mptsas3_state");
+			return (DCMD_ERR);
+		}
+		if (mdb_pwalk_dcmd("genunix`softstate",
+			"mpt_sas3`mptsas3", argc,
+			argv, (uintptr_t)mptsas3_state) == -1) {
+			mdb_warn("mdb_pwalk_dcmd failed");
+			return (DCMD_ERR);
+		}
+		return (DCMD_OK);
+	}
+
+	if (mdb_getopts(argc, argv,
+	    's', MDB_OPT_SETBITS, TRUE, &slot_info,
+	    'd', MDB_OPT_SETBITS, TRUE, &device_info,
+	    't', MDB_OPT_SETBITS, TRUE, &target_info,
+	    'p', MDB_OPT_SETBITS, TRUE, &port_info,
+	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
+	    'D', MDB_OPT_SETBITS, TRUE, &debug_log,
+	    NULL) != argc)
+		return (DCMD_USAGE);
+
+
+	if (mdb_vread(&m, sizeof (m), addr) == -1) {
+		mdb_warn("couldn't read mpt struct at 0x%p", addr);
+		return (DCMD_ERR);
+	}
+
+	s = mdb_alloc(sizeof (mptsas_slots_t), UM_SLEEP);
+
+	if (mdb_vread(s, sizeof (mptsas_slots_t),
+	    (uintptr_t)m.m_active) == -1) {
+		mdb_warn("couldn't read small mptsas_slots_t at 0x%p",
+		    m.m_active);
+		mdb_free(s, sizeof (mptsas_slots_t));
+		return (DCMD_ERR);
+	}
+
+	nslots = s->m_n_normal;
+
+	mdb_free(s, sizeof (mptsas_slots_t));
+
+	slot_size = sizeof (mptsas_slots_t) +
+	    (sizeof (mptsas_cmd_t *) * (nslots-1));
+
+	s = mdb_alloc(slot_size, UM_SLEEP);
+
+	if (mdb_vread(s, slot_size, (uintptr_t)m.m_active) == -1) {
+		mdb_warn("couldn't read large mptsas_slots_t at 0x%p",
+		    m.m_active);
+		mdb_free(s, slot_size);
+		return (DCMD_ERR);
+	}
+
+	/* processing completed */
+
+	if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) ||
+	    (flags & DCMD_LOOPFIRST) || slot_info || device_info ||
+	    target_info) {
+		if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
+			mdb_printf("\n");
+		mdb_printf("        mptsas_t inst ncmds suspend  power");
+		mdb_printf("\n");
+		mdb_printf("========================================="
+		    "=======================================");
+		mdb_printf("\n");
+	}
+
+	mdb_printf("%16p %4d %5d ", addr, m.m_instance, m.m_ncmds);
+	mdb_printf("%7d", m.m_suspended);
+	switch (m.m_power_level) {
+		case PM_LEVEL_D0:
+			mdb_printf(" ON=D0 ");
+			break;
+		case PM_LEVEL_D1:
+			mdb_printf("    D1 ");
+			break;
+		case PM_LEVEL_D2:
+			mdb_printf("    D2 ");
+			break;
+		case PM_LEVEL_D3:
+			mdb_printf("OFF=D3 ");
+			break;
+		default:
+			mdb_printf("INVALD ");
+	}
+	mdb_printf("\n");
+
+	mdb_inc_indent(17);
+
+	if (target_info)
+		display_targets(&m, verbose);
+
+	if (port_info)
+		display_ports(&m);
+
+	if (device_info)
+		display_deviceinfo(&m);
+
+	if (slot_info)
+		display_slotinfo(&m, s);
+
+	if (debug_log)
+		dump_debug_log(&m);
+
+	mdb_dec_indent(17);
+
+	mdb_free(s, slot_size);
+
+	return (rv);
+}
+
+void
+mptsas_help()
+{
+	mdb_printf("Prints summary information about each mpt_sas3 instance, "
+	    "including warning\nmessages when slot usage doesn't match "
+	    "summary information.\n"
+	    "Without the address of a \"struct mptsas\", prints every "
+	    "instance.\n\n"
+	    "Switches:\n"
+	    "  -t[v]  includes information about targets, v = be more verbose\n"
+	    "  -p     includes information about port\n"
+	    "  -s     includes information about mpt slots\n"
+	    "  -d     includes information about the hardware\n"
+	    "  -D     print the mptsas specific debug log\n");
+}
+
+static const mdb_dcmd_t dcmds[] = {
+	{ "mptsas3", "?[-tpsdD]", "print mpt_sas3 information", mptsas_dcmd,
+	    mptsas_help}, { NULL }
+};
+
+static const mdb_modinfo_t modinfo = {
+	MDB_API_VERSION, dcmds, NULL
+};
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+	return (&modinfo);
+}
