diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-05-02 16:59:25 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 07:04:46 -0400 |
commit | d06b487b78f28a02efdcdcc9ec295bf230b9d0e8 (patch) | |
tree | 7be7614402ec7cd7f3b90455d2162b62b28418de /drivers | |
parent | 5b3f2bd877382eaf4b5a7d90fdec72ef14b9ec97 (diff) |
isci: implement I_T_nexus_reset
This is a requirement for 2.6.39's new libata eh.
Still some questions about lldd_dev_gone racing against dev->lldd_dev
lookups, but we are at least no more broken than mvsas in this regard.
We also short-circuit I_T_nexus_reset invocations from the device
discovery path (IDEV_EH similar to MVS_DEV_EH) to filter out the
resulting domain rediscoveries triggered by the reset.
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/isci/remote_device.c | 1 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_device.h | 1 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 138 |
3 files changed, 76 insertions, 64 deletions
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 15c1e6c3b223..ee6fe1db8f99 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c | |||
@@ -891,6 +891,7 @@ static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_ | |||
891 | 891 | ||
892 | clear_bit(IDEV_START_PENDING, &idev->flags); | 892 | clear_bit(IDEV_START_PENDING, &idev->flags); |
893 | clear_bit(IDEV_STOP_PENDING, &idev->flags); | 893 | clear_bit(IDEV_STOP_PENDING, &idev->flags); |
894 | clear_bit(IDEV_EH, &idev->flags); | ||
894 | wake_up(&ihost->eventq); | 895 | wake_up(&ihost->eventq); |
895 | } | 896 | } |
896 | 897 | ||
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index c7db4998b8f9..2e433b7f16b3 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h | |||
@@ -134,6 +134,7 @@ struct isci_remote_device { | |||
134 | #define IDEV_START_PENDING 0 | 134 | #define IDEV_START_PENDING 0 |
135 | #define IDEV_STOP_PENDING 1 | 135 | #define IDEV_STOP_PENDING 1 |
136 | #define IDEV_ALLOCATED 2 | 136 | #define IDEV_ALLOCATED 2 |
137 | #define IDEV_EH 3 | ||
137 | unsigned long flags; | 138 | unsigned long flags; |
138 | struct isci_port *isci_port; | 139 | struct isci_port *isci_port; |
139 | struct domain_device *domain_dev; | 140 | struct domain_device *domain_dev; |
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 8449d8abd66a..3a3f54677e5b 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -1022,9 +1022,11 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun) | |||
1022 | "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", | 1022 | "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", |
1023 | __func__, domain_device, isci_host, isci_device); | 1023 | __func__, domain_device, isci_host, isci_device); |
1024 | 1024 | ||
1025 | if (isci_device != NULL) | 1025 | if (isci_device != NULL) { |
1026 | device_stopping = (isci_device->status == isci_stopping) | 1026 | device_stopping = (isci_device->status == isci_stopping) |
1027 | || (isci_device->status == isci_stopped); | 1027 | || (isci_device->status == isci_stopped); |
1028 | set_bit(IDEV_EH, &isci_device->flags); | ||
1029 | } | ||
1028 | 1030 | ||
1029 | /* If there is a device reset pending on any request in the | 1031 | /* If there is a device reset pending on any request in the |
1030 | * device's list, fail this LUN reset request in order to | 1032 | * device's list, fail this LUN reset request in order to |
@@ -1069,12 +1071,6 @@ int isci_task_clear_nexus_ha(struct sas_ha_struct *ha) | |||
1069 | return TMF_RESP_FUNC_FAILED; | 1071 | return TMF_RESP_FUNC_FAILED; |
1070 | } | 1072 | } |
1071 | 1073 | ||
1072 | int isci_task_I_T_nexus_reset(struct domain_device *dev) | ||
1073 | { | ||
1074 | return TMF_RESP_FUNC_FAILED; | ||
1075 | } | ||
1076 | |||
1077 | |||
1078 | /* Task Management Functions. Must be called from process context. */ | 1074 | /* Task Management Functions. Must be called from process context. */ |
1079 | 1075 | ||
1080 | /** | 1076 | /** |
@@ -1171,6 +1167,12 @@ int isci_task_abort_task(struct sas_task *task) | |||
1171 | device_stopping = (isci_device->status == isci_stopping) | 1167 | device_stopping = (isci_device->status == isci_stopping) |
1172 | || (isci_device->status == isci_stopped); | 1168 | || (isci_device->status == isci_stopped); |
1173 | 1169 | ||
1170 | /* XXX need to fix device lookup lifetime (needs to be done | ||
1171 | * under scic_lock, among other things...), but for now assume | ||
1172 | * the device is available like the above code | ||
1173 | */ | ||
1174 | set_bit(IDEV_EH, &isci_device->flags); | ||
1175 | |||
1174 | /* This version of the driver will fail abort requests for | 1176 | /* This version of the driver will fail abort requests for |
1175 | * SATA/STP. Failing the abort request this way will cause the | 1177 | * SATA/STP. Failing the abort request this way will cause the |
1176 | * SCSI error handler thread to escalate to LUN reset | 1178 | * SCSI error handler thread to escalate to LUN reset |
@@ -1481,86 +1483,94 @@ isci_task_request_complete(struct isci_host *ihost, | |||
1481 | complete(tmf_complete); | 1483 | complete(tmf_complete); |
1482 | } | 1484 | } |
1483 | 1485 | ||
1484 | /** | 1486 | static int isci_reset_device(struct domain_device *dev, int hard_reset) |
1485 | * isci_bus_reset_handler() - This function performs a target reset of the | ||
1486 | * device referenced by "cmd'. This function is exported through the | ||
1487 | * "struct scsi_host_template" structure such that it is called when an I/O | ||
1488 | * recovery process has escalated to a target reset. Note that this function | ||
1489 | * is called from the scsi error handler event thread, so may block on calls. | ||
1490 | * @scsi_cmd: This parameter specifies the target to be reset. | ||
1491 | * | ||
1492 | * SUCCESS if the reset process was successful, else FAILED. | ||
1493 | */ | ||
1494 | int isci_bus_reset_handler(struct scsi_cmnd *cmd) | ||
1495 | { | 1487 | { |
1496 | struct domain_device *dev = cmd_to_domain_dev(cmd); | 1488 | struct isci_remote_device *idev = dev->lldd_dev; |
1497 | struct isci_host *isci_host = dev_to_ihost(dev); | 1489 | struct sas_phy *phy = sas_find_local_phy(dev); |
1498 | unsigned long flags = 0; | 1490 | struct isci_host *ihost = dev_to_ihost(dev); |
1499 | enum sci_status status; | 1491 | enum sci_status status; |
1500 | int base_status; | 1492 | unsigned long flags; |
1501 | struct isci_remote_device *isci_dev = dev->lldd_dev; | 1493 | int rc; |
1502 | 1494 | ||
1503 | dev_dbg(&isci_host->pdev->dev, | 1495 | dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev); |
1504 | "%s: cmd %p, isci_dev %p\n", | ||
1505 | __func__, cmd, isci_dev); | ||
1506 | 1496 | ||
1507 | if (!isci_dev) { | 1497 | if (!idev) { |
1508 | dev_warn(&isci_host->pdev->dev, | 1498 | dev_warn(&ihost->pdev->dev, |
1509 | "%s: isci_dev is GONE!\n", | 1499 | "%s: idev is GONE!\n", |
1510 | __func__); | 1500 | __func__); |
1511 | 1501 | ||
1512 | return TMF_RESP_FUNC_COMPLETE; /* Nothing to reset. */ | 1502 | return TMF_RESP_FUNC_COMPLETE; /* Nothing to reset. */ |
1513 | } | 1503 | } |
1514 | 1504 | ||
1515 | spin_lock_irqsave(&isci_host->scic_lock, flags); | 1505 | spin_lock_irqsave(&ihost->scic_lock, flags); |
1516 | status = scic_remote_device_reset(&isci_dev->sci); | 1506 | status = scic_remote_device_reset(&idev->sci); |
1517 | if (status != SCI_SUCCESS) { | 1507 | if (status != SCI_SUCCESS) { |
1518 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | 1508 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
1519 | 1509 | ||
1520 | scmd_printk(KERN_WARNING, cmd, | 1510 | dev_warn(&ihost->pdev->dev, |
1521 | "%s: scic_remote_device_reset(%p) returned %d!\n", | 1511 | "%s: scic_remote_device_reset(%p) returned %d!\n", |
1522 | __func__, isci_dev, status); | 1512 | __func__, idev, status); |
1523 | 1513 | ||
1524 | return TMF_RESP_FUNC_FAILED; | 1514 | return TMF_RESP_FUNC_FAILED; |
1525 | } | 1515 | } |
1526 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | 1516 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
1527 | 1517 | ||
1528 | /* Make sure all pending requests are able to be fully terminated. */ | 1518 | /* Make sure all pending requests are able to be fully terminated. */ |
1529 | isci_device_clear_reset_pending(isci_host, isci_dev); | 1519 | isci_device_clear_reset_pending(ihost, idev); |
1530 | 1520 | ||
1531 | /* Terminate in-progress I/O now. */ | 1521 | rc = sas_phy_reset(phy, hard_reset); |
1532 | isci_remote_device_nuke_requests(isci_host, isci_dev); | 1522 | msleep(2000); /* just like mvsas */ |
1533 | 1523 | ||
1534 | /* Call into the libsas default handler (which calls sas_phy_reset). */ | 1524 | /* Terminate in-progress I/O now. */ |
1535 | base_status = sas_eh_bus_reset_handler(cmd); | 1525 | isci_remote_device_nuke_requests(ihost, idev); |
1536 | 1526 | ||
1537 | if (base_status != SUCCESS) { | 1527 | spin_lock_irqsave(&ihost->scic_lock, flags); |
1528 | status = scic_remote_device_reset_complete(&idev->sci); | ||
1529 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | ||
1538 | 1530 | ||
1539 | /* There can be cases where the resets to individual devices | 1531 | if (status != SCI_SUCCESS) { |
1540 | * behind an expander will fail because of an unplug of the | 1532 | dev_warn(&ihost->pdev->dev, |
1541 | * expander itself. | 1533 | "%s: scic_remote_device_reset_complete(%p) " |
1542 | */ | 1534 | "returned %d!\n", __func__, idev, status); |
1543 | scmd_printk(KERN_WARNING, cmd, | ||
1544 | "%s: sas_eh_bus_reset_handler(%p) returned %d!\n", | ||
1545 | __func__, cmd, base_status); | ||
1546 | } | 1535 | } |
1547 | 1536 | ||
1548 | /* WHAT TO DO HERE IF sas_phy_reset FAILS? */ | 1537 | dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev); |
1549 | spin_lock_irqsave(&isci_host->scic_lock, flags); | ||
1550 | status = scic_remote_device_reset_complete(&isci_dev->sci); | ||
1551 | spin_unlock_irqrestore(&isci_host->scic_lock, flags); | ||
1552 | 1538 | ||
1553 | if (status != SCI_SUCCESS) { | 1539 | return rc; |
1554 | scmd_printk(KERN_WARNING, cmd, | 1540 | } |
1555 | "%s: scic_remote_device_reset_complete(%p) " | ||
1556 | "returned %d!\n", | ||
1557 | __func__, isci_dev, status); | ||
1558 | } | ||
1559 | /* WHAT TO DO HERE IF scic_remote_device_reset_complete FAILS? */ | ||
1560 | 1541 | ||
1561 | dev_dbg(&isci_host->pdev->dev, | 1542 | int isci_task_I_T_nexus_reset(struct domain_device *dev) |
1562 | "%s: cmd %p, isci_dev %p complete.\n", | 1543 | { |
1563 | __func__, cmd, isci_dev); | 1544 | struct isci_host *ihost = dev_to_ihost(dev); |
1545 | int ret = TMF_RESP_FUNC_FAILED, hard_reset = 1; | ||
1546 | struct isci_remote_device *idev; | ||
1547 | unsigned long flags; | ||
1548 | |||
1549 | /* XXX mvsas is not protecting against ->lldd_dev_gone(), are we | ||
1550 | * being too paranoid, or is mvsas busted?! | ||
1551 | */ | ||
1552 | spin_lock_irqsave(&ihost->scic_lock, flags); | ||
1553 | idev = dev->lldd_dev; | ||
1554 | if (!idev || !test_bit(IDEV_EH, &idev->flags)) | ||
1555 | ret = TMF_RESP_FUNC_COMPLETE; | ||
1556 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | ||
1557 | |||
1558 | if (ret == TMF_RESP_FUNC_COMPLETE) | ||
1559 | return ret; | ||
1560 | |||
1561 | if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) | ||
1562 | hard_reset = 0; | ||
1563 | |||
1564 | return isci_reset_device(dev, hard_reset); | ||
1565 | } | ||
1566 | |||
1567 | int isci_bus_reset_handler(struct scsi_cmnd *cmd) | ||
1568 | { | ||
1569 | struct domain_device *dev = sdev_to_domain_dev(cmd->device); | ||
1570 | int hard_reset = 1; | ||
1571 | |||
1572 | if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) | ||
1573 | hard_reset = 0; | ||
1564 | 1574 | ||
1565 | return TMF_RESP_FUNC_COMPLETE; | 1575 | return isci_reset_device(dev, hard_reset); |
1566 | } | 1576 | } |