]> err.no Git - linux-2.6/blobdiff - drivers/scsi/libsas/sas_scsi_host.c
[SCSI] libsas: Reset timer on taskless scsi_cmnds in sas_scsi_timed_out
[linux-2.6] / drivers / scsi / libsas / sas_scsi_host.c
index e46e79355b776ec98a407962694a8e3756414571..3f647c695da5c940b784915ec38bd4e5e5d19130 100644 (file)
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_sas.h>
 #include "../scsi_sas_internal.h"
+#include "../scsi_transport_api.h"
 
 #include <linux/err.h>
 #include <linux/blkdev.h>
@@ -46,6 +48,7 @@ static void sas_scsi_task_done(struct sas_task *task)
 {
        struct task_status_struct *ts = &task->task_status;
        struct scsi_cmnd *sc = task->uldd_task;
+       struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(sc->device->host);
        unsigned ts_flags = task->task_state_flags;
        int hs = 0, stat = 0;
 
@@ -116,7 +119,7 @@ static void sas_scsi_task_done(struct sas_task *task)
        sas_free_task(task);
        /* This is very ugly but this is how SCSI Core works. */
        if (ts_flags & SAS_TASK_STATE_ABORTED)
-               scsi_finish_command(sc);
+               scsi_eh_finish_cmd(sc, &sas_ha->eh_done_q);
        else
                sc->scsi_done(sc);
 }
@@ -307,6 +310,15 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)
                spin_unlock_irqrestore(&core->task_queue_lock, flags);
        }
 
+       spin_lock_irqsave(&task->task_state_lock, flags);
+       if (task->task_state_flags & SAS_TASK_INITIATOR_ABORTED) {
+               spin_unlock_irqrestore(&task->task_state_lock, flags);
+               SAS_DPRINTK("%s: task 0x%p already aborted\n",
+                           __FUNCTION__, task);
+               return TASK_IS_ABORTED;
+       }
+       spin_unlock_irqrestore(&task->task_state_lock, flags);
+
        for (i = 0; i < 5; i++) {
                SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task);
                res = si->dft->lldd_abort_task(task);
@@ -409,13 +421,16 @@ Again:
        SAS_DPRINTK("going over list...\n");
        list_for_each_entry_safe(cmd, n, &error_q, eh_entry) {
                struct sas_task *task = TO_SAS_TASK(cmd);
+               list_del_init(&cmd->eh_entry);
 
+               if (!task) {
+                       SAS_DPRINTK("%s: taskless cmd?!\n", __FUNCTION__);
+                       continue;
+               }
                SAS_DPRINTK("trying to find task 0x%p\n", task);
-               list_del_init(&cmd->eh_entry);
                res = sas_scsi_find_task(task);
 
                cmd->eh_eflags = 0;
-               shost->host_failed--;
 
                switch (res) {
                case TASK_IS_DONE:
@@ -491,6 +506,7 @@ Again:
                }
        }
 out:
+       scsi_eh_flush_done_q(&ha->eh_done_q);
        SAS_DPRINTK("--- Exit %s\n", __FUNCTION__);
        return;
 clear_q:
@@ -508,18 +524,35 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
        unsigned long flags;
 
        if (!task) {
-               SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n",
-                           cmd, task);
-               return EH_HANDLED;
+               cmd->timeout_per_command /= 2;
+               SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n",
+                           cmd, task, (cmd->timeout_per_command ?
+                           "EH_RESET_TIMER" : "EH_NOT_HANDLED"));
+               if (!cmd->timeout_per_command)
+                       return EH_NOT_HANDLED;
+               return EH_RESET_TIMER;
        }
 
        spin_lock_irqsave(&task->task_state_lock, flags);
+       if (task->task_state_flags & SAS_TASK_INITIATOR_ABORTED) {
+               spin_unlock_irqrestore(&task->task_state_lock, flags);
+               SAS_DPRINTK("command 0x%p, task 0x%p, aborted by initiator: "
+                           "EH_NOT_HANDLED\n", cmd, task);
+               return EH_NOT_HANDLED;
+       }
        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",
                            cmd, task);
                return EH_HANDLED;
        }
+       if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) {
+               spin_unlock_irqrestore(&task->task_state_lock, flags);
+               SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: "
+                           "EH_RESET_TIMER\n",
+                           cmd, task);
+               return EH_RESET_TIMER;
+       }
        task->task_state_flags |= SAS_TASK_STATE_ABORTED;
        spin_unlock_irqrestore(&task->task_state_lock, flags);
 
@@ -777,6 +810,66 @@ 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)
+{
+       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) {
+               spin_unlock_irqrestore(&task->task_state_lock, flags);
+               SAS_DPRINTK("%s: Task %p already aborted.\n", __FUNCTION__,
+                           task);
+               return 0;
+       }
+
+       task->task_state_flags |= SAS_TASK_INITIATOR_ABORTED;
+       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);
+
+       if (!si->dft->lldd_abort_task)
+               return -ENODEV;
+
+       res = si->dft->lldd_abort_task(task);
+       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);
+               return 0;
+       }
+
+       spin_lock_irqsave(&task->task_state_lock, flags);
+       task->task_state_flags &= ~SAS_TASK_INITIATOR_ABORTED;
+       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);
+
+       return -EAGAIN;
+}
+
+void sas_task_abort(struct work_struct *work)
+{
+       struct sas_task *task =
+               container_of(work, struct sas_task, abort_work);
+       int i;
+
+       for (i = 0; i < 5; i++)
+               if (!do_sas_task_abort(task))
+                       return;
+
+       SAS_DPRINTK("%s: Could not kill task!\n", __FUNCTION__);
+}
+
 EXPORT_SYMBOL_GPL(sas_queuecommand);
 EXPORT_SYMBOL_GPL(sas_target_alloc);
 EXPORT_SYMBOL_GPL(sas_slave_configure);
@@ -784,3 +877,6 @@ 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_phy_reset);
+EXPORT_SYMBOL_GPL(sas_phy_enable);