summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorsos <sos@FreeBSD.org>2006-02-25 17:27:33 +0000
committersos <sos@FreeBSD.org>2006-02-25 17:27:33 +0000
commit1474f527f420e968220e52710a0f51855147a5c7 (patch)
tree6df7cf6e731ea6fb8210bf84c04c012fe5cf78b8 /sys
parent609dc452d4d42f7c994e07c1cadc0b8ef948cece (diff)
downloadFreeBSD-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.c70
-rw-r--r--sys/dev/ata/ata-queue.c6
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
OpenPOWER on IntegriCloud