changeset 11133:e9413f8762c3

6897780 assertion failed: !list_link_active(&phyp->list_node), file: ...pmcs_subr.c, line: 7363 6895290 missing phy after enumeration failure during tran_tgt_init
author Jesse Butler <Jesse.Butler@Sun.COM>
date Fri, 20 Nov 2009 10:24:27 -0700
parents 56dcfb8bf4f8
children 8aa0c4ca6639
files usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_intr.c usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_proto.h
diffstat 3 files changed, 144 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_intr.c	Fri Nov 20 10:19:25 2009 -0700
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_intr.c	Fri Nov 20 10:24:27 2009 -0700
@@ -676,6 +676,16 @@
 			pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
 			    "PortID 0x%x: PHY 0x%x IN RESET",
 			    portid, phynum);
+			/* Entire port is down due to a host-initiated reset */
+			mutex_enter(&pptr->phy_lock);
+			iport = pptr->iport;
+			mutex_exit(&pptr->phy_lock);
+			if (iport) {
+				mutex_enter(&iport->lock);
+				pmcs_iport_teardown_phys(iport);
+				mutex_exit(&iport->lock);
+			}
+
 			break;
 		}
 		if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_LOSTCOMM) {
@@ -688,6 +698,33 @@
 
 		if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_VALID) {
 			/*
+			 * This is not the last phy in the port, so if this
+			 * is the primary PHY, promote another PHY to primary.
+			 */
+			if (pptr == subphy) {
+				primary = !subphy->subsidiary;
+				ASSERT(primary);
+
+				tphyp = pptr;
+				pptr = pmcs_promote_next_phy(tphyp);
+
+				if (pptr) {
+					/* Update primary pptr in ports */
+					pwp->ports[portid] = pptr;
+					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr,
+					    NULL, "PortID 0x%x: PHY 0x%x "
+					    "promoted to primary", portid,
+					    pptr->phynum);
+				} else {
+					/* This should not happen */
+					pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr,
+					    NULL, "PortID 0x%x: PHY 0x%x: "
+					    "unable to promote phy", portid,
+					    phynum);
+				}
+			}
+
+			/*
 			 * Drop port width on the primary phy handle
 			 * No need to lock the entire tree for this
 			 */
--- a/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c	Fri Nov 20 10:19:25 2009 -0700
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c	Fri Nov 20 10:24:27 2009 -0700
@@ -1917,6 +1917,38 @@
 }
 
 /*
+ * Remove all phys from an iport's phymap and empty it's phylist.
+ * Called when a port has been reset by the host (see pmcs_intr.c).
+ */
+void
+pmcs_iport_teardown_phys(pmcs_iport_t *iport)
+{
+	pmcs_hw_t		*pwp;
+	sas_phymap_phys_t	*phys;
+	int			phynum;
+
+	ASSERT(iport);
+	ASSERT(mutex_owned(&iport->lock));
+	pwp = iport->pwp;
+	ASSERT(pwp);
+
+	/*
+	 * Remove all phys from the iport handle's phy list, unset its
+	 * primary phy and update its state.
+	 */
+	pmcs_remove_phy_from_iport(iport, NULL);
+	iport->pptr = NULL;
+	iport->ua_state = UA_PEND_DEACTIVATE;
+
+	/* Remove all phys from the phymap */
+	phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
+	while ((phynum = sas_phymap_phys_next(phys)) != -1) {
+		sas_phymap_phy_rem(pwp->hss_phymap, phynum);
+	}
+	sas_phymap_phys_free(phys);
+}
+
+/*
  * Query the phymap and populate the iport handle passed in.
  * Called with iport lock held.
  */
@@ -2038,6 +2070,79 @@
 	return (iport);
 }
 
+/*
+ * Promote the next phy on this iport to primary, and return it.
+ * Called when the primary PHY on a port is going down, but the port
+ * remains up (see pmcs_intr.c).
+ */
+pmcs_phy_t *
+pmcs_promote_next_phy(pmcs_phy_t *prev_primary)
+{
+	pmcs_iport_t *iport;
+	pmcs_phy_t *pptr, *next_pptr, *child;
+
+	mutex_enter(&prev_primary->phy_lock);
+	iport = prev_primary->iport;
+	mutex_exit(&prev_primary->phy_lock);
+	ASSERT(iport);
+
+	mutex_enter(&iport->lock);
+	for (pptr = list_head(&iport->phys); pptr != NULL; pptr = next_pptr) {
+		next_pptr = list_next(&iport->phys, pptr);
+
+		/* Use the first PHY in the list that is not the primary */
+		if (pptr != prev_primary) {
+			break;
+		}
+	}
+	iport->pptr = pptr;
+	mutex_exit(&iport->lock);
+
+	if (pptr == NULL) {
+		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
+		    "%s: unable to promote to new primary phy on iport [0x%p]",
+		    __func__, (void *)iport);
+		return (NULL);
+	}
+
+	/* Update the phy handle with the data from the previous primary */
+	mutex_enter(&pptr->phy_lock);
+	pmcs_lock_phy(prev_primary);
+
+	ASSERT(pptr->subsidiary);
+
+	pptr->children		= prev_primary->children;
+	child = pptr->children;
+	while (child) {
+		child->parent = pptr;
+		child = child->sibling;
+	}
+	pptr->ncphy		= prev_primary->ncphy;
+	pptr->width		= prev_primary->width;
+	pptr->dtype		= prev_primary->dtype;
+	pptr->pend_dtype	= prev_primary->pend_dtype;
+	pptr->tolerates_sas2	= prev_primary->tolerates_sas2;
+	pptr->atdt		= prev_primary->atdt;
+	pptr->portid		= prev_primary->portid;
+	pptr->link_rate		= prev_primary->link_rate;
+	pptr->configured	= prev_primary->configured;
+	pptr->iport		= prev_primary->iport;
+	pptr->target		= prev_primary->target;
+	pptr->subsidiary = 0;
+
+	prev_primary->subsidiary = 1;
+	prev_primary->children = NULL;
+	pmcs_unlock_phy(prev_primary);
+
+	/*
+	 * We call pmcs_unlock_phy() on pptr because it now contains the
+	 * list of children.
+	 */
+	pmcs_unlock_phy(pptr);
+
+	return (pptr);
+}
+
 void
 pmcs_rele_iport(pmcs_iport_t *iport)
 {
--- a/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_proto.h	Fri Nov 20 10:19:25 2009 -0700
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_proto.h	Fri Nov 20 10:24:27 2009 -0700
@@ -313,8 +313,10 @@
 void pmcs_ssp_event_recovery(pmcs_hw_t *);
 
 pmcs_iport_t *pmcs_get_iport_by_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr);
+pmcs_phy_t *pmcs_promote_next_phy(pmcs_phy_t *pptr);
 void pmcs_rele_iport(pmcs_iport_t *iport);
 int pmcs_iport_configure_phys(pmcs_iport_t *iport);
+void pmcs_iport_teardown_phys(pmcs_iport_t *iport);
 
 void pmcs_lock_phy(pmcs_phy_t *);
 void pmcs_unlock_phy(pmcs_phy_t *);