--- a/sys/bus/gpio/gpio_acpi/gpio_acpi.c +++ b/sys/bus/gpio/gpio_acpi/gpio_acpi.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "opt_acpi.h" #include "acpi.h" @@ -61,6 +62,8 @@ struct acpi_gpio_handler_data { struct acpi_connection_info info; device_t dev; + int busy; /* in-flight space-handler count */ + int detaching; }; struct gpio_acpi_softc { @@ -191,6 +194,8 @@ handle = acpi_get_handle(sc->parent); data->dev = sc->parent; + data->busy = 0; + data->detaching = 0; s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO, &gpio_acpi_space_handler, NULL, data); if (ACPI_FAILURE(s)) { @@ -236,6 +241,21 @@ if (BitWidth == 0 || BitWidth > 64) return (AE_BAD_PARAMETER); + /* + * Refuse dispatches once detach has started and account for + * in-flight ones: AcpiRemoveAddressSpaceHandler() does not wait + * for running handlers (they execute with the AML interpreter + * exited, see AcpiEvAddressSpaceDispatch), and detach tears down + * everything they touch. + */ + if (data->detaching) + return (AE_ABORT_METHOD); + atomic_add_int(&data->busy, 1); + if (data->detaching) { + atomic_subtract_int(&data->busy, 1); + return (AE_ABORT_METHOD); + } + s = AcpiBufferToResource(info->Connection, info->Length, &Resource); if (ACPI_FAILURE(s)) { device_printf(dev, "AcpiBufferToResource failed\n"); @@ -299,6 +319,8 @@ err: ACPI_FREE(Resource); + atomic_subtract_int(&data->busy, 1); + wakeup(&data->busy); return (s); } @@ -487,12 +509,22 @@ gpio_acpi_detach(device_t dev) { struct gpio_acpi_softc *sc = device_get_softc(dev); + struct acpi_gpio_handler_data *data = &sc->space_handler_data; + + /* + * Stop new space-handler dispatches and drain in-flight ones + * before anything they touch is torn down (the parent driver's + * detach frees the provider softc, resources and lock that a + * running gpio_acpi_space_handler still uses). + */ + data->detaching = 1; + gpio_acpi_remove_address_space_handler(sc); + while (data->busy != 0) + tsleep(&data->busy, 0, "gpodet", 1); if (sc->infos != NULL) gpio_acpi_unmap_aei(sc); - gpio_acpi_remove_address_space_handler(sc); - return (0); }