summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata/ata-all.c
diff options
context:
space:
mode:
authorsos <sos@FreeBSD.org>2005-09-14 12:45:06 +0000
committersos <sos@FreeBSD.org>2005-09-14 12:45:06 +0000
commit3075efc7afec20b9694e3ec99848940bea14ea76 (patch)
tree9c8d827516807e33e8e188f2ee6f1ddb3e711311 /sys/dev/ata/ata-all.c
parentdb6ae269ba7d21c49a7b2e0ad9129194394c8839 (diff)
downloadFreeBSD-src-3075efc7afec20b9694e3ec99848940bea14ea76.zip
FreeBSD-src-3075efc7afec20b9694e3ec99848940bea14ea76.tar.gz
Harden the hotplug support for SATA devices.
This also fixes a few races that was present in the timeout/detach code. Sponsored by: pair.com
Diffstat (limited to 'sys/dev/ata/ata-all.c')
-rw-r--r--sys/dev/ata/ata-all.c49
1 files changed, 39 insertions, 10 deletions
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c
index a779409..40916d9 100644
--- a/sys/dev/ata/ata-all.c
+++ b/sys/dev/ata/ata-all.c
@@ -158,7 +158,7 @@ ata_detach(device_t dev)
device_t *children;
int nchildren, i;
- /* check that we have a vaild channel to detach */
+ /* check that we have a valid channel to detach */
if (!ch->r_irq)
return ENXIO;
@@ -183,10 +183,11 @@ int
ata_reinit(device_t dev)
{
struct ata_channel *ch = device_get_softc(dev);
+ struct ata_request *request;
device_t *children;
int nchildren, i;
- /* check that we have a vaild channel to reinit */
+ /* check that we have a valid channel to reinit */
if (!ch || !ch->r_irq)
return ENXIO;
@@ -216,12 +217,25 @@ ata_reinit(device_t dev)
* 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]) {
- device_printf(ch->running->dev,
- "FAILURE - device detached\n");
- ch->running->dev = NULL;
- ch->running = NULL;
+ 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))
+ ata_finish(request);
+ }
device_delete_child(dev, children[i]);
}
}
@@ -230,7 +244,23 @@ ata_reinit(device_t dev)
}
/* catch request in ch->running if we havn't already */
- ata_catch_inflight(dev);
+ 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) {
+ device_printf(request->dev,
+ "WARNING - %s requeued due to channel reset",
+ 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_REQUEUE;
+ ata_queue_request(request);
+ }
/* we're done release the channel for new work */
mtx_lock(&ch->state_mtx);
@@ -297,7 +327,7 @@ ata_interrupt(void *data)
mtx_lock(&ch->state_mtx);
do {
/* do we have a running request */
- if (!(request = ch->running) || (request->flags & ATA_R_TIMEOUT))
+ if (!(request = ch->running))
break;
ATA_DEBUG_RQ(request, "interrupt");
@@ -311,8 +341,7 @@ ata_interrupt(void *data)
/* check for the right state */
if (ch->state != ATA_ACTIVE && ch->state != ATA_STALL_QUEUE) {
- device_printf(request->dev,
- "interrupt state=%d unexpected\n", ch->state);
+ device_printf(request->dev, "interrupt on idle channel ignored\n");
break;
}
OpenPOWER on IntegriCloud