On Thu, 10 Feb 2022, Jan Stary wrote:
> > > When you build a kernel with this, do please add ACPI_DEBUG to your 
> > > kernel 
> > > config, so we can see more details about what the firmware is telling us.
> > 
> > Full dmesg below, without ACPI_DEBUG.
> > 
> > Also below, full /var/log/messages with ACPI_DEBUG,
> > as it spams dmesg so much that /var/run/dmesg.boot
> > does not really contain the booting kernel device messages,
> > being rolled off by the storm of ACPI_DEBUG messages.
> > (Is there a way to increase that buffer,
> > so that dmesg.boot would hold everything?)
> > Of course, this is only after syslogd has started;
> > hopefully the acpicpu events are there.
> > 
> > Both contain a log of the same scenario: cold start the machine on AC,
> > plug AC out, in, out, in; shutdown with the power button.
> 
> With MSGBUFSIZE cranked up,
> here is a dmesg containing all,
> up to before the shutdown.

Uh, wow, I had forgotten how horrifically verbose ACPI_DEBUG was.  I'm 
half inclined to delete all the uses of ACPI_DEBUG from acpicpu.c and use 
a different #define for them.

That said, the data shows the expected 0x81 notifications (and no 0x80 
notifications) on the CPU objects, and the values appear to be accurately 
parsed the acpicpu.c.  Whew.


So here's a revised diff that tries to make it safe for ACPI to notify us 
that a CPU's _CST has changed while that cpu is entering idle.  Revert the 
previous diff before trying to apply this one.  Please give it a shot; no 
need for ACPI_DEBUG now!


Philip


Index: sys/dev/acpi/acpicpu.c
===================================================================
RCS file: /data/src/openbsd/src/sys/dev/acpi/acpicpu.c,v
retrieving revision 1.91
diff -u -p -r1.91 acpicpu.c
--- sys/dev/acpi/acpicpu.c      9 Jan 2022 05:42:37 -0000       1.91
+++ sys/dev/acpi/acpicpu.c      11 Feb 2022 07:19:11 -0000
@@ -25,6 +25,7 @@
 #include <sys/malloc.h>
 #include <sys/queue.h>
 #include <sys/atomic.h>
+#include <sys/mutex.h>
 
 #include <machine/bus.h>
 #include <machine/cpu.h>
