diff options
author | Dan Williams <dan.j.williams@intel.com> | 2012-02-23 04:12:10 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2012-05-17 15:27:12 -0400 |
commit | eb608c3cb3f0a6b99252ea6a69fc0d2bbecf1f4f (patch) | |
tree | e28da3a8a530798c7c3368d16f88003ee3ee0643 /drivers | |
parent | abec912d71c44bbd642ce12ad98aab76f5a53163 (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/isci/host.c | 99 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 8 |
2 files changed, 55 insertions, 52 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 0fe372f93289..95c3da66ea4b 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c | |||
@@ -1046,7 +1046,7 @@ void isci_host_scan_start(struct Scsi_Host *shost) | |||
1046 | spin_unlock_irq(&ihost->scic_lock); | 1046 | spin_unlock_irq(&ihost->scic_lock); |
1047 | } | 1047 | } |
1048 | 1048 | ||
1049 | static void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status) | 1049 | static void isci_host_stop_complete(struct isci_host *ihost) |
1050 | { | 1050 | { |
1051 | sci_controller_disable_interrupts(ihost); | 1051 | sci_controller_disable_interrupts(ihost); |
1052 | clear_bit(IHOST_STOP_PENDING, &ihost->flags); | 1052 | clear_bit(IHOST_STOP_PENDING, &ihost->flags); |
@@ -1232,7 +1232,7 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost) | |||
1232 | switch (ihost->sm.current_state_id) { | 1232 | switch (ihost->sm.current_state_id) { |
1233 | case SCIC_RESET: | 1233 | case SCIC_RESET: |
1234 | case SCIC_READY: | 1234 | case SCIC_READY: |
1235 | case SCIC_STOPPED: | 1235 | case SCIC_STOPPING: |
1236 | case SCIC_FAILED: | 1236 | case SCIC_FAILED: |
1237 | /* | 1237 | /* |
1238 | * The reset operation is not a graceful cleanup, just | 1238 | * The reset operation is not a graceful cleanup, just |
@@ -1247,6 +1247,44 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost) | |||
1247 | } | 1247 | } |
1248 | } | 1248 | } |
1249 | 1249 | ||
1250 | static enum sci_status sci_controller_stop_phys(struct isci_host *ihost) | ||
1251 | { | ||
1252 | u32 index; | ||
1253 | enum sci_status status; | ||
1254 | enum sci_status phy_status; | ||
1255 | |||
1256 | status = SCI_SUCCESS; | ||
1257 | |||
1258 | for (index = 0; index < SCI_MAX_PHYS; index++) { | ||
1259 | phy_status = sci_phy_stop(&ihost->phys[index]); | ||
1260 | |||
1261 | if (phy_status != SCI_SUCCESS && | ||
1262 | phy_status != SCI_FAILURE_INVALID_STATE) { | ||
1263 | status = SCI_FAILURE; | ||
1264 | |||
1265 | dev_warn(&ihost->pdev->dev, | ||
1266 | "%s: Controller stop operation failed to stop " | ||
1267 | "phy %d because of status %d.\n", | ||
1268 | __func__, | ||
1269 | ihost->phys[index].phy_index, phy_status); | ||
1270 | } | ||
1271 | } | ||
1272 | |||
1273 | return status; | ||
1274 | } | ||
1275 | |||
1276 | |||
1277 | /** | ||
1278 | * isci_host_deinit - shutdown frame reception and dma | ||
1279 | * @ihost: host to take down | ||
1280 | * | ||
1281 | * This is called in either the driver shutdown or the suspend path. In | ||
1282 | * the shutdown case libsas went through port teardown and normal device | ||
1283 | * removal (i.e. physical links stayed up to service scsi_device removal | ||
1284 | * commands). In the suspend case we disable the hardware without | ||
1285 | * notifying libsas of the link down events since we want libsas to | ||
1286 | * remember the domain across the suspend/resume cycle | ||
1287 | */ | ||
1250 | void isci_host_deinit(struct isci_host *ihost) | 1288 | void isci_host_deinit(struct isci_host *ihost) |
1251 | { | 1289 | { |
1252 | int i; | 1290 | int i; |
@@ -1255,16 +1293,6 @@ void isci_host_deinit(struct isci_host *ihost) | |||
1255 | for (i = 0; i < isci_gpio_count(ihost); i++) | 1293 | for (i = 0; i < isci_gpio_count(ihost); i++) |
1256 | writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]); | 1294 | writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]); |
1257 | 1295 | ||
1258 | for (i = 0; i < SCI_MAX_PORTS; i++) { | ||
1259 | struct isci_port *iport = &ihost->ports[i]; | ||
1260 | struct isci_remote_device *idev, *d; | ||
1261 | |||
1262 | list_for_each_entry_safe(idev, d, &iport->remote_dev_list, node) { | ||
1263 | if (test_bit(IDEV_ALLOCATED, &idev->flags)) | ||
1264 | isci_remote_device_stop(ihost, idev); | ||
1265 | } | ||
1266 | } | ||
1267 | |||
1268 | set_bit(IHOST_STOP_PENDING, &ihost->flags); | 1296 | set_bit(IHOST_STOP_PENDING, &ihost->flags); |
1269 | 1297 | ||
1270 | spin_lock_irq(&ihost->scic_lock); | 1298 | spin_lock_irq(&ihost->scic_lock); |
@@ -1273,6 +1301,13 @@ void isci_host_deinit(struct isci_host *ihost) | |||
1273 | 1301 | ||
1274 | wait_for_stop(ihost); | 1302 | wait_for_stop(ihost); |
1275 | 1303 | ||
1304 | /* phy stop is after controller stop to allow port and device to | ||
1305 | * go idle before shutting down the phys, but the expectation is | ||
1306 | * that i/o has been shut off well before we reach this | ||
1307 | * function. | ||
1308 | */ | ||
1309 | sci_controller_stop_phys(ihost); | ||
1310 | |||
1276 | /* disable sgpio: where the above wait should give time for the | 1311 | /* disable sgpio: where the above wait should give time for the |
1277 | * enclosure to sample the gpios going inactive | 1312 | * enclosure to sample the gpios going inactive |
1278 | */ | 1313 | */ |
@@ -1476,32 +1511,6 @@ static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm) | |||
1476 | sci_controller_set_interrupt_coalescence(ihost, 0, 0); | 1511 | sci_controller_set_interrupt_coalescence(ihost, 0, 0); |
1477 | } | 1512 | } |
1478 | 1513 | ||
1479 | static enum sci_status sci_controller_stop_phys(struct isci_host *ihost) | ||
1480 | { | ||
1481 | u32 index; | ||
1482 | enum sci_status status; | ||
1483 | enum sci_status phy_status; | ||
1484 | |||
1485 | status = SCI_SUCCESS; | ||
1486 | |||
1487 | for (index = 0; index < SCI_MAX_PHYS; index++) { | ||
1488 | phy_status = sci_phy_stop(&ihost->phys[index]); | ||
1489 | |||
1490 | if (phy_status != SCI_SUCCESS && | ||
1491 | phy_status != SCI_FAILURE_INVALID_STATE) { | ||
1492 | status = SCI_FAILURE; | ||
1493 | |||
1494 | dev_warn(&ihost->pdev->dev, | ||
1495 | "%s: Controller stop operation failed to stop " | ||
1496 | "phy %d because of status %d.\n", | ||
1497 | __func__, | ||
1498 | ihost->phys[index].phy_index, phy_status); | ||
1499 | } | ||
1500 | } | ||
1501 | |||
1502 | return status; | ||
1503 | } | ||
1504 | |||
1505 | static enum sci_status sci_controller_stop_ports(struct isci_host *ihost) | 1514 | static enum sci_status sci_controller_stop_ports(struct isci_host *ihost) |
1506 | { | 1515 | { |
1507 | u32 index; | 1516 | u32 index; |
@@ -1561,10 +1570,11 @@ static void sci_controller_stopping_state_enter(struct sci_base_state_machine *s | |||
1561 | { | 1570 | { |
1562 | struct isci_host *ihost = container_of(sm, typeof(*ihost), sm); | 1571 | struct isci_host *ihost = container_of(sm, typeof(*ihost), sm); |
1563 | 1572 | ||
1564 | /* Stop all of the components for this controller */ | ||
1565 | sci_controller_stop_phys(ihost); | ||
1566 | sci_controller_stop_ports(ihost); | ||
1567 | sci_controller_stop_devices(ihost); | 1573 | sci_controller_stop_devices(ihost); |
1574 | sci_controller_stop_ports(ihost); | ||
1575 | |||
1576 | if (!sci_controller_has_remote_devices_stopping(ihost)) | ||
1577 | isci_host_stop_complete(ihost); | ||
1568 | } | 1578 | } |
1569 | 1579 | ||
1570 | static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm) | 1580 | 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[] = { | |||
1621 | .enter_state = sci_controller_stopping_state_enter, | 1631 | .enter_state = sci_controller_stopping_state_enter, |
1622 | .exit_state = sci_controller_stopping_state_exit, | 1632 | .exit_state = sci_controller_stopping_state_exit, |
1623 | }, | 1633 | }, |
1624 | [SCIC_STOPPED] = {}, | ||
1625 | [SCIC_FAILED] = {} | 1634 | [SCIC_FAILED] = {} |
1626 | }; | 1635 | }; |
1627 | 1636 | ||
@@ -1641,7 +1650,7 @@ static void controller_timeout(unsigned long data) | |||
1641 | sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT); | 1650 | sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT); |
1642 | else if (sm->current_state_id == SCIC_STOPPING) { | 1651 | else if (sm->current_state_id == SCIC_STOPPING) { |
1643 | sci_change_state(sm, SCIC_FAILED); | 1652 | sci_change_state(sm, SCIC_FAILED); |
1644 | isci_host_stop_complete(ihost, SCI_FAILURE_TIMEOUT); | 1653 | isci_host_stop_complete(ihost); |
1645 | } else /* / @todo Now what do we want to do in this case? */ | 1654 | } else /* / @todo Now what do we want to do in this case? */ |
1646 | dev_err(&ihost->pdev->dev, | 1655 | dev_err(&ihost->pdev->dev, |
1647 | "%s: Controller timer fired when controller was not " | 1656 | "%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, | |||
2452 | } | 2461 | } |
2453 | } | 2462 | } |
2454 | 2463 | ||
2455 | static bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost) | 2464 | bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost) |
2456 | { | 2465 | { |
2457 | u32 index; | 2466 | u32 index; |
2458 | 2467 | ||
@@ -2478,7 +2487,7 @@ void sci_controller_remote_device_stopped(struct isci_host *ihost, | |||
2478 | } | 2487 | } |
2479 | 2488 | ||
2480 | if (!sci_controller_has_remote_devices_stopping(ihost)) | 2489 | if (!sci_controller_has_remote_devices_stopping(ihost)) |
2481 | sci_change_state(&ihost->sm, SCIC_STOPPED); | 2490 | isci_host_stop_complete(ihost); |
2482 | } | 2491 | } |
2483 | 2492 | ||
2484 | void sci_controller_post_request(struct isci_host *ihost, u32 request) | 2493 | void sci_controller_post_request(struct isci_host *ihost, u32 request) |
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 4695162f406e..a89c0e3c5a14 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h | |||
@@ -277,13 +277,6 @@ enum sci_controller_states { | |||
277 | SCIC_STOPPING, | 277 | SCIC_STOPPING, |
278 | 278 | ||
279 | /** | 279 | /** |
280 | * This state indicates that the controller has successfully been stopped. | ||
281 | * In this state no new IO operations are permitted. | ||
282 | * This state is entered from the STOPPING state. | ||
283 | */ | ||
284 | SCIC_STOPPED, | ||
285 | |||
286 | /** | ||
287 | * This state indicates that the controller could not successfully be | 280 | * This state indicates that the controller could not successfully be |
288 | * initialized. In this state no new IO operations are permitted. | 281 | * initialized. In this state no new IO operations are permitted. |
289 | * This state is entered from the INITIALIZING state. | 282 | * This state is entered from the INITIALIZING state. |
@@ -479,6 +472,7 @@ int isci_host_init(struct isci_host *); | |||
479 | void isci_host_completion_routine(unsigned long data); | 472 | void isci_host_completion_routine(unsigned long data); |
480 | void isci_host_deinit(struct isci_host *); | 473 | void isci_host_deinit(struct isci_host *); |
481 | void sci_controller_disable_interrupts(struct isci_host *ihost); | 474 | void sci_controller_disable_interrupts(struct isci_host *ihost); |
475 | bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost); | ||
482 | 476 | ||
483 | enum sci_status sci_controller_start_io( | 477 | enum sci_status sci_controller_start_io( |
484 | struct isci_host *ihost, | 478 | struct isci_host *ihost, |