diff options
author | sos <sos@FreeBSD.org> | 2005-09-14 12:45:06 +0000 |
---|---|---|
committer | sos <sos@FreeBSD.org> | 2005-09-14 12:45:06 +0000 |
commit | 3075efc7afec20b9694e3ec99848940bea14ea76 (patch) | |
tree | 9c8d827516807e33e8e188f2ee6f1ddb3e711311 /sys/dev/ata/ata-all.c | |
parent | db6ae269ba7d21c49a7b2e0ad9129194394c8839 (diff) | |
download | FreeBSD-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.c | 49 |
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; } |