@@ -80,6 +81,7 @@ void  acpicpu_setperf_ppc_change(struct a
 #define CST_FLAG_FALLBACK              0x4000  /* fallback for broken _CST */
 #define CST_FLAG_SKIP                  0x8000  /* state is worse choice */
 
+#define FLAGS_NOCST            0x01
 #define FLAGS_MWAIT_ONLY       0x02
 #define FLAGS_BMCHECK          0x04
 #define FLAGS_NOTHROTTLE       0x08
@@ -130,6 +132,11 @@ struct acpicpu_softc {
        struct cpu_info         *sc_ci;
        SLIST_HEAD(,acpi_cstate) sc_cstates;
 
+       /* sc_mtx protects sc_cstates_active and sc_mwait_only */
+       struct mutex            sc_mtx;
+       struct acpi_cstate      *sc_cstates_active;
+       int                     sc_mwait_only;
+
        bus_space_tag_t         sc_iot;
        bus_space_handle_t      sc_ioh;
 
@@ -161,10 +168,12 @@ struct acpicpu_softc {
 
 void   acpicpu_add_cstatepkg(struct aml_value *, void *);
 void   acpicpu_add_cdeppkg(struct aml_value *, void *);
+void   acpicpu_cst_activate(struct acpicpu_softc *);
 int    acpicpu_getppc(struct acpicpu_softc *);
 int    acpicpu_getpct(struct acpicpu_softc *);
 int    acpicpu_getpss(struct acpicpu_softc *);
 int    acpicpu_getcst(struct acpicpu_softc *);
+void   acpicpu_free_states(struct acpi_cstate *);
 void   acpicpu_getcst_from_fadt(struct acpicpu_softc *);
 void   acpicpu_print_one_cst(struct acpi_cstate *_cx);
 void   acpicpu_print_cst(struct acpicpu_softc *_sc);
@@ -511,10 +520,10 @@ acpicpu_getcst(struct acpicpu_softc *sc)
        int                     use_nonmwait;
 
        /* delete the existing list */
-       while ((cx = SLIST_FIRST(&sc->sc_cstates)) != NULL) {
-               SLIST_REMOVE_HEAD(&sc->sc_cstates, link);
-               free(cx, M_DEVBUF, sizeof(*cx));
-       }
+       cx = SLIST_FIRST(&sc->sc_cstates);
+       SLIST_INIT(&sc->sc_cstates);
+       if (cx != sc->sc_cstates_active)
+               acpicpu_free_states(cx);
 
        /* provide a fallback C1-via-halt in case _CST's C1 is bogus */
        acpicpu_add_cstate(sc, ACPI_STATE_C1, CST_METH_HALT,
@@ -526,17 +535,18 @@ acpicpu_getcst(struct acpicpu_softc *sc)
        aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc);
        aml_freevalue(&res);
 
+       use_nonmwait = 0;
+
        /* only have fallback state?  then no _CST objects were understood */
        cx = SLIST_FIRST(&sc->sc_cstates);
        if (cx->flags & CST_FLAG_FALLBACK)
-               return (1);
+               goto done;
 
        /*
         * Skip states >= C2 if the CPU's LAPIC timer stops in deep
         * states (i.e., it doesn't have the 'ARAT' bit set).
         * Also keep track if all the states we'll use use mwait.
         */
-       use_nonmwait = 0;
        while ((next_cx = SLIST_NEXT(cx, link)) != NULL) {
                if (cx->state > 1 &&
                    (sc->sc_ci->ci_feature_tpmflags & TPM_ARAT) == 0)
@@ -545,6 +555,7 @@ acpicpu_getcst(struct acpicpu_softc *sc)
                        use_nonmwait = 1;
                cx = next_cx;
        }
+done:
        if (use_nonmwait)
                sc->sc_flags &= ~FLAGS_MWAIT_ONLY;
        else
@@ -558,6 +569,16 @@ acpicpu_getcst(struct acpicpu_softc *sc)
        return (0);
 }
 
+void
+acpicpu_free_states(struct acpi_cstate *cx)
+{
+       while (cx != NULL) {
+               struct acpi_cstate *next_cx = SLIST_NEXT(cx, link);
+               free(cx, M_DEVBUF, sizeof(*cx));
+               cx = next_cx;
+       }
+}
+
 /*
  * old-style fixed C-state info in the FADT.
  * Note that this has extra restrictions on values and flags.
@@ -689,7 +710,9 @@ acpicpu_attach(struct device *parent, st
        sc->sc_acpi = (struct acpi_softc *)parent;
        sc->sc_devnode = aa->aaa_node;
 
+       mtx_init(&sc->sc_mtx, IPL_NONE);
        SLIST_INIT(&sc->sc_cstates);
+       sc->sc_cstates_active = NULL;
 
        if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
            "_UID", 0, NULL, &uid) == 0)
@@ -734,9 +757,10 @@ acpicpu_attach(struct device *parent, st
 #endif
 
        /* Get C-States from _CST or FADT */
-       if (acpicpu_getcst(sc) || SLIST_EMPTY(&sc->sc_cstates))
+       if (acpicpu_getcst(sc) || SLIST_EMPTY(&sc->sc_cstates)) {
                acpicpu_getcst_from_fadt(sc);
-       else {
+               sc->sc_flags |= FLAGS_NOCST;
+       } else {
                /* Notify BIOS we use _CST objects */
                if (sc->sc_acpi->sc_fadt->cst_cnt) {
                        acpi_write_pmreg(sc->sc_acpi, ACPIREG_SMICMD, 0,
@@ -794,9 +818,6 @@ acpicpu_attach(struct device *parent, st
                                    0, sc->sc_acpi->sc_fadt->pstate_cnt);
                        }
 
-                       aml_register_notify(sc->sc_devnode, NULL,
-                           acpicpu_notify, sc, ACPIDEV_NOPOLL);
-
                        acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
                            sc->sc_pct.pct_status.grd_gas.address_space_id,
                            sc->sc_pct.pct_status.grd_gas.address,
@@ -838,6 +859,39 @@ acpicpu_attach(struct device *parent, st
        }
 
        printf("\n");
+       acpicpu_cst_activate(sc);
+
+       /*
+        * If we understood either the provided _CST or _PPC
+        * resources, register for notification of changes.
+        */
+       if ((sc->sc_flags & FLAGS_NOCST) == 0 ||
+           (sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT)) == 0)
+       {
+#ifdef ACPI_DEBUG
+               printf(": %s: enabling notification\n", sc->sc_devnode->name);
+#endif
+               aml_register_notify(sc->sc_devnode, NULL,
+                   acpicpu_notify, sc, ACPIDEV_NOPOLL);
+       }
+}
+
+void
+acpicpu_cst_activate(struct acpicpu_softc *sc)
+{
+       struct acpi_cstate      *cx, *old_states;
+
+       mtx_enter(&sc->sc_mtx);
+       cx = SLIST_FIRST(&sc->sc_cstates);
+       old_states = sc->sc_cstates_active;
+       if (cx != old_states)
+               sc->sc_cstates_active = cx;
+       else
+               old_states = NULL;
+       sc->sc_mwait_only = (sc->sc_flags & FLAGS_MWAIT_ONLY) != 0;
+       mtx_leave(&sc->sc_mtx);
+
+       acpicpu_free_states(old_states);
 }
 
 int
@@ -1023,10 +1077,13 @@ acpicpu_notify(struct aml_node *node, in
                break;
 
        case 0x81:      /* _CST changed, retrieve new values */
+               if (sc->sc_flags & FLAGS_NOCST)
+                       break;
                acpicpu_getcst(sc);
                printf("%s: notify", DEVNAME(sc));
                acpicpu_print_cst(sc);
                printf("\n");
+               acpicpu_cst_activate(sc);
                break;
 
        default:
@@ -1151,18 +1208,23 @@ acpicpu_setperf(int level)
 void
 acpicpu_idle(void)
 {
-       struct cpu_info *ci = curcpu();
-       struct acpicpu_softc *sc = (struct acpicpu_softc *)ci->ci_acpicpudev;
-       struct acpi_cstate *best, *cx;
-       unsigned long itime;
+       struct cpu_info         *ci = curcpu();
+       struct acpicpu_softc    *sc = (struct acpicpu_softc *)ci->ci_acpicpudev;
+       struct acpi_cstate      *best, *cx;
+       unsigned long           itime;
+       u_short                 state;
+       short                   method;
+       u_int                   address;
 
        if (sc == NULL) {
                __asm volatile("sti");
                panic("null acpicpu");
        }
 
+       mtx_enter(&sc->sc_mtx);
+
        /* possibly update the MWAIT_ONLY flag in cpu_info */
-       if (sc->sc_flags & FLAGS_MWAIT_ONLY) {
+       if (sc->sc_mwait_only) {
                if ((ci->ci_mwait & MWAIT_ONLY) == 0)
                        atomic_setbits_int(&ci->ci_mwait, MWAIT_ONLY);
        } else if (ci->ci_mwait & MWAIT_ONLY)
@@ -1172,7 +1234,7 @@ acpicpu_idle(void)
         * Find the first state with a latency we'll accept, ignoring
         * states marked skippable
         */
-       best = cx = SLIST_FIRST(&sc->sc_cstates);
+       best = cx = sc->sc_cstates_active;
        while ((cx->flags & CST_FLAG_SKIP) ||
            cx->latency * 3 > sc->sc_prev_sleep) {
                if ((cx = SLIST_NEXT(cx, link)) == NULL)
@@ -1196,18 +1258,23 @@ acpicpu_idle(void)
                best = cx;
        }
 
+       /* made our choice; save the values we need and unlock */
+       state   = best->state;
+       method  = best->method;
+       address = best->address;
+       mtx_leave(&sc->sc_mtx);
 
-       atomic_inc_long(&cst_stats[best->state]);
+       atomic_inc_long(&cst_stats[state]);
 
        itime = tick / 2;
-       switch (best->method) {
+       switch (method) {
        default:
        case CST_METH_HALT:
                __asm volatile("sti; hlt");
                break;
 
        case CST_METH_IO_HALT:
-               inb((u_short)best->address);
+               inb((u_short)address);
                __asm volatile("sti; hlt");
                break;
 
@@ -1238,7 +1305,7 @@ acpicpu_idle(void)
                 * something to the queue and called cpu_unidle() between
                 * the check in sched_idle() and here.
                 */
-               hints = (unsigned)best->address;
+               hints = (unsigned)address;
                microuptime(&start);
                atomic_setbits_int(&ci->ci_mwait, MWAIT_IDLING);
                if (cpu_is_idle(ci)) {
@@ -1265,7 +1332,7 @@ acpicpu_idle(void)
                }
 
        case CST_METH_GAS_IO:
-               inb((u_short)best->address);
+               inb((u_short)address);
                /* something harmless to give system time to change state */
                acpi_read_pmreg(acpi_softc, ACPIREG_PM1_STS, 0);
                break;

Reply via email to