diff options
author | sos <sos@FreeBSD.org> | 2006-02-25 17:27:33 +0000 |
---|---|---|
committer | sos <sos@FreeBSD.org> | 2006-02-25 17:27:33 +0000 |
commit | 1474f527f420e968220e52710a0f51855147a5c7 (patch) | |
tree | 6df7cf6e731ea6fb8210bf84c04c012fe5cf78b8 /sys | |
parent | 609dc452d4d42f7c994e07c1cadc0b8ef948cece (diff) | |
download | FreeBSD-src-1474f527f420e968220e52710a0f51855147a5c7.zip FreeBSD-src-1474f527f420e968220e52710a0f51855147a5c7.tar.gz |
Fix ata_reinit so it does things in the right order to prevent panic's.
Lock the channel so master/slave setups wont trash during reinit.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ata/ata-all.c | 70 | ||||
-rw-r--r-- | sys/dev/ata/ata-queue.c | 6 |
2 files changed, 33 insertions, 43 deletions
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c index a830d1d..9a1c376 100644 --- a/sys/dev/ata/ata-all.c +++ b/sys/dev/ata/ata-all.c @@ -160,6 +160,11 @@ ata_detach(device_t dev) if (!ch->r_irq) return ENXIO; + /* grap the channel lock so no new requests gets launched */ + mtx_lock(&ch->state_mtx); + ch->state |= ATA_STALL_QUEUE; + mtx_unlock(&ch->state_mtx); + /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) @@ -196,9 +201,14 @@ ata_reinit(device_t dev) while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) tsleep(&dev, PRIBIO, "atarini", 1); - /* unconditionally grap the channel lock */ + /* catch eventual request in ch->running */ mtx_lock(&ch->state_mtx); - ch->state = ATA_STALL_QUEUE; + if ((request = ch->running)) + callout_stop(&request->callout); + ch->running = NULL; + + /* unconditionally grap the channel lock */ + ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* reset the controller HW, the channel and device(s) */ @@ -208,48 +218,32 @@ ata_reinit(device_t dev) if (!device_get_children(dev, &children, &nchildren)) { mtx_lock(&Giant); /* newbus suckage it needs Giant */ for (i = 0; i < nchildren; i++) { - if (children[i] && device_is_attached(children[i])) - if (ATA_REINIT(children[i])) { - /* - * if we have a running request and its device matches - * this child we need to inform the request that the - * device is gone and remove it from ch->running - */ - mtx_lock(&ch->state_mtx); - if (ch->running && ch->running->dev == children[i]) { - callout_stop(&ch->running->callout); - request = ch->running; - ch->running = NULL; - } - else - request = NULL; - mtx_unlock(&ch->state_mtx); - - if (request) { - request->result = ENXIO; - device_printf(request->dev, - "FAILURE - device detached\n"); - - /* if not timeout finish request here */ - if (!(request->flags & ATA_R_TIMEOUT)) + /* did any children go missing ? */ + if (children[i] && device_is_attached(children[i]) && + ATA_REINIT(children[i])) { + /* + * if we had a running request and its device matches + * this child we need to inform the request that the + * device is gone. + */ + if (request && request->dev == children[i]) { + request->result = ENXIO; + device_printf(request->dev, "FAILURE - device detached\n"); + + /* if not timeout finish request here */ + if (!(request->flags & ATA_R_TIMEOUT)) ata_finish(request); - } - device_delete_child(dev, children[i]); + request = NULL; } + device_delete_child(dev, children[i]); + } } free(children, M_TEMP); mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } - /* catch request in ch->running if we havn't already */ - mtx_lock(&ch->state_mtx); - if ((request = ch->running)) - callout_stop(&request->callout); - ch->running = NULL; - mtx_unlock(&ch->state_mtx); - - /* if we got one put it on the queue again */ - if (request) { + /* if we still have a good request put it on the queue again */ + if (request && !(request->flags & ATA_R_TIMEOUT)) { device_printf(request->dev, "WARNING - %s requeued due to channel reset", ata_cmd2str(request)); @@ -335,7 +329,7 @@ ata_interrupt(void *data) ATA_DEBUG_RQ(request, "interrupt"); /* safetycheck for the right state */ - if (ch->state != ATA_ACTIVE && ch->state != ATA_STALL_QUEUE) { + if (ch->state == ATA_IDLE) { device_printf(request->dev, "interrupt on idle channel ignored\n"); break; } diff --git a/sys/dev/ata/ata-queue.c b/sys/dev/ata/ata-queue.c index e7116fe..036b008 100644 --- a/sys/dev/ata/ata-queue.c +++ b/sys/dev/ata/ata-queue.c @@ -61,7 +61,7 @@ ata_queue_request(struct ata_request *request) if (!request->callback && !(request->flags & ATA_R_REQUEUE)) sema_init(&request->done, 0, "ATA request done"); - /* in ATA_STALL_QUEUE state we call HW directly (used only during reinit) */ + /* in ATA_STALL_QUEUE state we call HW directly */ if ((ch->state & ATA_STALL_QUEUE) && (request->flags & ATA_R_CONTROL)) { mtx_lock(&ch->state_mtx); ch->running = request; @@ -505,7 +505,6 @@ ata_fail_requests(device_t dev) if ((request = ch->running) && (!dev || request->dev == dev)) { callout_stop(&request->callout); ch->running = NULL; - ch->state = ATA_IDLE; request->result = ENXIO; TAILQ_INSERT_TAIL(&fail_requests, request, chain); } @@ -527,9 +526,6 @@ ata_fail_requests(device_t dev) TAILQ_REMOVE(&fail_requests, request, chain); ata_finish(request); } - - /* we might have work for the other device on this channel */ - ata_start(ch->dev); } static u_int64_t |