Some time ago, I had added support for using the MWAIT instruction in the
idle loop. Various people found that made their boxes run hot, to the
point that several developers diked it out of their own builds; I've
committed one of those yesteryad pending a proper fix.
So, to start on that: the diff below expands our handling of the ACPI _CST
values to detect the Intel "functional fixed hardware" register type for
C-state control and report it in the acpicpu dmesg lines, ala:
acpicpu0 at acpi0: C3, C2, C1(mwait), PSS
I have diff on top of this that adds callbacks and amd64 bits to properly
notify CPUs of the C1 type and thus enable mwait use if the _CST specifies
it, but let's first see if the _CST output matches our expectations.
IN PARTICULAR, IF YOUR BOX RAN HOT WITH MWAIT, please run with this diff
report your dmesg!
Philip
Index: dev/acpi/acpicpu.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpicpu.c,v
retrieving revision 1.62
diff -u -p -r1.62 acpicpu.c
--- dev/acpi/acpicpu.c 9 Dec 2014 06:58:29 -0000 1.62
+++ dev/acpi/acpicpu.c 14 Dec 2014 21:41:44 -0000
@@ -111,7 +111,8 @@ struct acpicpu_softc {
int sc_pss_len;
int sc_ppc;
- int sc_level;
+ short sc_level;
+ u_int8_t sc_cst_c1_type;
struct acpicpu_pss *sc_pss;
struct acpicpu_pct sc_pct;
@@ -120,6 +121,8 @@ struct acpicpu_softc {
u_int32_t sc_pct_ctrl_as;
u_int32_t sc_pct_stat_len;
u_int32_t sc_pct_ctrl_len;
+ u_int64_t sc_cst_c1_addr; /* or mwait hint */
+
/*
* XXX: _PPC Change listener
* PPC changes can occur when for example a machine is disconnected
@@ -135,6 +138,7 @@ void acpicpu_add_cstatepkg(struct aml
int acpicpu_getppc(struct acpicpu_softc *);
int acpicpu_getpct(struct acpicpu_softc *);
int acpicpu_getpss(struct acpicpu_softc *);
+int acpicpu_getcst(struct acpicpu_softc *);
struct acpi_cstate *acpicpu_add_cstate(struct acpicpu_softc *, int, int, int,
int);
void acpicpu_set_pdc(struct acpicpu_softc *);
@@ -284,13 +288,11 @@ acpicpu_add_cstate(struct acpicpu_softc
switch (type) {
case ACPI_STATE_C2:
- if (latency > ACPI_MAX_C2_LATENCY || !address ||
- (sc->sc_flags & FLAGS_NO_C2))
+ if (!address || (sc->sc_flags & FLAGS_NO_C2))
goto bad;
break;
case ACPI_STATE_C3:
- if (latency > ACPI_MAX_C3_LATENCY || !address ||
- (sc->sc_flags & FLAGS_NO_C3))
+ if (!address || (sc->sc_flags & FLAGS_NO_C3))
goto bad;
break;
}
@@ -315,6 +317,7 @@ void
acpicpu_add_cstatepkg(struct aml_value *val, void *arg)
{
struct acpicpu_softc *sc = arg;
+ int64_t state;
#if defined(ACPI_DEBUG) && !defined(SMALL_KERNEL)
aml_showvalue(val, 0);
@@ -322,11 +325,77 @@ acpicpu_add_cstatepkg(struct aml_value *
if (val->type != AML_OBJTYPE_PACKAGE || val->length != 4)
return;
- acpicpu_add_cstate(sc, val->v_package[1]->v_integer,
- val->v_package[2]->v_integer,
+ state = val->v_package[1]->v_integer;
+
+ /*
+ * Is there an C1 state implemented via either "I/O then halt"
+ * or mwait? Look for a "generic register" which is of
+ * Functional Fixed Hardware type for intel
+ */
+ if (state == 1 && val->v_package[0]->type == AML_OBJTYPE_BUFFER) {
+ u_int8_t *buf = val->v_package[0]->v_buffer;
+
+ if (buf[0] == LR_GENREGISTER &&
+ buf[1] == 0x0C && /* length (low) */
+ buf[2] == 0x00 && /* length (high) */
+ buf[3] == GAS_FUNCTIONAL_FIXED &&
+ buf[4] == 0x01) { /* vendor == intel */
+
+ /* extract the bottom 32 bit of address */
+ sc->sc_cst_c1_addr = buf[8] + (buf[9] << 8) +
+ (buf[10] << 16) + (buf[11] << 24);
+
+ switch (buf[5]) {
+ case CST_C1_HALT:
+ sc->sc_cst_c1_type = CST_C1_HALT;
+ break;
+
+ case CST_C1_IO_HALT:
+ sc->sc_cst_c1_type = CST_C1_IO_HALT;
+
+ /* extract the top 32 bits of address */
+ sc->sc_cst_c1_addr +=
+ (u_int64_t)(buf[12] + (buf[13] << 8) +
+ (buf[14] << 16) + (buf[15] << 24)) << 32;
+ break;
+
+ case CST_C1_MWAIT:
+ /* skip if bus master avoidance required */
+ if ((buf[6] & 0x02) == 0)
+ sc->sc_cst_c1_type = CST_C1_MWAIT;
+ break;
+ }
+ }
+ }
+
+ acpicpu_add_cstate(sc, state, val->v_package[2]->v_integer,
val->v_package[3]->v_integer, -1);
}
+int
+acpicpu_getcst(struct acpicpu_softc *sc)
+{
+ struct aml_value res;
+ struct acpi_cstate *cx;
+
+ /* delete the existing list */
+ while ((cx = SLIST_FIRST(&sc->sc_cstates)) != NULL) {
+ SLIST_REMOVE_HEAD(&sc->sc_cstates, link);
+ free(cx, M_DEVBUF, 0);
+ }
+
+ /* reset the C1 info */
+ sc->sc_cst_c1_type = CST_C1_NONE;
+
+ if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res))
+ return (1);
+
+ aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc);
+ aml_freevalue(&res);
+
+ return (0);
+}
+
int
acpicpu_match(struct device *parent, void *match, void *aux)
@@ -386,15 +455,13 @@ acpicpu_attach(struct device *parent, st
#endif
/* Get C-States from _CST or FADT */
- if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res)) {
- aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc);
- aml_freevalue(&res);
- }
- else {
+ if (acpicpu_getcst(sc)) {
/* Some systems don't export a full PBLK reduce functionality */
- if (sc->sc_pblk_len < 5)
+ if (sc->sc_pblk_len < 5 ||
+ sc->sc_acpi->sc_fadt->p_lvl2_lat > ACPI_MAX_C2_LATENCY)
sc->sc_flags |= FLAGS_NO_C2;
- if (sc->sc_pblk_len < 6)
+ if (sc->sc_pblk_len < 6 ||
+ sc->sc_acpi->sc_fadt->p_lvl3_lat > ACPI_MAX_C3_LATENCY)
sc->sc_flags |= FLAGS_NO_C3;
acpicpu_add_cstate(sc, ACPI_STATE_C2,
sc->sc_acpi->sc_fadt->p_lvl2_lat, -1,
@@ -470,6 +537,21 @@ acpicpu_attach(struct device *parent, st
break;
case ACPI_STATE_C1:
printf(" C1");
+ switch (sc->sc_cst_c1_type) {
+ case CST_C1_HALT:
+ printf("(halt)");
+ break;
+ case CST_C1_IO_HALT:
+ printf("(I/O-halt)");
+ break;
+ case CST_C1_MWAIT:
+ printf("(mwait");
+ if (sc->sc_cst_c1_addr != 0)
+ printf(".%llx",
+ sc->sc_cst_c1_addr);
+ printf(")");
+ break;
+ }
break;
case ACPI_STATE_C2:
printf(" C2");
@@ -682,8 +764,12 @@ acpicpu_notify(struct aml_node *node, in
acpicpu_getpss(sc);
if (sc->sc_notify)
sc->sc_notify(sc->sc_pss, sc->sc_pss_len);
+ break;
+ case 0x81: /* _CST changed, retrieve new values */
+ acpicpu_getcst(sc);
break;
+
default:
printf("%s: unhandled cpu event %x\n", DEVNAME(sc),
notify_type);
Index: dev/acpi/acpidev.h
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpidev.h,v
retrieving revision 1.36
diff -u -p -r1.36 acpidev.h
--- dev/acpi/acpidev.h 23 Nov 2014 20:33:47 -0000 1.36
+++ dev/acpi/acpidev.h 14 Dec 2014 21:41:44 -0000
@@ -229,6 +229,12 @@ struct acpicpu_pss {
int acpicpu_fetch_pss(struct acpicpu_pss **);
void acpicpu_set_notify(void (*)(struct acpicpu_pss *, int));
+
+#define CST_C1_NONE (-1)
+#define CST_C1_HALT 0x0
+#define CST_C1_IO_HALT 0x1
+#define CST_C1_MWAIT 0x2
+
/*
* XXX this is returned in a buffer and is not a "natural" type.
*