ioat(4): Don't process events past queue head

Fix a race where the completion routine could overrun the active ring
area in some situations.
This commit is contained in:
Conrad Meyer 2016-08-22 14:51:07 +00:00
parent 351a9c7c0b
commit 0283c0f581
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=304602

View File

@ -678,19 +678,12 @@ ioat_process_events(struct ioat_softc *ioat)
}
completed = 0;
comp_update = *ioat->comp_update;
comp_update = ioat_get_chansts(ioat);
CTR4(KTR_IOAT, "%s channel=%u hw_status=0x%lx last_seen=0x%lx",
__func__, ioat->chan_idx, comp_update, ioat->last_seen);
status = comp_update & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_MASK;
if (status == ioat->last_seen) {
/*
* If we landed in process_events and nothing has been
* completed, check for a timeout due to channel halt.
*/
comp_update = ioat_get_chansts(ioat);
goto out;
}
while (1) {
while (ioat_get_active(ioat) > 0) {
desc = ioat_get_ring_entry(ioat, ioat->tail);
dmadesc = &desc->bus_dmadesc;
CTR4(KTR_IOAT, "channel=%u completing desc %u ok cb %p(%p)",
@ -704,17 +697,13 @@ ioat_process_events(struct ioat_softc *ioat)
ioat->tail++;
if (desc->hw_desc_bus_addr == status)
break;
KASSERT(ioat_get_active(ioat) > 0, ("overrunning ring t:%u "
"h:%u st:0x%016lx last_seen:%016lx completed:%u\n",
ioat->tail, ioat->head, comp_update, ioat->last_seen,
completed));
}
ioat->last_seen = desc->hw_desc_bus_addr;
ioat->stats.descriptors_processed += completed;
if (completed != 0) {
ioat->last_seen = desc->hw_desc_bus_addr;
ioat->stats.descriptors_processed += completed;
}
out:
ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
/* Perform a racy check first; only take the locks if it passes. */