isci: fix controller stop
1/ notify waiters when controller stop completes (fixes 10 second stall unloading the driver) 2/ make sure phy stop is after port and device stop Cc: Richard Boyd <richard.g.boyd@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
parent
abec912d71
commit
eb608c3cb3
2 changed files with 55 additions and 52 deletions
|
@ -1046,7 +1046,7 @@ void isci_host_scan_start(struct Scsi_Host *shost)
|
||||||
spin_unlock_irq(&ihost->scic_lock);
|
spin_unlock_irq(&ihost->scic_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status)
|
static void isci_host_stop_complete(struct isci_host *ihost)
|
||||||
{
|
{
|
||||||
sci_controller_disable_interrupts(ihost);
|
sci_controller_disable_interrupts(ihost);
|
||||||
clear_bit(IHOST_STOP_PENDING, &ihost->flags);
|
clear_bit(IHOST_STOP_PENDING, &ihost->flags);
|
||||||
|
@ -1232,7 +1232,7 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost)
|
||||||
switch (ihost->sm.current_state_id) {
|
switch (ihost->sm.current_state_id) {
|
||||||
case SCIC_RESET:
|
case SCIC_RESET:
|
||||||
case SCIC_READY:
|
case SCIC_READY:
|
||||||
case SCIC_STOPPED:
|
case SCIC_STOPPING:
|
||||||
case SCIC_FAILED:
|
case SCIC_FAILED:
|
||||||
/*
|
/*
|
||||||
* The reset operation is not a graceful cleanup, just
|
* The reset operation is not a graceful cleanup, just
|
||||||
|
@ -1247,6 +1247,44 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum sci_status sci_controller_stop_phys(struct isci_host *ihost)
|
||||||
|
{
|
||||||
|
u32 index;
|
||||||
|
enum sci_status status;
|
||||||
|
enum sci_status phy_status;
|
||||||
|
|
||||||
|
status = SCI_SUCCESS;
|
||||||
|
|
||||||
|
for (index = 0; index < SCI_MAX_PHYS; index++) {
|
||||||
|
phy_status = sci_phy_stop(&ihost->phys[index]);
|
||||||
|
|
||||||
|
if (phy_status != SCI_SUCCESS &&
|
||||||
|
phy_status != SCI_FAILURE_INVALID_STATE) {
|
||||||
|
status = SCI_FAILURE;
|
||||||
|
|
||||||
|
dev_warn(&ihost->pdev->dev,
|
||||||
|
"%s: Controller stop operation failed to stop "
|
||||||
|
"phy %d because of status %d.\n",
|
||||||
|
__func__,
|
||||||
|
ihost->phys[index].phy_index, phy_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isci_host_deinit - shutdown frame reception and dma
|
||||||
|
* @ihost: host to take down
|
||||||
|
*
|
||||||
|
* This is called in either the driver shutdown or the suspend path. In
|
||||||
|
* the shutdown case libsas went through port teardown and normal device
|
||||||
|
* removal (i.e. physical links stayed up to service scsi_device removal
|
||||||
|
* commands). In the suspend case we disable the hardware without
|
||||||
|
* notifying libsas of the link down events since we want libsas to
|
||||||
|
* remember the domain across the suspend/resume cycle
|
||||||
|
*/
|
||||||
void isci_host_deinit(struct isci_host *ihost)
|
void isci_host_deinit(struct isci_host *ihost)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -1255,16 +1293,6 @@ void isci_host_deinit(struct isci_host *ihost)
|
||||||
for (i = 0; i < isci_gpio_count(ihost); i++)
|
for (i = 0; i < isci_gpio_count(ihost); i++)
|
||||||
writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
|
writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
|
||||||
|
|
||||||
for (i = 0; i < SCI_MAX_PORTS; i++) {
|
|
||||||
struct isci_port *iport = &ihost->ports[i];
|
|
||||||
struct isci_remote_device *idev, *d;
|
|
||||||
|
|
||||||
list_for_each_entry_safe(idev, d, &iport->remote_dev_list, node) {
|
|
||||||
if (test_bit(IDEV_ALLOCATED, &idev->flags))
|
|
||||||
isci_remote_device_stop(ihost, idev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_bit(IHOST_STOP_PENDING, &ihost->flags);
|
set_bit(IHOST_STOP_PENDING, &ihost->flags);
|
||||||
|
|
||||||
spin_lock_irq(&ihost->scic_lock);
|
spin_lock_irq(&ihost->scic_lock);
|
||||||
|
@ -1273,6 +1301,13 @@ void isci_host_deinit(struct isci_host *ihost)
|
||||||
|
|
||||||
wait_for_stop(ihost);
|
wait_for_stop(ihost);
|
||||||
|
|
||||||
|
/* phy stop is after controller stop to allow port and device to
|
||||||
|
* go idle before shutting down the phys, but the expectation is
|
||||||
|
* that i/o has been shut off well before we reach this
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
sci_controller_stop_phys(ihost);
|
||||||
|
|
||||||
/* disable sgpio: where the above wait should give time for the
|
/* disable sgpio: where the above wait should give time for the
|
||||||
* enclosure to sample the gpios going inactive
|
* enclosure to sample the gpios going inactive
|
||||||
*/
|
*/
|
||||||
|
@ -1476,32 +1511,6 @@ static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm)
|
||||||
sci_controller_set_interrupt_coalescence(ihost, 0, 0);
|
sci_controller_set_interrupt_coalescence(ihost, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum sci_status sci_controller_stop_phys(struct isci_host *ihost)
|
|
||||||
{
|
|
||||||
u32 index;
|
|
||||||
enum sci_status status;
|
|
||||||
enum sci_status phy_status;
|
|
||||||
|
|
||||||
status = SCI_SUCCESS;
|
|
||||||
|
|
||||||
for (index = 0; index < SCI_MAX_PHYS; index++) {
|
|
||||||
phy_status = sci_phy_stop(&ihost->phys[index]);
|
|
||||||
|
|
||||||
if (phy_status != SCI_SUCCESS &&
|
|
||||||
phy_status != SCI_FAILURE_INVALID_STATE) {
|
|
||||||
status = SCI_FAILURE;
|
|
||||||
|
|
||||||
dev_warn(&ihost->pdev->dev,
|
|
||||||
"%s: Controller stop operation failed to stop "
|
|
||||||
"phy %d because of status %d.\n",
|
|
||||||
__func__,
|
|
||||||
ihost->phys[index].phy_index, phy_status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum sci_status sci_controller_stop_ports(struct isci_host *ihost)
|
static enum sci_status sci_controller_stop_ports(struct isci_host *ihost)
|
||||||
{
|
{
|
||||||
u32 index;
|
u32 index;
|
||||||
|
@ -1561,10 +1570,11 @@ static void sci_controller_stopping_state_enter(struct sci_base_state_machine *s
|
||||||
{
|
{
|
||||||
struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
|
struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
|
||||||
|
|
||||||
/* Stop all of the components for this controller */
|
|
||||||
sci_controller_stop_phys(ihost);
|
|
||||||
sci_controller_stop_ports(ihost);
|
|
||||||
sci_controller_stop_devices(ihost);
|
sci_controller_stop_devices(ihost);
|
||||||
|
sci_controller_stop_ports(ihost);
|
||||||
|
|
||||||
|
if (!sci_controller_has_remote_devices_stopping(ihost))
|
||||||
|
isci_host_stop_complete(ihost);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm)
|
static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm)
|
||||||
|
@ -1621,7 +1631,6 @@ static const struct sci_base_state sci_controller_state_table[] = {
|
||||||
.enter_state = sci_controller_stopping_state_enter,
|
.enter_state = sci_controller_stopping_state_enter,
|
||||||
.exit_state = sci_controller_stopping_state_exit,
|
.exit_state = sci_controller_stopping_state_exit,
|
||||||
},
|
},
|
||||||
[SCIC_STOPPED] = {},
|
|
||||||
[SCIC_FAILED] = {}
|
[SCIC_FAILED] = {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1641,7 +1650,7 @@ static void controller_timeout(unsigned long data)
|
||||||
sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT);
|
sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT);
|
||||||
else if (sm->current_state_id == SCIC_STOPPING) {
|
else if (sm->current_state_id == SCIC_STOPPING) {
|
||||||
sci_change_state(sm, SCIC_FAILED);
|
sci_change_state(sm, SCIC_FAILED);
|
||||||
isci_host_stop_complete(ihost, SCI_FAILURE_TIMEOUT);
|
isci_host_stop_complete(ihost);
|
||||||
} else /* / @todo Now what do we want to do in this case? */
|
} else /* / @todo Now what do we want to do in this case? */
|
||||||
dev_err(&ihost->pdev->dev,
|
dev_err(&ihost->pdev->dev,
|
||||||
"%s: Controller timer fired when controller was not "
|
"%s: Controller timer fired when controller was not "
|
||||||
|
@ -2452,7 +2461,7 @@ void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost)
|
bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost)
|
||||||
{
|
{
|
||||||
u32 index;
|
u32 index;
|
||||||
|
|
||||||
|
@ -2478,7 +2487,7 @@ void sci_controller_remote_device_stopped(struct isci_host *ihost,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sci_controller_has_remote_devices_stopping(ihost))
|
if (!sci_controller_has_remote_devices_stopping(ihost))
|
||||||
sci_change_state(&ihost->sm, SCIC_STOPPED);
|
isci_host_stop_complete(ihost);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sci_controller_post_request(struct isci_host *ihost, u32 request)
|
void sci_controller_post_request(struct isci_host *ihost, u32 request)
|
||||||
|
|
|
@ -276,13 +276,6 @@ enum sci_controller_states {
|
||||||
*/
|
*/
|
||||||
SCIC_STOPPING,
|
SCIC_STOPPING,
|
||||||
|
|
||||||
/**
|
|
||||||
* This state indicates that the controller has successfully been stopped.
|
|
||||||
* In this state no new IO operations are permitted.
|
|
||||||
* This state is entered from the STOPPING state.
|
|
||||||
*/
|
|
||||||
SCIC_STOPPED,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This state indicates that the controller could not successfully be
|
* This state indicates that the controller could not successfully be
|
||||||
* initialized. In this state no new IO operations are permitted.
|
* initialized. In this state no new IO operations are permitted.
|
||||||
|
@ -479,6 +472,7 @@ int isci_host_init(struct isci_host *);
|
||||||
void isci_host_completion_routine(unsigned long data);
|
void isci_host_completion_routine(unsigned long data);
|
||||||
void isci_host_deinit(struct isci_host *);
|
void isci_host_deinit(struct isci_host *);
|
||||||
void sci_controller_disable_interrupts(struct isci_host *ihost);
|
void sci_controller_disable_interrupts(struct isci_host *ihost);
|
||||||
|
bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost);
|
||||||
|
|
||||||
enum sci_status sci_controller_start_io(
|
enum sci_status sci_controller_start_io(
|
||||||
struct isci_host *ihost,
|
struct isci_host *ihost,
|
||||||
|
|
Reference in a new issue