summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2018-06-23 12:22:17 +0200
committerMartin K. Petersen <martin.petersen@oracle.com>2018-06-26 12:27:06 -0400
commitda32baea17e36b2bd95cf38b07d1297daf1d98cf (patch)
tree21927789e33f033443585ae68dee2dd2ad767c8a /drivers
parentab83108460a2a0d71c5bb22830036c4646e94e63 (diff)
scsi: core: add scsi_io_completion_action helper
Place scsi_io_completion()'s complex error processing associated with a local enumeration into a static helper function. That enumeration's values start with "ACTION_" so use the suffix "_action" in the helper function's name. Signed-off-by: Douglas Gilbert <dgilbert@interlog.com> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Reviewed-by: Bart Van Assche <bart.vanassche@wdc.com> Reviewed-by: Hannes Reinecke <hare@suse.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/scsi_lib.c329
1 files changed, 175 insertions, 154 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 02892408962b..16a7e6846cac 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -761,6 +761,172 @@ static blk_status_t scsi_result_to_blk_status(struct scsi_cmnd *cmd, int result)
}
}
+/* Helper for scsi_io_completion() when special action required. */
+static void scsi_io_completion_action(struct scsi_cmnd *cmd, int result)
+{
+ struct request_queue *q = cmd->device->request_queue;
+ struct request *req = cmd->request;
+ int level = 0;
+ enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY,
+ ACTION_DELAYED_RETRY} action;
+ unsigned long wait_for = (cmd->allowed + 1) * req->timeout;
+ struct scsi_sense_hdr sshdr;
+ bool sense_valid;
+ bool sense_current = true; /* false implies "deferred sense" */
+ blk_status_t blk_stat;
+
+ sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
+ if (sense_valid)
+ sense_current = !scsi_sense_is_deferred(&sshdr);
+
+ blk_stat = scsi_result_to_blk_status(cmd, result);
+
+ if (host_byte(result) == DID_RESET) {
+ /* Third party bus reset or reset for error recovery
+ * reasons. Just retry the command and see what
+ * happens.
+ */
+ action = ACTION_RETRY;
+ } else if (sense_valid && sense_current) {
+ switch (sshdr.sense_key) {
+ case UNIT_ATTENTION:
+ if (cmd->device->removable) {
+ /* Detected disc change. Set a bit
+ * and quietly refuse further access.
+ */
+ cmd->device->changed = 1;
+ action = ACTION_FAIL;
+ } else {
+ /* Must have been a power glitch, or a
+ * bus reset. Could not have been a
+ * media change, so we just retry the
+ * command and see what happens.
+ */
+ action = ACTION_RETRY;
+ }
+ break;
+ case ILLEGAL_REQUEST:
+ /* If we had an ILLEGAL REQUEST returned, then
+ * we may have performed an unsupported
+ * command. The only thing this should be
+ * would be a ten byte read where only a six
+ * byte read was supported. Also, on a system
+ * where READ CAPACITY failed, we may have
+ * read past the end of the disk.
+ */
+ if ((cmd->device->use_10_for_rw &&
+ sshdr.asc == 0x20 && sshdr.ascq == 0x00) &&
+ (cmd->cmnd[0] == READ_10 ||
+ cmd->cmnd[0] == WRITE_10)) {
+ /* This will issue a new 6-byte command. */
+ cmd->device->use_10_for_rw = 0;
+ action = ACTION_REPREP;
+ } else if (sshdr.asc == 0x10) /* DIX */ {
+ action = ACTION_FAIL;
+ blk_stat = BLK_STS_PROTECTION;
+ /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
+ } else if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
+ action = ACTION_FAIL;
+ blk_stat = BLK_STS_TARGET;
+ } else
+ action = ACTION_FAIL;
+ break;
+ case ABORTED_COMMAND:
+ action = ACTION_FAIL;
+ if (sshdr.asc == 0x10) /* DIF */
+ blk_stat = BLK_STS_PROTECTION;
+ break;
+ case NOT_READY:
+ /* If the device is in the process of becoming
+ * ready, or has a temporary blockage, retry.
+ */
+ if (sshdr.asc == 0x04) {
+ switch (sshdr.ascq) {
+ case 0x01: /* becoming ready */
+ case 0x04: /* format in progress */
+ case 0x05: /* rebuild in progress */
+ case 0x06: /* recalculation in progress */
+ case 0x07: /* operation in progress */
+ case 0x08: /* Long write in progress */
+ case 0x09: /* self test in progress */
+ case 0x14: /* space allocation in progress */
+ case 0x1a: /* start stop unit in progress */
+ case 0x1b: /* sanitize in progress */
+ case 0x1d: /* configuration in progress */
+ case 0x24: /* depopulation in progress */
+ action = ACTION_DELAYED_RETRY;
+ break;
+ default:
+ action = ACTION_FAIL;
+ break;
+ }
+ } else
+ action = ACTION_FAIL;
+ break;
+ case VOLUME_OVERFLOW:
+ /* See SSC3rXX or current. */
+ action = ACTION_FAIL;
+ break;
+ default:
+ action = ACTION_FAIL;
+ break;
+ }
+ } else
+ action = ACTION_FAIL;
+
+ if (action != ACTION_FAIL &&
+ time_before(cmd->jiffies_at_alloc + wait_for, jiffies))
+ action = ACTION_FAIL;
+
+ switch (action) {
+ case ACTION_FAIL:
+ /* Give up and fail the remainder of the request */
+ if (!(req->rq_flags & RQF_QUIET)) {
+ static DEFINE_RATELIMIT_STATE(_rs,
+ DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+
+ if (unlikely(scsi_logging_level))
+ level =
+ SCSI_LOG_LEVEL(SCSI_LOG_MLCOMPLETE_SHIFT,
+ SCSI_LOG_MLCOMPLETE_BITS);
+
+ /*
+ * if logging is enabled the failure will be printed
+ * in scsi_log_completion(), so avoid duplicate messages
+ */
+ if (!level && __ratelimit(&_rs)) {
+ scsi_print_result(cmd, NULL, FAILED);
+ if (driver_byte(result) & DRIVER_SENSE)
+ scsi_print_sense(cmd);
+ scsi_print_command(cmd);
+ }
+ }
+ if (!scsi_end_request(req, blk_stat, blk_rq_err_bytes(req), 0))
+ return;
+ /*FALLTHRU*/
+ case ACTION_REPREP:
+ /* Unprep the request and put it back at the head of the queue.
+ * A new command will be prepared and issued.
+ */
+ if (q->mq_ops) {
+ scsi_mq_requeue_cmd(cmd);
+ } else {
+ scsi_release_buffers(cmd);
+ scsi_requeue_command(q, cmd);
+ }
+ break;
+ case ACTION_RETRY:
+ /* Retry the same command immediately */
+ __scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY, false);
+ break;
+ case ACTION_DELAYED_RETRY:
+ /* Retry the same command after a delay */
+ __scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY, false);
+ break;
+ }
+}
+
/*
* Helper for scsi_io_completion() when cmd->result is non-zero. Returns a
* new result that may suppress further error checking. Also modifies
@@ -869,20 +1035,9 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
struct request_queue *q = cmd->device->request_queue;
struct request *req = cmd->request;
blk_status_t blk_stat = BLK_STS_OK;
- struct scsi_sense_hdr sshdr;
- bool sense_valid = false;
- bool sense_current = true; /* false implies "deferred sense" */
- int level = 0;
- enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY,
- ACTION_DELAYED_RETRY} action;
- unsigned long wait_for = (cmd->allowed + 1) * req->timeout;
- if (result) { /* does not necessarily mean there is an error */
- sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
- if (sense_valid)
- sense_current = !scsi_sense_is_deferred(&sshdr);
+ if (result) /* does not necessarily mean there is an error */
result = scsi_io_completion_nz_result(cmd, result, &blk_stat);
- }
if (blk_rq_is_passthrough(req)) {
/*
@@ -936,138 +1091,12 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* If there had been no error, but we have leftover bytes in the
* requeues just queue the command up again.
*/
- if (result == 0)
- goto requeue;
-
- blk_stat = scsi_result_to_blk_status(cmd, result);
-
- if (host_byte(result) == DID_RESET) {
- /* Third party bus reset or reset for error recovery
- * reasons. Just retry the command and see what
- * happens.
- */
- action = ACTION_RETRY;
- } else if (sense_valid && sense_current) {
- switch (sshdr.sense_key) {
- case UNIT_ATTENTION:
- if (cmd->device->removable) {
- /* Detected disc change. Set a bit
- * and quietly refuse further access.
- */
- cmd->device->changed = 1;
- action = ACTION_FAIL;
- } else {
- /* Must have been a power glitch, or a
- * bus reset. Could not have been a
- * media change, so we just retry the
- * command and see what happens.
- */
- action = ACTION_RETRY;
- }
- break;
- case ILLEGAL_REQUEST:
- /* If we had an ILLEGAL REQUEST returned, then
- * we may have performed an unsupported
- * command. The only thing this should be
- * would be a ten byte read where only a six
- * byte read was supported. Also, on a system
- * where READ CAPACITY failed, we may have
- * read past the end of the disk.
- */
- if ((cmd->device->use_10_for_rw &&
- sshdr.asc == 0x20 && sshdr.ascq == 0x00) &&
- (cmd->cmnd[0] == READ_10 ||
- cmd->cmnd[0] == WRITE_10)) {
- /* This will issue a new 6-byte command. */
- cmd->device->use_10_for_rw = 0;
- action = ACTION_REPREP;
- } else if (sshdr.asc == 0x10) /* DIX */ {
- action = ACTION_FAIL;
- blk_stat = BLK_STS_PROTECTION;
- /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
- } else if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
- action = ACTION_FAIL;
- blk_stat = BLK_STS_TARGET;
- } else
- action = ACTION_FAIL;
- break;
- case ABORTED_COMMAND:
- action = ACTION_FAIL;
- if (sshdr.asc == 0x10) /* DIF */
- blk_stat = BLK_STS_PROTECTION;
- break;
- case NOT_READY:
- /* If the device is in the process of becoming
- * ready, or has a temporary blockage, retry.
- */
- if (sshdr.asc == 0x04) {
- switch (sshdr.ascq) {
- case 0x01: /* becoming ready */
- case 0x04: /* format in progress */
- case 0x05: /* rebuild in progress */
- case 0x06: /* recalculation in progress */
- case 0x07: /* operation in progress */
- case 0x08: /* Long write in progress */
- case 0x09: /* self test in progress */
- case 0x14: /* space allocation in progress */
- case 0x1a: /* start stop unit in progress */
- case 0x1b: /* sanitize in progress */
- case 0x1d: /* configuration in progress */
- case 0x24: /* depopulation in progress */
- action = ACTION_DELAYED_RETRY;
- break;
- default:
- action = ACTION_FAIL;
- break;
- }
- } else
- action = ACTION_FAIL;
- break;
- case VOLUME_OVERFLOW:
- /* See SSC3rXX or current. */
- action = ACTION_FAIL;
- break;
- default:
- action = ACTION_FAIL;
- break;
- }
- } else
- action = ACTION_FAIL;
-
- if (action != ACTION_FAIL &&
- time_before(cmd->jiffies_at_alloc + wait_for, jiffies))
- action = ACTION_FAIL;
-
- switch (action) {
- case ACTION_FAIL:
- /* Give up and fail the remainder of the request */
- if (!(req->rq_flags & RQF_QUIET)) {
- static DEFINE_RATELIMIT_STATE(_rs,
- DEFAULT_RATELIMIT_INTERVAL,
- DEFAULT_RATELIMIT_BURST);
-
- if (unlikely(scsi_logging_level))
- level = SCSI_LOG_LEVEL(SCSI_LOG_MLCOMPLETE_SHIFT,
- SCSI_LOG_MLCOMPLETE_BITS);
-
- /*
- * if logging is enabled the failure will be printed
- * in scsi_log_completion(), so avoid duplicate messages
- */
- if (!level && __ratelimit(&_rs)) {
- scsi_print_result(cmd, NULL, FAILED);
- if (driver_byte(result) & DRIVER_SENSE)
- scsi_print_sense(cmd);
- scsi_print_command(cmd);
- }
- }
- if (!scsi_end_request(req, blk_stat, blk_rq_err_bytes(req), 0))
- return;
- /*FALLTHRU*/
- case ACTION_REPREP:
- requeue:
- /* Unprep the request and put it back at the head of the queue.
- * A new command will be prepared and issued.
+ if (result == 0) {
+ /*
+ * Unprep the request and put it back at the head of the
+ * queue. A new command will be prepared and issued.
+ * This block is the same as case ACTION_REPREP in
+ * scsi_io_completion_action() above.
*/
if (q->mq_ops) {
scsi_mq_requeue_cmd(cmd);
@@ -1075,16 +1104,8 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
scsi_release_buffers(cmd);
scsi_requeue_command(q, cmd);
}
- break;
- case ACTION_RETRY:
- /* Retry the same command immediately */
- __scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY, false);
- break;
- case ACTION_DELAYED_RETRY:
- /* Retry the same command after a delay */
- __scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY, false);
- break;
- }
+ } else
+ scsi_io_completion_action(cmd, result);
}
static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb)