> Date: Thu, 10 Feb 2022 23:46:43 -0800
> From: [email protected]
>
> 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.
Go for it.
> 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;
>
>