From 27dca831a651289cb6506fb213739cfda3dce49a Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Mon, 21 May 2018 20:23:04 +0000 Subject: [PATCH] stop and restart kernel event timers in the suspend / resume cycle I have a system that is very unstable after resuming from suspend-to-RAM but only if HPET is used as the event timer. The theory is that SMM code / firmware could be enabling HPET for its own uses and unexpected interrupts cause a trouble for it. Originally I wanted to solve the problem in hpet_suspend() method, but that was insufficient as the event timer could get reprogrammed again. So, it's better, for my case and in general, to stop the event timer(s) before entering the hardware suspend. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D15413 --- sys/dev/acpica/acpi.c | 3 +++ sys/kern/kern_clocksource.c | 16 ++++++++++++++++ sys/sys/systm.h | 2 ++ 3 files changed, 21 insertions(+) diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c index 33f88b2839f..aec4e278053 100644 --- a/sys/dev/acpica/acpi.c +++ b/sys/dev/acpica/acpi.c @@ -2958,6 +2958,7 @@ acpi_EnterSleepState(struct acpi_softc *sc, int state) if (sc->acpi_sleep_delay > 0) DELAY(sc->acpi_sleep_delay * 1000000); + suspendclock(); intr = intr_disable(); if (state != ACPI_STATE_S1) { sleep_result = acpi_sleep_machdep(sc, state); @@ -3028,6 +3029,8 @@ acpi_EnterSleepState(struct acpi_softc *sc, int state) * process. This handles both the error and success cases. */ backout: + if (slp_state >= ACPI_SS_SLP_PREP) + resumeclock(); if (slp_state >= ACPI_SS_GPE_SET) { acpi_wake_prep_walk(state); sc->acpi_sstate = ACPI_STATE_S0; diff --git a/sys/kern/kern_clocksource.c b/sys/kern/kern_clocksource.c index 7b7ca34e609..9676923104e 100644 --- a/sys/kern/kern_clocksource.c +++ b/sys/kern/kern_clocksource.c @@ -698,6 +698,22 @@ cpu_initclocks_ap(void) spinlock_exit(); } +void +suspendclock(void) +{ + ET_LOCK(); + configtimer(0); + ET_UNLOCK(); +} + +void +resumeclock(void) +{ + ET_LOCK(); + configtimer(1); + ET_UNLOCK(); +} + /* * Switch to profiling clock rates. */ diff --git a/sys/sys/systm.h b/sys/sys/systm.h index 995e06774d6..2bc0efffc80 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -333,6 +333,8 @@ void startprofclock(struct proc *); void stopprofclock(struct proc *); void cpu_startprofclock(void); void cpu_stopprofclock(void); +void suspendclock(void); +void resumeclock(void); sbintime_t cpu_idleclock(void); void cpu_activeclock(void); void cpu_new_callout(int cpu, sbintime_t bt, sbintime_t bt_opt);