xref: /onnv-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_ds.c (revision 13103:17b823d436a7)
111090SDavid.Hollister@Sun.COM /*
211090SDavid.Hollister@Sun.COM  * CDDL HEADER START
311090SDavid.Hollister@Sun.COM  *
411090SDavid.Hollister@Sun.COM  * The contents of this file are subject to the terms of the
511090SDavid.Hollister@Sun.COM  * Common Development and Distribution License (the "License").
611090SDavid.Hollister@Sun.COM  * You may not use this file except in compliance with the License.
711090SDavid.Hollister@Sun.COM  *
811090SDavid.Hollister@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911090SDavid.Hollister@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011090SDavid.Hollister@Sun.COM  * See the License for the specific language governing permissions
1111090SDavid.Hollister@Sun.COM  * and limitations under the License.
1211090SDavid.Hollister@Sun.COM  *
1311090SDavid.Hollister@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411090SDavid.Hollister@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511090SDavid.Hollister@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611090SDavid.Hollister@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711090SDavid.Hollister@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811090SDavid.Hollister@Sun.COM  *
1911090SDavid.Hollister@Sun.COM  * CDDL HEADER END
2012060SDavid.Hollister@Sun.COM  */
2112060SDavid.Hollister@Sun.COM /*
2212060SDavid.Hollister@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2311090SDavid.Hollister@Sun.COM  */
2411090SDavid.Hollister@Sun.COM 
2511090SDavid.Hollister@Sun.COM /*
2611090SDavid.Hollister@Sun.COM  * PM8001 device state recovery routines
2711090SDavid.Hollister@Sun.COM  */
2811090SDavid.Hollister@Sun.COM 
2911090SDavid.Hollister@Sun.COM #include <sys/scsi/adapters/pmcs/pmcs.h>
3011090SDavid.Hollister@Sun.COM 
3111090SDavid.Hollister@Sun.COM /*
3211090SDavid.Hollister@Sun.COM  * SAS Topology Configuration
3311090SDavid.Hollister@Sun.COM  */
3411267SJesse.Butler@Sun.COM static void pmcs_ds_operational(pmcs_phy_t *pptr, pmcs_xscsi_t *tgt);
3511090SDavid.Hollister@Sun.COM static void pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp,
3611347SRamana.Srikanth@Sun.COM     pmcs_xscsi_t *tgt, pmcs_hw_t *pwp, const char *func_name,
3711090SDavid.Hollister@Sun.COM     char *reason_string);
3811090SDavid.Hollister@Sun.COM 
3911090SDavid.Hollister@Sun.COM /*
4011090SDavid.Hollister@Sun.COM  * Get device state.  Called with statlock and PHY lock held.
4111090SDavid.Hollister@Sun.COM  */
4211090SDavid.Hollister@Sun.COM static int
pmcs_get_dev_state(pmcs_hw_t * pwp,pmcs_phy_t * phyp,pmcs_xscsi_t * xp,uint8_t * ds)4311090SDavid.Hollister@Sun.COM pmcs_get_dev_state(pmcs_hw_t *pwp, pmcs_phy_t *phyp, pmcs_xscsi_t *xp,
4411090SDavid.Hollister@Sun.COM     uint8_t *ds)
4511090SDavid.Hollister@Sun.COM {
4611090SDavid.Hollister@Sun.COM 	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE];
4711090SDavid.Hollister@Sun.COM 	int result;
4811090SDavid.Hollister@Sun.COM 	struct pmcwork *pwrk;
4911090SDavid.Hollister@Sun.COM 
5011090SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG3, phyp, xp, "%s: tgt(0x%p)", __func__,
5111090SDavid.Hollister@Sun.COM 	    (void *)xp);
5211090SDavid.Hollister@Sun.COM 
5311090SDavid.Hollister@Sun.COM 	if (xp != NULL) {
5411090SDavid.Hollister@Sun.COM 		ASSERT(mutex_owned(&xp->statlock));
5511090SDavid.Hollister@Sun.COM 	}
5611256SRamana.Srikanth@Sun.COM 
5711256SRamana.Srikanth@Sun.COM 	if (phyp == NULL) {
5811256SRamana.Srikanth@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, NULL, xp,
5911256SRamana.Srikanth@Sun.COM 		    "%s: PHY is NULL", __func__);
6011256SRamana.Srikanth@Sun.COM 		return (-1);
6111256SRamana.Srikanth@Sun.COM 	}
6211090SDavid.Hollister@Sun.COM 	ASSERT(mutex_owned(&phyp->phy_lock));
6311090SDavid.Hollister@Sun.COM 
6411090SDavid.Hollister@Sun.COM 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp);
6511090SDavid.Hollister@Sun.COM 	if (pwrk == NULL) {
6611090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, phyp, xp, pmcs_nowrk, __func__);
6711090SDavid.Hollister@Sun.COM 		return (-1);
6811090SDavid.Hollister@Sun.COM 	}
6911090SDavid.Hollister@Sun.COM 	pwrk->arg = msg;
7011090SDavid.Hollister@Sun.COM 	pwrk->dtype = phyp->dtype;
7111090SDavid.Hollister@Sun.COM 
7211090SDavid.Hollister@Sun.COM 	if (phyp->valid_device_id == 0) {
7311090SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, pwrk);
7411090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, xp,
7511090SDavid.Hollister@Sun.COM 		    "%s: Invalid DeviceID", __func__);
7611090SDavid.Hollister@Sun.COM 		return (-1);
7711090SDavid.Hollister@Sun.COM 	}
7811090SDavid.Hollister@Sun.COM 	htag = pwrk->htag;
7911090SDavid.Hollister@Sun.COM 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8011090SDavid.Hollister@Sun.COM 	    PMCIN_GET_DEVICE_STATE));
8111090SDavid.Hollister@Sun.COM 	msg[1] = LE_32(pwrk->htag);
8211090SDavid.Hollister@Sun.COM 	msg[2] = LE_32(phyp->device_id);
8311347SRamana.Srikanth@Sun.COM 	CLEAN_MESSAGE(msg, 3);
8411090SDavid.Hollister@Sun.COM 
8511090SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8611090SDavid.Hollister@Sun.COM 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8711090SDavid.Hollister@Sun.COM 	if (ptr == NULL) {
8811090SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8911090SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, pwrk);
9011090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, phyp, xp, pmcs_nomsg, __func__);
9111090SDavid.Hollister@Sun.COM 		return (-1);
9211090SDavid.Hollister@Sun.COM 	}
9311090SDavid.Hollister@Sun.COM 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
9411090SDavid.Hollister@Sun.COM 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
9511090SDavid.Hollister@Sun.COM 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9611090SDavid.Hollister@Sun.COM 
9711090SDavid.Hollister@Sun.COM 	if (xp != NULL) {
9811090SDavid.Hollister@Sun.COM 		mutex_exit(&xp->statlock);
9911090SDavid.Hollister@Sun.COM 	}
10011090SDavid.Hollister@Sun.COM 	pmcs_unlock_phy(phyp);
10111090SDavid.Hollister@Sun.COM 	WAIT_FOR(pwrk, 1000, result);
10212462Sjesse.butler@oracle.com 	pmcs_pwork(pwp, pwrk);
10311090SDavid.Hollister@Sun.COM 	pmcs_lock_phy(phyp);
10411090SDavid.Hollister@Sun.COM 
10511090SDavid.Hollister@Sun.COM 	if (xp != NULL) {
10611090SDavid.Hollister@Sun.COM 		mutex_enter(&xp->statlock);
10711090SDavid.Hollister@Sun.COM 	}
10811090SDavid.Hollister@Sun.COM 
10911090SDavid.Hollister@Sun.COM 	if (result) {
11011090SDavid.Hollister@Sun.COM 		pmcs_timed_out(pwp, htag, __func__);
11111090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, xp,
11211090SDavid.Hollister@Sun.COM 		    "%s: cmd timed out, returning", __func__);
11311090SDavid.Hollister@Sun.COM 		return (-1);
11411090SDavid.Hollister@Sun.COM 	}
11511090SDavid.Hollister@Sun.COM 	if (LE_32(msg[2]) == 0) {
11611090SDavid.Hollister@Sun.COM 		*ds = (uint8_t)(LE_32(msg[4]));
11711090SDavid.Hollister@Sun.COM 		if (xp == NULL) {
11811090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
11911090SDavid.Hollister@Sun.COM 			    "%s: retrieved_ds=0x%x", __func__, *ds);
12011090SDavid.Hollister@Sun.COM 		} else if (*ds !=  xp->dev_state) {
12111090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
12211090SDavid.Hollister@Sun.COM 			    "%s: retrieved_ds=0x%x, target_ds=0x%x", __func__,
12311090SDavid.Hollister@Sun.COM 			    *ds, xp->dev_state);
12411090SDavid.Hollister@Sun.COM 		}
12511090SDavid.Hollister@Sun.COM 		return (0);
12611090SDavid.Hollister@Sun.COM 	} else {
12711090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
12811090SDavid.Hollister@Sun.COM 		    "%s: cmd failed Status(0x%x), returning ", __func__,
12911090SDavid.Hollister@Sun.COM 		    LE_32(msg[2]));
13011090SDavid.Hollister@Sun.COM 		return (-1);
13111090SDavid.Hollister@Sun.COM 	}
13211090SDavid.Hollister@Sun.COM }
13311090SDavid.Hollister@Sun.COM 
13411090SDavid.Hollister@Sun.COM /*
13511090SDavid.Hollister@Sun.COM  * Set device state.  Called with target's statlock and PHY lock held.
13611090SDavid.Hollister@Sun.COM  */
13711090SDavid.Hollister@Sun.COM static int
pmcs_set_dev_state(pmcs_hw_t * pwp,pmcs_phy_t * phyp,pmcs_xscsi_t * xp,uint8_t ds)13811090SDavid.Hollister@Sun.COM pmcs_set_dev_state(pmcs_hw_t *pwp, pmcs_phy_t *phyp, pmcs_xscsi_t *xp,
13911090SDavid.Hollister@Sun.COM     uint8_t ds)
14011090SDavid.Hollister@Sun.COM {
14111090SDavid.Hollister@Sun.COM 	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE];
14211090SDavid.Hollister@Sun.COM 	int result;
14311090SDavid.Hollister@Sun.COM 	uint8_t pds, nds;
14411090SDavid.Hollister@Sun.COM 	struct pmcwork *pwrk;
14511090SDavid.Hollister@Sun.COM 
14611090SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
14711090SDavid.Hollister@Sun.COM 	    "%s: ds: 0x%x tgt: 0x%p phy: 0x%p", __func__, ds, (void *)xp,
14811090SDavid.Hollister@Sun.COM 	    (void *)phyp);
14911090SDavid.Hollister@Sun.COM 
15011090SDavid.Hollister@Sun.COM 	if (phyp == NULL) {
15111090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, NULL, xp,
15211090SDavid.Hollister@Sun.COM 		    "%s: PHY is NULL", __func__);
15311090SDavid.Hollister@Sun.COM 		return (-1);
15411090SDavid.Hollister@Sun.COM 	}
15511090SDavid.Hollister@Sun.COM 
15611090SDavid.Hollister@Sun.COM 	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp);
15711090SDavid.Hollister@Sun.COM 	if (pwrk == NULL) {
15811090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, phyp, xp, pmcs_nowrk, __func__);
15911090SDavid.Hollister@Sun.COM 		return (-1);
16011090SDavid.Hollister@Sun.COM 	}
16111090SDavid.Hollister@Sun.COM 	if (phyp->valid_device_id == 0) {
16211090SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, pwrk);
16311090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
16411090SDavid.Hollister@Sun.COM 		    "%s: Invalid DeviceID", __func__);
16511090SDavid.Hollister@Sun.COM 		return (-1);
16611090SDavid.Hollister@Sun.COM 	}
16711090SDavid.Hollister@Sun.COM 	pwrk->arg = msg;
16811090SDavid.Hollister@Sun.COM 	pwrk->dtype = phyp->dtype;
16911090SDavid.Hollister@Sun.COM 	htag = pwrk->htag;
17011090SDavid.Hollister@Sun.COM 	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
17111090SDavid.Hollister@Sun.COM 	    PMCIN_SET_DEVICE_STATE));
17211090SDavid.Hollister@Sun.COM 	msg[1] = LE_32(pwrk->htag);
17311090SDavid.Hollister@Sun.COM 	msg[2] = LE_32(phyp->device_id);
17411090SDavid.Hollister@Sun.COM 	msg[3] = LE_32(ds);
17511347SRamana.Srikanth@Sun.COM 	CLEAN_MESSAGE(msg, 4);
17611090SDavid.Hollister@Sun.COM 
17711090SDavid.Hollister@Sun.COM 	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
17811090SDavid.Hollister@Sun.COM 	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
17911090SDavid.Hollister@Sun.COM 	if (ptr == NULL) {
18011090SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
18111090SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, pwrk);
18211090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, phyp, xp, pmcs_nomsg, __func__);
18311090SDavid.Hollister@Sun.COM 		return (-1);
18411090SDavid.Hollister@Sun.COM 	}
18511090SDavid.Hollister@Sun.COM 	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
18611090SDavid.Hollister@Sun.COM 	pwrk->state = PMCS_WORK_STATE_ONCHIP;
18711090SDavid.Hollister@Sun.COM 	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
18811090SDavid.Hollister@Sun.COM 
18911090SDavid.Hollister@Sun.COM 	if (xp != NULL) {
19011090SDavid.Hollister@Sun.COM 		mutex_exit(&xp->statlock);
19111090SDavid.Hollister@Sun.COM 	}
19211090SDavid.Hollister@Sun.COM 	pmcs_unlock_phy(phyp);
19311090SDavid.Hollister@Sun.COM 	WAIT_FOR(pwrk, 1000, result);
19412462Sjesse.butler@oracle.com 	pmcs_pwork(pwp, pwrk);
19511090SDavid.Hollister@Sun.COM 	pmcs_lock_phy(phyp);
19611090SDavid.Hollister@Sun.COM 	if (xp != NULL) {
19711090SDavid.Hollister@Sun.COM 		mutex_enter(&xp->statlock);
19811090SDavid.Hollister@Sun.COM 	}
19911090SDavid.Hollister@Sun.COM 
20011090SDavid.Hollister@Sun.COM 	if (result) {
20111090SDavid.Hollister@Sun.COM 		pmcs_timed_out(pwp, htag, __func__);
20211090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
20311090SDavid.Hollister@Sun.COM 		    "%s: cmd timed out, returning", __func__);
20411090SDavid.Hollister@Sun.COM 		return (-1);
20511090SDavid.Hollister@Sun.COM 	}
20611090SDavid.Hollister@Sun.COM 	if (LE_32(msg[2]) == 0) {
20711090SDavid.Hollister@Sun.COM 		pds = (uint8_t)(LE_32(msg[4]) >> 4);
20811090SDavid.Hollister@Sun.COM 		nds = (uint8_t)(LE_32(msg[4]) & 0x0000000f);
20911090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
21011090SDavid.Hollister@Sun.COM 		    "%s: previous_ds=0x%x, new_ds=0x%x", __func__, pds, nds);
21111090SDavid.Hollister@Sun.COM 		if (xp != NULL) {
21211090SDavid.Hollister@Sun.COM 			xp->dev_state = nds;
21311090SDavid.Hollister@Sun.COM 		}
21411090SDavid.Hollister@Sun.COM 		return (0);
21511090SDavid.Hollister@Sun.COM 	} else {
21611090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
21711090SDavid.Hollister@Sun.COM 		    "%s: cmd failed Status(0x%x), returning ", __func__,
21811090SDavid.Hollister@Sun.COM 		    LE_32(msg[2]));
21911090SDavid.Hollister@Sun.COM 		return (-1);
22011090SDavid.Hollister@Sun.COM 	}
22111090SDavid.Hollister@Sun.COM }
22211090SDavid.Hollister@Sun.COM 
22311267SJesse.Butler@Sun.COM static void
pmcs_ds_operational(pmcs_phy_t * pptr,pmcs_xscsi_t * tgt)22411267SJesse.Butler@Sun.COM pmcs_ds_operational(pmcs_phy_t *pptr, pmcs_xscsi_t *tgt)
22511267SJesse.Butler@Sun.COM {
22611267SJesse.Butler@Sun.COM 	pmcs_hw_t	*pwp;
22711267SJesse.Butler@Sun.COM 
22811267SJesse.Butler@Sun.COM 	ASSERT(pptr);
22911267SJesse.Butler@Sun.COM 	pwp = pptr->pwp;
23011267SJesse.Butler@Sun.COM 
23111267SJesse.Butler@Sun.COM 	if (tgt != NULL) {
23211267SJesse.Butler@Sun.COM 		tgt->recover_wait = 0;
23311267SJesse.Butler@Sun.COM 	}
23411267SJesse.Butler@Sun.COM 	pptr->ds_recovery_retries = 0;
23511267SJesse.Butler@Sun.COM 
23611267SJesse.Butler@Sun.COM 	if ((pptr->ds_prev_good_recoveries == 0) ||
23711267SJesse.Butler@Sun.COM 	    (ddi_get_lbolt() - pptr->last_good_recovery >
23811267SJesse.Butler@Sun.COM 	    drv_usectohz(PMCS_MAX_DS_RECOVERY_TIME))) {
23911267SJesse.Butler@Sun.COM 		pptr->last_good_recovery = ddi_get_lbolt();
24011267SJesse.Butler@Sun.COM 		pptr->ds_prev_good_recoveries = 1;
24111267SJesse.Butler@Sun.COM 	} else if (ddi_get_lbolt() < pptr->last_good_recovery +
24211267SJesse.Butler@Sun.COM 	    drv_usectohz(PMCS_MAX_DS_RECOVERY_TIME)) {
24311267SJesse.Butler@Sun.COM 		pptr->ds_prev_good_recoveries++;
24411267SJesse.Butler@Sun.COM 	} else {
24511347SRamana.Srikanth@Sun.COM 		pmcs_handle_ds_recovery_error(pptr, tgt, pwp, __func__,
24611347SRamana.Srikanth@Sun.COM 		    "Max recovery attempts reached. Declaring PHY dead");
24711267SJesse.Butler@Sun.COM 	}
24811267SJesse.Butler@Sun.COM 
24911267SJesse.Butler@Sun.COM 	/* Don't bother to run the work queues if the PHY is dead */
25011267SJesse.Butler@Sun.COM 	if (!pptr->dead) {
25111267SJesse.Butler@Sun.COM 		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
25211267SJesse.Butler@Sun.COM 		(void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
25311267SJesse.Butler@Sun.COM 		    pwp, DDI_NOSLEEP);
25411267SJesse.Butler@Sun.COM 	}
25511267SJesse.Butler@Sun.COM }
25611267SJesse.Butler@Sun.COM 
25711090SDavid.Hollister@Sun.COM void
pmcs_dev_state_recovery(pmcs_hw_t * pwp,pmcs_phy_t * phyp)25811090SDavid.Hollister@Sun.COM pmcs_dev_state_recovery(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
25911090SDavid.Hollister@Sun.COM {
26011167SRamana.Srikanth@Sun.COM 	boolean_t reschedule = B_FALSE;
26111090SDavid.Hollister@Sun.COM 	uint8_t	ds, tgt_dev_state;
26211090SDavid.Hollister@Sun.COM 	int rc;
26311090SDavid.Hollister@Sun.COM 	pmcs_xscsi_t *tgt;
26411090SDavid.Hollister@Sun.COM 	pmcs_phy_t *pptr, *pnext, *pchild;
26511090SDavid.Hollister@Sun.COM 
26611090SDavid.Hollister@Sun.COM 	/*
26711090SDavid.Hollister@Sun.COM 	 * First time, check to see if we're already performing recovery
26811090SDavid.Hollister@Sun.COM 	 */
26911090SDavid.Hollister@Sun.COM 	if (phyp == NULL) {
27011090SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
27111090SDavid.Hollister@Sun.COM 		if (pwp->ds_err_recovering) {
27211090SDavid.Hollister@Sun.COM 			mutex_exit(&pwp->lock);
27311090SDavid.Hollister@Sun.COM 			SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
27411090SDavid.Hollister@Sun.COM 			return;
27511090SDavid.Hollister@Sun.COM 		}
27611090SDavid.Hollister@Sun.COM 
27711090SDavid.Hollister@Sun.COM 		pwp->ds_err_recovering = 1;
27811090SDavid.Hollister@Sun.COM 		pptr = pwp->root_phys;
27911090SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
28011090SDavid.Hollister@Sun.COM 	} else {
28111090SDavid.Hollister@Sun.COM 		pptr = phyp;
28211090SDavid.Hollister@Sun.COM 	}
28311090SDavid.Hollister@Sun.COM 
28411090SDavid.Hollister@Sun.COM 	while (pptr) {
28511090SDavid.Hollister@Sun.COM 		/*
28611090SDavid.Hollister@Sun.COM 		 * Since ds_err_recovering is set, we can be assured these
28711090SDavid.Hollister@Sun.COM 		 * PHYs won't disappear on us while we do this.
28811090SDavid.Hollister@Sun.COM 		 */
28911090SDavid.Hollister@Sun.COM 		pmcs_lock_phy(pptr);
29011090SDavid.Hollister@Sun.COM 		pchild = pptr->children;
29111090SDavid.Hollister@Sun.COM 		pnext = pptr->sibling;
29211090SDavid.Hollister@Sun.COM 		pmcs_unlock_phy(pptr);
29311090SDavid.Hollister@Sun.COM 
29411090SDavid.Hollister@Sun.COM 		if (pchild) {
29511090SDavid.Hollister@Sun.COM 			pmcs_dev_state_recovery(pwp, pchild);
29611090SDavid.Hollister@Sun.COM 		}
29711090SDavid.Hollister@Sun.COM 
29811090SDavid.Hollister@Sun.COM 		tgt = NULL;
29911090SDavid.Hollister@Sun.COM 		pmcs_lock_phy(pptr);
30011090SDavid.Hollister@Sun.COM 
30111347SRamana.Srikanth@Sun.COM 		if (pptr->dead || !pptr->valid_device_id) {
30211347SRamana.Srikanth@Sun.COM 			goto next_phy;
30311347SRamana.Srikanth@Sun.COM 		}
30411347SRamana.Srikanth@Sun.COM 
30511347SRamana.Srikanth@Sun.COM 		if (pptr->iport && (pptr->iport->ua_state != UA_ACTIVE)) {
30611347SRamana.Srikanth@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
30711347SRamana.Srikanth@Sun.COM 			    "%s: No DS recovery on PHY %s, iport not active",
30811347SRamana.Srikanth@Sun.COM 			    __func__, pptr->path);
30911090SDavid.Hollister@Sun.COM 			goto next_phy;
31011090SDavid.Hollister@Sun.COM 		}
31111090SDavid.Hollister@Sun.COM 
31211090SDavid.Hollister@Sun.COM 		tgt = pptr->target;
31311090SDavid.Hollister@Sun.COM 
31411090SDavid.Hollister@Sun.COM 		if (tgt != NULL) {
31511090SDavid.Hollister@Sun.COM 			mutex_enter(&tgt->statlock);
31611090SDavid.Hollister@Sun.COM 			if (tgt->recover_wait == 0) {
31711090SDavid.Hollister@Sun.COM 				goto next_phy;
31811090SDavid.Hollister@Sun.COM 			}
31911090SDavid.Hollister@Sun.COM 			tgt_dev_state = tgt->dev_state;
32011090SDavid.Hollister@Sun.COM 		} else {
32111090SDavid.Hollister@Sun.COM 			tgt_dev_state = PMCS_DEVICE_STATE_NOT_AVAILABLE;
32211090SDavid.Hollister@Sun.COM 		}
32311090SDavid.Hollister@Sun.COM 
32411090SDavid.Hollister@Sun.COM 		if (pptr->prev_recovery) {
32511090SDavid.Hollister@Sun.COM 			if (ddi_get_lbolt() - pptr->prev_recovery <
32611090SDavid.Hollister@Sun.COM 			    drv_usectohz(PMCS_DS_RECOVERY_INTERVAL)) {
32711090SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, tgt,
32811090SDavid.Hollister@Sun.COM 				    "%s: DS recovery on PHY %s "
32911090SDavid.Hollister@Sun.COM 				    "re-invoked too soon. Skipping...",
33011090SDavid.Hollister@Sun.COM 				    __func__, pptr->path);
33111167SRamana.Srikanth@Sun.COM 				if ((tgt) && (tgt->recover_wait)) {
33211167SRamana.Srikanth@Sun.COM 					reschedule = B_TRUE;
33311167SRamana.Srikanth@Sun.COM 				}
33411090SDavid.Hollister@Sun.COM 				goto next_phy;
33511090SDavid.Hollister@Sun.COM 			}
33611090SDavid.Hollister@Sun.COM 		}
33711090SDavid.Hollister@Sun.COM 		pptr->prev_recovery = ddi_get_lbolt();
33811090SDavid.Hollister@Sun.COM 
33911090SDavid.Hollister@Sun.COM 		/*
34011090SDavid.Hollister@Sun.COM 		 * Step 1: Put the device into the IN_RECOVERY state
34111090SDavid.Hollister@Sun.COM 		 */
34211090SDavid.Hollister@Sun.COM 		rc = pmcs_get_dev_state(pwp, pptr, tgt, &ds);
34311090SDavid.Hollister@Sun.COM 		if (rc != 0) {
34411090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
34511090SDavid.Hollister@Sun.COM 			    "%s: pmcs_get_dev_state on PHY %s "
34611090SDavid.Hollister@Sun.COM 			    "failed (rc=%d)",
34711090SDavid.Hollister@Sun.COM 			    __func__, pptr->path, rc);
34811090SDavid.Hollister@Sun.COM 
34911090SDavid.Hollister@Sun.COM 			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
35011347SRamana.Srikanth@Sun.COM 			    __func__, "pmcs_get_dev_state");
35111090SDavid.Hollister@Sun.COM 
35211090SDavid.Hollister@Sun.COM 			goto next_phy;
35311090SDavid.Hollister@Sun.COM 		}
35411090SDavid.Hollister@Sun.COM 
35511267SJesse.Butler@Sun.COM 		/* If the chip says it's operational, we're done */
35611267SJesse.Butler@Sun.COM 		if (ds == PMCS_DEVICE_STATE_OPERATIONAL) {
35711267SJesse.Butler@Sun.COM 			pmcs_ds_operational(pptr, tgt);
35811267SJesse.Butler@Sun.COM 			goto next_phy;
35911267SJesse.Butler@Sun.COM 		}
36011267SJesse.Butler@Sun.COM 
36111090SDavid.Hollister@Sun.COM 		if ((tgt_dev_state == ds) &&
36211090SDavid.Hollister@Sun.COM 		    (ds == PMCS_DEVICE_STATE_IN_RECOVERY)) {
36311090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, tgt,
36411090SDavid.Hollister@Sun.COM 			    "%s: Target 0x%p already IN_RECOVERY", __func__,
36511090SDavid.Hollister@Sun.COM 			    (void *)tgt);
36611090SDavid.Hollister@Sun.COM 		} else {
36711090SDavid.Hollister@Sun.COM 			if (tgt != NULL) {
36811090SDavid.Hollister@Sun.COM 				tgt->dev_state = ds;
36911090SDavid.Hollister@Sun.COM 			}
37011090SDavid.Hollister@Sun.COM 			tgt_dev_state = ds;
37111090SDavid.Hollister@Sun.COM 			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
37211090SDavid.Hollister@Sun.COM 			rc = pmcs_send_err_recovery_cmd(pwp, ds, pptr, tgt);
37311090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, tgt,
37411090SDavid.Hollister@Sun.COM 			    "%s: pmcs_send_err_recovery_cmd "
37511090SDavid.Hollister@Sun.COM 			    "result(%d) tgt(0x%p) ds(0x%x) tgt->ds(0x%x)",
37611090SDavid.Hollister@Sun.COM 			    __func__, rc, (void *)tgt, ds, tgt_dev_state);
37711090SDavid.Hollister@Sun.COM 
37811090SDavid.Hollister@Sun.COM 			if (rc) {
37911090SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
38011090SDavid.Hollister@Sun.COM 				    "%s: pmcs_send_err_recovery_cmd to PHY %s "
38111090SDavid.Hollister@Sun.COM 				    "failed (rc=%d)",
38211090SDavid.Hollister@Sun.COM 				    __func__, pptr->path, rc);
38311090SDavid.Hollister@Sun.COM 
38411090SDavid.Hollister@Sun.COM 				pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
38511347SRamana.Srikanth@Sun.COM 				    __func__, "pmcs_send_err_recovery_cmd");
38611090SDavid.Hollister@Sun.COM 
38711090SDavid.Hollister@Sun.COM 				goto next_phy;
38811090SDavid.Hollister@Sun.COM 			}
38911090SDavid.Hollister@Sun.COM 		}
39011090SDavid.Hollister@Sun.COM 
39111090SDavid.Hollister@Sun.COM 		/*
39211267SJesse.Butler@Sun.COM 		 * Step 2: Perform a hard reset on the PHY.
39311347SRamana.Srikanth@Sun.COM 		 */
39411347SRamana.Srikanth@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, tgt,
39511347SRamana.Srikanth@Sun.COM 		    "%s: Issue HARD_RESET to PHY %s", __func__,
39611347SRamana.Srikanth@Sun.COM 		    pptr->path);
39711347SRamana.Srikanth@Sun.COM 		/*
39811347SRamana.Srikanth@Sun.COM 		 * Must release statlock here because pmcs_reset_phy
39911347SRamana.Srikanth@Sun.COM 		 * will drop and reacquire the PHY lock.
40011090SDavid.Hollister@Sun.COM 		 */
40111347SRamana.Srikanth@Sun.COM 		if (tgt != NULL) {
40211347SRamana.Srikanth@Sun.COM 			mutex_exit(&tgt->statlock);
40311347SRamana.Srikanth@Sun.COM 		}
40411347SRamana.Srikanth@Sun.COM 		rc = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_HARD_RESET);
40511347SRamana.Srikanth@Sun.COM 		if (tgt != NULL) {
40611347SRamana.Srikanth@Sun.COM 			mutex_enter(&tgt->statlock);
40711347SRamana.Srikanth@Sun.COM 		}
40811347SRamana.Srikanth@Sun.COM 		if (rc) {
40911347SRamana.Srikanth@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
41011347SRamana.Srikanth@Sun.COM 			    "%s: HARD_RESET to PHY %s failed (rc=%d)",
41111347SRamana.Srikanth@Sun.COM 			    __func__, pptr->path, rc);
41211090SDavid.Hollister@Sun.COM 
41311347SRamana.Srikanth@Sun.COM 			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
41411347SRamana.Srikanth@Sun.COM 			    __func__, "HARD_RESET");
41511090SDavid.Hollister@Sun.COM 
41611347SRamana.Srikanth@Sun.COM 			goto next_phy;
41711090SDavid.Hollister@Sun.COM 		}
41811090SDavid.Hollister@Sun.COM 
41911090SDavid.Hollister@Sun.COM 		/*
42011090SDavid.Hollister@Sun.COM 		 * Step 3: Abort all I/Os to the device
42111090SDavid.Hollister@Sun.COM 		 */
42211090SDavid.Hollister@Sun.COM 		if (pptr->abort_all_start) {
42311090SDavid.Hollister@Sun.COM 			while (pptr->abort_all_start) {
42411090SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
42511090SDavid.Hollister@Sun.COM 				    "%s: Waiting for outstanding ABORT_ALL on "
42611090SDavid.Hollister@Sun.COM 				    "PHY 0x%p", __func__, (void *)pptr);
42711090SDavid.Hollister@Sun.COM 				cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
42811090SDavid.Hollister@Sun.COM 			}
42911090SDavid.Hollister@Sun.COM 		} else {
43011090SDavid.Hollister@Sun.COM 			if (tgt != NULL) {
43111090SDavid.Hollister@Sun.COM 				mutex_exit(&tgt->statlock);
43211090SDavid.Hollister@Sun.COM 			}
43311090SDavid.Hollister@Sun.COM 			rc = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
43411090SDavid.Hollister@Sun.COM 			if (tgt != NULL) {
43511090SDavid.Hollister@Sun.COM 				mutex_enter(&tgt->statlock);
43611090SDavid.Hollister@Sun.COM 			}
43711090SDavid.Hollister@Sun.COM 			if (rc != 0) {
43811090SDavid.Hollister@Sun.COM 				pptr->abort_pending = 1;
43911090SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
44011090SDavid.Hollister@Sun.COM 				    "%s: pmcs_abort to PHY %s failed (rc=%d)",
44111090SDavid.Hollister@Sun.COM 				    __func__, pptr->path, rc);
44211090SDavid.Hollister@Sun.COM 
44311090SDavid.Hollister@Sun.COM 				pmcs_handle_ds_recovery_error(pptr, tgt,
44411347SRamana.Srikanth@Sun.COM 				    pwp, __func__, "pmcs_abort");
44511090SDavid.Hollister@Sun.COM 
44611090SDavid.Hollister@Sun.COM 				goto next_phy;
44711090SDavid.Hollister@Sun.COM 			}
44811090SDavid.Hollister@Sun.COM 		}
44911090SDavid.Hollister@Sun.COM 
45011090SDavid.Hollister@Sun.COM 		/*
45111090SDavid.Hollister@Sun.COM 		 * Step 4: Set the device back to OPERATIONAL state
45211090SDavid.Hollister@Sun.COM 		 */
45311090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, tgt,
45411090SDavid.Hollister@Sun.COM 		    "%s: Set PHY/tgt 0x%p/0x%p to OPERATIONAL state",
45511090SDavid.Hollister@Sun.COM 		    __func__, (void *)pptr, (void *)tgt);
45611090SDavid.Hollister@Sun.COM 		rc = pmcs_set_dev_state(pwp, pptr, tgt,
45711090SDavid.Hollister@Sun.COM 		    PMCS_DEVICE_STATE_OPERATIONAL);
45811090SDavid.Hollister@Sun.COM 		if (rc == 0) {
45911267SJesse.Butler@Sun.COM 			pmcs_ds_operational(pptr, tgt);
46011090SDavid.Hollister@Sun.COM 		} else {
46111090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, tgt,
46211090SDavid.Hollister@Sun.COM 			    "%s: Failed to SET tgt 0x%p to OPERATIONAL state",
46311090SDavid.Hollister@Sun.COM 			    __func__, (void *)tgt);
46411090SDavid.Hollister@Sun.COM 
46511090SDavid.Hollister@Sun.COM 			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
46611347SRamana.Srikanth@Sun.COM 			    __func__, "SET tgt to OPERATIONAL state");
46711090SDavid.Hollister@Sun.COM 
46811090SDavid.Hollister@Sun.COM 			goto next_phy;
46911090SDavid.Hollister@Sun.COM 		}
47011090SDavid.Hollister@Sun.COM 
47111090SDavid.Hollister@Sun.COM next_phy:
47211090SDavid.Hollister@Sun.COM 		if (tgt) {
47311090SDavid.Hollister@Sun.COM 			mutex_exit(&tgt->statlock);
47411090SDavid.Hollister@Sun.COM 		}
47511090SDavid.Hollister@Sun.COM 		pmcs_unlock_phy(pptr);
47611090SDavid.Hollister@Sun.COM 		pptr = pnext;
47711090SDavid.Hollister@Sun.COM 	}
47811090SDavid.Hollister@Sun.COM 
47911090SDavid.Hollister@Sun.COM 	/*
48011090SDavid.Hollister@Sun.COM 	 * Only clear ds_err_recovering if we're exiting for good and not
48111090SDavid.Hollister@Sun.COM 	 * just unwinding from recursion
48211090SDavid.Hollister@Sun.COM 	 */
48311090SDavid.Hollister@Sun.COM 	if (phyp == NULL) {
48411090SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
48511090SDavid.Hollister@Sun.COM 		pwp->ds_err_recovering = 0;
48611090SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
48711090SDavid.Hollister@Sun.COM 	}
48811167SRamana.Srikanth@Sun.COM 
48911167SRamana.Srikanth@Sun.COM 	if (reschedule) {
49011167SRamana.Srikanth@Sun.COM 		SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
49111167SRamana.Srikanth@Sun.COM 	}
49211090SDavid.Hollister@Sun.COM }
49311090SDavid.Hollister@Sun.COM 
49411090SDavid.Hollister@Sun.COM /*
49511090SDavid.Hollister@Sun.COM  * Called with target's statlock held (if target is non-NULL) and PHY lock held.
49611090SDavid.Hollister@Sun.COM  */
49711090SDavid.Hollister@Sun.COM int
pmcs_send_err_recovery_cmd(pmcs_hw_t * pwp,uint8_t dev_state,pmcs_phy_t * phyp,pmcs_xscsi_t * tgt)49811090SDavid.Hollister@Sun.COM pmcs_send_err_recovery_cmd(pmcs_hw_t *pwp, uint8_t dev_state, pmcs_phy_t *phyp,
49911090SDavid.Hollister@Sun.COM     pmcs_xscsi_t *tgt)
50011090SDavid.Hollister@Sun.COM {
50111090SDavid.Hollister@Sun.COM 	int rc = -1;
50211090SDavid.Hollister@Sun.COM 	uint8_t tgt_dev_state = PMCS_DEVICE_STATE_NOT_AVAILABLE;
50311090SDavid.Hollister@Sun.COM 
50411090SDavid.Hollister@Sun.COM 	if (tgt != NULL) {
50511090SDavid.Hollister@Sun.COM 		ASSERT(mutex_owned(&tgt->statlock));
50611090SDavid.Hollister@Sun.COM 		if (tgt->recovering) {
50711090SDavid.Hollister@Sun.COM 			return (0);
50811090SDavid.Hollister@Sun.COM 		}
50911090SDavid.Hollister@Sun.COM 
51011090SDavid.Hollister@Sun.COM 		tgt->recovering = 1;
51111090SDavid.Hollister@Sun.COM 		tgt_dev_state = tgt->dev_state;
51211090SDavid.Hollister@Sun.COM 	}
51311090SDavid.Hollister@Sun.COM 
51411090SDavid.Hollister@Sun.COM 	if (phyp == NULL) {
51511090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, NULL, tgt,
51611090SDavid.Hollister@Sun.COM 		    "%s: PHY is NULL", __func__);
51711090SDavid.Hollister@Sun.COM 		return (-1);
51811090SDavid.Hollister@Sun.COM 	}
51911090SDavid.Hollister@Sun.COM 
52011090SDavid.Hollister@Sun.COM 	ASSERT(mutex_owned(&phyp->phy_lock));
52111090SDavid.Hollister@Sun.COM 
52211090SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, tgt,
52311090SDavid.Hollister@Sun.COM 	    "%s: ds: 0x%x, tgt ds(0x%x)", __func__, dev_state, tgt_dev_state);
52411090SDavid.Hollister@Sun.COM 
52511090SDavid.Hollister@Sun.COM 	switch (dev_state) {
52611090SDavid.Hollister@Sun.COM 	case PMCS_DEVICE_STATE_IN_RECOVERY:
52711090SDavid.Hollister@Sun.COM 		if (tgt_dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
52811090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, tgt,
52911090SDavid.Hollister@Sun.COM 			    "%s: Target 0x%p already IN_RECOVERY", __func__,
53011090SDavid.Hollister@Sun.COM 			    (void *)tgt);
53111090SDavid.Hollister@Sun.COM 			rc = 0;	/* This is not an error */
53211090SDavid.Hollister@Sun.COM 			goto no_action;
53311090SDavid.Hollister@Sun.COM 		}
53411090SDavid.Hollister@Sun.COM 
53511090SDavid.Hollister@Sun.COM 		rc = pmcs_set_dev_state(pwp, phyp, tgt,
53611090SDavid.Hollister@Sun.COM 		    PMCS_DEVICE_STATE_IN_RECOVERY);
53711090SDavid.Hollister@Sun.COM 		if (rc != 0) {
53811090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, tgt,
53911090SDavid.Hollister@Sun.COM 			    "%s(1): Failed to set tgt(0x%p) to IN_RECOVERY",
54011090SDavid.Hollister@Sun.COM 			    __func__, (void *)tgt);
54111090SDavid.Hollister@Sun.COM 		}
54211090SDavid.Hollister@Sun.COM 
54311090SDavid.Hollister@Sun.COM 		break;
54411090SDavid.Hollister@Sun.COM 
54511090SDavid.Hollister@Sun.COM 	case PMCS_DEVICE_STATE_OPERATIONAL:
54611090SDavid.Hollister@Sun.COM 		if (tgt_dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) {
54711090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, tgt,
54811090SDavid.Hollister@Sun.COM 			    "%s: Target 0x%p not ready to go OPERATIONAL",
54911090SDavid.Hollister@Sun.COM 			    __func__, (void *)tgt);
55011090SDavid.Hollister@Sun.COM 			goto no_action;
55111090SDavid.Hollister@Sun.COM 		}
55211090SDavid.Hollister@Sun.COM 
55311090SDavid.Hollister@Sun.COM 		rc = pmcs_set_dev_state(pwp, phyp, tgt,
55411090SDavid.Hollister@Sun.COM 		    PMCS_DEVICE_STATE_OPERATIONAL);
55511090SDavid.Hollister@Sun.COM 		if (tgt != NULL) {
55611090SDavid.Hollister@Sun.COM 			tgt->reset_success = 1;
55711090SDavid.Hollister@Sun.COM 		}
55811090SDavid.Hollister@Sun.COM 		if (rc != 0) {
55911090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, tgt,
56011090SDavid.Hollister@Sun.COM 			    "%s(2): Failed to SET tgt(0x%p) to OPERATIONAL",
56111090SDavid.Hollister@Sun.COM 			    __func__, (void *)tgt);
56211090SDavid.Hollister@Sun.COM 			if (tgt != NULL) {
56311090SDavid.Hollister@Sun.COM 				tgt->reset_success = 0;
56411090SDavid.Hollister@Sun.COM 			}
56511090SDavid.Hollister@Sun.COM 		}
56611090SDavid.Hollister@Sun.COM 
56711090SDavid.Hollister@Sun.COM 		break;
56811090SDavid.Hollister@Sun.COM 
56911090SDavid.Hollister@Sun.COM 	case PMCS_DEVICE_STATE_NON_OPERATIONAL:
57011090SDavid.Hollister@Sun.COM 		PHY_CHANGED(pwp, phyp);
57111090SDavid.Hollister@Sun.COM 		RESTART_DISCOVERY(pwp);
57211090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, tgt,
57311090SDavid.Hollister@Sun.COM 		    "%s: Device at %s is non-operational",
57411090SDavid.Hollister@Sun.COM 		    __func__, phyp->path);
57511090SDavid.Hollister@Sun.COM 		if (tgt != NULL) {
57611090SDavid.Hollister@Sun.COM 			tgt->dev_state = PMCS_DEVICE_STATE_NON_OPERATIONAL;
57711090SDavid.Hollister@Sun.COM 		}
57811090SDavid.Hollister@Sun.COM 		rc = 0;
57911090SDavid.Hollister@Sun.COM 
58011090SDavid.Hollister@Sun.COM 		break;
58111090SDavid.Hollister@Sun.COM 
58211090SDavid.Hollister@Sun.COM 	default:
58311090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, tgt,
58411090SDavid.Hollister@Sun.COM 		    "%s: Invalid state requested (%d)", __func__,
58511090SDavid.Hollister@Sun.COM 		    dev_state);
58611090SDavid.Hollister@Sun.COM 		break;
58711090SDavid.Hollister@Sun.COM 
58811090SDavid.Hollister@Sun.COM 	}
58911090SDavid.Hollister@Sun.COM 
59011090SDavid.Hollister@Sun.COM no_action:
59111090SDavid.Hollister@Sun.COM 	if (tgt != NULL) {
59211090SDavid.Hollister@Sun.COM 		tgt->recovering = 0;
59311090SDavid.Hollister@Sun.COM 	}
59411090SDavid.Hollister@Sun.COM 	return (rc);
59511090SDavid.Hollister@Sun.COM }
59611090SDavid.Hollister@Sun.COM 
59711090SDavid.Hollister@Sun.COM /*
59811090SDavid.Hollister@Sun.COM  * Start ssp event recovery. We have to schedule recovery operation because
59911090SDavid.Hollister@Sun.COM  * it involves sending multiple commands to device and we should not do it
60011090SDavid.Hollister@Sun.COM  * in the interrupt context.
60111090SDavid.Hollister@Sun.COM  * If it is failure of a recovery command, let the recovery thread deal with it.
60212862Sjesse.butler@oracle.com  * Called with the work lock held.
60311090SDavid.Hollister@Sun.COM  */
60411090SDavid.Hollister@Sun.COM void
pmcs_start_ssp_event_recovery(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * iomb,size_t amt)60511090SDavid.Hollister@Sun.COM pmcs_start_ssp_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
60611090SDavid.Hollister@Sun.COM     size_t amt)
60711090SDavid.Hollister@Sun.COM {
60811090SDavid.Hollister@Sun.COM 	pmcs_xscsi_t *tgt = pwrk->xp;
60911090SDavid.Hollister@Sun.COM 	uint32_t event = LE_32(iomb[2]);
61011090SDavid.Hollister@Sun.COM 	pmcs_phy_t *pptr = pwrk->phy;
61112060SDavid.Hollister@Sun.COM 	pmcs_cb_t callback;
61211090SDavid.Hollister@Sun.COM 	uint32_t tag;
61311090SDavid.Hollister@Sun.COM 
61411090SDavid.Hollister@Sun.COM 	if (tgt != NULL) {
61511090SDavid.Hollister@Sun.COM 		mutex_enter(&tgt->statlock);
61611090SDavid.Hollister@Sun.COM 		if (!tgt->assigned) {
61711090SDavid.Hollister@Sun.COM 			if (pptr) {
61811090SDavid.Hollister@Sun.COM 				pmcs_dec_phy_ref_count(pptr);
61911090SDavid.Hollister@Sun.COM 			}
62011090SDavid.Hollister@Sun.COM 			pptr = NULL;
62111090SDavid.Hollister@Sun.COM 			pwrk->phy = NULL;
62211090SDavid.Hollister@Sun.COM 		}
62311090SDavid.Hollister@Sun.COM 		mutex_exit(&tgt->statlock);
62411090SDavid.Hollister@Sun.COM 	}
62511355SDavid.Hollister@Sun.COM 
62611090SDavid.Hollister@Sun.COM 	if (pptr == NULL) {
62711090SDavid.Hollister@Sun.COM 		/*
62811090SDavid.Hollister@Sun.COM 		 * No target, need to run RE-DISCOVERY here.
62911090SDavid.Hollister@Sun.COM 		 */
63011090SDavid.Hollister@Sun.COM 		if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) {
63111090SDavid.Hollister@Sun.COM 			pwrk->state = PMCS_WORK_STATE_INTR;
63211090SDavid.Hollister@Sun.COM 		}
63311090SDavid.Hollister@Sun.COM 		/*
63411090SDavid.Hollister@Sun.COM 		 * Although we cannot mark phy to force abort nor mark phy
63511090SDavid.Hollister@Sun.COM 		 * as changed, killing of a target would take care of aborting
63611090SDavid.Hollister@Sun.COM 		 * commands for the device.
63711090SDavid.Hollister@Sun.COM 		 */
63811090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
63911090SDavid.Hollister@Sun.COM 		    "%s: No valid target for event processing. Reconfigure.",
64011090SDavid.Hollister@Sun.COM 		    __func__);
64111090SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, pwrk);
64211090SDavid.Hollister@Sun.COM 		RESTART_DISCOVERY(pwp);
64311090SDavid.Hollister@Sun.COM 		return;
64411090SDavid.Hollister@Sun.COM 	} else {
64512862Sjesse.butler@oracle.com 		/* We have a phy pointer, we'll need to lock it */
64612862Sjesse.butler@oracle.com 		mutex_exit(&pwrk->lock);
64711090SDavid.Hollister@Sun.COM 		pmcs_lock_phy(pptr);
64812862Sjesse.butler@oracle.com 		mutex_enter(&pwrk->lock);
64912462Sjesse.butler@oracle.com 		if (tgt != NULL) {
65011355SDavid.Hollister@Sun.COM 			mutex_enter(&tgt->statlock);
65111355SDavid.Hollister@Sun.COM 		}
65211090SDavid.Hollister@Sun.COM 		if (event == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
65312462Sjesse.butler@oracle.com 			if ((tgt != NULL) && (tgt->dev_state !=
65412462Sjesse.butler@oracle.com 			    PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
65511090SDavid.Hollister@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
65611090SDavid.Hollister@Sun.COM 				    "%s: Device at %s is non-operational",
65711090SDavid.Hollister@Sun.COM 				    __func__, pptr->path);
65811090SDavid.Hollister@Sun.COM 				tgt->dev_state =
65911090SDavid.Hollister@Sun.COM 				    PMCS_DEVICE_STATE_NON_OPERATIONAL;
66011090SDavid.Hollister@Sun.COM 			}
66111090SDavid.Hollister@Sun.COM 			pptr->abort_pending = 1;
66212462Sjesse.butler@oracle.com 			if (tgt != NULL) {
66311355SDavid.Hollister@Sun.COM 				mutex_exit(&tgt->statlock);
66411355SDavid.Hollister@Sun.COM 			}
66512862Sjesse.butler@oracle.com 			mutex_exit(&pwrk->lock);
66611090SDavid.Hollister@Sun.COM 			pmcs_unlock_phy(pptr);
66711090SDavid.Hollister@Sun.COM 			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
66811090SDavid.Hollister@Sun.COM 			RESTART_DISCOVERY(pwp);
66911090SDavid.Hollister@Sun.COM 			return;
67011090SDavid.Hollister@Sun.COM 		}
67111090SDavid.Hollister@Sun.COM 
67211090SDavid.Hollister@Sun.COM 		/*
67311090SDavid.Hollister@Sun.COM 		 * If this command is run in WAIT mode, it is a failing recovery
67411090SDavid.Hollister@Sun.COM 		 * command. If so, just wake up recovery thread waiting for
67511090SDavid.Hollister@Sun.COM 		 * command completion.
67611090SDavid.Hollister@Sun.COM 		 */
67711090SDavid.Hollister@Sun.COM 		tag = PMCS_TAG_TYPE(pwrk->htag);
67811090SDavid.Hollister@Sun.COM 		if (tag == PMCS_TAG_TYPE_WAIT) {
67911090SDavid.Hollister@Sun.COM 			pwrk->htag |= PMCS_TAG_DONE;
68011090SDavid.Hollister@Sun.COM 			if (pwrk->arg && amt) {
68111090SDavid.Hollister@Sun.COM 				(void) memcpy(pwrk->arg, iomb, amt);
68211090SDavid.Hollister@Sun.COM 			}
68311090SDavid.Hollister@Sun.COM 			cv_signal(&pwrk->sleep_cv);
68412462Sjesse.butler@oracle.com 			if (tgt != NULL) {
68511355SDavid.Hollister@Sun.COM 				mutex_exit(&tgt->statlock);
68611355SDavid.Hollister@Sun.COM 			}
68712862Sjesse.butler@oracle.com 			mutex_exit(&pwrk->lock);
68811090SDavid.Hollister@Sun.COM 			pmcs_unlock_phy(pptr);
68911090SDavid.Hollister@Sun.COM 			return;
69011090SDavid.Hollister@Sun.COM 		}
69111090SDavid.Hollister@Sun.COM 
69212462Sjesse.butler@oracle.com 		if (tgt == NULL) {
69311355SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, NULL,
69411355SDavid.Hollister@Sun.COM 			    "%s: Not scheduling SSP event recovery for NULL tgt"
69511355SDavid.Hollister@Sun.COM 			    " pwrk(%p) tag(0x%x)", __func__, (void *)pwrk,
69611355SDavid.Hollister@Sun.COM 			    pwrk->htag);
69712862Sjesse.butler@oracle.com 			mutex_exit(&pwrk->lock);
69812862Sjesse.butler@oracle.com 			pmcs_unlock_phy(pptr);
69911355SDavid.Hollister@Sun.COM 			return;
70011355SDavid.Hollister@Sun.COM 		}
70111355SDavid.Hollister@Sun.COM 
70211090SDavid.Hollister@Sun.COM 		/*
70312060SDavid.Hollister@Sun.COM 		 * If the SSP event was an OPEN_RETRY_TIMEOUT, we don't want
70412060SDavid.Hollister@Sun.COM 		 * to go through the recovery (abort/LU reset) process.
70512060SDavid.Hollister@Sun.COM 		 * Simply complete the command and return it as STATUS_BUSY.
70612060SDavid.Hollister@Sun.COM 		 * This will cause the target driver to simply retry.
70712060SDavid.Hollister@Sun.COM 		 */
70812060SDavid.Hollister@Sun.COM 		if (event == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT) {
70912060SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
71012060SDavid.Hollister@Sun.COM 			    "%s: Got OPEN_RETRY_TIMEOUT event (htag 0x%08x)",
71112060SDavid.Hollister@Sun.COM 			    __func__, pwrk->htag);
71212060SDavid.Hollister@Sun.COM 
71312060SDavid.Hollister@Sun.COM 			mutex_exit(&tgt->statlock);
71412862Sjesse.butler@oracle.com 			/* Note: work remains locked for the callback */
71512060SDavid.Hollister@Sun.COM 			pmcs_unlock_phy(pptr);
71612060SDavid.Hollister@Sun.COM 			pwrk->ssp_event = event;
71712060SDavid.Hollister@Sun.COM 			callback = (pmcs_cb_t)pwrk->ptr;
71812060SDavid.Hollister@Sun.COM 			(*callback)(pwp, pwrk, iomb);
71912060SDavid.Hollister@Sun.COM 			return;
72012060SDavid.Hollister@Sun.COM 		}
72112060SDavid.Hollister@Sun.COM 
72212060SDavid.Hollister@Sun.COM 		/*
72311090SDavid.Hollister@Sun.COM 		 * To recover from primary failures,
72411090SDavid.Hollister@Sun.COM 		 * we need to schedule handling events recovery.
72511090SDavid.Hollister@Sun.COM 		 */
72611090SDavid.Hollister@Sun.COM 		tgt->event_recovery = 1;
72711090SDavid.Hollister@Sun.COM 		mutex_exit(&tgt->statlock);
72812862Sjesse.butler@oracle.com 		pwrk->ssp_event = event;
72912862Sjesse.butler@oracle.com 		mutex_exit(&pwrk->lock);
73011090SDavid.Hollister@Sun.COM 		pmcs_unlock_phy(pptr);
73111090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
73211090SDavid.Hollister@Sun.COM 		    "%s: Scheduling SSP event recovery for tgt(0x%p) "
73311090SDavid.Hollister@Sun.COM 		    "pwrk(%p) tag(0x%x)", __func__, (void *)tgt, (void *)pwrk,
73411090SDavid.Hollister@Sun.COM 		    pwrk->htag);
73511090SDavid.Hollister@Sun.COM 		SCHEDULE_WORK(pwp, PMCS_WORK_SSP_EVT_RECOVERY);
73611090SDavid.Hollister@Sun.COM 	}
73711090SDavid.Hollister@Sun.COM 
73811090SDavid.Hollister@Sun.COM 	/* Work cannot be completed until event recovery is completed. */
73911090SDavid.Hollister@Sun.COM }
74011090SDavid.Hollister@Sun.COM 
74111090SDavid.Hollister@Sun.COM /*
74211090SDavid.Hollister@Sun.COM  * SSP target event recovery
743*13103Ssrikanth.suravajhala@oracle.com  * phy->lock should be held upon entry.
744*13103Ssrikanth.suravajhala@oracle.com  * pwrk->lock should be held upon entry and gets released by this routine.
745*13103Ssrikanth.suravajhala@oracle.com  * tgt->statlock should not be held.
74611090SDavid.Hollister@Sun.COM  */
74711090SDavid.Hollister@Sun.COM void
pmcs_tgt_event_recovery(pmcs_hw_t * pwp,pmcwork_t * pwrk)74811090SDavid.Hollister@Sun.COM pmcs_tgt_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk)
74911090SDavid.Hollister@Sun.COM {
75011090SDavid.Hollister@Sun.COM 	pmcs_phy_t *pptr = pwrk->phy;
75111090SDavid.Hollister@Sun.COM 	pmcs_cmd_t *sp = pwrk->arg;
75211090SDavid.Hollister@Sun.COM 	pmcs_lun_t *lun = sp->cmd_lun;
75311090SDavid.Hollister@Sun.COM 	pmcs_xscsi_t *tgt = pwrk->xp;
75411090SDavid.Hollister@Sun.COM 	uint32_t event;
75511090SDavid.Hollister@Sun.COM 	uint32_t htag;
75611090SDavid.Hollister@Sun.COM 	uint32_t status;
75711090SDavid.Hollister@Sun.COM 	int rv;
75811090SDavid.Hollister@Sun.COM 
75911090SDavid.Hollister@Sun.COM 	ASSERT(pwrk->arg != NULL);
76011090SDavid.Hollister@Sun.COM 	ASSERT(pwrk->xp != NULL);
76111090SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
76211090SDavid.Hollister@Sun.COM 	    "%s: event recovery for target 0x%p", __func__, (void *)pwrk->xp);
76311090SDavid.Hollister@Sun.COM 	htag = pwrk->htag;
76411090SDavid.Hollister@Sun.COM 	event = pwrk->ssp_event;
76511090SDavid.Hollister@Sun.COM 	pwrk->ssp_event = 0xffffffff;
76612060SDavid.Hollister@Sun.COM 
767*13103Ssrikanth.suravajhala@oracle.com 	mutex_exit(&pwrk->lock);
768*13103Ssrikanth.suravajhala@oracle.com 
76911090SDavid.Hollister@Sun.COM 	if (event == PMCOUT_STATUS_XFER_ERR_BREAK ||
77011090SDavid.Hollister@Sun.COM 	    event == PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY ||
77111090SDavid.Hollister@Sun.COM 	    event == PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT) {
77211090SDavid.Hollister@Sun.COM 		/* Command may be still pending on device */
77311090SDavid.Hollister@Sun.COM 		rv = pmcs_ssp_tmf(pwp, pptr, SAS_QUERY_TASK, htag,
77411090SDavid.Hollister@Sun.COM 		    lun->lun_num, &status);
77511090SDavid.Hollister@Sun.COM 		if (rv != 0) {
77611090SDavid.Hollister@Sun.COM 			goto out;
77711090SDavid.Hollister@Sun.COM 		}
77811090SDavid.Hollister@Sun.COM 		if (status == SAS_RSP_TMF_COMPLETE) {
77911090SDavid.Hollister@Sun.COM 			/* Command NOT pending on a device */
78011090SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
78111090SDavid.Hollister@Sun.COM 			    "%s: No pending command for tgt 0x%p",
78211090SDavid.Hollister@Sun.COM 			    __func__, (void *)tgt);
78311090SDavid.Hollister@Sun.COM 			/* Nothing more to do, just abort it on chip */
78411090SDavid.Hollister@Sun.COM 			htag = 0;
78511090SDavid.Hollister@Sun.COM 		}
78611090SDavid.Hollister@Sun.COM 	}
78711090SDavid.Hollister@Sun.COM 	/*
78811090SDavid.Hollister@Sun.COM 	 * All other events left the command pending in the host
78911090SDavid.Hollister@Sun.COM 	 * Send abort task and abort it on the chip
79011090SDavid.Hollister@Sun.COM 	 */
79111090SDavid.Hollister@Sun.COM 	if (htag != 0) {
79211090SDavid.Hollister@Sun.COM 		if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, htag,
79311090SDavid.Hollister@Sun.COM 		    lun->lun_num, &status))
79411090SDavid.Hollister@Sun.COM 			goto out;
79511090SDavid.Hollister@Sun.COM 	}
796*13103Ssrikanth.suravajhala@oracle.com 	(void) pmcs_abort(pwp, pptr, htag, 0, 1);
79711090SDavid.Hollister@Sun.COM 	/*
79811090SDavid.Hollister@Sun.COM 	 * Abort either took care of work completion, or put device in
79911090SDavid.Hollister@Sun.COM 	 * a recovery state
80011090SDavid.Hollister@Sun.COM 	 */
80111090SDavid.Hollister@Sun.COM 	return;
80211090SDavid.Hollister@Sun.COM out:
80311090SDavid.Hollister@Sun.COM 	/* Abort failed, do full device recovery */
804*13103Ssrikanth.suravajhala@oracle.com 	mutex_enter(&pwrk->lock);
805*13103Ssrikanth.suravajhala@oracle.com 	tgt = pwrk->xp;
806*13103Ssrikanth.suravajhala@oracle.com 	mutex_exit(&pwrk->lock);
807*13103Ssrikanth.suravajhala@oracle.com 	if (tgt != NULL) {
808*13103Ssrikanth.suravajhala@oracle.com 		mutex_enter(&tgt->statlock);
809*13103Ssrikanth.suravajhala@oracle.com 		pmcs_start_dev_state_recovery(tgt, pptr);
810*13103Ssrikanth.suravajhala@oracle.com 		mutex_exit(&tgt->statlock);
81111090SDavid.Hollister@Sun.COM 	}
81211090SDavid.Hollister@Sun.COM }
81311090SDavid.Hollister@Sun.COM 
81411090SDavid.Hollister@Sun.COM /*
81511090SDavid.Hollister@Sun.COM  * SSP event recovery task.
81611090SDavid.Hollister@Sun.COM  */
81711090SDavid.Hollister@Sun.COM void
pmcs_ssp_event_recovery(pmcs_hw_t * pwp)81811090SDavid.Hollister@Sun.COM pmcs_ssp_event_recovery(pmcs_hw_t *pwp)
81911090SDavid.Hollister@Sun.COM {
82011090SDavid.Hollister@Sun.COM 	int idx;
82111090SDavid.Hollister@Sun.COM 	pmcs_xscsi_t *tgt;
82211090SDavid.Hollister@Sun.COM 	pmcs_cmd_t *cp;
82311090SDavid.Hollister@Sun.COM 	pmcwork_t *pwrk;
82411090SDavid.Hollister@Sun.COM 	pmcs_phy_t *pphy;
82511090SDavid.Hollister@Sun.COM 	int er_flag;
82611090SDavid.Hollister@Sun.COM 	uint32_t idxpwrk;
82711090SDavid.Hollister@Sun.COM 
82811090SDavid.Hollister@Sun.COM restart:
82911090SDavid.Hollister@Sun.COM 	for (idx = 0; idx < pwp->max_dev; idx++) {
83011090SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->lock);
83111090SDavid.Hollister@Sun.COM 		tgt = pwp->targets[idx];
83211090SDavid.Hollister@Sun.COM 		mutex_exit(&pwp->lock);
83311347SRamana.Srikanth@Sun.COM 		if (tgt == NULL) {
83411347SRamana.Srikanth@Sun.COM 			continue;
83511347SRamana.Srikanth@Sun.COM 		}
83611347SRamana.Srikanth@Sun.COM 
83711347SRamana.Srikanth@Sun.COM 		mutex_enter(&tgt->statlock);
83811347SRamana.Srikanth@Sun.COM 		if (!tgt->assigned) {
83911090SDavid.Hollister@Sun.COM 			mutex_exit(&tgt->statlock);
84011347SRamana.Srikanth@Sun.COM 			continue;
84111347SRamana.Srikanth@Sun.COM 		}
84211347SRamana.Srikanth@Sun.COM 		pphy = tgt->phy;
84311347SRamana.Srikanth@Sun.COM 		er_flag = tgt->event_recovery;
84411347SRamana.Srikanth@Sun.COM 		mutex_exit(&tgt->statlock);
84511347SRamana.Srikanth@Sun.COM 
84611347SRamana.Srikanth@Sun.COM 		if ((pphy == NULL) || (er_flag == 0)) {
84711347SRamana.Srikanth@Sun.COM 			continue;
84811347SRamana.Srikanth@Sun.COM 		}
84911347SRamana.Srikanth@Sun.COM 
85011347SRamana.Srikanth@Sun.COM 		pmcs_lock_phy(pphy);
85111347SRamana.Srikanth@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pphy, tgt,
85211347SRamana.Srikanth@Sun.COM 		    "%s: found target(0x%p)", __func__, (void *) tgt);
85311090SDavid.Hollister@Sun.COM 
85411347SRamana.Srikanth@Sun.COM 		/* Check what cmd expects recovery */
85511347SRamana.Srikanth@Sun.COM 		mutex_enter(&tgt->aqlock);
85611347SRamana.Srikanth@Sun.COM 		STAILQ_FOREACH(cp, &tgt->aq, cmd_next) {
85711347SRamana.Srikanth@Sun.COM 			idxpwrk = PMCS_TAG_INDEX(cp->cmd_tag);
85811347SRamana.Srikanth@Sun.COM 			pwrk = &pwp->work[idxpwrk];
859*13103Ssrikanth.suravajhala@oracle.com 			mutex_enter(&pwrk->lock);
86011347SRamana.Srikanth@Sun.COM 			if (pwrk->htag != cp->cmd_tag) {
86111347SRamana.Srikanth@Sun.COM 				/*
86211347SRamana.Srikanth@Sun.COM 				 * aq may contain TMF commands, so we
86311347SRamana.Srikanth@Sun.COM 				 * may not find work structure with htag
86411347SRamana.Srikanth@Sun.COM 				 */
865*13103Ssrikanth.suravajhala@oracle.com 				mutex_exit(&pwrk->lock);
866*13103Ssrikanth.suravajhala@oracle.com 				continue;
86711347SRamana.Srikanth@Sun.COM 			}
868*13103Ssrikanth.suravajhala@oracle.com 			if (!PMCS_COMMAND_DONE(pwrk) &&
869*13103Ssrikanth.suravajhala@oracle.com 			    (pwrk->ssp_event != 0) &&
87011347SRamana.Srikanth@Sun.COM 			    (pwrk->ssp_event != PMCS_REC_EVENT)) {
87111347SRamana.Srikanth@Sun.COM 				pmcs_prt(pwp, PMCS_PRT_DEBUG, pphy, tgt,
87211347SRamana.Srikanth@Sun.COM 				    "%s: pwrk(%p) htag(0x%x)",
87311347SRamana.Srikanth@Sun.COM 				    __func__, (void *) pwrk, cp->cmd_tag);
87411090SDavid.Hollister@Sun.COM 				mutex_exit(&tgt->aqlock);
875*13103Ssrikanth.suravajhala@oracle.com 				/*
876*13103Ssrikanth.suravajhala@oracle.com 				 * pwrk->lock gets dropped in
877*13103Ssrikanth.suravajhala@oracle.com 				 * pmcs_tgt_event_recovery()
878*13103Ssrikanth.suravajhala@oracle.com 				 */
87911347SRamana.Srikanth@Sun.COM 				pmcs_tgt_event_recovery(pwp, pwrk);
88011090SDavid.Hollister@Sun.COM 				pmcs_unlock_phy(pphy);
881*13103Ssrikanth.suravajhala@oracle.com 				/* All bets are off on tgt/aq now, restart */
88211347SRamana.Srikanth@Sun.COM 				goto restart;
88311090SDavid.Hollister@Sun.COM 			}
884*13103Ssrikanth.suravajhala@oracle.com 			mutex_exit(&pwrk->lock);
88511090SDavid.Hollister@Sun.COM 		}
88611347SRamana.Srikanth@Sun.COM 		mutex_exit(&tgt->aqlock);
887*13103Ssrikanth.suravajhala@oracle.com 		mutex_enter(&tgt->statlock);
88811347SRamana.Srikanth@Sun.COM 		tgt->event_recovery = 0;
88911347SRamana.Srikanth@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, pphy, tgt,
89011347SRamana.Srikanth@Sun.COM 		    "%s: end of SSP event recovery for target(0x%p)",
89111347SRamana.Srikanth@Sun.COM 		    __func__, (void *) tgt);
89211347SRamana.Srikanth@Sun.COM 		mutex_exit(&tgt->statlock);
89311347SRamana.Srikanth@Sun.COM 		pmcs_unlock_phy(pphy);
89411090SDavid.Hollister@Sun.COM 	}
89511090SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
89611090SDavid.Hollister@Sun.COM 	    "%s: end of SSP event recovery for pwp(0x%p)", __func__,
89711090SDavid.Hollister@Sun.COM 	    (void *) pwp);
89811090SDavid.Hollister@Sun.COM }
89911090SDavid.Hollister@Sun.COM 
90011090SDavid.Hollister@Sun.COM void
pmcs_start_dev_state_recovery(pmcs_xscsi_t * xp,pmcs_phy_t * phyp)90111090SDavid.Hollister@Sun.COM pmcs_start_dev_state_recovery(pmcs_xscsi_t *xp, pmcs_phy_t *phyp)
90211090SDavid.Hollister@Sun.COM {
90311090SDavid.Hollister@Sun.COM 	ASSERT(mutex_owned(&xp->statlock));
90411090SDavid.Hollister@Sun.COM 	ASSERT(xp->pwp != NULL);
90511090SDavid.Hollister@Sun.COM 
90611090SDavid.Hollister@Sun.COM 	if (xp->recover_wait == 0) {
90711090SDavid.Hollister@Sun.COM 		pmcs_prt(xp->pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, xp,
90811090SDavid.Hollister@Sun.COM 		    "%s: Start ds_recovery for tgt 0x%p/PHY 0x%p (%s)",
90911090SDavid.Hollister@Sun.COM 		    __func__, (void *)xp, (void *)phyp, phyp->path);
91011090SDavid.Hollister@Sun.COM 		xp->recover_wait = 1;
91111090SDavid.Hollister@Sun.COM 
91211090SDavid.Hollister@Sun.COM 		/*
91311090SDavid.Hollister@Sun.COM 		 * Rather than waiting for the watchdog timer, we'll
91411090SDavid.Hollister@Sun.COM 		 * kick it right now.
91511090SDavid.Hollister@Sun.COM 		 */
91611090SDavid.Hollister@Sun.COM 		SCHEDULE_WORK(xp->pwp, PMCS_WORK_DS_ERR_RECOVERY);
91711090SDavid.Hollister@Sun.COM 		(void) ddi_taskq_dispatch(xp->pwp->tq, pmcs_worker, xp->pwp,
91811090SDavid.Hollister@Sun.COM 		    DDI_NOSLEEP);
91911090SDavid.Hollister@Sun.COM 	}
92011090SDavid.Hollister@Sun.COM }
92111090SDavid.Hollister@Sun.COM 
92211090SDavid.Hollister@Sun.COM /*
92311090SDavid.Hollister@Sun.COM  * Increment the phy ds error retry count.
92411090SDavid.Hollister@Sun.COM  * If too many retries, mark phy dead and restart discovery;
92511090SDavid.Hollister@Sun.COM  * otherwise schedule ds recovery.
92611090SDavid.Hollister@Sun.COM  */
92711090SDavid.Hollister@Sun.COM static void
pmcs_handle_ds_recovery_error(pmcs_phy_t * phyp,pmcs_xscsi_t * tgt,pmcs_hw_t * pwp,const char * func_name,char * reason_string)92811090SDavid.Hollister@Sun.COM pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp, pmcs_xscsi_t *tgt,
92911347SRamana.Srikanth@Sun.COM     pmcs_hw_t *pwp, const char *func_name, char *reason_string)
93011090SDavid.Hollister@Sun.COM {
93111090SDavid.Hollister@Sun.COM 	ASSERT(mutex_owned(&phyp->phy_lock));
93211090SDavid.Hollister@Sun.COM 	ASSERT((tgt == NULL) || mutex_owned(&tgt->statlock));
93311090SDavid.Hollister@Sun.COM 
93411090SDavid.Hollister@Sun.COM 	phyp->ds_recovery_retries++;
93511090SDavid.Hollister@Sun.COM 
93611090SDavid.Hollister@Sun.COM 	if (phyp->ds_recovery_retries > PMCS_MAX_DS_RECOVERY_RETRIES) {
93711090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, tgt,
93811090SDavid.Hollister@Sun.COM 		    "%s: retry limit reached after %s to PHY %s failed",
93911090SDavid.Hollister@Sun.COM 		    func_name, reason_string, phyp->path);
94011090SDavid.Hollister@Sun.COM 		if (tgt != NULL) {
94111090SDavid.Hollister@Sun.COM 			tgt->recover_wait = 0;
94211090SDavid.Hollister@Sun.COM 		}
94311347SRamana.Srikanth@Sun.COM 		/*
94411347SRamana.Srikanth@Sun.COM 		 * Mark the PHY as dead and it and its parent as changed,
94511347SRamana.Srikanth@Sun.COM 		 * then restart discovery
94611347SRamana.Srikanth@Sun.COM 		 */
94711090SDavid.Hollister@Sun.COM 		phyp->dead = 1;
94811347SRamana.Srikanth@Sun.COM 		PHY_CHANGED(pwp, phyp);
94911347SRamana.Srikanth@Sun.COM 		if (phyp->parent)
95011347SRamana.Srikanth@Sun.COM 			PHY_CHANGED(pwp, phyp->parent);
95111090SDavid.Hollister@Sun.COM 		RESTART_DISCOVERY(pwp);
95211090SDavid.Hollister@Sun.COM 	} else if ((phyp->ds_prev_good_recoveries >
95311090SDavid.Hollister@Sun.COM 	    PMCS_MAX_DS_RECOVERY_RETRIES) &&
95411090SDavid.Hollister@Sun.COM 	    (phyp->last_good_recovery + drv_usectohz(PMCS_MAX_DS_RECOVERY_TIME)
95511090SDavid.Hollister@Sun.COM 	    < ddi_get_lbolt())) {
95611090SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, tgt, "%s: max number of "
95711090SDavid.Hollister@Sun.COM 		    "successful recoveries reached, declaring PHY %s dead",
95811090SDavid.Hollister@Sun.COM 		    __func__, phyp->path);
95911090SDavid.Hollister@Sun.COM 		if (tgt != NULL) {
96011090SDavid.Hollister@Sun.COM 			tgt->recover_wait = 0;
96111090SDavid.Hollister@Sun.COM 		}
96211347SRamana.Srikanth@Sun.COM 		/*
96311347SRamana.Srikanth@Sun.COM 		 * Mark the PHY as dead and its parent as changed,
96411347SRamana.Srikanth@Sun.COM 		 * then restart discovery
96511347SRamana.Srikanth@Sun.COM 		 */
96611090SDavid.Hollister@Sun.COM 		phyp->dead = 1;
96711347SRamana.Srikanth@Sun.COM 		PHY_CHANGED(pwp, phyp);
96811347SRamana.Srikanth@Sun.COM 		if (phyp->parent)
96911347SRamana.Srikanth@Sun.COM 			PHY_CHANGED(pwp, phyp->parent);
97011090SDavid.Hollister@Sun.COM 		RESTART_DISCOVERY(pwp);
97111090SDavid.Hollister@Sun.COM 	} else {
97211090SDavid.Hollister@Sun.COM 		SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
97311090SDavid.Hollister@Sun.COM 	}
97411090SDavid.Hollister@Sun.COM }
975