From 6c195505b1f995019b32baa30da27b6fe0eee39b Mon Sep 17 00:00:00 2001 From: kettenis Date: Fri, 7 Jun 2024 16:53:35 +0000 Subject: [PATCH] Make sure we select the deepest possible C-state during suspend-to-idle. ok deraadt@, guenther@, mlarkin@, jsg@ --- sys/arch/amd64/amd64/cpu.c | 7 +++- sys/arch/amd64/amd64/ipifuncs.c | 7 +++- sys/arch/amd64/amd64/machdep.c | 3 +- sys/arch/amd64/include/cpu.h | 3 +- sys/arch/i386/i386/machdep.c | 3 +- sys/arch/i386/include/cpu.h | 3 +- sys/dev/acpi/acpicpu.c | 67 ++++++++++++++++++++++++++++++++- 7 files changed, 84 insertions(+), 9 deletions(-) diff --git a/sys/arch/amd64/amd64/cpu.c b/sys/arch/amd64/amd64/cpu.c index 6714c0237f9..8df6de03b89 100644 --- a/sys/arch/amd64/amd64/cpu.c +++ b/sys/arch/amd64/amd64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.189 2024/05/29 12:21:33 kettenis Exp $ */ +/* $OpenBSD: cpu.c,v 1.190 2024/06/07 16:53:35 kettenis Exp $ */ /* $NetBSD: cpu.c,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */ /*- @@ -1469,7 +1469,10 @@ int cpu_suspended; void cpu_suspend_cycle(void) { - cpu_idle_cycle_fcn(); + if (cpu_suspend_cycle_fcn) + cpu_suspend_cycle_fcn(); + else + cpu_idle_cycle_fcn(); } int diff --git a/sys/arch/amd64/amd64/ipifuncs.c b/sys/arch/amd64/amd64/ipifuncs.c index 5c330d7a139..482fc9eebb4 100644 --- a/sys/arch/amd64/amd64/ipifuncs.c +++ b/sys/arch/amd64/amd64/ipifuncs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipifuncs.c,v 1.38 2023/10/30 12:50:59 mvs Exp $ */ +/* $OpenBSD: ipifuncs.c,v 1.39 2024/06/07 16:53:35 kettenis Exp $ */ /* $NetBSD: ipifuncs.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $ */ /*- @@ -128,7 +128,10 @@ x86_64_ipi_halt(struct cpu_info *ci) wbinvd(); for(;;) { - __asm volatile("hlt"); + if (cpu_suspend_cycle_fcn) + cpu_suspend_cycle_fcn(); + else + __asm volatile("hlt"); } } diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index 343cc0f7e52..35cea0b5810 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.293 2024/04/29 00:29:48 jsg Exp $ */ +/* $OpenBSD: machdep.c,v 1.294 2024/06/07 16:53:35 kettenis Exp $ */ /* $NetBSD: machdep.c,v 1.3 2003/05/07 22:58:18 fvdl Exp $ */ /*- @@ -162,6 +162,7 @@ char machine[] = MACHINE; */ void cpu_idle_cycle_hlt(void); void (*cpu_idle_cycle_fcn)(void) = &cpu_idle_cycle_hlt; +void (*cpu_suspend_cycle_fcn)(void); /* the following is used externally for concurrent handlers */ int setperf_prio = 0; diff --git a/sys/arch/amd64/include/cpu.h b/sys/arch/amd64/include/cpu.h index fab34e5bd9d..90f5dd9e905 100644 --- a/sys/arch/amd64/include/cpu.h +++ b/sys/arch/amd64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.171 2024/05/29 12:21:33 kettenis Exp $ */ +/* $OpenBSD: cpu.h,v 1.172 2024/06/07 16:53:35 kettenis Exp $ */ /* $NetBSD: cpu.h,v 1.1 2003/04/26 18:39:39 fvdl Exp $ */ /*- @@ -420,6 +420,7 @@ void cpu_proc_fork(struct proc *, struct proc *); int amd64_pa_used(paddr_t); #define cpu_idle_enter() do { /* nothing */ } while (0) extern void (*cpu_idle_cycle_fcn)(void); +extern void (*cpu_suspend_cycle_fcn)(void); #define cpu_idle_cycle() (*cpu_idle_cycle_fcn)() #define cpu_idle_leave() do { /* nothing */ } while (0) extern void (*initclock_func)(void); diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index a75259a7474..49999ca6685 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.671 2024/05/26 13:37:32 kettenis Exp $ */ +/* $OpenBSD: machdep.c,v 1.672 2024/06/07 16:53:35 kettenis Exp $ */ /* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */ /*- @@ -167,6 +167,7 @@ char machine[] = MACHINE; void (*cpu_idle_leave_fcn)(void) = NULL; void (*cpu_idle_cycle_fcn)(void) = NULL; void (*cpu_idle_enter_fcn)(void) = NULL; +void (*cpu_suspend_cycle_fcn)(void); struct uvm_constraint_range isa_constraint = { 0x0, 0x00ffffffUL }; diff --git a/sys/arch/i386/include/cpu.h b/sys/arch/i386/include/cpu.h index 1c2d693846c..565f6159c74 100644 --- a/sys/arch/i386/include/cpu.h +++ b/sys/arch/i386/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.189 2024/05/21 23:16:06 jsg Exp $ */ +/* $OpenBSD: cpu.h,v 1.190 2024/06/07 16:53:35 kettenis Exp $ */ /* $NetBSD: cpu.h,v 1.35 1996/05/05 19:29:26 christos Exp $ */ /*- @@ -375,6 +375,7 @@ extern const struct cpu_cpuid_nameclass i386_cpuid_cpus[]; extern void (*cpu_idle_enter_fcn)(void); extern void (*cpu_idle_cycle_fcn)(void); extern void (*cpu_idle_leave_fcn)(void); +extern void (*cpu_suspend_cycle_fcn)(void); extern int cpuspeed; diff --git a/sys/dev/acpi/acpicpu.c b/sys/dev/acpi/acpicpu.c index 0a5ee1fea7d..6a126faddf1 100644 --- a/sys/dev/acpi/acpicpu.c +++ b/sys/dev/acpi/acpicpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpicpu.c,v 1.92 2022/04/06 18:59:27 naddy Exp $ */ +/* $OpenBSD: acpicpu.c,v 1.93 2024/06/07 16:53:35 kettenis Exp $ */ /* * Copyright (c) 2005 Marco Peereboom * Copyright (c) 2015 Philip Guenther @@ -172,6 +172,7 @@ void acpicpu_add_cstate(struct acpicpu_softc *_sc, int _state, int _method, int _flags, int _latency, int _power, uint64_t _address); void acpicpu_set_pdc(struct acpicpu_softc *); void acpicpu_idle(void); +void acpicpu_suspend(void); #if 0 void acpicpu_set_throttle(struct acpicpu_softc *, int); @@ -747,6 +748,7 @@ acpicpu_attach(struct device *parent, struct device *self, void *aux) extern uint32_t acpi_force_bm; cpu_idle_cycle_fcn = &acpicpu_idle; + cpu_suspend_cycle_fcn = &acpicpu_suspend; /* * C3 (and maybe C2?) needs BM_RLD to be set to @@ -1277,3 +1279,66 @@ acpicpu_idle(void) sc->sc_prev_sleep = (sc->sc_prev_sleep + (sc->sc_prev_sleep >> 1) + itime) >> 1; } + +void +acpicpu_suspend(void) +{ + extern int cpu_suspended; + struct cpu_info *ci = curcpu(); + struct acpicpu_softc *sc = (struct acpicpu_softc *)ci->ci_acpicpudev; + struct acpi_cstate *best, *cx; + + if (sc == NULL) { + __asm volatile("sti"); + panic("null acpicpu"); + } + + /* + * Find the lowest usable state. + */ + best = cx = SLIST_FIRST(&sc->sc_cstates); + while ((cx->flags & CST_FLAG_SKIP)) { + if ((cx = SLIST_NEXT(cx, link)) == NULL) + break; + best = cx; + } + + switch (best->method) { + default: + case CST_METH_HALT: + __asm volatile("sti; hlt"); + break; + + case CST_METH_IO_HALT: + inb((u_short)best->address); + __asm volatile("sti; hlt"); + break; + + case CST_METH_MWAIT: + { + unsigned int hints; + + hints = (unsigned)best->address; + /* intel errata AAI65: cflush before monitor */ + if (ci->ci_cflushsz != 0 && + strcmp(cpu_vendor, "GenuineIntel") == 0) { + membar_sync(); + clflush((unsigned long)&cpu_suspended); + membar_sync(); + } + + monitor(&cpu_suspended, 0, 0); + if (cpu_suspended || !CPU_IS_PRIMARY(ci)) + mwait(0, hints); + + break; + } + + case CST_METH_GAS_IO: + inb((u_short)best->address); + /* something harmless to give system time to change state */ + acpi_read_pmreg(acpi_softc, ACPIREG_PM1_STS, 0); + break; + + } +} -- 2.20.1