]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/scsi_error.c
Merge branch 'drm-next-4.12' of git://people.freedesktop.org/~agd5f/linux into drm...
[karo-tx-linux.git] / drivers / scsi / scsi_error.c
index 2db412dd4b4477f973d25d24f543fb0601f2ba2a..ecc07dab893dc473c831227e567cddbbb04e1852 100644 (file)
@@ -46,6 +46,8 @@
 
 #include <trace/events/scsi.h>
 
+#include <asm/unaligned.h>
+
 static void scsi_eh_done(struct scsi_cmnd *scmd);
 
 /*
@@ -162,13 +164,7 @@ scmd_eh_abort_handler(struct work_struct *work)
                }
        }
 
-       if (!scsi_eh_scmd_add(scmd, 0)) {
-               SCSI_LOG_ERROR_RECOVERY(3,
-                       scmd_printk(KERN_WARNING, scmd,
-                                   "terminate aborted command\n"));
-               set_host_byte(scmd, DID_TIME_OUT);
-               scsi_finish_command(scmd);
-       }
+       scsi_eh_scmd_add(scmd);
 }
 
 /**
@@ -188,7 +184,6 @@ scsi_abort_command(struct scsi_cmnd *scmd)
                /*
                 * Retry after abort failed, escalate to next level.
                 */
-               scmd->eh_eflags &= ~SCSI_EH_ABORT_SCHEDULED;
                SCSI_LOG_ERROR_RECOVERY(3,
                        scmd_printk(KERN_INFO, scmd,
                                    "previous abort failed\n"));
@@ -196,19 +191,7 @@ scsi_abort_command(struct scsi_cmnd *scmd)
                return FAILED;
        }
 
-       /*
-        * Do not try a command abort if
-        * SCSI EH has already started.
-        */
        spin_lock_irqsave(shost->host_lock, flags);
-       if (scsi_host_in_recovery(shost)) {
-               spin_unlock_irqrestore(shost->host_lock, flags);
-               SCSI_LOG_ERROR_RECOVERY(3,
-                       scmd_printk(KERN_INFO, scmd,
-                                   "not aborting, host in recovery\n"));
-               return FAILED;
-       }
-
        if (shost->eh_deadline != -1 && !shost->last_reset)
                shost->last_reset = jiffies;
        spin_unlock_irqrestore(shost->host_lock, flags);
@@ -221,40 +204,47 @@ scsi_abort_command(struct scsi_cmnd *scmd)
 }
 
 /**
- * scsi_eh_scmd_add - add scsi cmd to error handling.
+ * scsi_eh_reset - call into ->eh_action to reset internal counters
  * @scmd:      scmd to run eh on.
- * @eh_flag:   optional SCSI_EH flag.
  *
- * Return value:
- *     0 on failure.
+ * The scsi driver might be carrying internal state about the
+ * devices, so we need to call into the driver to reset the
+ * internal state once the error handler is started.
  */
-int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
+static void scsi_eh_reset(struct scsi_cmnd *scmd)
+{
+       if (!blk_rq_is_passthrough(scmd->request)) {
+               struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd);
+               if (sdrv->eh_reset)
+                       sdrv->eh_reset(scmd);
+       }
+}
+
+/**
+ * scsi_eh_scmd_add - add scsi cmd to error handling.
+ * @scmd:      scmd to run eh on.
+ */
+void scsi_eh_scmd_add(struct scsi_cmnd *scmd)
 {
        struct Scsi_Host *shost = scmd->device->host;
        unsigned long flags;
-       int ret = 0;
+       int ret;
 
-       if (!shost->ehandler)
-               return 0;
+       WARN_ON_ONCE(!shost->ehandler);
 
        spin_lock_irqsave(shost->host_lock, flags);
-       if (scsi_host_set_state(shost, SHOST_RECOVERY))
-               if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY))
-                       goto out_unlock;
-
+       if (scsi_host_set_state(shost, SHOST_RECOVERY)) {
+               ret = scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY);
+               WARN_ON_ONCE(ret);
+       }
        if (shost->eh_deadline != -1 && !shost->last_reset)
                shost->last_reset = jiffies;
 
