isci: Check IDEV_GONE before performing abort path operations.

In the link fail path, set IDEV_GONE for every device on the domain
when the last link in the port fails.

In the abort path functions like isci_reset_device, make sure that
there has not already been a detected domain failure with the device
by checking IDEV_GONE, before performing any kind of hard reset, SMP
phy control, or TMF operation.

The check for IDEV_GONE makes sure that the device in the abort path
really has control of the port with which it is associated.  This
prevents starting hard resets at incorrect times and scheduling
unnecessary LUN resets for SATA devices.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 084f8f7..6bc74eb 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -421,7 +421,7 @@
 	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 @@
 		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 @@
 	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 @@
 
 	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 @@
 
 		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 @@
 				: "<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 @@
 			     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 @@
 	 * primary duty of this function is to cleanup tasks, so that is the
 	 * relevant status.
 	 */
+	if (!test_bit(IDEV_GONE, &idev->flags)) {
+		if (scsi_is_sas_phy_local(phy)) {
+			struct isci_phy *iphy = &ihost->phys[phy->number];
 
-	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));
-
+			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);