diff options
author | sos <sos@FreeBSD.org> | 2004-01-11 22:08:34 +0000 |
---|---|---|
committer | sos <sos@FreeBSD.org> | 2004-01-11 22:08:34 +0000 |
commit | d6c015472804fd004386a182a3fb3be3384d4ad2 (patch) | |
tree | 257dd7f1dda826312c0f73c67ce8a715f993fd74 /sys/dev/ata/ata-queue.c | |
parent | f20180c215090bb04cfb535a04f33e2b8e6b61bf (diff) | |
download | FreeBSD-src-d6c015472804fd004386a182a3fb3be3384d4ad2.zip FreeBSD-src-d6c015472804fd004386a182a3fb3be3384d4ad2.tar.gz |
Overhaul of the timeout/reinit framework. This should clear up most
of the leftovers from the old version that really doesn't work anymore.
Add a reset function for host-end of the ATA channel. This is needed
for the SiI3112 in order to whack it back to reality if a device
locks up the SATA interface (thereby preventing that we can reset the
device). The result is that ATA now recovers from the timeouts that
happens with the SiI3112A and more or less all disks based on old
PATA electronics with a Marvell PATA->SATA converter. This includes
lots of the popular SATA dongles and the WDC Raptor disks..
Diffstat (limited to 'sys/dev/ata/ata-queue.c')
-rw-r--r-- | sys/dev/ata/ata-queue.c | 244 |
1 files changed, 154 insertions, 90 deletions
diff --git a/sys/dev/ata/ata-queue.c b/sys/dev/ata/ata-queue.c index 6fd30fc..415d398 100644 --- a/sys/dev/ata/ata-queue.c +++ b/sys/dev/ata/ata-queue.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org> + * Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,8 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/bus.h> #include <sys/conf.h> -#include <sys/lock.h> -#include <sys/mutex.h> +#include <sys/sema.h> #include <sys/taskqueue.h> #include <machine/bus.h> #include <sys/rman.h> @@ -51,7 +50,6 @@ static char *ata_skey2str(u_int8_t); /* local vars */ static MALLOC_DEFINE(M_ATA_REQ, "ATA request", "ATA request"); -static int atadebug = 0; /* * ATA request related functions @@ -64,42 +62,65 @@ ata_alloc_request(void) request = malloc(sizeof(struct ata_request), M_ATA_REQ, M_NOWAIT | M_ZERO); if (!request) printf("FAILURE - malloc ATA request failed\n"); + sema_init(&request->done, 0, "ATA request done"); return request; } void ata_free_request(struct ata_request *request) { + sema_destroy(&request->done); free(request, M_ATA_REQ); } void ata_queue_request(struct ata_request *request) { - /* mark request as virgin (it might be a reused one) */ + /* mark request as virgin (it might be a retry) */ request->result = request->status = request->error = 0; - request->flags &= ~ATA_R_DONE; - /* put request on the locked queue at the specified location */ - mtx_lock(&request->device->channel->queue_mtx); - if (request->flags & ATA_R_AT_HEAD) - TAILQ_INSERT_HEAD(&request->device->channel->ata_queue, request, chain); - else - TAILQ_INSERT_TAIL(&request->device->channel->ata_queue, request, chain); - mtx_unlock(&request->device->channel->queue_mtx); + if (request->device->channel->flags & ATA_IMMEDIATE_MODE) { - /* should we skip start ? */ - if (!(request->flags & ATA_R_SKIPSTART)) - ata_start(request->device->channel); + // request->flags |= ATA_R_DEBUG; - /* if this was a requeue op callback/sleep already setup */ - if (request->flags & ATA_R_REQUEUE) - return; + /* arm timeout */ + if (!request->timeout_handle.callout && !dumping) { + request->timeout_handle = + timeout((timeout_t*)ata_timeout, request, request->timeout*hz); + } - /* if this is not a callback and we havn't seen DONE yet -> sleep */ - if (!request->callback) { - while (!(request->flags & ATA_R_DONE)) - tsleep(request, PRIBIO, "atareq", hz/10); + /* kick HW into action */ + if (request->device->channel->hw.transaction(request) == + ATA_OP_CONTINUES) { + ATA_DEBUG_RQ(request, "wait for completition"); + sema_wait(&request->done); + } + } + else { + /* put request on the locked queue at the specified location */ + mtx_lock(&request->device->channel->queue_mtx); + if (request->flags & ATA_R_AT_HEAD) + TAILQ_INSERT_HEAD(&request->device->channel->ata_queue, + request, chain); + else + TAILQ_INSERT_TAIL(&request->device->channel->ata_queue, + request, chain); + mtx_unlock(&request->device->channel->queue_mtx); + + ATA_DEBUG_RQ(request, "queued"); + + /* should we skip start ? */ + if (!(request->flags & ATA_R_SKIPSTART)) + ata_start(request->device->channel); + + /* if this is a requeued request callback/sleep is already setup */ + if (request->flags & ATA_R_REQUEUE) + return; + /* if this is not a callback wait until request is completed */ + if (!request->callback) { + ATA_DEBUG_RQ(request, "wait for completition"); + sema_wait(&request->done); + } } } @@ -156,14 +177,16 @@ ata_start(struct ata_channel *ch) { struct ata_request *request; + /* if in immediate mode, just skip start requests (stall queue) */ + if (ch->flags & ATA_IMMEDIATE_MODE) + return; + /* lock the ATA HW for this request */ ch->locking(ch, ATA_LF_LOCK); if (!ATA_LOCK_CH(ch, ATA_ACTIVE)) { return; } -if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n"); - /* if we dont have any work, ask the subdriver(s) */ mtx_lock(&ch->queue_mtx); if (TAILQ_EMPTY(&ch->ata_queue)) { @@ -178,6 +201,8 @@ if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n"); TAILQ_REMOVE(&ch->ata_queue, request, chain); mtx_unlock(&ch->queue_mtx); + ATA_DEBUG_RQ(request, "starting"); + /* arm timeout */ if (!request->timeout_handle.callout && !dumping) { request->timeout_handle = @@ -188,8 +213,6 @@ if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n"); if (ch->hw.transaction(request) == ATA_OP_CONTINUES) return; - /* untimeout request */ - untimeout((timeout_t *)ata_timeout, request, request->timeout_handle); ata_finish(request); } else @@ -202,9 +225,16 @@ if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n"); void ata_finish(struct ata_request *request) { + ATA_DEBUG_RQ(request, "taskqueue completition"); + /* request is done schedule it for completition */ - TASK_INIT(&request->task, 0, ata_completed, request); - taskqueue_enqueue(taskqueue_swi, &request->task); + if (request->device->channel->flags & ATA_IMMEDIATE_MODE) { + ata_completed(request, 0); + } + else { + TASK_INIT(&request->task, 0, ata_completed, request); + taskqueue_enqueue(taskqueue_swi, &request->task); + } } /* current command finished, clean up and return result */ @@ -214,27 +244,60 @@ ata_completed(void *context, int pending) struct ata_request *request = (struct ata_request *)context; struct ata_channel *channel = request->device->channel; - /* untimeout request now we have control back */ - untimeout((timeout_t *)ata_timeout, request, request->timeout_handle); + ATA_DEBUG_RQ(request, "completed called"); - /* do the all the magic for completition evt retry etc etc */ - if ((request->status & (ATA_S_CORR | ATA_S_ERROR)) == ATA_S_CORR) - ata_prtdev(request->device, "WARNING - %s soft error (ECC corrected)\n", - ata_cmd2str(request)); + if (request->flags & ATA_R_TIMEOUT) { + ata_reinit(channel); - /* if this is a UDMA CRC error, retry request */ - if (request->flags & ATA_R_DMA && request->error & ATA_E_ICRC) { - if (request->retries--) { - ata_prtdev(request->device, - "WARNING - %s UDMA ICRC error (retrying request)\n", - ata_cmd2str(request)); - request->flags &= ~ATA_R_SKIPSTART; + /* if retries still permit, reinject this request */ + if (request->retries-- > 0) { + request->flags &= ~(ATA_R_TIMEOUT | ATA_R_SKIPSTART); + request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE); ata_queue_request(request); return; } + + /* otherwise just finish with error */ + else { + if (!(request->flags & ATA_R_QUIET)) + ata_prtdev(request->device, + "FAILURE - %s timed out\n", + ata_cmd2str(request)); + request->result = EIO; + } + } + else { + /* untimeout request now we have control back */ + untimeout((timeout_t *)ata_timeout, request, request->timeout_handle); + + /* do the all the magic for completition evt retry etc etc */ + if ((request->status & (ATA_S_CORR | ATA_S_ERROR)) == ATA_S_CORR) { + ata_prtdev(request->device, + "WARNING - %s soft error (ECC corrected)", + ata_cmd2str(request)); + if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) + printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf("\n"); + } + + /* if this is a UDMA CRC error, retry request */ + if (request->flags & ATA_R_DMA && request->error & ATA_E_ICRC) { + if (request->retries-- > 0) { + ata_prtdev(request->device, + "WARNING - %s UDMA ICRC error (retrying request)", + ata_cmd2str(request)); + if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) + printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf("\n"); + request->flags &= ~ATA_R_SKIPSTART; + ata_queue_request(request); + return; + } + } } switch (request->flags & ATA_R_ATAPI) { + /* ATA errors */ default: if (request->status & ATA_S_ERROR) { @@ -250,8 +313,7 @@ ata_completed(void *context, int pending) if ((request->flags & ATA_R_DMA) && (request->dmastat & ATA_BMSTAT_ERROR)) printf(" dma=0x%02x", request->dmastat); - if (!(request->flags & ATA_R_ATAPI) && - !(request->flags & ATA_R_CONTROL)) + if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); printf("\n"); } @@ -306,74 +368,67 @@ ata_completed(void *context, int pending) break; } - request->flags |= ATA_R_DONE; + ATA_DEBUG_RQ(request, "completed callback/wakeup"); + if (request->callback) (request->callback)(request); else - wakeup(request); + sema_post(&request->done); + ata_start(channel); } static void ata_timeout(struct ata_request *request) { - struct ata_channel *ch = request->device->channel; - int quiet = request->flags & ATA_R_QUIET; + ATA_DEBUG_RQ(request, "timeout"); /* clear timeout etc */ request->timeout_handle.callout = NULL; - /* call hw.interrupt to try finish up the command */ - ch->hw.interrupt(request->device->channel); - if (ch->running != request) { - if (!quiet) - ata_prtdev(request->device, - "WARNING - %s recovered from missing interrupt\n", + if (request->flags & ATA_R_INTR_SEEN) { + if (request->retries-- > 0) { + ata_prtdev(request->device, + "WARNING - %s interrupt was seen but timeout fired", + ata_cmd2str(request)); + if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) + printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf("\n"); + + /* re-arm timeout */ + if (!request->timeout_handle.callout && !dumping) { + request->timeout_handle = + timeout((timeout_t*)ata_timeout, request, + request->timeout * hz); + } + } + else { + ata_prtdev(request->device, + "WARNING - %s interrupt was seen but taskqueue stalled", ata_cmd2str(request)); + if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) + printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf("\n"); + ata_completed(request, 0); + } return; } - /* if this was a DMA request stop the engine to be on the safe side */ - if (request->flags & ATA_R_DMA) { - request->dmastat = - request->device->channel->dma->stop(request->device->channel); - } - /* report that we timed out */ - if (request->retries > 0 && !(request->flags & ATA_R_QUIET)) + if (!(request->flags & ATA_R_QUIET)) { ata_prtdev(request->device, - "TIMEOUT - %s retrying (%d retr%s left)\n", + "TIMEOUT - %s retrying (%d retr%s left)", ata_cmd2str(request), request->retries, request->retries == 1 ? "y" : "ies"); - - /* try to adjust HW's attitude towards work */ - ata_reinit(request->device->channel); - - /* if device disappeared nothing more to do here */ - if (!request->device->softc) { - if (!(request->flags & ATA_R_QUIET)) - ata_prtdev(request->device, - "FAILURE - %s device lockup/removed\n", - ata_cmd2str(request)); - return; + if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) + printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf("\n"); } - /* if retries still permit, reinject this request */ - if (request->retries-- > 0) { - request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE); - request->flags &= ~ATA_R_SKIPSTART; - ata_queue_request(request); - } - /* otherwise just schedule finish with error */ - else { - if (!(request->flags & ATA_R_QUIET)) - ata_prtdev(request->device, - "FAILURE - %s timed out\n", - ata_cmd2str(request)); - request->status = ATA_S_ERROR; - TASK_INIT(&request->task, 0, ata_completed, request); - taskqueue_enqueue(taskqueue_swi, &request->task); - } + /* now simulate the missing interrupt */ + request->flags |= ATA_R_TIMEOUT; + request->device->channel->hw.interrupt(request->device->channel); + return; } char * @@ -464,7 +519,16 @@ ata_cmd2str(struct ata_request *request) case 0xe7: return ("FLUSHCACHE"); case 0xea: return ("FLUSHCACHE48"); case 0xec: return ("ATA_IDENTIFY"); - case 0xef: return ("SETFEATURES"); + case 0xef: + switch (request->u.ata.feature) { + case 0x03: return ("SETFEATURES SET TRANSFER MODE"); + case 0x02: return ("SETFEATURES ENABLE WCACHE"); + case 0x82: return ("SETFEATURES DISABLE WCACHE"); + case 0xaa: return ("SETFEATURES ENABLE RCACHE"); + case 0x55: return ("SETFEATURES DISABLE RCACHE"); + } + sprintf(buffer, "SETFEATURES 0x%02x", request->u.ata.feature); + return buffer; } } sprintf(buffer, "unknown CMD (0x%02x)", request->u.ata.command); |