summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/isci/port.c23
-rw-r--r--drivers/scsi/isci/task.c55
2 files changed, 57 insertions, 21 deletions
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index da0c4e1b9b30..2fb85bf75449 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -240,9 +240,32 @@ static void isci_port_link_down(struct isci_host *isci_host,
struct isci_phy *isci_phy,
struct isci_port *isci_port)
{
+ struct isci_remote_device *isci_device;
+
dev_dbg(&isci_host->pdev->dev,
"%s: isci_port = %p\n", __func__, isci_port);
+ if (isci_port) {
+
+ /* check to see if this is the last phy on this port. */
+ if (isci_phy->sas_phy.port &&
+ isci_phy->sas_phy.port->num_phys == 1) {
+ /* change the state for all devices on this port. The
+ * next task sent to this device will be returned as
+ * SAS_TASK_UNDELIVERED, and the scsi mid layer will
+ * remove the target
+ */
+ list_for_each_entry(isci_device,
+ &isci_port->remote_dev_list,
+ node) {
+ dev_dbg(&isci_host->pdev->dev,
+ "%s: isci_device = %p\n",
+ __func__, isci_device);
+ set_bit(IDEV_GONE, &isci_device->flags);
+ }
+ }
+ }
+
/* Notify libsas of the borken link, this will trigger calls to our
* isci_port_deformed and isci_dev_gone functions.
*/
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 084f8f73fade..6bc74eb012c9 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -421,7 +421,7 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
struct isci_host *ihost = dev_to_ihost(dev);
struct isci_remote_device *idev;
unsigned long flags;
- int ret;
+ int ret = TMF_RESP_FUNC_COMPLETE;
spin_lock_irqsave(&ihost->scic_lock, flags);
idev = isci_get_device(dev->lldd_dev);
@@ -447,12 +447,12 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
goto out;
}
/* All pending I/Os have been terminated and cleaned up. */
- if (dev_is_sata(dev)) {
- sas_ata_schedule_reset(dev);
- ret = TMF_RESP_FUNC_COMPLETE;
- } else {
- /* Send the task management part of the reset. */
- ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
+ if (!test_bit(IDEV_GONE, &idev->flags)) {
+ if (dev_is_sata(dev))
+ sas_ata_schedule_reset(dev);
+ else
+ /* Send the task management part of the reset. */
+ ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
}
out:
isci_put_device(idev);
@@ -512,8 +512,17 @@ int isci_task_abort_task(struct sas_task *task)
spin_unlock_irqrestore(&ihost->scic_lock, flags);
dev_warn(&ihost->pdev->dev,
- "%s: dev = %p, task = %p, old_request == %p\n",
- __func__, idev, task, old_request);
+ "%s: dev = %p (%s%s), task = %p, old_request == %p\n",
+ __func__, idev,
+ (dev_is_sata(task->dev) ? "STP/SATA"
+ : ((dev_is_expander(task->dev))
+ ? "SMP"
+ : "SSP")),
+ ((idev) ? ((test_bit(IDEV_GONE, &idev->flags))
+ ? " IDEV_GONE"
+ : "")
+ : " <NULL>"),
+ task, old_request);
/* Device reset conditions signalled in task_state_flags are the
* responsbility of libsas to observe at the start of the error
@@ -552,7 +561,8 @@ int isci_task_abort_task(struct sas_task *task)
if (task->task_proto == SAS_PROTOCOL_SMP ||
sas_protocol_ata(task->task_proto) ||
- test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
+ test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) ||
+ test_bit(IDEV_GONE, &idev->flags)) {
spin_unlock_irqrestore(&ihost->scic_lock, flags);
@@ -561,7 +571,8 @@ int isci_task_abort_task(struct sas_task *task)
dev_warn(&ihost->pdev->dev,
"%s: %s request"
- " or complete_in_target (%d), thus no TMF\n",
+ " or complete_in_target (%d), "
+ "or IDEV_GONE (%d), thus no TMF\n",
__func__,
((task->task_proto == SAS_PROTOCOL_SMP)
? "SMP"
@@ -570,7 +581,8 @@ int isci_task_abort_task(struct sas_task *task)
: "<other>")
),
test_bit(IREQ_COMPLETE_IN_TARGET,
- &old_request->flags));
+ &old_request->flags),
+ test_bit(IDEV_GONE, &idev->flags));
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
@@ -734,7 +746,7 @@ static int isci_reset_device(struct isci_host *ihost,
struct domain_device *dev,
struct isci_remote_device *idev)
{
- int rc = TMF_RESP_FUNC_COMPLETE, reset_stat;
+ int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1;
struct sas_phy *phy = sas_get_local_phy(dev);
struct isci_port *iport = dev->port->lldd_port;
@@ -752,14 +764,15 @@ static int isci_reset_device(struct isci_host *ihost,
* primary duty of this function is to cleanup tasks, so that is the
* relevant status.
*/
-
- if (scsi_is_sas_phy_local(phy)) {
- struct isci_phy *iphy = &ihost->phys[phy->number];
-
- reset_stat = isci_port_perform_hard_reset(ihost, iport, iphy);
- } else
- reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));
-
+ if (!test_bit(IDEV_GONE, &idev->flags)) {
+ if (scsi_is_sas_phy_local(phy)) {
+ struct isci_phy *iphy = &ihost->phys[phy->number];
+
+ reset_stat = isci_port_perform_hard_reset(ihost, iport,
+ iphy);
+ } else
+ reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));
+ }
/* Explicitly resume the RNC here, since there was no task sent. */
isci_remote_device_resume_from_abort(ihost, idev);