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.
This commit is contained in:
sos 2006-02-25 17:27:33 +00:00
parent 609dc452d4
commit 1474f527f4
2 changed files with 32 additions and 42 deletions

View File

@ -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);
/* 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 (request) {
request->result = ENXIO;
device_printf(request->dev,
"FAILURE - device detached\n");
/* if not timeout finish request here */
if (!(request->flags & ATA_R_TIMEOUT))
/* 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;
}

View File

@ -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