summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata
diff options
context:
space:
mode:
authorsos <sos@FreeBSD.org>1999-11-29 12:24:51 +0000
committersos <sos@FreeBSD.org>1999-11-29 12:24:51 +0000
commit3a4f419fb832328f0236c01dec5431d03c0a3415 (patch)
treecaae17fd1e0aa5c84ade5e759eedbf7689350e35 /sys/dev/ata
parent8da3ba86dcb25f842958179693b5d802d91ae681 (diff)
downloadFreeBSD-src-3a4f419fb832328f0236c01dec5431d03c0a3415.zip
FreeBSD-src-3a4f419fb832328f0236c01dec5431d03c0a3415.tar.gz
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.
Diffstat (limited to 'sys/dev/ata')
-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 3de8205..50c0476 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 7415d52..0e8659f 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 */
OpenPOWER on IntegriCloud