Make sure we select the deepest possible C-state during suspend-to-idle.
authorkettenis <kettenis@openbsd.org>
Fri, 7 Jun 2024 16:53:35 +0000 (16:53 +0000)
committerkettenis <kettenis@openbsd.org>
Fri, 7 Jun 2024 16:53:35 +0000 (16:53 +0000)
ok deraadt@, guenther@, mlarkin@, jsg@

sys/arch/amd64/amd64/cpu.c
sys/arch/amd64/amd64/ipifuncs.c
sys/arch/amd64/amd64/machdep.c
sys/arch/amd64/include/cpu.h
sys/arch/i386/i386/machdep.c
sys/arch/i386/include/cpu.h
sys/dev/acpi/acpicpu.c

index 6714c02..8df6de0 100644 (file)
@@ -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
index 5c330d7..482fc9e 100644 (file)
@@ -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");
        }
 }
 
index 343cc0f..35cea0b 100644 (file)
@@ -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;
index fab34e5..90f5dd9 100644 (file)
@@ -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);
index a75259a..49999ca 100644 (file)
@@ -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 };
index 1c2d693..565f615 100644 (file)
@@ -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;
 
index 0a5ee1f..6a126fa 100644 (file)
@@ -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 <marco@openbsd.org>
  * Copyright (c) 2015 Philip Guenther <guenther@openbsd.org>
@@ -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;
+
+       }
+}