Commit 3123e8db authored by James Bottomley's avatar James Bottomley Committed by James Bottomley

scsi: normalize fixed and descriptor sense data

From: Douglas Gilbert <dougg@torque.net>

The patch only touches two files: scsi.h and scsi_lib.c
It adds the proposed facility and then uses it in scsi_lib
in roughly 4 locations. IMO there were 3 sense processing errors:
   - block SG_IO did not get passed back deferred errors
     [SG_IO is a __pass-through__ interface!!]
   - MEDIUM_ERRORs _do_ get processed for deferred sense errors
     in scsi_io_completion() which seems unintended
     [I did not fix this one.]
   - invalid command operation code handling in
     __scsi_mode_sense() was just wrong

If people think this is a reasonable approach, then the rest of
the scsi mid-level and upper level driver could be converted.
As Kai pointed we may need some general routines to pick up the
sense data "extras".

The benefit of doing this conversion is that it may well
highlight a lot more sense data processing errors (if the
above is any guide).

Changes:
    - add structure to receive normalized sense data from either
      fixed or descriptor format sense data
    - add scsi_normalize_sense() function to populate that structure
    - add scsi_sense_is_deferred() function so deferred errors can
      be ignored in many contexts
    - apply new mechanism to sense data processing within the
      scsi_lib.c file

