]> err.no Git - linux-2.6/blobdiff - drivers/scsi/libsas/sas_scsi_host.c
[SCSI] libsas: Start I_T recovery if ABORT TASK fails
[linux-2.6] / drivers / scsi / libsas / sas_scsi_host.c
index d0c04ebeb8b095f5424033e59c07bb92332bfa36..9ffe7605fb9d6d2d075030ea35b33fbd65b75c07 100644 (file)
@@ -281,6 +281,7 @@ enum task_disposition {
        TASK_IS_ABORTED,
        TASK_IS_AT_LU,
        TASK_IS_NOT_AT_LU,
+       TASK_ABORT_FAILED,
 };
 
 static enum task_disposition sas_scsi_find_task(struct sas_task *task)
@@ -331,15 +332,21 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)
                        SAS_DPRINTK("%s: querying task 0x%p\n",
                                    __FUNCTION__, task);
                        res = si->dft->lldd_query_task(task);
-                       if (res == TMF_RESP_FUNC_SUCC) {
+                       switch (res) {
+                       case TMF_RESP_FUNC_SUCC:
                                SAS_DPRINTK("%s: task 0x%p at LU\n",
                                            __FUNCTION__, task);
                                return TASK_IS_AT_LU;
-                       } else if (res == TMF_RESP_FUNC_COMPLETE) {
+                       case TMF_RESP_FUNC_COMPLETE:
                                SAS_DPRINTK("%s: task 0x%p not at LU\n",
                                            __FUNCTION__, task);
                                return TASK_IS_NOT_AT_LU;
-                       }
+                       case TMF_RESP_FUNC_FAILED:
+                                SAS_DPRINTK("%s: task 0x%p failed to abort\n",
+                                                __FUNCTION__, task);
+                                return TASK_ABORT_FAILED;
+                        }
+
                }
        }
        return res;
@@ -475,6 +482,7 @@ Again:
                        }
                        /* fallthrough */
                case TASK_IS_NOT_AT_LU:
+               case TASK_ABORT_FAILED:
                        SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n",
                                    task);
                        tmf_resp = sas_recover_I_T(task->dev);
@@ -560,6 +568,7 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
        }
 
        spin_lock_irqsave(&task->task_state_lock, flags);
+       BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED);
        if (task->task_state_flags & SAS_TASK_STATE_DONE) {
                spin_unlock_irqrestore(&task->task_state_lock, flags);
                SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n",
@@ -588,8 +597,9 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)
        struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
        struct domain_device *found_dev = NULL;
        int i;
+       unsigned long flags;
 
-       spin_lock(&ha->phy_port_lock);
+       spin_lock_irqsave(&ha->phy_port_lock, flags);
        for (i = 0; i < ha->num_phys; i++) {
                struct asd_sas_port *port = ha->sas_port[i];
                struct domain_device *dev;
@@ -605,7 +615,7 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)
                spin_unlock(&port->dev_list_lock);
        }
  found:
-       spin_unlock(&ha->phy_port_lock);
+       spin_unlock_irqrestore(&ha->phy_port_lock, flags);
 
        return found_dev;
 }
@@ -830,44 +840,42 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha)
        spin_unlock_irqrestore(&core->task_queue_lock, flags);
 }
 
-static int do_sas_task_abort(struct sas_task *task)
+/*
+ * Call the LLDD task abort routine directly.  This function is intended for
+ * use by upper layers that need to tell the LLDD to abort a task.
+ */
+int __sas_task_abort(struct sas_task *task)
 {
-       struct scsi_cmnd *sc = task->uldd_task;
        struct sas_internal *si =
                to_sas_internal(task->dev->port->ha->core.shost->transportt);
        unsigned long flags;
        int res;
 
        spin_lock_irqsave(&task->task_state_lock, flags);
-       if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+       if (task->task_state_flags & SAS_TASK_STATE_ABORTED ||
+           task->task_state_flags & SAS_TASK_STATE_DONE) {
                spin_unlock_irqrestore(&task->task_state_lock, flags);
-               SAS_DPRINTK("%s: Task %p already aborted.\n", __FUNCTION__,
+               SAS_DPRINTK("%s: Task %p already finished.\n", __FUNCTION__,
                            task);
                return 0;
        }
-
-       if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
-               task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+       task->task_state_flags |= SAS_TASK_STATE_ABORTED;
        spin_unlock_irqrestore(&task->task_state_lock, flags);
 
        if (!si->dft->lldd_abort_task)
                return -ENODEV;
 
        res = si->dft->lldd_abort_task(task);
+
+       spin_lock_irqsave(&task->task_state_lock, flags);
        if ((task->task_state_flags & SAS_TASK_STATE_DONE) ||
            (res == TMF_RESP_FUNC_COMPLETE))
        {
-               /* SMP commands don't have scsi_cmds(?) */
-               if (!sc) {
-                       task->task_done(task);
-                       return 0;
-               }
-               scsi_req_abort_cmd(sc);
-               scsi_schedule_eh(sc->device->host);
+               spin_unlock_irqrestore(&task->task_state_lock, flags);
+               task->task_done(task);
                return 0;
        }
 
-       spin_lock_irqsave(&task->task_state_lock, flags);
        if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
                task->task_state_flags &= ~SAS_TASK_STATE_ABORTED;
        spin_unlock_irqrestore(&task->task_state_lock, flags);
@@ -875,17 +883,24 @@ static int do_sas_task_abort(struct sas_task *task)
        return -EAGAIN;
 }
 
-void sas_task_abort(struct work_struct *work)
+/*
+ * Tell an upper layer that it needs to initiate an abort for a given task.
+ * This should only ever be called by an LLDD.
+ */
+void sas_task_abort(struct sas_task *task)
 {
-       struct sas_task *task =
-               container_of(work, struct sas_task, abort_work);
-       int i;
+       struct scsi_cmnd *sc = task->uldd_task;
 
-       for (i = 0; i < 5; i++)
-               if (!do_sas_task_abort(task))
+       /* Escape for libsas internal commands */
+       if (!sc) {
+               if (!del_timer(&task->timer))
                        return;
+               task->timer.function(task->timer.data);
+               return;
+       }
 
-       SAS_DPRINTK("%s: Could not kill task!\n", __FUNCTION__);
+       scsi_req_abort_cmd(sc);
+       scsi_schedule_eh(sc->device->host);
 }
 
 EXPORT_SYMBOL_GPL(sas_queuecommand);
@@ -895,6 +910,7 @@ EXPORT_SYMBOL_GPL(sas_slave_destroy);
 EXPORT_SYMBOL_GPL(sas_change_queue_depth);
 EXPORT_SYMBOL_GPL(sas_change_queue_type);
 EXPORT_SYMBOL_GPL(sas_bios_param);
+EXPORT_SYMBOL_GPL(__sas_task_abort);
 EXPORT_SYMBOL_GPL(sas_task_abort);
 EXPORT_SYMBOL_GPL(sas_phy_reset);
 EXPORT_SYMBOL_GPL(sas_phy_enable);