Merge branches 'depends/rmk/io', 'depends/rmk/l2x0' and 'depends/rmk/gpio' into highb...
[pandora-kernel.git] / drivers / scsi / scsi_error.c
index abea2cf..a4b9cdb 100644 (file)
@@ -50,6 +50,8 @@
 #define BUS_RESET_SETTLE_TIME   (10)
 #define HOST_RESET_SETTLE_TIME  (10)
 
+static int scsi_eh_try_stu(struct scsi_cmnd *scmd);
+
 /* called with shost->host_lock held */
 void scsi_eh_wakeup(struct Scsi_Host *shost)
 {
@@ -946,6 +948,48 @@ retry_tur:
        }
 }
 
+/**
+ * scsi_eh_test_devices - check if devices are responding from error recovery.
+ * @cmd_list:  scsi commands in error recovery.
+ * @work_q:     queue for commands which still need more error recovery
+ * @done_q:     queue for commands which are finished
+ * @try_stu:    boolean on if a STU command should be tried in addition to TUR.
+ *
+ * Decription:
+ *    Tests if devices are in a working state.  Commands to devices now in
+ *    a working state are sent to the done_q while commands to devices which
+ *    are still failing to respond are returned to the work_q for more
+ *    processing.
+ **/
+static int scsi_eh_test_devices(struct list_head *cmd_list,
+                               struct list_head *work_q,
+                               struct list_head *done_q, int try_stu)
+{
+       struct scsi_cmnd *scmd, *next;
+       struct scsi_device *sdev;
+       int finish_cmds;
+
+       while (!list_empty(cmd_list)) {
+               scmd = list_entry(cmd_list->next, struct scsi_cmnd, eh_entry);
+               sdev = scmd->device;
+
+               finish_cmds = !scsi_device_online(scmd->device) ||
+                       (try_stu && !scsi_eh_try_stu(scmd) &&
+                        !scsi_eh_tur(scmd)) ||
+                       !scsi_eh_tur(scmd);
+
+               list_for_each_entry_safe(scmd, next, cmd_list, eh_entry)
+                       if (scmd->device == sdev) {
+                               if (finish_cmds)
+                                       scsi_eh_finish_cmd(scmd, done_q);
+                               else
+                                       list_move_tail(&scmd->eh_entry, work_q);
+                       }
+       }
+       return list_empty(work_q);
+}
+
+
 /**
  * scsi_eh_abort_cmds - abort pending commands.
  * @work_q:    &list_head for pending commands.
@@ -962,6 +1006,7 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
                              struct list_head *done_q)
 {
        struct scsi_cmnd *scmd, *next;
+       LIST_HEAD(check_list);
        int rtn;
 
        list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
@@ -973,11 +1018,10 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
                rtn = scsi_try_to_abort_cmd(scmd->device->host->hostt, scmd);
                if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
                        scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD;
-                       if (!scsi_device_online(scmd->device) ||
-                           rtn == FAST_IO_FAIL ||
-                           !scsi_eh_tur(scmd)) {
+                       if (rtn == FAST_IO_FAIL)
                                scsi_eh_finish_cmd(scmd, done_q);
-                       }
+                       else
+                               list_move_tail(&scmd->eh_entry, &check_list);
                } else
                        SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting"
                                                          " cmd failed:"
@@ -986,7 +1030,7 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
                                                          scmd));
        }
 
-       return list_empty(work_q);
+       return scsi_eh_test_devices(&check_list, work_q, done_q, 0);
 }
 
 /**
@@ -1137,6 +1181,7 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
                                struct list_head *done_q)
 {
        LIST_HEAD(tmp_list);
+       LIST_HEAD(check_list);
 
        list_splice_init(work_q, &tmp_list);
 
@@ -1161,9 +1206,9 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
                        if (scmd_id(scmd) != id)
                                continue;
 
-                       if ((rtn == SUCCESS || rtn == FAST_IO_FAIL)
-                           && (!scsi_device_online(scmd->device) ||
-                                rtn == FAST_IO_FAIL || !scsi_eh_tur(scmd)))
+                       if (rtn == SUCCESS)
+                               list_move_tail(&scmd->eh_entry, &check_list);
+                       else if (rtn == FAST_IO_FAIL)
                                scsi_eh_finish_cmd(scmd, done_q);
                        else
                                /* push back on work queue for further processing */
@@ -1171,7 +1216,7 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
                }
        }
 
-       return list_empty(work_q);
+       return scsi_eh_test_devices(&check_list, work_q, done_q, 0);
 }
 
 /**
@@ -1185,6 +1230,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost,
                             struct list_head *done_q)
 {
        struct scsi_cmnd *scmd, *chan_scmd, *next;
+       LIST_HEAD(check_list);
        unsigned int channel;
        int rtn;
 
@@ -1216,12 +1262,14 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost,
                rtn = scsi_try_bus_reset(chan_scmd);
                if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
                        list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
-                               if (channel == scmd_channel(scmd))
-                                       if (!scsi_device_online(scmd->device) ||
-                                           rtn == FAST_IO_FAIL ||
-                                           !scsi_eh_tur(scmd))
+                               if (channel == scmd_channel(scmd)) {
+                                       if (rtn == FAST_IO_FAIL)
                                                scsi_eh_finish_cmd(scmd,
                                                                   done_q);
+                                       else
+                                               list_move_tail(&scmd->eh_entry,
+                                                              &check_list);
+                               }
                        }
                } else {
                        SCSI_LOG_ERROR_RECOVERY(3, printk("%s: BRST"
@@ -1230,7 +1278,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost,
                                                          channel));
                }
        }
-       return list_empty(work_q);
+       return scsi_eh_test_devices(&check_list, work_q, done_q, 0);
 }
 
 /**
@@ -1242,6 +1290,7 @@ static int scsi_eh_host_reset(struct list_head *work_q,
                              struct list_head *done_q)
 {
        struct scsi_cmnd *scmd, *next;
+       LIST_HEAD(check_list);
        int rtn;
 
        if (!list_empty(work_q)) {
@@ -1252,12 +1301,10 @@ static int scsi_eh_host_reset(struct list_head *work_q,
                                                  , current->comm));
 
                rtn = scsi_try_host_reset(scmd);
-               if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
+               if (rtn == SUCCESS) {
+                       list_splice_init(work_q, &check_list);
+               } else if (rtn == FAST_IO_FAIL) {
                        list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
-                               if (!scsi_device_online(scmd->device) ||
-                                   rtn == FAST_IO_FAIL ||
-                                   (!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) ||
-                                   !scsi_eh_tur(scmd))
                                        scsi_eh_finish_cmd(scmd, done_q);
                        }
                } else {
@@ -1266,7 +1313,7 @@ static int scsi_eh_host_reset(struct list_head *work_q,
                                                          current->comm));
                }
        }
-       return list_empty(work_q);
+       return scsi_eh_test_devices(&check_list, work_q, done_q, 1);
 }
 
 /**