/*
* 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
* 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
*/
/*
*/
/*
* PM8001 device state recovery routines
*/
/*
* SAS Topology Configuration
*/
char *reason_string);
/*
* Get device state. Called with statlock and PHY lock held.
*/
static int
{
int result;
(void *)xp);
}
"%s: PHY is NULL", __func__);
return (-1);
}
return (-1);
}
if (phyp->valid_device_id == 0) {
"%s: Invalid DeviceID", __func__);
return (-1);
}
return (-1);
}
}
}
if (result) {
"%s: cmd timed out, returning", __func__);
return (-1);
}
"%s: retrieved_ds=0x%x, target_ds=0x%x", __func__,
}
return (0);
} else {
"%s: cmd failed Status(0x%x), returning ", __func__,
return (-1);
}
}
/*
* Set device state. Called with target's statlock and PHY lock held.
*/
static int
{
int result;
(void *)phyp);
"%s: PHY is NULL", __func__);
return (-1);
}
return (-1);
}
if (phyp->valid_device_id == 0) {
"%s: Invalid DeviceID", __func__);
return (-1);
}
return (-1);
}
}
}
if (result) {
"%s: cmd timed out, returning", __func__);
return (-1);
}
}
return (0);
} else {
"%s: cmd failed Status(0x%x), returning ", __func__,
return (-1);
}
}
static void
{
tgt->recover_wait = 0;
}
pptr->ds_recovery_retries = 0;
if ((pptr->ds_prev_good_recoveries == 0) ||
} else {
"Max recovery attempts reached. Declaring PHY dead");
}
/* Don't bother to run the work queues if the PHY is dead */
pwp, DDI_NOSLEEP);
}
}
void
{
int rc;
/*
* First time, check to see if we're already performing recovery
*/
if (pwp->ds_err_recovering) {
return;
}
} else {
}
while (pptr) {
/*
* Since ds_err_recovering is set, we can be assured these
* PHYs won't disappear on us while we do this.
*/
if (pchild) {
}
goto next_phy;
}
"%s: No DS recovery on PHY %s, iport not active",
goto next_phy;
}
if (tgt->recover_wait == 0) {
goto next_phy;
}
} else {
}
if (pptr->prev_recovery) {
"%s: DS recovery on PHY %s "
"re-invoked too soon. Skipping...",
reschedule = B_TRUE;
}
goto next_phy;
}
}
/*
* Step 1: Put the device into the IN_RECOVERY state
*/
if (rc != 0) {
"%s: pmcs_get_dev_state on PHY %s "
"failed (rc=%d)",
__func__, "pmcs_get_dev_state");
goto next_phy;
}
/* If the chip says it's operational, we're done */
if (ds == PMCS_DEVICE_STATE_OPERATIONAL) {
goto next_phy;
}
if ((tgt_dev_state == ds) &&
(ds == PMCS_DEVICE_STATE_IN_RECOVERY)) {
"%s: Target 0x%p already IN_RECOVERY", __func__,
(void *)tgt);
} else {
}
tgt_dev_state = ds;
"%s: pmcs_send_err_recovery_cmd "
"result(%d) tgt(0x%p) ds(0x%x) tgt->ds(0x%x)",
if (rc) {
"%s: pmcs_send_err_recovery_cmd to PHY %s "
"failed (rc=%d)",
__func__, "pmcs_send_err_recovery_cmd");
goto next_phy;
}
}
/*
* Step 2: Perform a hard reset on the PHY.
*/
"%s: Issue HARD_RESET to PHY %s", __func__,
/*
* Must release statlock here because pmcs_reset_phy
* will drop and reacquire the PHY lock.
*/
}
}
if (rc) {
"%s: HARD_RESET to PHY %s failed (rc=%d)",
__func__, "HARD_RESET");
goto next_phy;
}
/*
*/
if (pptr->abort_all_start) {
while (pptr->abort_all_start) {
"%s: Waiting for outstanding ABORT_ALL on "
}
} else {
}
}
if (rc != 0) {
"%s: pmcs_abort to PHY %s failed (rc=%d)",
goto next_phy;
}
}
/*
* Step 4: Set the device back to OPERATIONAL state
*/
if (rc == 0) {
} else {
"%s: Failed to SET tgt 0x%p to OPERATIONAL state",
__func__, "SET tgt to OPERATIONAL state");
goto next_phy;
}
if (tgt) {
}
}
/*
* Only clear ds_err_recovering if we're exiting for good and not
* just unwinding from recursion
*/
pwp->ds_err_recovering = 0;
}
if (reschedule) {
}
}
/*
* Called with target's statlock held (if target is non-NULL) and PHY lock held.
*/
int
{
if (tgt->recovering) {
return (0);
}
}
"%s: PHY is NULL", __func__);
return (-1);
}
switch (dev_state) {
if (tgt_dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
"%s: Target 0x%p already IN_RECOVERY", __func__,
(void *)tgt);
rc = 0; /* This is not an error */
goto no_action;
}
if (rc != 0) {
"%s(1): Failed to set tgt(0x%p) to IN_RECOVERY",
}
break;
if (tgt_dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) {
"%s: Target 0x%p not ready to go OPERATIONAL",
goto no_action;
}
}
if (rc != 0) {
"%s(2): Failed to SET tgt(0x%p) to OPERATIONAL",
tgt->reset_success = 0;
}
}
break;
"%s: Device at %s is non-operational",
}
rc = 0;
break;
default:
"%s: Invalid state requested (%d)", __func__,
break;
}
tgt->recovering = 0;
}
return (rc);
}
/*
* Start ssp event recovery. We have to schedule recovery operation because
* it involves sending multiple commands to device and we should not do it
* in the interrupt context.
* If it is failure of a recovery command, let the recovery thread deal with it.
* Called with the work lock held.
*/
void
{
if (pptr) {
}
}
}
/*
* No target, need to run RE-DISCOVERY here.
*/
}
/*
* Although we cannot mark phy to force abort nor mark phy
* as changed, killing of a target would take care of aborting
* commands for the device.
*/
"%s: No valid target for event processing. Reconfigure.",
__func__);
return;
} else {
/* We have a phy pointer, we'll need to lock it */
}
"%s: Device at %s is non-operational",
}
}
return;
}
/*
* If this command is run in WAIT mode, it is a failing recovery
* command. If so, just wake up recovery thread waiting for
* command completion.
*/
if (tag == PMCS_TAG_TYPE_WAIT) {
}
}
return;
}
"%s: Not scheduling SSP event recovery for NULL tgt"
return;
}
/*
* If the SSP event was an OPEN_RETRY_TIMEOUT, we don't want
* Simply complete the command and return it as STATUS_BUSY.
* This will cause the target driver to simply retry.
*/
"%s: Got OPEN_RETRY_TIMEOUT event (htag 0x%08x)",
/* Note: work remains locked for the callback */
return;
}
/*
* To recover from primary failures,
* we need to schedule handling events recovery.
*/
"%s: Scheduling SSP event recovery for tgt(0x%p) "
}
/* Work cannot be completed until event recovery is completed. */
}
/*
* SSP target event recovery
* phy->lock should be held upon entry.
* pwrk->lock should be held upon entry and gets released by this routine.
* tgt->statlock should not be held.
*/
void
{
int rv;
if (event == PMCOUT_STATUS_XFER_ERR_BREAK ||
/* Command may be still pending on device */
if (rv != 0) {
goto out;
}
if (status == SAS_RSP_TMF_COMPLETE) {
/* Command NOT pending on a device */
"%s: No pending command for tgt 0x%p",
/* Nothing more to do, just abort it on chip */
htag = 0;
}
}
/*
* All other events left the command pending in the host
* Send abort task and abort it on the chip
*/
if (htag != 0) {
goto out;
}
/*
* Abort either took care of work completion, or put device in
* a recovery state
*/
return;
out:
/* Abort failed, do full device recovery */
}
}
/*
* SSP event recovery task.
*/
void
{
int idx;
int er_flag;
continue;
}
continue;
}
continue;
}
/* Check what cmd expects recovery */
/*
* aq may contain TMF commands, so we
* may not find work structure with htag
*/
continue;
}
if (!PMCS_COMMAND_DONE(pwrk) &&
"%s: pwrk(%p) htag(0x%x)",
/*
* pwrk->lock gets dropped in
* pmcs_tgt_event_recovery()
*/
goto restart;
}
}
tgt->event_recovery = 0;
"%s: end of SSP event recovery for target(0x%p)",
}
"%s: end of SSP event recovery for pwp(0x%p)", __func__,
(void *) pwp);
}
void
{
if (xp->recover_wait == 0) {
/*
* Rather than waiting for the watchdog timer, we'll
* kick it right now.
*/
}
}
/*
* Increment the phy ds error retry count.
* If too many retries, mark phy dead and restart discovery;
* otherwise schedule ds recovery.
*/
static void
{
"%s: retry limit reached after %s to PHY %s failed",
tgt->recover_wait = 0;
}
/*
* Mark the PHY as dead and it and its parent as changed,
* then restart discovery
*/
} else if ((phyp->ds_prev_good_recoveries >
< ddi_get_lbolt())) {
"successful recoveries reached, declaring PHY %s dead",
tgt->recover_wait = 0;
}
/*
* Mark the PHY as dead and its parent as changed,
* then restart discovery
*/
} else {
}
}