aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorSøren Schmidt <sos@FreeBSD.org>1999-11-29 12:24:51 +0000
committerSøren Schmidt <sos@FreeBSD.org>1999-11-29 12:24:51 +0000
commit60c94e7628c68bef298a3806cabc9b068c1352b2 (patch)
tree24ea7dd4bd4d5e96609bba4c54c84983559b9bee /sys/dev
parent8f04f6c729c7859869de928d7c592d0a6fc72700 (diff)
downloadsrc-60c94e7628c68bef298a3806cabc9b068c1352b2.tar.gz
src-60c94e7628c68bef298a3806cabc9b068c1352b2.zip
Better error handeling:
On UDMA CRC errors retry operation as it might be a fluke, if not fall back to PIO mode on the failing drive. If you get alot of these your cabeling is most likely not good enough. On HARD error using DMA, retry once using PIO, if it succeds using PIO fall back to PIO mode on the failing drive.
Notes
Notes: svn path=/head/; revision=53882
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ata/ata-disk.c87
-rw-r--r--sys/dev/ata/ata-disk.h1
2 files changed, 58 insertions, 30 deletions
diff --git a/sys/dev/ata/ata-disk.c b/sys/dev/ata/ata-disk.c
index 3de82054cfe6..50c0476816c3 100644
--- a/sys/dev/ata/ata-disk.c
+++ b/sys/dev/ata/ata-disk.c
@@ -90,7 +90,7 @@ static struct intr_config_hook *ad_attach_hook;
MALLOC_DEFINE(M_AD, "AD driver", "ATA disk driver");
/* defines */
-#define AD_MAX_RETRIES 5
+#define AD_MAX_RETRIES 3
static __inline int
apiomode(struct ata_params *ap)
@@ -435,7 +435,7 @@ ad_transfer(struct ad_request *request)
request->timeout_handle.callout = NULL;
else
request->timeout_handle =
- timeout((timeout_t*)ad_timeout, request, 3*hz);
+ timeout((timeout_t*)ad_timeout, request, 5*hz);
/* setup transfer parameters */
count = howmany(request->bytecount, DEV_BSIZE);
@@ -464,7 +464,8 @@ ad_transfer(struct ad_request *request)
/* does this drive & transfer work with DMA ? */
request->flags &= ~AR_F_DMA_USED;
if ((adp->flags & AD_F_DMA_ENABLED) &&
- !ata_dmasetup(adp->controller, adp->unit,
+ !(request->flags & AR_F_FORCE_PIO) &&
+ !ata_dmasetup(adp->controller, adp->unit,
(void *)request->data, request->bytecount,
(request->flags & AR_F_READ))) {
request->flags |= AR_F_DMA_USED;
@@ -477,8 +478,8 @@ ad_transfer(struct ad_request *request)
else
cmd = request->flags & AR_F_READ ? ATA_C_READ : ATA_C_WRITE;
- ata_command(adp->controller, adp->unit, cmd, cylinder, head,
- sector, count, 0, ATA_IMMEDIATE);
+ ata_command(adp->controller, adp->unit, cmd,
+ cylinder, head, sector, count, 0, ATA_IMMEDIATE);
}
/* if this is a DMA transaction start it, return and wait for interrupt */
@@ -503,9 +504,8 @@ ad_transfer(struct ad_request *request)
/* ready to write PIO data ? */
if (ata_wait(adp->controller, adp->unit,
- ATA_S_READY | ATA_S_DSC | ATA_S_DRQ) < 0) {
+ (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) < 0)
printf("ad_transfer: timeout waiting for DRQ");
- }
/* output the data */
#ifdef ATA_16BIT_ONLY
@@ -536,32 +536,51 @@ ad_interrupt(struct ad_request *request)
/* get drive status */
if (ata_wait(adp->controller, adp->unit, 0) < 0)
printf("ad_interrupt: timeout waiting for status");
- if (adp->controller->status & (ATA_S_ERROR | ATA_S_CORR) ||
+
+ if (adp->controller->status & ATA_S_CORR)
+ printf("ad%d: soft error ECC corrected\n", adp->lun);
+
+ if ((adp->controller->status & ATA_S_ERROR) ||
(request->flags & AR_F_DMA_USED && dma_stat != ATA_BMSTAT_INTERRUPT)) {
oops:
- if (adp->controller->status & ATA_S_ERROR) {
- printf("ad%d: %s %s error blk# %d", adp->lun,
- (adp->controller->error & ATA_E_ICRC) ? "UDMA CRC" : "HARD",
- (request->flags & AR_F_READ) ? "READ" : "WRITE",
- request->blockaddr + (request->donecount / DEV_BSIZE));
- /* if this is a UDMA CRC error, reinject request */
- if (adp->controller->error & ATA_E_ICRC &&
- request->retries++ < AD_MAX_RETRIES) {
- /* disarm timeout for this transfer */
- untimeout((timeout_t *)ad_timeout, request,
- request->timeout_handle);
- TAILQ_INSERT_HEAD(&adp->controller->ata_queue, request, chain);
+ printf("ad%d: %s %s ERROR blk# %d", adp->lun,
+ (adp->controller->error & ATA_E_ICRC) ? "UDMA CRC" : "HARD",
+ (request->flags & AR_F_READ) ? "READ" : "WRITE",
+ request->blockaddr + (request->donecount / DEV_BSIZE));
+
+ /* if this is a UDMA CRC error, reinject request */
+ if (adp->controller->error & ATA_E_ICRC) {
+ untimeout((timeout_t *)ad_timeout, request,request->timeout_handle);
+
+ if (request->retries++ < AD_MAX_RETRIES)
printf(" retrying\n");
- return ATA_OP_FINISHED;
- }
else {
- printf(" status=%02x error=%02x\n",
- adp->controller->status, adp->controller->error);
- request->flags |= AR_F_ERROR;
+ adp->flags &= ~AD_F_DMA_ENABLED;
+ printf(" falling back to PIO mode\n");
}
+ TAILQ_INSERT_HEAD(&adp->controller->ata_queue, request, chain);
+ return ATA_OP_FINISHED;
}
- else if (adp->controller->status & ATA_S_CORR)
- printf("ad%d: soft error ECC corrected\n", adp->lun);
+
+ /* if using DMA, try once again in PIO mode */
+ if (request->flags & AR_F_DMA_USED) {
+ untimeout((timeout_t *)ad_timeout, request,request->timeout_handle);
+
+ request->flags |= AR_F_FORCE_PIO;
+ TAILQ_INSERT_HEAD(&adp->controller->ata_queue, request, chain);
+ return ATA_OP_FINISHED;
+ }
+
+ request->flags |= AR_F_ERROR;
+ printf(" status=%02x error=%02x\n",
+ adp->controller->status, adp->controller->error);
+ }
+
+ /* if we arrived here with forced PIO mode, DMA doesn't work right */
+ if (request->flags & AR_F_FORCE_PIO) {
+ adp->flags &= ~AD_F_DMA_ENABLED;
+ printf("ad%d: DMA problem encountered, fallback to PIO mode\n",
+ adp->lun);
}
/* if this was a PIO read operation, get the data */
@@ -573,8 +592,8 @@ oops:
!= (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ))
printf("ad_interrupt: read interrupt arrived early");
- if (ata_wait(adp->controller, adp->unit,
- ATA_S_READY | ATA_S_DSC | ATA_S_DRQ) != 0){
+ if (ata_wait(adp->controller, adp->unit,
+ (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) != 0) {
printf("ad_interrupt: read error detected late");
goto oops;
}
@@ -653,8 +672,16 @@ ad_timeout(struct ad_request *request)
adp->controller->lun,
(adp->unit == ATA_MASTER) ? "master" : "slave");
- if (request->flags & AR_F_DMA_USED)
+ if (request->flags & AR_F_DMA_USED) {
ata_dmadone(adp->controller);
+ if (request->retries == AD_MAX_RETRIES) {
+ adp->flags &= ~AD_F_DMA_ENABLED;
+ printf("ata%d-%s: ad_timeout: trying fallback to PIO mode\n",
+ adp->controller->lun,
+ (adp->unit == ATA_MASTER) ? "master" : "slave");
+ request->retries = 0;
+ }
+ }
/* if retries still permit, reinject this request */
if (request->retries++ < AD_MAX_RETRIES)
diff --git a/sys/dev/ata/ata-disk.h b/sys/dev/ata/ata-disk.h
index 7415d52d9f60..0e8659fe667d 100644
--- a/sys/dev/ata/ata-disk.h
+++ b/sys/dev/ata/ata-disk.h
@@ -163,6 +163,7 @@ struct ad_request {
#define AR_F_READ 0x0001
#define AR_F_ERROR 0x0002
#define AR_F_DMA_USED 0x0004
+#define AR_F_FORCE_PIO 0x0008
int8_t *data; /* pointer to data buf */
struct buf *bp; /* associated buf ptr */