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.c74
1 files changed, 52 insertions, 22 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index c67cdcdc3ba8..f869108fd969 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -48,7 +48,7 @@
#include <trace/events/scsi.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/*
* These should *probably* be handled by the host itself.
@@ -61,11 +61,11 @@ static int scsi_eh_try_stu(struct scsi_cmnd *scmd);
static enum scsi_disposition scsi_try_to_abort_cmd(const struct scsi_host_template *,
struct scsi_cmnd *);
-void scsi_eh_wakeup(struct Scsi_Host *shost)
+void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy)
{
lockdep_assert_held(shost->host_lock);
- if (scsi_host_busy(shost) == shost->host_failed) {
+ if (busy == shost->host_failed) {
trace_scsi_eh_wakeup(shost);
wake_up_process(shost->ehandler);
SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost,
@@ -88,7 +88,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {
shost->host_eh_scheduled++;
- scsi_eh_wakeup(shost);
+ scsi_eh_wakeup(shost, scsi_host_busy(shost));
}
spin_unlock_irqrestore(shost->host_lock, flags);
@@ -282,11 +282,12 @@ static void scsi_eh_inc_host_failed(struct rcu_head *head)
{
struct scsi_cmnd *scmd = container_of(head, typeof(*scmd), rcu);
struct Scsi_Host *shost = scmd->device->host;
+ unsigned int busy = scsi_host_busy(shost);
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
shost->host_failed++;
- scsi_eh_wakeup(shost);
+ scsi_eh_wakeup(shost, busy);
spin_unlock_irqrestore(shost->host_lock, flags);
}
@@ -301,6 +302,7 @@ void scsi_eh_scmd_add(struct scsi_cmnd *scmd)
int ret;
WARN_ON_ONCE(!shost->ehandler);
+ WARN_ON_ONCE(!test_bit(SCMD_STATE_INFLIGHT, &scmd->state));
spin_lock_irqsave(shost->host_lock, flags);
if (scsi_host_set_state(shost, SHOST_RECOVERY)) {
@@ -545,6 +547,18 @@ enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd)
scsi_report_sense(sdev, &sshdr);
+ if (sshdr.sense_key == UNIT_ATTENTION) {
+ /*
+ * Increment the counters for Power on/Reset or New Media so
+ * that all ULDs interested in these can see that those have
+ * happened, even if someone else gets the sense data.
+ */
+ if (sshdr.asc == 0x28)
+ atomic_inc(&sdev->ua_new_media_ctr);
+ else if (sshdr.asc == 0x29)
+ atomic_inc(&sdev->ua_por_ctr);
+ }
+
if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY;
@@ -651,7 +665,8 @@ enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd)
* if the device is in the process of becoming ready, we
* should retry.
*/
- if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
+ if ((sshdr.asc == 0x04) &&
+ (sshdr.ascq == 0x01 || sshdr.ascq == 0x0a))
return NEEDS_RETRY;
/*
* if the device is not started, we need to wake
@@ -709,6 +724,13 @@ enum scsi_disposition scsi_check_sense(struct scsi_cmnd *scmd)
return SUCCESS;
case COMPLETED:
+ /*
+ * A command using command duration limits (CDL) with a
+ * descriptor set with policy 0xD may be completed with success
+ * and the sense data DATA CURRENTLY UNAVAILABLE, indicating
+ * that the command was in fact aborted because it exceeded its
+ * duration limit. Never retry these commands.
+ */
if (sshdr.asc == 0x55 && sshdr.ascq == 0x0a) {
set_scsi_ml_byte(scmd, SCSIML_STAT_DL_TIMEOUT);
req->cmd_flags |= REQ_FAILFAST_DEV;
@@ -727,6 +749,9 @@ static void scsi_handle_queue_ramp_up(struct scsi_device *sdev)
const struct scsi_host_template *sht = sdev->host->hostt;
struct scsi_device *tmp_sdev;
+ if (!sdev->budget_map.map)
+ return;
+
if (!sht->track_queue_depth ||
sdev->queue_depth >= sdev->max_queue_depth)
return;
@@ -1152,6 +1177,7 @@ retry:
scsi_log_send(scmd);
scmd->submitter = SUBMITTED_BY_SCSI_ERROR_HANDLER;
+ scmd->flags |= SCMD_LAST;
/*
* Lock sdev->state_mutex to avoid that scsi_device_quiesce() can
@@ -2195,15 +2221,18 @@ void scsi_eh_flush_done_q(struct list_head *done_q)
struct scsi_cmnd *scmd, *next;
list_for_each_entry_safe(scmd, next, done_q, eh_entry) {
+ struct scsi_device *sdev = scmd->device;
+
list_del_init(&scmd->eh_entry);
- if (scsi_device_online(scmd->device) &&
- !scsi_noretry_cmd(scmd) && scsi_cmd_retry_allowed(scmd) &&
- scsi_eh_should_retry_cmd(scmd)) {
+ if (scsi_device_online(sdev) && !scsi_noretry_cmd(scmd) &&
+ scsi_cmd_retry_allowed(scmd) &&
+ scsi_eh_should_retry_cmd(scmd)) {
SCSI_LOG_ERROR_RECOVERY(3,
scmd_printk(KERN_INFO, scmd,
"%s: flush retry cmd\n",
current->comm));
scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
+ blk_mq_kick_requeue_list(sdev->request_queue);
} else {
/*
* If just we got sense for the device (called
@@ -2357,14 +2386,14 @@ int scsi_error_handler(void *data)
return 0;
}
-/*
- * Function: scsi_report_bus_reset()
+/**
+ * scsi_report_bus_reset() - report bus reset observed
*
- * Purpose: Utility function used by low-level drivers to report that
- * they have observed a bus reset on the bus being handled.
+ * Utility function used by low-level drivers to report that
+ * they have observed a bus reset on the bus being handled.
*
- * Arguments: shost - Host in question
- * channel - channel on which reset was observed.
+ * @shost: Host in question
+ * @channel: channel on which reset was observed.
*
* Returns: Nothing
*
@@ -2389,15 +2418,15 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel)
}
EXPORT_SYMBOL(scsi_report_bus_reset);
-/*
- * Function: scsi_report_device_reset()
+/**
+ * scsi_report_device_reset() - report device reset observed
*
- * Purpose: Utility function used by low-level drivers to report that
- * they have observed a device reset on the device being handled.
+ * Utility function used by low-level drivers to report that
+ * they have observed a device reset on the device being handled.
*
- * Arguments: shost - Host in question
- * channel - channel on which reset was observed
- * target - target on which reset was observed
+ * @shost: Host in question
+ * @channel: channel on which reset was observed
+ * @target: target on which reset was observed
*
* Returns: Nothing
*
@@ -2459,6 +2488,7 @@ scsi_ioctl_reset(struct scsi_device *dev, int __user *arg)
scsi_init_command(dev, scmd);
scmd->submitter = SUBMITTED_BY_SCSI_RESET_IOCTL;
+ scmd->flags |= SCMD_LAST;
memset(&scmd->sdb, 0, sizeof(scmd->sdb));
scmd->cmd_len = 0;