aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Cox <alan@redhat.com>2004-08-17 17:51:52 -0400
committerJeff Garzik <jgarzik@pobox.com>2004-08-17 17:51:52 -0400
commitd0ecdda0ff01a79dcf67130d48954483978243c6 (patch)
tree3a7765720fa781166c083b7d4200deca8c92d02a /drivers
parent19b4c542b88d3c30b019f9b43e977fe6399e7d6d (diff)
downloadhistory-d0ecdda0ff01a79dcf67130d48954483978243c6.tar.gz
[libata] improve translation of ATA errors to SCSI sense codes
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/libata-scsi.c158
-rw-r--r--drivers/scsi/libata.h2
2 files changed, 142 insertions, 18 deletions
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index a01ed8974d2208..aa9011bb7aa080 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -160,34 +160,158 @@ struct ata_queued_cmd *ata_scsi_qc_new(struct ata_port *ap,
* ata_to_sense_error - convert ATA error to SCSI error
* @qc: Command that we are erroring out
*
- * Converts an ATA error into a SCSI error.
- *
- * Right now, this routine is laughably primitive. We
- * don't even examine what ATA told us, we just look at
- * the command data direction, and return a fatal SCSI
- * sense error based on that.
+ * Converts an ATA error into a SCSI error. While we are at it
+ * we decode and dump the ATA error for the user so that they
+ * have some idea what really happened at the non make-believe
+ * layer.
*
* LOCKING:
* spin_lock_irqsave(host_set lock)
*/
-void ata_to_sense_error(struct ata_queued_cmd *qc)
+void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat)
{
struct scsi_cmnd *cmd = qc->scsicmd;
+ u8 err = 0;
+ unsigned char *sb = cmd->sense_buffer;
+ /* Based on the 3ware driver translation table */
+ static unsigned char sense_table[][4] = {
+ /* BBD|ECC|ID|MAR */
+ {0xd1, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command
+ /* BBD|ECC|ID */
+ {0xd0, ABORTED_COMMAND, 0x00, 0x00}, // Device busy Aborted command
+ /* ECC|MC|MARK */
+ {0x61, HARDWARE_ERROR, 0x00, 0x00}, // Device fault Hardware error
+ /* ICRC|ABRT */ /* NB: ICRC & !ABRT is BBD */
+ {0x84, ABORTED_COMMAND, 0x47, 0x00}, // Data CRC error SCSI parity error
+ /* MC|ID|ABRT|TRK0|MARK */
+ {0x37, NOT_READY, 0x04, 0x00}, // Unit offline Not ready
+ /* MCR|MARK */
+ {0x09, NOT_READY, 0x04, 0x00}, // Unrecovered disk error Not ready
+ /* Bad address mark */
+ {0x01, MEDIUM_ERROR, 0x13, 0x00}, // Address mark not found Address mark not found for data field
+ /* TRK0 */
+ {0x02, HARDWARE_ERROR, 0x00, 0x00}, // Track 0 not found Hardware error
+ /* Abort & !ICRC */
+ {0x04, ABORTED_COMMAND, 0x00, 0x00}, // Aborted command Aborted command
+ /* Media change request */
+ {0x08, NOT_READY, 0x04, 0x00}, // Media change request FIXME: faking offline
+ /* SRV */
+ {0x10, ABORTED_COMMAND, 0x14, 0x00}, // ID not found Recorded entity not found
+ /* Media change */
+ {0x08, NOT_READY, 0x04, 0x00}, // Media change FIXME: faking offline
+ /* ECC */
+ {0x40, MEDIUM_ERROR, 0x11, 0x04}, // Uncorrectable ECC error Unrecovered read error
+ /* BBD - block marked bad */
+ {0x80, MEDIUM_ERROR, 0x11, 0x04}, // Block marked bad Medium error, unrecovered read error
+ {0xFF, 0xFF, 0xFF, 0xFF}, // END mark
+ };
+ static unsigned char stat_table[][4] = {
+ /* Must be first because BUSY means no other bits valid */
+ {0x80, ABORTED_COMMAND, 0x47, 0x00}, // Busy, fake parity for now
+ {0x20, HARDWARE_ERROR, 0x00, 0x00}, // Device fault
+ {0x08, ABORTED_COMMAND, 0x47, 0x00}, // Timed out in xfer, fake parity for now
+ {0x04, RECOVERED_ERROR, 0x11, 0x00}, // Recovered ECC error Medium error, recovered
+ {0xFF, 0xFF, 0xFF, 0xFF}, // END mark
+ };
+ int i = 0;
cmd->result = SAM_STAT_CHECK_CONDITION;
-
- cmd->sense_buffer[0] = 0x70;
- cmd->sense_buffer[2] = MEDIUM_ERROR;
- cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */
-
+
+ /*
+ * Is this an error we can process/parse
+ */
+
+ if(drv_stat & ATA_ERR)
+ /* Read the err bits */
+ err = ata_chk_err(qc->ap);
+
+ /* Display the ATA level error info */
+
+ printk(KERN_WARNING "ata%u: status=0x%02x { ", qc->ap->id, drv_stat);
+ if(drv_stat & 0x80)
+ {
+ printk("Busy ");
+ err = 0; /* Data is not valid in this case */
+ }
+ else {
+ if(drv_stat & 0x40) printk("DriveReady ");
+ if(drv_stat & 0x20) printk("DeviceFault ");
+ if(drv_stat & 0x10) printk("SeekComplete ");
+ if(drv_stat & 0x08) printk("DataRequest ");
+ if(drv_stat & 0x04) printk("CorrectedError ");
+ if(drv_stat & 0x02) printk("Index ");
+ if(drv_stat & 0x01) printk("Error ");
+ }
+ printk("}\n");
+
+ if(err)
+ {
+ printk(KERN_WARNING "ata%u: error=0x%02x { ", qc->ap->id, err);
+ if(err & 0x04) printk("DriveStatusError ");
+ if(err & 0x80)
+ {
+ if(err & 0x04)
+ printk("BadCRC ");
+ else
+ printk("Sector ");
+ }
+ if(err & 0x40) printk("UncorrectableError ");
+ if(err & 0x10) printk("SectorIdNotFound ");
+ if(err & 0x02) printk("TrackZeroNotFound ");
+ if(err & 0x01) printk("AddrMarkNotFound ");
+ printk("}\n");
+
+ /* Should we dump sector info here too ?? */
+ }
+
+
+ /* Look for err */
+ while(sense_table[i][0] != 0xFF)
+ {
+ /* Look for best matches first */
+ if((sense_table[i][0] & err) == sense_table[i][0])
+ {
+ sb[0] = 0x70;
+ sb[2] = sense_table[i][1];
+ sb[7] = 0x0a;
+ sb[12] = sense_table[i][2];
+ sb[13] = sense_table[i][3];
+ return;
+ }
+ i++;
+ }
+ /* No immediate match */
+ if(err)
+ printk(KERN_DEBUG "ata%u: no sense translation for 0x%02x\n", qc->ap->id, err);
+
+ /* Fall back to interpreting status bits */
+ while(stat_table[i][0] != 0xFF)
+ {
+ if(stat_table[i][0] & drv_stat)
+ {
+ sb[0] = 0x70;
+ sb[2] = stat_table[i][1];
+ sb[7] = 0x0a;
+ sb[12] = stat_table[i][2];
+ sb[13] = stat_table[i][3];
+ return;
+ }
+ i++;
+ }
+ /* No error ?? */
+ printk(KERN_ERR "ata%u: called with no error (%02X)!\n", qc->ap->id, drv_stat);
/* additional-sense-code[-qualifier] */
+
+ sb[0] = 0x70;
+ sb[2] = MEDIUM_ERROR;
+ sb[7] = 0x0A;
if (cmd->sc_data_direction == SCSI_DATA_READ) {
- cmd->sense_buffer[12] = 0x11; /* "unrecovered read error" */
- cmd->sense_buffer[13] = 0x04;
+ sb[12] = 0x11; /* "unrecovered read error" */
+ sb[13] = 0x04;
} else {
- cmd->sense_buffer[12] = 0x0C; /* "write error - */
- cmd->sense_buffer[13] = 0x02; /* auto-reallocation failed" */
+ sb[12] = 0x0C; /* "write error - */
+ sb[13] = 0x02; /* auto-reallocation failed" */
}
}
@@ -500,7 +624,7 @@ static int ata_scsi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat)
struct scsi_cmnd *cmd = qc->scsicmd;
if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ)))
- ata_to_sense_error(qc);
+ ata_to_sense_error(qc, drv_stat);
else
cmd->result = SAM_STAT_GOOD;
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index c09f953d749af7..68c268e41e4572 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -46,7 +46,7 @@ extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
/* libata-scsi.c */
-extern void ata_to_sense_error(struct ata_queued_cmd *qc);
+extern void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat);
extern int ata_scsi_error(struct Scsi_Host *host);
extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen);