Index: sys/dev/acpica/acpi_ged.c =================================================================== --- sys/dev/acpica/acpi_ged.c +++ sys/dev/acpica/acpi_ged.c @@ -232,12 +232,13 @@ continue; } } - +#ifdef EARLY_AP_STARTUP if (bus_setup_intr(dev, sc->evts[i].r, INTR_TYPE_MISC | INTR_MPSAFE, NULL, acpi_ged_intr, &sc->evts[i], &sc->evts[i].cookie) != 0) { device_printf(dev, "Failed to setup intr %d\n", i); } +#endif } return_VALUE(0); @@ -263,3 +264,59 @@ return (0); } + +#ifndef EARLY_AP_STARTUP +/* + * Defer interrupt setup until after SMP is started. This is needed + * because on platforms not using EARLY_AP_STARTUP, we are running + * with just a single core when ACPI GED attaches, and it may + * live-lock the single CPU when it encounters an error at boot. This + * is because the interrupt is level triggered, and is unmasked when + * the ithread returns, but not acknowleged until AcpiEvaluateObject() + * is run, which is deferred to a taskqueue context by + * AcpiOsExecute(). So we keep adding tasks and never running any of + * them, and boot hangs in an interrupt storm. + * + * By deferring bus_setup_intr(), the GED interrupt remains masked + * until there are other CPUs to to handle the events. + * + * Ideally, we'd run AcpiEvaluateObject() directly in the ithread + * context, but it may do things (such as sleep), which are not + * appropropriate in an ithread. + */ + +static void +acpi_ged_setup_intr(device_t dev) +{ + struct acpi_ged_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->numevts; i++) { + if (bus_setup_intr(dev, sc->evts[i].r, + INTR_TYPE_MISC | INTR_MPSAFE, NULL, acpi_ged_intr, + &sc->evts[i], &sc->evts[i].cookie) != 0) { + device_printf(dev, "Failed to setup intr %d\n", i); + acpi_ged_detach(dev); + } + } +} + +static void +ged_intr_init(void *arg __unused) +{ + device_t dev; + devclass_t ged = devclass_find("acpi_ged"); + int unit; + + for (unit = 0; unit < devclass_get_maxunit(ged); unit++) { + dev = devclass_get_device(ged, unit); + if (dev == NULL) + continue; + acpi_ged_setup_intr(dev); + } + +} + +SYSINIT(ged_intr_attach, SI_SUB_LAST, SI_ORDER_ANY, ged_intr_init, NULL); + +#endif /* EARLY_AP_STARTUP */