diff --git a/sys/dev/acpica/acpi_hp/acpi_hp.c b/sys/dev/acpica/acpi_hp/acpi_hp.c --- a/sys/dev/acpica/acpi_hp/acpi_hp.c +++ b/sys/dev/acpica/acpi_hp/acpi_hp.c @@ -588,7 +588,18 @@ } else { if (sc->has_notify) { - ACPI_WMI_REMOVE_EVENT_HANDLER(dev, + /* + * DF-2383: the acpi_wmi_if KBI dispatches on the + * device_t passed here. Passing our own dev + * resolves to kobj_error_method() (ENXIO) because + * acpi_hp does not implement acpi_wmi_if -- the + * notify handler was in fact never unregistered + * and stayed armed across detach/unload. Target + * the WMI provider device so its (draining) + * remove path actually runs before our softc is + * freed by newbus. + */ + ACPI_WMI_REMOVE_EVENT_HANDLER(sc->wmi_dev, ACPI_HP_WMI_EVENT_GUID); } sysctl_ctx_free(&sc->sysctl_ctx); diff --git a/sys/dev/acpica/acpi_wmi/acpi_wmi.c b/sys/dev/acpica/acpi_wmi/acpi_wmi.c --- a/sys/dev/acpica/acpi_wmi/acpi_wmi.c +++ b/sys/dev/acpica/acpi_wmi/acpi_wmi.c @@ -75,6 +75,7 @@ pid_t wmistat_open_pid; /* pid operating on /dev/wmistat */ int wmistat_bufptr; /* /dev/wmistat ptr to buffer position */ TAILQ_HEAD(wmi_info_list_head, wmi_info) wmi_info_list; + int wmi_dispatching; /* in-flight notify dispatches */ }; /* @@ -456,6 +457,17 @@ winfo->event_handler = NULL; winfo->event_handler_user_data = NULL; } + /* + * DF-2381/DF-2383: AcpiOsWaitEventsComplete() is an empty no-op on + * DragonFly (contrib/dev/acpica/source/include/platform/ + * acdragonfly.h:219-220), and acpi_wmi_notify_handler invokes the + * client handler AFTER releasing the WMI serial lock. A dispatch + * that copied the handler before it was NULLed above can therefore + * still be running while our caller (e.g. acpi_hp_detach) frees + * its softc. Drain in-flight dispatches before returning. + */ + while (sc->wmi_dispatching > 0) + lksleep(sc, &acpi_wmi_serial, 0, "wmidrain", 0); ACPI_SERIAL_END(acpi_wmi); return (status); @@ -642,6 +654,7 @@ if (winfo->event_handler) { handler = winfo->event_handler; handler_data = winfo->event_handler_user_data; + sc->wmi_dispatching++; break; } } @@ -649,6 +662,10 @@ ACPI_SERIAL_END(acpi_wmi); if (handler) { handler(h, notify, handler_data); + ACPI_SERIAL_BEGIN(acpi_wmi); + if (--sc->wmi_dispatching == 0) + wakeup(sc); + ACPI_SERIAL_END(acpi_wmi); } }