summaryrefslogtreecommitdiffstats
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.c128
1 files changed, 93 insertions, 35 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 21505962f539..83e591b60193 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -223,12 +223,80 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
}
#endif
+ /**
+ * scsi_report_lun_change - Set flag on all *other* devices on the same target
+ * to indicate that a UNIT ATTENTION is expected.
+ * @sdev: Device reporting the UNIT ATTENTION
+ */
+static void scsi_report_lun_change(struct scsi_device *sdev)
+{
+ sdev->sdev_target->expecting_lun_change = 1;
+}
+
+/**
+ * scsi_report_sense - Examine scsi sense information and log messages for
+ * certain conditions, also issue uevents for some of them.
+ * @sdev: Device reporting the sense code
+ * @sshdr: sshdr to be examined
+ */
+static void scsi_report_sense(struct scsi_device *sdev,
+ struct scsi_sense_hdr *sshdr)
+{
+ enum scsi_device_event evt_type = SDEV_EVT_MAXBITS; /* i.e. none */
+
+ if (sshdr->sense_key == UNIT_ATTENTION) {
+ if (sshdr->asc == 0x3f && sshdr->ascq == 0x03) {
+ evt_type = SDEV_EVT_INQUIRY_CHANGE_REPORTED;
+ sdev_printk(KERN_WARNING, sdev,
+ "Inquiry data has changed");
+ } else if (sshdr->asc == 0x3f && sshdr->ascq == 0x0e) {
+ evt_type = SDEV_EVT_LUN_CHANGE_REPORTED;
+ scsi_report_lun_change(sdev);
+ sdev_printk(KERN_WARNING, sdev,
+ "Warning! Received an indication that the "
+ "LUN assignments on this target have "
+ "changed. The Linux SCSI layer does not "
+ "automatically remap LUN assignments.\n");
+ } else if (sshdr->asc == 0x3f)
+ sdev_printk(KERN_WARNING, sdev,
+ "Warning! Received an indication that the "
+ "operating parameters on this target have "
+ "changed. The Linux SCSI layer does not "
+ "automatically adjust these parameters.\n");
+
+ if (sshdr->asc == 0x38 && sshdr->ascq == 0x07) {
+ evt_type = SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED;
+ sdev_printk(KERN_WARNING, sdev,
+ "Warning! Received an indication that the "
+ "LUN reached a thin provisioning soft "
+ "threshold.\n");
+ }
+
+ if (sshdr->asc == 0x2a && sshdr->ascq == 0x01) {
+ evt_type = SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED;
+ sdev_printk(KERN_WARNING, sdev,
+ "Mode parameters changed");
+ } else if (sshdr->asc == 0x2a && sshdr->ascq == 0x09) {
+ evt_type = SDEV_EVT_CAPACITY_CHANGE_REPORTED;
+ sdev_printk(KERN_WARNING, sdev,
+ "Capacity data has changed");
+ } else if (sshdr->asc == 0x2a)
+ sdev_printk(KERN_WARNING, sdev,
+ "Parameters changed");
+ }
+
+ if (evt_type != SDEV_EVT_MAXBITS) {
+ set_bit(evt_type, sdev->pending_events);
+ schedule_work(&sdev->event_work);
+ }
+}
+
/**
* scsi_check_sense - Examine scsi cmd sense
* @scmd: Cmd to have sense checked.
*
* Return value:
- * SUCCESS or FAILED or NEEDS_RETRY or TARGET_ERROR
+ * SUCCESS or FAILED or NEEDS_RETRY or ADD_TO_MLQUEUE
*
* Notes:
* When a deferred error is detected the current command has
@@ -250,6 +318,8 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
*/
return SUCCESS;
+ scsi_report_sense(sdev, &sshdr);
+
if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY;
@@ -315,6 +385,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
}
}
/*
+ * we might also expect a cc/ua if another LUN on the target
+ * reported a UA with an ASC/ASCQ of 3F 0E -
+ * REPORTED LUNS DATA HAS CHANGED.
+ */
+ if (scmd->device->sdev_target->expecting_lun_change &&
+ sshdr.asc == 0x3f && sshdr.ascq == 0x0e)
+ return NEEDS_RETRY;
+ /*
* if the device is in the process of becoming ready, we
* should retry.
*/
@@ -327,26 +405,6 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
if (scmd->device->allow_restart &&
(sshdr.asc == 0x04) && (sshdr.ascq == 0x02))
return FAILED;
-
- if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e)
- scmd_printk(KERN_WARNING, scmd,
- "Warning! Received an indication that the "
- "LUN assignments on this target have "
- "changed. The Linux SCSI layer does not "
- "automatically remap LUN assignments.\n");
- else if (sshdr.asc == 0x3f)
- scmd_printk(KERN_WARNING, scmd,
- "Warning! Received an indication that the "
- "operating parameters on this target have "
- "changed. The Linux SCSI layer does not "
- "automatically adjust these parameters.\n");
-
- if (sshdr.asc == 0x38 && sshdr.ascq == 0x07)
- scmd_printk(KERN_WARNING, scmd,
- "Warning! Received an indication that the "
- "LUN reached a thin provisioning soft "
- "threshold.\n");
-
/*
* Pass the UA upwards for a determination in the completion
* functions.
@@ -354,18 +412,25 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
return SUCCESS;
/* these are not supported */
+ case DATA_PROTECT:
+ if (sshdr.asc == 0x27 && sshdr.ascq == 0x07) {
+ /* Thin provisioning hard threshold reached */
+ set_host_byte(scmd, DID_ALLOC_FAILURE);
+ return SUCCESS;
+ }
case COPY_ABORTED:
case VOLUME_OVERFLOW:
case MISCOMPARE:
case BLANK_CHECK:
- case DATA_PROTECT:
- return TARGET_ERROR;
+ set_host_byte(scmd, DID_TARGET_FAILURE);
+ return SUCCESS;
case MEDIUM_ERROR:
if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */
sshdr.asc == 0x13 || /* AMNF DATA FIELD */
sshdr.asc == 0x14) { /* RECORD NOT FOUND */
- return TARGET_ERROR;
+ set_host_byte(scmd, DID_MEDIUM_ERROR);
+ return SUCCESS;
}
return NEEDS_RETRY;
@@ -373,14 +438,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
if (scmd->device->retry_hwerror)
return ADD_TO_MLQUEUE;
else
- return TARGET_ERROR;
+ set_host_byte(scmd, DID_TARGET_FAILURE);
case ILLEGAL_REQUEST:
if (sshdr.asc == 0x20 || /* Invalid command operation code */
sshdr.asc == 0x21 || /* Logical block address out of range */
sshdr.asc == 0x24 || /* Invalid field in cdb */
sshdr.asc == 0x26) { /* Parameter value invalid */
- return TARGET_ERROR;
+ set_host_byte(scmd, DID_TARGET_FAILURE);
}
return SUCCESS;
@@ -843,7 +908,6 @@ retry:
case SUCCESS:
case NEEDS_RETRY:
case FAILED:
- case TARGET_ERROR:
break;
case ADD_TO_MLQUEUE:
rtn = NEEDS_RETRY;
@@ -1568,6 +1632,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
*/
return ADD_TO_MLQUEUE;
case GOOD:
+ if (scmd->cmnd[0] == REPORT_LUNS)
+ scmd->device->sdev_target->expecting_lun_change = 0;
scsi_handle_queue_ramp_up(scmd->device);
case COMMAND_TERMINATED:
return SUCCESS;
@@ -1577,14 +1643,6 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
rtn = scsi_check_sense(scmd);
if (rtn == NEEDS_RETRY)
goto maybe_retry;
- else if (rtn == TARGET_ERROR) {
- /*
- * Need to modify host byte to signal a
- * permanent target failure
- */
- set_host_byte(scmd, DID_TARGET_FAILURE);
- rtn = SUCCESS;
- }
/* if rtn == FAILED, we have no sense information;
* returning FAILED will wake the error handler thread
* to collect the sense and redo the decide
OpenPOWER on IntegriCloud