Patch reformatted by: Christoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent a4d56cc4
...@@ -1843,3 +1843,79 @@ scsi_reset_provider(struct scsi_device *dev, int flag) ...@@ -1843,3 +1843,79 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
scsi_next_command(scmd); scsi_next_command(scmd);
return rtn; return rtn;
} }
/**
* scsi_normalize_sense - normalize main elements from either fixed or
* descriptor sense data format into a common format.
*
* @sense_buffer: byte array containing sense data returned by device
* @sb_len: number of valid bytes in sense_buffer
* @sshdr: pointer to instance of structure that common
* elements are written to.
*
* Notes:
* The "main elements" from sense data are: response_code, sense_key,
* asc, ascq and additional_length (only for descriptor format).
*
* Typically this function can be called after a device has
* responded to a SCSI command with the CHECK_CONDITION status.
*
* Return value:
* 1 if valid sense data information found, else 0;
**/
int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
struct scsi_sense_hdr *sshdr)
{
if (!sense_buffer || !sb_len || (sense_buffer[0] & 0x70) != 0x70)
return 0;
memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
sshdr->response_code = (sense_buffer[0] & 0x7f);
if (sshdr->response_code >= 0x72) {
/*
* descriptor format
*/
if (sb_len > 1)
sshdr->sense_key = (sense_buffer[1] & 0xf);
if (sb_len > 2)
sshdr->asc = sense_buffer[2];
if (sb_len > 3)
sshdr->ascq = sense_buffer[3];
if (sb_len > 7)
sshdr->additional_length = sense_buffer[7];
} else {
/*
* fixed format
*/
if (sb_len > 2)
sshdr->sense_key = (sense_buffer[2] & 0xf);
if (sb_len > 7) {
sb_len = (sb_len < (sense_buffer[7] + 8)) ?
sb_len : (sense_buffer[7] + 8);
if (sb_len > 12)
sshdr->asc = sense_buffer[12];
if (sb_len > 13)
sshdr->ascq = sense_buffer[13];
}
}
return 1;
}
EXPORT_SYMBOL(scsi_normalize_sense);
int scsi_request_normalize_sense(struct scsi_request *sreq,
struct scsi_sense_hdr *sshdr)
{
return scsi_normalize_sense(sreq->sr_sense_buffer,
sizeof(sreq->sr_sense_buffer), sshdr);
}
EXPORT_SYMBOL(scsi_request_normalize_sense);
int scsi_command_normalize_sense(struct scsi_cmnd *cmd,
struct scsi_sense_hdr *sshdr)
{
return scsi_normalize_sense(cmd->sense_buffer,
sizeof(cmd->sense_buffer), sshdr);
}
EXPORT_SYMBOL(scsi_command_normalize_sense);
...@@ -692,6 +692,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, ...@@ -692,6 +692,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
request_queue_t *q = cmd->device->request_queue; request_queue_t *q = cmd->device->request_queue;
struct request *req = cmd->request; struct request *req = cmd->request;
int clear_errors = 1; int clear_errors = 1;
struct scsi_sense_hdr sshdr;
/* /*
* Free up any indirection buffers we allocated for DMA purposes. * Free up any indirection buffers we allocated for DMA purposes.
...@@ -715,7 +716,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, ...@@ -715,7 +716,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
(CHECK_CONDITION << 1) : (result & 0xff); (CHECK_CONDITION << 1) : (result & 0xff);
if (result) { if (result) {
clear_errors = 0; clear_errors = 0;
if (cmd->sense_buffer[0] & 0x70) { if (scsi_command_normalize_sense(cmd, &sshdr)) {
/*
* SG_IO wants to know about deferred errors
*/
int len = 8 + cmd->sense_buffer[7]; int len = 8 + cmd->sense_buffer[7];
if (len > SCSI_SENSE_BUFFERSIZE) if (len > SCSI_SENSE_BUFFERSIZE)
...@@ -774,17 +778,17 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, ...@@ -774,17 +778,17 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
* can choose a block to remap, etc. * can choose a block to remap, etc.
*/ */
if (driver_byte(result) != 0) { if (driver_byte(result) != 0) {
if ((cmd->sense_buffer[0] & 0x7f) == 0x70) { if (scsi_command_normalize_sense(cmd, &sshdr) &&
!scsi_sense_is_deferred(&sshdr)) {
/* /*
* If the device is in the process of becoming ready, * If the device is in the process of becoming ready,
* retry. * retry.
*/ */
if (cmd->sense_buffer[12] == 0x04 && if (sshdr.asc == 0x04 && sshdr.ascq == 0x01) {
cmd->sense_buffer[13] == 0x01) {
scsi_requeue_command(q, cmd); scsi_requeue_command(q, cmd);
return; return;
} }
if ((cmd->sense_buffer[2] & 0xf) == UNIT_ATTENTION) { if (sshdr.sense_key == UNIT_ATTENTION) {
if (cmd->device->removable) { if (cmd->device->removable) {
/* detected disc change. set a bit /* detected disc change. set a bit
* and quietly refuse further access. * and quietly refuse further access.
...@@ -813,7 +817,11 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, ...@@ -813,7 +817,11 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
* failed, we may have read past the end of the disk. * failed, we may have read past the end of the disk.
*/ */
switch (cmd->sense_buffer[2]) { /*
* XXX: Following is probably broken since deferred errors
* fall through [dpg 20040827]
*/
switch (sshdr.sense_key) {
case ILLEGAL_REQUEST: case ILLEGAL_REQUEST:
if (cmd->device->use_10_for_rw && if (cmd->device->use_10_for_rw &&
(cmd->cmnd[0] == READ_10 || (cmd->cmnd[0] == READ_10 ||
...@@ -835,8 +843,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, ...@@ -835,8 +843,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
req->rq_disk ? req->rq_disk->disk_name : ""); req->rq_disk ? req->rq_disk->disk_name : "");
cmd = scsi_end_request(cmd, 0, this_count, 1); cmd = scsi_end_request(cmd, 0, this_count, 1);
return; return;
break;
case MEDIUM_ERROR:
case VOLUME_OVERFLOW: case VOLUME_OVERFLOW:
printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ", printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ",
cmd->device->host->host_no, (int) cmd->device->channel, cmd->device->host->host_no, (int) cmd->device->channel,
...@@ -1496,8 +1502,7 @@ __scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage, ...@@ -1496,8 +1502,7 @@ __scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage,
} }
sreq->sr_cmd_len = 0; sreq->sr_cmd_len = 0;
sreq->sr_sense_buffer[0] = 0; memset(sreq->sr_sense_buffer, 0, sizeof(sreq->sr_sense_buffer));
sreq->sr_sense_buffer[2] = 0;
sreq->sr_data_direction = DMA_FROM_DEVICE; sreq->sr_data_direction = DMA_FROM_DEVICE;
memset(buffer, 0, len); memset(buffer, 0, len);
...@@ -1508,15 +1513,22 @@ __scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage, ...@@ -1508,15 +1513,22 @@ __scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage,
* ILLEGAL REQUEST sense return identifies the actual command * ILLEGAL REQUEST sense return identifies the actual command
* byte as the problem. MODE_SENSE commands can return * byte as the problem. MODE_SENSE commands can return
* ILLEGAL REQUEST if the code page isn't supported */ * ILLEGAL REQUEST if the code page isn't supported */
if (use_10_for_ms && ! scsi_status_is_good(sreq->sr_result) &&
(driver_byte(sreq->sr_result) & DRIVER_SENSE) && if (use_10_for_ms && !scsi_status_is_good(sreq->sr_result) &&
sreq->sr_sense_buffer[2] == ILLEGAL_REQUEST && (driver_byte(sreq->sr_result) & DRIVER_SENSE)) {
(sreq->sr_sense_buffer[4] & 0x40) == 0x40 && struct scsi_sense_hdr sshdr;
sreq->sr_sense_buffer[5] == 0 &&
sreq->sr_sense_buffer[6] == 0 ) { if (scsi_request_normalize_sense(sreq, &sshdr)) {
if ((sshdr.sense_key == ILLEGAL_REQUEST) &&
(sshdr.asc == 0x20) && (sshdr.ascq == 0)) {
/*
* Invalid command operation code
*/
sreq->sr_device->use_10_for_ms = 0; sreq->sr_device->use_10_for_ms = 0;
goto retry; goto retry;
} }
}
}
if(scsi_status_is_good(sreq->sr_result)) { if(scsi_status_is_good(sreq->sr_result)) {
data->header_length = header_length; data->header_length = header_length;
...@@ -1739,4 +1751,3 @@ scsi_device_resume(struct scsi_device *sdev) ...@@ -1739,4 +1751,3 @@ scsi_device_resume(struct scsi_device *sdev)
scsi_run_queue(sdev->request_queue); scsi_run_queue(sdev->request_queue);
} }
EXPORT_SYMBOL(scsi_device_resume); EXPORT_SYMBOL(scsi_device_resume);
...@@ -3,14 +3,47 @@ ...@@ -3,14 +3,47 @@
struct scsi_cmnd; struct scsi_cmnd;
struct scsi_device; struct scsi_device;
struct scsi_request;
struct Scsi_Host; struct Scsi_Host;
/*
* This is a slightly modified SCSI sense "descriptor" format header.
* The addition is to allow the 0x70 and 0x71 response codes. The idea
* is to place the salient data from either "fixed" or "descriptor" sense
* format into one structure to ease application processing.
*
* The original sense buffer should be kept around for those cases
* in which more information is required (e.g. the LBA of a MEDIUM ERROR).
*/
struct scsi_sense_hdr { /* See SPC-3 section 4.5 */
u8 response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
u8 sense_key;
u8 asc;
u8 ascq;
u8 byte4;
u8 byte5;
u8 byte6;
u8 additional_length; /* always 0 for fixed sense format */
};
extern void scsi_add_timer(struct scsi_cmnd *, int, extern void scsi_add_timer(struct scsi_cmnd *, int,
void (*)(struct scsi_cmnd *)); void (*)(struct scsi_cmnd *));
extern int scsi_delete_timer(struct scsi_cmnd *); extern int scsi_delete_timer(struct scsi_cmnd *);
extern void scsi_report_bus_reset(struct Scsi_Host *, int); extern void scsi_report_bus_reset(struct Scsi_Host *, int);
extern void scsi_report_device_reset(struct Scsi_Host *, int, int); extern void scsi_report_device_reset(struct Scsi_Host *, int, int);
extern int scsi_block_when_processing_errors(struct scsi_device *); extern int scsi_block_when_processing_errors(struct scsi_device *);
extern int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
struct scsi_sense_hdr *sshdr);
extern int scsi_request_normalize_sense(struct scsi_request *sreq,
struct scsi_sense_hdr *sshdr);
extern int scsi_command_normalize_sense(struct scsi_cmnd *cmd,
struct scsi_sense_hdr *sshdr);
static inline int scsi_sense_is_deferred(struct scsi_sense_hdr *sshdr)
{
return ((sshdr->response_code >= 0x70) && (sshdr->response_code & 1));
}
/* /*
* Reset request from external source * Reset request from external source
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment