static void sas_ata_task_done(struct sas_task *task)
 {
        struct ata_queued_cmd *qc = task->uldd_task;
-       struct domain_device *dev;
+       struct domain_device *dev = task->dev;
        struct task_status_struct *stat = &task->task_status;
        struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf;
-       struct sas_ha_struct *sas_ha;
+       struct sas_ha_struct *sas_ha = dev->port->ha;
        enum ata_completion_errors ac;
        unsigned long flags;
        struct ata_link *link;
        struct ata_port *ap;
 
+       spin_lock_irqsave(&dev->done_lock, flags);
+       if (test_bit(SAS_HA_FROZEN, &sas_ha->state))
+               task = NULL;
+       else if (qc && qc->scsicmd)
+               ASSIGN_SAS_TASK(qc->scsicmd, NULL);
+       spin_unlock_irqrestore(&dev->done_lock, flags);
+
+       /* check if libsas-eh got to the task before us */
+       if (unlikely(!task))
+               return;
+
        if (!qc)
                goto qc_already_gone;
 
        ap = qc->ap;
-       dev = ap->private_data;
-       sas_ha = dev->port->ha;
        link = &ap->link;
 
        spin_lock_irqsave(ap->lock, flags);
        }
 
        qc->lldd_task = NULL;
-       if (qc->scsicmd)
-               ASSIGN_SAS_TASK(qc->scsicmd, NULL);
        ata_qc_complete(qc);
        spin_unlock_irqrestore(ap->lock, flags);
 
        sas_enable_revalidation(sas_ha);
 }
 
-int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task,
-                     enum blk_eh_timer_return *rtn)
-{
-       struct domain_device *ddev = cmd_to_domain_dev(cmd);
-
-       if (!dev_is_sata(ddev) || task)
-               return 0;
-
-       /* we're a sata device with no task, so this must be a libata
-        * eh timeout.  Ideally should hook into libata timeout
-        * handling, but there's no point, it just wants to activate
-        * the eh thread */
-       *rtn = BLK_EH_NOT_HANDLED;
-       return 1;
-}
-
 int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
               struct list_head *done_q)
 {
 
 static void sas_scsi_task_done(struct sas_task *task)
 {
        struct scsi_cmnd *sc = task->uldd_task;
+       struct domain_device *dev = task->dev;
+       struct sas_ha_struct *ha = dev->port->ha;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->done_lock, flags);
+       if (test_bit(SAS_HA_FROZEN, &ha->state))
+               task = NULL;
+       else
+               ASSIGN_SAS_TASK(sc, NULL);
+       spin_unlock_irqrestore(&dev->done_lock, flags);
 
-       if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
-               /* Aborted tasks will be completed by the error handler */
+       if (unlikely(!task)) {
+               /* task will be completed by the error handler */
                SAS_DPRINTK("task done but aborted\n");
                return;
        }
                return;
        }
 
-       ASSIGN_SAS_TASK(sc, NULL);
        sas_end_task(sc, task);
        sc->scsi_done(sc);
 }
        TASK_IS_DONE,
        TASK_IS_ABORTED,
        TASK_IS_AT_LU,
+       TASK_IS_NOT_AT_HA,
        TASK_IS_NOT_AT_LU,
        TASK_ABORT_FAILED,
 };
                struct scsi_core *core = &ha->core;
                struct sas_task *t, *n;
 
+               mutex_lock(&core->task_queue_flush);
                spin_lock_irqsave(&core->task_queue_lock, flags);
-               list_for_each_entry_safe(t, n, &core->task_queue, list) {
+               list_for_each_entry_safe(t, n, &core->task_queue, list)
                        if (task == t) {
                                list_del_init(&t->list);
-                               spin_unlock_irqrestore(&core->task_queue_lock,
-                                                      flags);
-                               SAS_DPRINTK("%s: task 0x%p aborted from "
-                                           "task_queue\n",
-                                           __func__, task);
-                               return TASK_IS_ABORTED;
+                               break;
                        }
-               }
                spin_unlock_irqrestore(&core->task_queue_lock, flags);
