DF-2307 / fix.diff
--- a/sys/bus/gpio/gpio_acpi/gpio_acpi.c +++ b/sys/bus/gpio/gpio_acpi/gpio_acpi.c @@ -56,6 +56,7 @@ u_int pin; void *cookie; int trigger; + volatile int inflight; /* DF-2307: queued AcpiOsExecute items */ }; struct acpi_gpio_handler_data { @@ -314,6 +315,13 @@ ACPI_STATUS s; char buf[5]; + /* + * DF-2307: if unmap_aei leaked this info because we would not drain + * in time, dev is NULL -- evaluate nothing and leave. + */ + if (info->dev == NULL) + goto out; + handle = acpi_get_handle(device_get_parent(info->dev)); if (info->trigger == ACPI_EDGE_SENSITIVE) { ksnprintf(buf, sizeof(buf), "_E%02X", info->pin); @@ -336,6 +344,10 @@ if (ACPI_FAILURE(s)) device_printf(info->dev, "evaluating _EVT failed\n"); } +out: + /* DF-2307: unmap_aei waits on this before freeing the info. */ + atomic_subtract_int(&info->inflight, 1); + wakeup(info); } static void @@ -344,8 +356,12 @@ struct acpi_event_info *info = (struct acpi_event_info *)arg; ACPI_STATUS s; + /* DF-2307: account the item BEFORE queueing so unmap_aei can never + * observe a false zero. */ + atomic_add_int(&info->inflight, 1); s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg); if (ACPI_FAILURE(s)) { + atomic_subtract_int(&info->inflight, 1); device_printf(info->dev, "AcpiOsExecute for Acpi Event handler failed\n"); } @@ -449,7 +465,23 @@ KKASSERT(info->dev != NULL); GPIO_TEARDOWN_INTR(sc->parent, info->cookie); GPIO_FREE_INTR(sc->parent, info->cookie); - /* XXX Wait until ACPI Event handler has finished */ + /* + * DF-2307: TEARDOWN stops NEW dispatches but does not drain + * items gpio_acpi_aei_handler already queued through + * AcpiOsExecute. Wait for them; if the ACPI task queue is + * stalled past the grace period, LEAK this info instead of + * freeing memory the deferred handler still dereferences. + */ + { + int spin; + + for (spin = 0; + spin < 10 * hz && info->inflight != 0; + spin++) + tsleep(info, 0, "gpioaei", 1); + if (info->inflight != 0) + continue; /* leak: never memset/free */ + } memset(info, 0, sizeof(*info)); } kfree(sc->infos, M_DEVBUF); |