From 8e35a1398c5db981cd1a2d7635de9c15dd648527 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 27 Oct 2011 15:05:32 -0700 Subject: [PATCH] [SCSI] isci: Fix hard reset timeout conditions. 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 Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/port.c | 101 +++++++++++++++++++++++---------------- drivers/scsi/isci/port.h | 1 + 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 8e59c8865dcd..bfeb87905aaf 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -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. @@ -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); } @@ -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; @@ -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. @@ -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; @@ -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); @@ -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; @@ -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; } diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index b50ecd4e8f9c..e84d22a309e3 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -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; -- GitLab