+               mutex_unlock(&core->task_queue_flush);
+
+               if (task == t)
+                       return TASK_IS_NOT_AT_HA;
        }
 
        for (i = 0; i < 5; i++) {
 }
 
 static int sas_eh_handle_sas_errors(struct Scsi_Host *shost,
-                                   struct list_head *work_q,
-                                   struct list_head *done_q)
+                                   struct list_head *work_q)
 {
        struct scsi_cmnd *cmd, *n;
        enum task_disposition res = TASK_IS_DONE;
 
 Again:
        list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
-               struct sas_task *task = TO_SAS_TASK(cmd);
+               struct domain_device *dev = cmd_to_domain_dev(cmd);
+               struct sas_task *task;
+
+               spin_lock_irqsave(&dev->done_lock, flags);
+               /* by this point the lldd has either observed
+                * SAS_HA_FROZEN and is leaving the task alone, or has
+                * won the race with eh and decided to complete it
+                */
+               task = TO_SAS_TASK(cmd);
+               spin_unlock_irqrestore(&dev->done_lock, flags);
 
                if (!task)
                        continue;
                cmd->eh_eflags = 0;
 
                switch (res) {
+               case TASK_IS_NOT_AT_HA:
+                       SAS_DPRINTK("%s: task 0x%p is not at ha: %s\n",
+                                   __func__, task,
+                                   cmd->retries ? "retry" : "aborted");
+                       if (cmd->retries)
+                               cmd->retries--;
+                       sas_eh_finish_cmd(cmd);
+                       continue;
                case TASK_IS_DONE:
                        SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
                                    task);
         * Deal with commands that still have SAS tasks (i.e. they didn't
         * complete via the normal sas_task completion mechanism)
         */
-       if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q))
+       set_bit(SAS_HA_FROZEN, &ha->state);
+       if (sas_eh_handle_sas_errors(shost, &eh_work_q))
                goto out;
 
        /*
                        scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);
 
 out:
+       clear_bit(SAS_HA_FROZEN, &ha->state);
+       if (ha->lldd_max_execute_num > 1)
+               wake_up_process(ha->core.queue_thread);
+
        /* now link into libata eh --- if we have any ata devices */
        sas_ata_strategy_handler(shost);
 
 
 enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
 {
-       struct sas_task *task = TO_SAS_TASK(cmd);
-       unsigned long flags;
-       enum blk_eh_timer_return rtn;
-
-       if (sas_ata_timed_out(cmd, task, &rtn))
-               return rtn;
-
-       if (!task) {
-               cmd->request->timeout /= 2;
-               SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n",
-                           cmd, task, (cmd->request->timeout ?
-                           "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED"));
-               if (!cmd->request->timeout)
-                       return BLK_EH_NOT_HANDLED;
-               return BLK_EH_RESET_TIMER;
-       }
-
-       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: "
-                           "BLK_EH_HANDLED\n", cmd, task);
-               return BLK_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: "
-                           "BLK_EH_RESET_TIMER\n",
-                           cmd, task);
-               return BLK_EH_RESET_TIMER;
-       }
-       task->task_state_flags |= SAS_TASK_STATE_ABORTED;
-       spin_unlock_irqrestore(&task->task_state_lock, flags);
-
-       SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n",
-                   cmd, task);
+       scmd_printk(KERN_DEBUG, cmd, "command %p timed out\n", cmd);
 
        return BLK_EH_NOT_HANDLED;
 }
        int res;
        struct sas_internal *i = to_sas_internal(core->shost->transportt);
 
+       mutex_lock(&core->task_queue_flush);
        spin_lock_irqsave(&core->task_queue_lock, flags);
        while (!kthread_should_stop() &&
-              !list_empty(&core->task_queue)) {
+              !list_empty(&core->task_queue) &&
+              !test_bit(SAS_HA_FROZEN, &sas_ha->state)) {
 
                can_queue = sas_ha->lldd_queue_size - core->task_queue_size;
                if (can_queue >= 0) {
                }
        }
        spin_unlock_irqrestore(&core->task_queue_lock, flags);
+       mutex_unlock(&core->task_queue_flush);
 }
 
 /**
        struct scsi_core *core = &sas_ha->core;
 
        spin_lock_init(&core->task_queue_lock);
+       mutex_init(&core->task_queue_flush);
        core->task_queue_size = 0;
        INIT_LIST_HEAD(&core->task_queue);