From 85f95fffd78f81cbbb8a948a3789e94f275bd1e2 Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Tue, 27 Jan 2015 17:33:18 +0000 Subject: [PATCH] hook userland threads suspend + resume into acpi suspend code Also, split power_suspend into power_suspend and power_suspend_early. power_suspend_early is called before the userland is frozen. power_suspend is called after the userland is frozen. Currently only VT switching is hooked to power_suspend_early. This is needed because switching away from X server requires its cooperation, so obviously X server must not be frozen when that happens. Freezing userland during ACPI suspend is useful because not all drivers correctly handle suspension concurrent with other activity. This is especially applicable to drivers ported from other operating systems that suspend all software activity between placing drivers and hardware into suspended state. In particular drm2/radeon (radeonkms) depends on the described procedure. The driver does not have any internal synchronization between suspension activities and processing of userland requests. Many thanks to kib for the code that allows to freeze and thaw all userland threads. Note that ideally we also need to park / inhibit (non-special) kernel threads as well to ensure that they do not call into drivers. MFC after: 17 days --- sys/dev/acpica/acpi.c | 4 ++++ sys/dev/syscons/syscons.c | 2 +- sys/dev/vt/vt_core.c | 4 ++-- sys/sys/eventhandler.h | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c index 0e8998b0949..a4732c47a64 100644 --- a/sys/dev/acpica/acpi.c +++ b/sys/dev/acpica/acpi.c @@ -2749,6 +2749,8 @@ acpi_EnterSleepState(struct acpi_softc *sc, int state) return_ACPI_STATUS (AE_OK); } + EVENTHANDLER_INVOKE(power_suspend_early); + stop_all_proc(); EVENTHANDLER_INVOKE(power_suspend); if (smp_started) { @@ -2892,6 +2894,8 @@ backout: thread_unlock(curthread); } + resume_all_proc(); + EVENTHANDLER_INVOKE(power_resume); /* Allow another sleep request after a while. */ diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c index 6a19523ee2e..2ab28895a1f 100644 --- a/sys/dev/syscons/syscons.c +++ b/sys/dev/syscons/syscons.c @@ -549,7 +549,7 @@ sc_attach_unit(int unit, int flags) /* Register suspend/resume/shutdown callbacks for the kernel console. */ if (sc_console_unit == unit) { - EVENTHANDLER_REGISTER(power_suspend, scsuspend, NULL, + EVENTHANDLER_REGISTER(power_suspend_early, scsuspend, NULL, EVENTHANDLER_PRI_ANY); EVENTHANDLER_REGISTER(power_resume, scresume, NULL, EVENTHANDLER_PRI_ANY); diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c index d70073090a9..47b3c8a67d9 100644 --- a/sys/dev/vt/vt_core.c +++ b/sys/dev/vt/vt_core.c @@ -2556,8 +2556,8 @@ vt_upgrade(struct vt_device *vd) vd->vd_timer_armed = 1; /* Register suspend/resume handlers. */ - EVENTHANDLER_REGISTER(power_suspend, vt_suspend_handler, vd, - EVENTHANDLER_PRI_ANY); + EVENTHANDLER_REGISTER(power_suspend_early, vt_suspend_handler, + vd, EVENTHANDLER_PRI_ANY); EVENTHANDLER_REGISTER(power_resume, vt_resume_handler, vd, EVENTHANDLER_PRI_ANY); } diff --git a/sys/sys/eventhandler.h b/sys/sys/eventhandler.h index 4e77d5b63b9..d82ece71eda 100644 --- a/sys/sys/eventhandler.h +++ b/sys/sys/eventhandler.h @@ -182,6 +182,7 @@ EVENTHANDLER_DECLARE(shutdown_final, shutdown_fn); typedef void (*power_change_fn)(void *); EVENTHANDLER_DECLARE(power_resume, power_change_fn); EVENTHANDLER_DECLARE(power_suspend, power_change_fn); +EVENTHANDLER_DECLARE(power_suspend_early, power_change_fn); /* Low memory event */ typedef void (*vm_lowmem_handler_t)(void *, int);