summaryrefslogtreecommitdiff
path: root/drivers/scsi/scsi_error.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/scsi_error.c')
-rw-r--r--drivers/scsi/scsi_error.c184
1 files changed, 50 insertions, 134 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 2db412dd4b44..ecc07dab893d 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -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);