Skip to content

Commit

Permalink
[SCSI] isci: Fix hard reset timeout conditions.
Browse files Browse the repository at this point in the history
A hard reset can timeout before or after the last phy in the
port goes away.  If after, then notify the OS that the last
phy has failed.

The recovery for the failed hard reset has been removed.
This recovery code was unecessary in that the link would
recover from the failure normally by a new link reset sequence
or hotplug of the remote device.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
  • Loading branch information
Jeff Skirvin authored and James Bottomley committed Oct 31, 2011
1 parent 5412e25 commit 8e35a13
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 42 deletions.
101 changes: 59 additions & 42 deletions drivers/scsi/isci/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,34 @@ static void isci_port_stop_complete(struct isci_host *ihost,
dev_dbg(&ihost->pdev->dev, "Port stop complete\n");
}


static bool is_port_ready_state(enum sci_port_states state)
{
switch (state) {
case SCI_PORT_READY:
case SCI_PORT_SUB_WAITING:
case SCI_PORT_SUB_OPERATIONAL:
case SCI_PORT_SUB_CONFIGURING:
return true;
default:
return false;
}
}

/* flag dummy rnc hanling when exiting a ready state */
static void port_state_machine_change(struct isci_port *iport,
enum sci_port_states state)
{
struct sci_base_state_machine *sm = &iport->sm;
enum sci_port_states old_state = sm->current_state_id;

if (is_port_ready_state(old_state) && !is_port_ready_state(state))
iport->ready_exit = true;

sci_change_state(sm, state);
iport->ready_exit = false;
}

/**
* isci_port_hard_reset_complete() - This function is called by the sci core
* when the hard reset complete notification has been received.
Expand All @@ -368,6 +396,26 @@ static void isci_port_hard_reset_complete(struct isci_port *isci_port,
/* Save the status of the hard reset from the port. */
isci_port->hard_reset_status = completion_status;

if (completion_status != SCI_SUCCESS) {

/* The reset failed. The port state is now SCI_PORT_FAILED. */
if (isci_port->active_phy_mask == 0) {

/* Generate the link down now to the host, since it
* was intercepted by the hard reset state machine when
* it really happened.
*/
isci_port_link_down(isci_port->isci_host,
&isci_port->isci_host->phys[
isci_port->last_active_phy],
isci_port);
}
/* Advance the port state so that link state changes will be
* noticed.
*/
port_state_machine_change(isci_port, SCI_PORT_SUB_WAITING);

}
complete_all(&isci_port->hard_reset_complete);
}

Expand Down Expand Up @@ -657,6 +705,8 @@ void sci_port_deactivate_phy(struct isci_port *iport, struct isci_phy *iphy,
struct isci_host *ihost = iport->owning_controller;

iport->active_phy_mask &= ~(1 << iphy->phy_index);
if (!iport->active_phy_mask)
iport->last_active_phy = iphy->phy_index;

iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN;

Expand All @@ -683,33 +733,6 @@ static void sci_port_invalid_link_up(struct isci_port *iport, struct isci_phy *i
}
}

static bool is_port_ready_state(enum sci_port_states state)
{
switch (state) {
case SCI_PORT_READY:
case SCI_PORT_SUB_WAITING:
case SCI_PORT_SUB_OPERATIONAL:
case SCI_PORT_SUB_CONFIGURING:
return true;
default:
return false;
}
}

/* flag dummy rnc hanling when exiting a ready state */
static void port_state_machine_change(struct isci_port *iport,
enum sci_port_states state)
{
struct sci_base_state_machine *sm = &iport->sm;
enum sci_port_states old_state = sm->current_state_id;

if (is_port_ready_state(old_state) && !is_port_ready_state(state))
iport->ready_exit = true;

sci_change_state(sm, state);
iport->ready_exit = false;
}

/**
* sci_port_general_link_up_handler - phy can be assigned to port?
* @sci_port: sci_port object for which has a phy that has gone link up.
Expand Down Expand Up @@ -1622,7 +1645,8 @@ void sci_port_construct(struct isci_port *iport, u8 index,
iport->logical_port_index = SCIC_SDS_DUMMY_PORT;
iport->physical_port_index = index;
iport->active_phy_mask = 0;
iport->ready_exit = false;
iport->last_active_phy = 0;
iport->ready_exit = false;

iport->owning_controller = ihost;

Expand Down Expand Up @@ -1676,7 +1700,7 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
{
unsigned long flags;
enum sci_status status;
int idx, ret = TMF_RESP_FUNC_COMPLETE;
int ret = TMF_RESP_FUNC_COMPLETE;

dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n",
__func__, iport);
Expand All @@ -1697,8 +1721,13 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
"%s: iport = %p; hard reset completion\n",
__func__, iport);

if (iport->hard_reset_status != SCI_SUCCESS)
if (iport->hard_reset_status != SCI_SUCCESS) {
ret = TMF_RESP_FUNC_FAILED;

dev_err(&ihost->pdev->dev,
"%s: iport = %p; hard reset failed (0x%x)\n",
__func__, iport, iport->hard_reset_status);
}
} else {
ret = TMF_RESP_FUNC_FAILED;

Expand All @@ -1718,18 +1747,6 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
"%s: iport = %p; hard reset failed "
"(0x%x) - driving explicit link fail for all phys\n",
__func__, iport, iport->hard_reset_status);

/* Down all phys in the port. */
spin_lock_irqsave(&ihost->scic_lock, flags);
for (idx = 0; idx < SCI_MAX_PHYS; ++idx) {
struct isci_phy *iphy = iport->phy_table[idx];

if (!iphy)
continue;
sci_phy_stop(iphy);
sci_phy_start(iphy);
}
spin_unlock_irqrestore(&ihost->scic_lock, flags);
}
return ret;
}
Expand Down
1 change: 1 addition & 0 deletions drivers/scsi/isci/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ struct isci_port {
u8 logical_port_index;
u8 physical_port_index;
u8 active_phy_mask;
u8 last_active_phy;
u16 reserved_rni;
u16 reserved_tag;
u32 started_request_count;
Expand Down

0 comments on commit 8e35a13

Please sign in to comment.