--- a/sys/dev/acpica/acpi_button.c +++ b/sys/dev/acpica/acpi_button.c @@ -57,6 +57,7 @@ static int acpi_button_probe(device_t dev); static int acpi_button_attach(device_t dev); +static int acpi_button_detach(device_t dev); static int acpi_button_suspend(device_t dev); static int acpi_button_resume(device_t dev); static void acpi_button_notify_handler(ACPI_HANDLE h, UINT32 notify, @@ -75,6 +76,7 @@ /* Device interface */ DEVMETHOD(device_probe, acpi_button_probe), DEVMETHOD(device_attach, acpi_button_attach), + DEVMETHOD(device_detach, acpi_button_detach); DEVMETHOD(device_suspend, acpi_button_suspend), DEVMETHOD(device_shutdown, acpi_button_suspend), DEVMETHOD(device_resume, acpi_button_resume), @@ -173,6 +175,37 @@ } static int +acpi_button_detach(device_t dev) +{ + struct acpi_button_softc *sc; + int event; + + sc = device_get_softc(dev); + event = (sc->button_type == ACPI_SLEEP_BUTTON) ? + ACPI_EVENT_SLEEP_BUTTON : ACPI_EVENT_POWER_BUTTON; + + /* + * Undo acpi_button_attach(): without this the ACPICA notify handler + * (or fixed-event handler) keeps a pointer to our softc forever, + * pinning the driver/module and turning any future detach into a + * use-after-free. + * + * NOTE: AcpiRemoveNotifyHandler() waits for in-progress handler + * invocations but not for callbacks already queued to + * AcpiOsExecute(); a complete fix would drain those before + * returning (e.g. a generation counter checked by + * acpi_button_notify_sleep/wakeup). + */ + if (sc->fixed) + AcpiRemoveFixedEventHandler(event, acpi_button_fixed_handler); + else + AcpiRemoveNotifyHandler(sc->button_handle, ACPI_DEVICE_NOTIFY, + acpi_button_notify_handler); + + return (0); +} + +static int acpi_button_suspend(device_t dev) { return (0);