diff options
author | Hannes Reinecke <hare@suse.de> | 2011-01-18 04:13:11 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2011-02-12 11:33:08 -0500 |
commit | 63583cca745f440167bf27877182dc13e19d4bcf (patch) | |
tree | c1eb3ec5d35cd71c6373e82992710c91f2cb8bdd | |
parent | 7a1e9d829f8bd821466c5ea834ad6f378740d2be (diff) |
[SCSI] Add detailed SCSI I/O errors
Instead of just passing 'EIO' for any I/O error we should be
notifying the upper layers with more details about the cause
of this error.
Update the possible I/O errors to:
- ENOLINK: Link failure between host and target
- EIO: Retryable I/O error
- EREMOTEIO: Non-retryable I/O error
- EBADE: I/O error restricted to the I_T_L nexus
'Retryable' in this context means that an I/O error _might_ be
restricted to the I_T_L nexus (vulgo: path), so retrying on another
nexus / path might succeed.
'Non-retryable' in general refers to a target failure, so this
error will always be generated regardless of the I_T_L nexus
it was send on.
I/O errors restricted to the I_T_L nexus might be retried
on another nexus / path, but they should _not_ be queued
if no paths are available.
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r-- | drivers/scsi/scsi_error.c | 24 | ||||
-rw-r--r-- | drivers/scsi/scsi_lib.c | 28 | ||||
-rw-r--r-- | include/scsi/scsi.h | 5 |
3 files changed, 48 insertions, 9 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 45c75649b9e0..991de3c15cfc 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -223,7 +223,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, | |||
223 | * @scmd: Cmd to have sense checked. | 223 | * @scmd: Cmd to have sense checked. |
224 | * | 224 | * |
225 | * Return value: | 225 | * Return value: |
226 | * SUCCESS or FAILED or NEEDS_RETRY | 226 | * SUCCESS or FAILED or NEEDS_RETRY or TARGET_ERROR |
227 | * | 227 | * |
228 | * Notes: | 228 | * Notes: |
229 | * When a deferred error is detected the current command has | 229 | * When a deferred error is detected the current command has |
@@ -326,17 +326,19 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) | |||
326 | */ | 326 | */ |
327 | return SUCCESS; | 327 | return SUCCESS; |
328 | 328 | ||
329 | /* these three are not supported */ | 329 | /* these are not supported */ |
330 | case COPY_ABORTED: | 330 | case COPY_ABORTED: |
331 | case VOLUME_OVERFLOW: | 331 | case VOLUME_OVERFLOW: |
332 | case MISCOMPARE: | 332 | case MISCOMPARE: |
333 | return SUCCESS; | 333 | case BLANK_CHECK: |
334 | case DATA_PROTECT: | ||
335 | return TARGET_ERROR; | ||
334 | 336 | ||
335 | case MEDIUM_ERROR: | 337 | case MEDIUM_ERROR: |
336 | if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */ | 338 | if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */ |
337 | sshdr.asc == 0x13 || /* AMNF DATA FIELD */ | 339 | sshdr.asc == 0x13 || /* AMNF DATA FIELD */ |
338 | sshdr.asc == 0x14) { /* RECORD NOT FOUND */ | 340 | sshdr.asc == 0x14) { /* RECORD NOT FOUND */ |
339 | return SUCCESS; | 341 | return TARGET_ERROR; |
340 | } | 342 | } |
341 | return NEEDS_RETRY; | 343 | return NEEDS_RETRY; |
342 | 344 | ||
@@ -344,11 +346,9 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) | |||
344 | if (scmd->device->retry_hwerror) | 346 | if (scmd->device->retry_hwerror) |
345 | return ADD_TO_MLQUEUE; | 347 | return ADD_TO_MLQUEUE; |
346 | else | 348 | else |
347 | return SUCCESS; | 349 | return TARGET_ERROR; |
348 | 350 | ||
349 | case ILLEGAL_REQUEST: | 351 | case ILLEGAL_REQUEST: |
350 | case BLANK_CHECK: | ||
351 | case DATA_PROTECT: | ||
352 | default: | 352 | default: |
353 | return SUCCESS; | 353 | return SUCCESS; |
354 | } | 354 | } |
@@ -787,6 +787,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, | |||
787 | case SUCCESS: | 787 | case SUCCESS: |
788 | case NEEDS_RETRY: | 788 | case NEEDS_RETRY: |
789 | case FAILED: | 789 | case FAILED: |
790 | case TARGET_ERROR: | ||
790 | break; | 791 | break; |
791 | case ADD_TO_MLQUEUE: | 792 | case ADD_TO_MLQUEUE: |
792 | rtn = NEEDS_RETRY; | 793 | rtn = NEEDS_RETRY; |
@@ -1469,6 +1470,14 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) | |||
1469 | rtn = scsi_check_sense(scmd); | 1470 | rtn = scsi_check_sense(scmd); |
1470 | if (rtn == NEEDS_RETRY) | 1471 | if (rtn == NEEDS_RETRY) |
1471 | goto maybe_retry; | 1472 | goto maybe_retry; |
1473 | else if (rtn == TARGET_ERROR) { | ||
1474 | /* | ||
1475 | * Need to modify host byte to signal a | ||
1476 | * permanent target failure | ||
1477 | */ | ||
1478 | scmd->result |= (DID_TARGET_FAILURE << 16); | ||
1479 | rtn = SUCCESS; | ||
1480 | } | ||
1472 | /* if rtn == FAILED, we have no sense information; | 1481 | /* if rtn == FAILED, we have no sense information; |
1473 | * returning FAILED will wake the error handler thread | 1482 | * returning FAILED will wake the error handler thread |
1474 | * to collect the sense and redo the decide | 1483 | * to collect the sense and redo the decide |
@@ -1486,6 +1495,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) | |||
1486 | case RESERVATION_CONFLICT: | 1495 | case RESERVATION_CONFLICT: |
1487 | sdev_printk(KERN_INFO, scmd->device, | 1496 | sdev_printk(KERN_INFO, scmd->device, |
1488 | "reservation conflict\n"); | 1497 | "reservation conflict\n"); |
1498 | scmd->result |= (DID_NEXUS_FAILURE << 16); | ||
1489 | return SUCCESS; /* causes immediate i/o error */ | 1499 | return SUCCESS; /* causes immediate i/o error */ |
1490 | default: | 1500 | default: |
1491 | return FAILED; | 1501 | return FAILED; |
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9045c52abd25..8d4ef8efa3cd 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c | |||
@@ -667,6 +667,30 @@ void scsi_release_buffers(struct scsi_cmnd *cmd) | |||
667 | } | 667 | } |
668 | EXPORT_SYMBOL(scsi_release_buffers); | 668 | EXPORT_SYMBOL(scsi_release_buffers); |
669 | 669 | ||
670 | static int __scsi_error_from_host_byte(struct scsi_cmnd *cmd, int result) | ||
671 | { | ||
672 | int error = 0; | ||
673 | |||
674 | switch(host_byte(result)) { | ||
675 | case DID_TRANSPORT_FAILFAST: | ||
676 | error = -ENOLINK; | ||
677 | break; | ||
678 | case DID_TARGET_FAILURE: | ||
679 | cmd->result |= (DID_OK << 16); | ||
680 | error = -EREMOTEIO; | ||
681 | break; | ||
682 | case DID_NEXUS_FAILURE: | ||
683 | cmd->result |= (DID_OK << 16); | ||
684 | error = -EBADE; | ||
685 | break; | ||
686 | default: | ||
687 | error = -EIO; | ||
688 | break; | ||
689 | } | ||
690 | |||
691 | return error; | ||
692 | } | ||
693 | |||
670 | /* | 694 | /* |
671 | * Function: scsi_io_completion() | 695 | * Function: scsi_io_completion() |
672 | * | 696 | * |
@@ -737,7 +761,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) | |||
737 | req->sense_len = len; | 761 | req->sense_len = len; |
738 | } | 762 | } |
739 | if (!sense_deferred) | 763 | if (!sense_deferred) |
740 | error = -EIO; | 764 | error = __scsi_error_from_host_byte(cmd, result); |
741 | } | 765 | } |
742 | 766 | ||
743 | req->resid_len = scsi_get_resid(cmd); | 767 | req->resid_len = scsi_get_resid(cmd); |
@@ -796,7 +820,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) | |||
796 | if (scsi_end_request(cmd, error, good_bytes, result == 0) == NULL) | 820 | if (scsi_end_request(cmd, error, good_bytes, result == 0) == NULL) |
797 | return; | 821 | return; |
798 | 822 | ||
799 | error = -EIO; | 823 | error = __scsi_error_from_host_byte(cmd, result); |
800 | 824 | ||
801 | if (host_byte(result) == DID_RESET) { | 825 | if (host_byte(result) == DID_RESET) { |
802 | /* Third party bus reset or reset for error recovery | 826 | /* Third party bus reset or reset for error recovery |
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 648d23358038..ead8dd054480 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h | |||
@@ -434,6 +434,10 @@ static inline int scsi_is_wlun(unsigned int lun) | |||
434 | * recover the link. Transport class will | 434 | * recover the link. Transport class will |
435 | * retry or fail IO */ | 435 | * retry or fail IO */ |
436 | #define DID_TRANSPORT_FAILFAST 0x0f /* Transport class fastfailed the io */ | 436 | #define DID_TRANSPORT_FAILFAST 0x0f /* Transport class fastfailed the io */ |
437 | #define DID_TARGET_FAILURE 0x10 /* Permanent target failure, do not retry on | ||
438 | * other paths */ | ||
439 | #define DID_NEXUS_FAILURE 0x11 /* Permanent nexus failure, retry on other | ||
440 | * paths might yield different results */ | ||
437 | #define DRIVER_OK 0x00 /* Driver status */ | 441 | #define DRIVER_OK 0x00 /* Driver status */ |
438 | 442 | ||
439 | /* | 443 | /* |
@@ -463,6 +467,7 @@ static inline int scsi_is_wlun(unsigned int lun) | |||
463 | #define TIMEOUT_ERROR 0x2007 | 467 | #define TIMEOUT_ERROR 0x2007 |
464 | #define SCSI_RETURN_NOT_HANDLED 0x2008 | 468 | #define SCSI_RETURN_NOT_HANDLED 0x2008 |
465 | #define FAST_IO_FAIL 0x2009 | 469 | #define FAST_IO_FAIL 0x2009 |
470 | #define TARGET_ERROR 0x200A | ||
466 | 471 | ||
467 | /* | 472 | /* |
468 | * Midlevel queue return values. | 473 | * Midlevel queue return values. |