Move the error handling path out of scsi_io_completion and into an
out of line helper.

Signed-off-by: Christoph Hellwig <h...@lst.de>
---
 drivers/scsi/scsi_lib.c | 263 +++++++++++++++++++++++++-----------------------
 1 file changed, 136 insertions(+), 127 deletions(-)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 2221bf1..cc5d404 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -742,6 +742,132 @@ static int __scsi_error_from_host_byte(struct scsi_cmnd 
*cmd, int result)
        return error;
 }
 
+static noinline bool
+scsi_handle_ioerror(struct scsi_cmnd *cmd, int result,
+               struct scsi_sense_hdr *sshdr)
+{
+       struct request *req = cmd->request;
+       unsigned long wait_for = (cmd->allowed + 1) * req->timeout;
+       int error = 0;
+       enum {
+               ACTION_FAIL,
+               ACTION_REPREP,
+               ACTION_RETRY,
+               ACTION_DELAYED_RETRY,
+       } action = ACTION_FAIL;
+
+       error = __scsi_error_from_host_byte(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 (sshdr) {
+               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;
+                       } 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 */ {
+                               error = -EILSEQ;
+                       /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
+                       } else if (sshdr->asc == 0x20 || sshdr->asc == 0x24) {
+                               error = -EREMOTEIO;
+                       }
+                       break;
+               case ABORTED_COMMAND:
+                       if (sshdr->asc == 0x10) /* DIF */
+                               error = -EILSEQ;
+                       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 */
+                                       action = ACTION_DELAYED_RETRY;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+                       break;
+               case VOLUME_OVERFLOW:
+                       /* See SSC3rXX or current. */
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       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->cmd_flags & REQ_QUIET)) {
+                       scsi_print_result(cmd);
+                       if (driver_byte(result) & DRIVER_SENSE)
+                               scsi_print_sense("", cmd);
+                       scsi_print_command(cmd);
+               }
+               if (!scsi_end_request(req, error, blk_rq_err_bytes(req), 0))
+                       break;
+               /*FALLTHRU*/
+       case ACTION_REPREP:
+               return false;
+       case ACTION_RETRY:
+               /* Retry the same command immediately */
+               __scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY, 0);
+               break;
+       case ACTION_DELAYED_RETRY:
+               /* Retry the same command after a delay */
+               __scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY, 0);
+               break;
+       }
+
+       return true;
+}
+
 /*
  * Function:    scsi_io_completion()
  *
@@ -779,9 +905,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int 
good_bytes)
        struct scsi_sense_hdr sshdr;
        int sense_valid = 0;
        int sense_deferred = 0;
-       enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY,
-             ACTION_DELAYED_RETRY} action;
-       unsigned long wait_for = (cmd->allowed + 1) * req->timeout;
 
        if (result) {
                sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
@@ -867,7 +990,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int 
good_bytes)
        /*
         * If we finished all bytes in the request we are done now.
         */
-       if (!scsi_end_request(req, error, good_bytes, 0))
+       if (likely(!scsi_end_request(req, error, good_bytes, 0)))
                return;
 
        /*
@@ -880,132 +1003,18 @@ 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.
+        * Try to handle errors if we got a non-zero result.
         */
-       if (result == 0)
-               goto requeue;
-
-       error = __scsi_error_from_host_byte(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_deferred) {
-               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;
-                               error = -EILSEQ;
-                       /* INVALID COMMAND OPCODE or INVALID FIELD IN CDB */
-                       } else if (sshdr.asc == 0x20 || sshdr.asc == 0x24) {
-                               action = ACTION_FAIL;
-                               error = -EREMOTEIO;
-                       } else
-                               action = ACTION_FAIL;
-                       break;
-               case ABORTED_COMMAND:
-                       action = ACTION_FAIL;
-                       if (sshdr.asc == 0x10) /* DIF */
-                               error = -EILSEQ;
-                       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 */
-                                       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->cmd_flags & REQ_QUIET)) {
-                       scsi_print_result(cmd);
-                       if (driver_byte(result) & DRIVER_SENSE)
-                               scsi_print_sense("", cmd);
-                       scsi_print_command(cmd);
-               }
-               if (!scsi_end_request(req, error, blk_rq_err_bytes(req), 0))
+       if (result != 0) {
+               if (scsi_handle_ioerror(cmd, result,
+                               sense_valid && !sense_deferred ? &sshdr : NULL))
                        return;
-               /*FALLTHRU*/
-       case ACTION_REPREP:
-       requeue:
-               scsi_requeue_command(cmd);
-               break;
-       case ACTION_RETRY:
-               /* Retry the same command immediately */
-               __scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY, 0);
-               break;
-       case ACTION_DELAYED_RETRY:
-               /* Retry the same command after a delay */
-               __scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY, 0);
-               break;
        }
+
+       /*
+        * Queue up the leftovers again.
+        */
+       scsi_requeue_command(cmd);
 }
 
 static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb)
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to