[SCSI] Fix 'Device not ready' issue on mpt2sas
[cascardo/linux.git] / drivers / scsi / scsi_error.c
index d0f71e5..de2337f 100644 (file)
@@ -42,6 +42,8 @@
 
 #include <trace/events/scsi.h>
 
+static void scsi_eh_done(struct scsi_cmnd *scmd);
+
 #define SENSE_TIMEOUT          (10*HZ)
 
 /*
@@ -241,6 +243,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
        if (! scsi_command_normalize_sense(scmd, &sshdr))
                return FAILED;  /* no valid sense data */
 
+       if (scmd->cmnd[0] == TEST_UNIT_READY && scmd->scsi_done != scsi_eh_done)
+               /*
+                * nasty: for mid-layer issued TURs, we need to return the
+                * actual sense data without any recovery attempt.  For eh
+                * issued ones, we need to try to recover and interpret
+                */
+               return SUCCESS;
+
        if (scsi_sense_is_deferred(&sshdr))
                return NEEDS_RETRY;
 
@@ -1687,6 +1697,20 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
         * requests are started.
         */
        scsi_run_host_queues(shost);
+
+       /*
+        * if eh is active and host_eh_scheduled is pending we need to re-run
+        * recovery.  we do this check after scsi_run_host_queues() to allow
+        * everything pent up since the last eh run a chance to make forward
+        * progress before we sync again.  Either we'll immediately re-run
+        * recovery or scsi_device_unbusy() will wake us again when these
+        * pending commands complete.
+        */
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (shost->host_eh_scheduled)
+               if (scsi_host_set_state(shost, SHOST_RECOVERY))
+                       WARN_ON(scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY));
+       spin_unlock_irqrestore(shost->host_lock, flags);
 }
 
 /**
@@ -1804,15 +1828,14 @@ int scsi_error_handler(void *data)
         * We never actually get interrupted because kthread_run
         * disables signal delivery for the created thread.
         */
-       set_current_state(TASK_INTERRUPTIBLE);
        while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
                if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) ||
                    shost->host_failed != shost->host_busy) {
                        SCSI_LOG_ERROR_RECOVERY(1,
                                printk("Error handler scsi_eh_%d sleeping\n",
                                        shost->host_no));
                        schedule();
-                       set_current_state(TASK_INTERRUPTIBLE);
                        continue;
                }
 
@@ -1849,7 +1872,6 @@ int scsi_error_handler(void *data)
                scsi_restart_operations(shost);
                if (!shost->eh_noresume)
                        scsi_autopm_put_host(shost);
-               set_current_state(TASK_INTERRUPTIBLE);
        }
        __set_current_state(TASK_RUNNING);