Move the error handling path out of scsi_io_completion and into an
out of line helper.
Signed-off-by: Christoph Hellwig
---
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;
+ de