-       ret = 1;
-       if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED)
-               eh_flag &= ~SCSI_EH_CANCEL_CMD;
-       scmd->eh_eflags |= eh_flag;
+       scsi_eh_reset(scmd);
        list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
        shost->host_failed++;
        scsi_eh_wakeup(shost);
- out_unlock:
        spin_unlock_irqrestore(shost->host_lock, flags);
-       return ret;
 }
 
 /**
@@ -283,13 +273,10 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
                rtn = host->hostt->eh_timed_out(scmd);
 
        if (rtn == BLK_EH_NOT_HANDLED) {
-               if (!host->hostt->no_async_abort &&
-                   scsi_abort_command(scmd) == SUCCESS)
-                       return BLK_EH_NOT_HANDLED;
-
-               set_host_byte(scmd, DID_TIME_OUT);
-               if (!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))
-                       rtn = BLK_EH_HANDLED;
+               if (scsi_abort_command(scmd) != SUCCESS) {
+                       set_host_byte(scmd, DID_TIME_OUT);
+                       scsi_eh_scmd_add(scmd);
+               }
        }
 
        return rtn;
@@ -341,7 +328,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
                list_for_each_entry(scmd, work_q, eh_entry) {
                        if (scmd->device == sdev) {
                                ++total_failures;
-                               if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD)
+                               if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED)
                                        ++cmd_cancel;
                                else
                                        ++cmd_failed;
@@ -931,6 +918,7 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
        ses->result = scmd->result;
        ses->underflow = scmd->underflow;
        ses->prot_op = scmd->prot_op;
+       ses->eh_eflags = scmd->eh_eflags;
 
        scmd->prot_op = SCSI_PROT_NORMAL;
        scmd->eh_eflags = 0;
@@ -994,6 +982,7 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
        scmd->result = ses->result;
        scmd->underflow = ses->underflow;
        scmd->prot_op = ses->prot_op;
+       scmd->eh_eflags = ses->eh_eflags;
 }
 EXPORT_SYMBOL(scsi_eh_restore_cmnd);
 
@@ -1126,7 +1115,6 @@ static int scsi_eh_action(struct scsi_cmnd *scmd, int rtn)
  */
 void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, struct list_head *done_q)
 {
-       scmd->eh_eflags = 0;
        list_move_tail(&scmd->eh_entry, done_q);
 }
 EXPORT_SYMBOL(scsi_eh_finish_cmd);
@@ -1163,8 +1151,7 @@ int scsi_eh_get_sense(struct list_head *work_q,
         * should not get sense.
         */
        list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
-               if ((scmd->eh_eflags & SCSI_EH_CANCEL_CMD) ||
-                   (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) ||
+               if ((scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) ||
                    SCSI_SENSE_VALID(scmd))
                        continue;
 
@@ -1304,61 +1291,6 @@ static int scsi_eh_test_devices(struct list_head *cmd_list,
        return list_empty(work_q);
 }
 
-
-/**
- * scsi_eh_abort_cmds - abort pending commands.
- * @work_q:    &list_head for pending commands.
- * @done_q:    &list_head for processed commands.
- *
- * Decription:
- *    Try and see whether or not it makes sense to try and abort the
- *    running command.  This only works out to be the case if we have one
- *    command that has timed out.  If the command simply failed, it makes
- *    no sense to try and abort the command, since as far as the shost
- *    adapter is concerned, it isn't running.
- */
-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;
-       struct Scsi_Host *shost;
-
-       list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
-               if (!(scmd->eh_eflags & SCSI_EH_CANCEL_CMD))
-                       continue;
-               shost = scmd->device->host;
-               if (scsi_host_eh_past_deadline(shost)) {
-                       list_splice_init(&check_list, work_q);
-                       SCSI_LOG_ERROR_RECOVERY(3,
-                               scmd_printk(KERN_INFO, scmd,
-                                           "%s: skip aborting cmd, past eh deadline\n",
-                                           current->comm));
-                       return list_empty(work_q);
-               }
-               SCSI_LOG_ERROR_RECOVERY(3,
-                       scmd_printk(KERN_INFO, scmd,
-                                    "%s: aborting cmd\n", current->comm));
-               rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
-               if (rtn == FAILED) {
-                       SCSI_LOG_ERROR_RECOVERY(3,
-                               scmd_printk(KERN_INFO, scmd,
-                                           "%s: aborting cmd failed\n",
-                                            current->comm));
-                       list_splice_init(&check_list, work_q);
-                       return list_empty(work_q);
-               }
-               scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD;
-               if (rtn == FAST_IO_FAIL)
-                       scsi_eh_finish_cmd(scmd, done_q);
-               else
-                       list_move_tail(&scmd->eh_entry, &check_list);
-       }
-
-       return scsi_eh_test_devices(&check_list, work_q, done_q, 0);
-}
-
 /**
  * scsi_eh_try_stu - Send START_UNIT to device.
  * @scmd:      &scsi_cmnd to send START_UNIT
@@ -1701,11 +1633,6 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q,
                sdev_printk(KERN_INFO, scmd->device, "Device offlined - "
                            "not ready after error recovery\n");
                scsi_device_set_state(scmd->device, SDEV_OFFLINE);
-               if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD) {
-                       /*
-                        * FIXME: Handle lost cmds.
-                        */
-               }
                scsi_eh_finish_cmd(scmd, done_q);
        }
        return;
@@ -2149,8 +2076,7 @@ static void scsi_unjam_host(struct Scsi_Host *shost)
        SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(shost, &eh_work_q));
 
        if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))
-               if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))
-                       scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
+               scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
 
        spin_lock_irqsave(shost->host_lock, flags);
        if (shost->eh_deadline != -1)
@@ -2437,44 +2363,34 @@ EXPORT_SYMBOL(scsi_command_normalize_sense);
  *                     field will be placed if found.
  *
  * Return value:
- *     1 if information field found, 0 if not found.
+ *     true if information field found, false if not found.
  */
-int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
-                           u64 * info_out)
+bool scsi_get_sense_info_fld(const u8 *sense_buffer, int sb_len,
+                            u64 *info_out)
 {
-       int j;
        const u8 * ucp;
-       u64 ull;
 
        if (sb_len < 7)
-               return 0;
+               return false;
        switch (sense_buffer[0] & 0x7f) {
        case 0x70:
        case 0x71:
                if (sense_buffer[0] & 0x80) {
-                       *info_out = (sense_buffer[3] << 24) +
-                                   (sense_buffer[4] << 16) +
-                                   (sense_buffer[5] << 8) + sense_buffer[6];
-                       return 1;
-               } else
-                       return 0;
+                       *info_out = get_unaligned_be32(&sense_buffer[3]);
+                       return true;
+               }
+               return false;
        case 0x72:
        case 0x73:
                ucp = scsi_sense_desc_find(sense_buffer, sb_len,
                                           0 /* info desc */);
                if (ucp && (0xa == ucp[1])) {
-                       ull = 0;
-                       for (j = 0; j < 8; ++j) {
-                               if (j > 0)
-                                       ull <<= 8;
-                               ull |= ucp[4 + j];
-                       }
-                       *info_out = ull;
-                       return 1;
-               } else
-                       return 0;
+                       *info_out = get_unaligned_be64(&ucp[4]);
+                       return true;
+               }
+               return false;
        default:
-               return 0;
+               return false;
        }
 }
 EXPORT_SYMBOL(scsi_get_sense_info_fld);