diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf index af7b6ff6302e..145077d171d3 100644 --- a/libexec/rc/rc.conf +++ b/libexec/rc/rc.conf @@ -719,8 +719,12 @@ devfs_set_rulesets="" # A list of /mount/dev=ruleset_name settings to devfs_load_rulesets="YES" # Enable to always load the default rulesets performance_cx_lowest="NONE" # Online CPU idle state performance_cpu_freq="NONE" # Online CPU frequency +performance_tz0_active="NONE" # Online forced active cooling zone +performance_tz0_PSV="NONE" # Online passive cooling threshold economy_cx_lowest="Cmax" # Offline CPU idle state economy_cpu_freq="NONE" # Offline CPU frequency +economy_tz0_active="NONE" # Offline forced active cooling zone +economy_tz0_PSV="NONE" # Offline passive cooling threshold virecover_enable="YES" # Perform housekeeping for the vi(1) editor ugidfw_enable="NO" # Load mac_bsdextended(4) rules on boot bsdextended_script="/etc/rc.bsdextended" # Default mac_bsdextended(4) diff --git a/libexec/rc/rc.d/power_profile b/libexec/rc/rc.d/power_profile index b7de08063437..9061f5f29b46 100755 --- a/libexec/rc/rc.d/power_profile +++ b/libexec/rc/rc.d/power_profile @@ -80,6 +80,14 @@ case ${state} in esac # Set the various sysctls based on the profile's values. +node="hw.acpi.thermal.tz0._PSV" +eval value=\$${profile}_tz0_PSV +sysctl_set + +node="hw.acpi.thermal.tz0.active" +eval value=\$${profile}_tz0_active +sysctl_set + node="hw.acpi.cpu.cx_lowest" highest_value="C1" lowest_value="Cmax" diff --git a/share/man/man4/acpi_thermal.4 b/share/man/man4/acpi_thermal.4 index d1cf8ee60ef2..05aeba12b6db 100644 --- a/share/man/man4/acpi_thermal.4 +++ b/share/man/man4/acpi_thermal.4 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 17, 2007 +.Dd July 31, 2022 .Dt ACPI_THERMAL 4 .Os .Sh NAME @@ -84,6 +84,9 @@ Current temperature for this zone. .It Va hw.acpi.thermal.tz%d._PSV Temperature to start passive cooling by throttling down CPU, etc. This value can be overridden by the user. +.It Va hw.acpi.thermal.tz%d._CR3 +Temperature to start critical suspend to RAM (S3). +This value can be overridden by the user. .It Va hw.acpi.thermal.tz%d._HOT Temperature to start critical suspend to disk (S4). This value can be overridden by the user. diff --git a/sys/dev/acpica/acpi_thermal.c b/sys/dev/acpica/acpi_thermal.c index 9ff86ae8d827..8d3cfbedc7cb 100644 --- a/sys/dev/acpica/acpi_thermal.c +++ b/sys/dev/acpica/acpi_thermal.c @@ -60,7 +60,7 @@ ACPI_MODULE_NAME("THERMAL") #define TZ_NOTIFY_TEMPERATURE 0x80 /* Temperature changed. */ #define TZ_NOTIFY_LEVELS 0x81 /* Cooling levels changed. */ #define TZ_NOTIFY_DEVICES 0x82 /* Device lists changed. */ -#define TZ_NOTIFY_CRITICAL 0xcc /* Fake notify that _CRT/_HOT reached. */ +#define TZ_NOTIFY_CRITICAL 0xcc /* Fake notify that _CRT/_HOT/_CR3 reached. */ /* Check for temperature changes every 10 seconds by default */ #define TZ_POLLRATE 10 @@ -78,6 +78,7 @@ struct acpi_tz_zone { ACPI_BUFFER al[TZ_NUMLEVELS]; int crt; int hot; + int cr3; ACPI_BUFFER psl; int psv; int tc1; @@ -97,8 +98,9 @@ struct acpi_tz_softc { int tz_thflags; /*Current temp-related flags*/ #define TZ_THFLAG_NONE 0 #define TZ_THFLAG_PSV (1<<0) -#define TZ_THFLAG_HOT (1<<2) -#define TZ_THFLAG_CRT (1<<3) +#define TZ_THFLAG_CR3 (1<<2) +#define TZ_THFLAG_HOT (1<<3) +#define TZ_THFLAG_CRT (1<<4) int tz_flags; #define TZ_FLAG_NO_SCP (1<<0) /*No _SCP method*/ #define TZ_FLAG_GETPROFILE (1<<1) /*Get power_profile in timeout*/ @@ -282,6 +284,10 @@ acpi_tz_attach(device_t dev) OID_AUTO, "_PSV", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, offsetof(struct acpi_tz_softc, tz_zone.psv), acpi_tz_temp_sysctl, "IK", "passive cooling temp setpoint"); + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_CR3", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, + offsetof(struct acpi_tz_softc, tz_zone.cr3), acpi_tz_temp_sysctl, "IK", + "too warm temp setpoint (standby now)"); SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "_HOT", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, offsetof(struct acpi_tz_softc, tz_zone.hot), acpi_tz_temp_sysctl, "IK", @@ -421,6 +427,7 @@ acpi_tz_establish(struct acpi_tz_softc *sc) } acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt); acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot); + acpi_tz_getparam(sc, "_CR3", &sc->tz_zone.cr3); sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER; sc->tz_zone.psl.Pointer = NULL; AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl); @@ -438,6 +445,7 @@ acpi_tz_establish(struct acpi_tz_softc *sc) */ acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT"); acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT"); + acpi_tz_sanity(sc, &sc->tz_zone.cr3, "_CR3"); acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV"); for (i = 0; i < TZ_NUMLEVELS; i++) acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx"); @@ -495,6 +503,7 @@ acpi_tz_get_temperature(struct acpi_tz_softc *sc) static void acpi_tz_monitor(void *Context) { + struct acpi_softc *acpi_sc; struct acpi_tz_softc *sc; struct timespec curtime; int temp; @@ -545,6 +554,8 @@ acpi_tz_monitor(void *Context) newflags = TZ_THFLAG_NONE; if (sc->tz_zone.psv != -1 && temp >= sc->tz_zone.psv) newflags |= TZ_THFLAG_PSV; + if (sc->tz_zone.cr3 != -1 && temp >= sc->tz_zone.cr3) + newflags |= TZ_THFLAG_CR3; if (sc->tz_zone.hot != -1 && temp >= sc->tz_zone.hot) newflags |= TZ_THFLAG_HOT; if (sc->tz_zone.crt != -1 && temp >= sc->tz_zone.crt) @@ -595,7 +606,7 @@ acpi_tz_monitor(void *Context) /* XXX (de)activate any passive cooling that may be required. */ /* - * If the temperature is at _HOT or _CRT, increment our event count. + * If the temperature is at _CR3, _HOT or _CRT, increment our event count. * If it has occurred enough times, shutdown the system. This is * needed because some systems will report an invalid high temperature * for one poll cycle. It is suspected this is due to the embedded @@ -604,13 +615,18 @@ acpi_tz_monitor(void *Context) * * If we're almost at that threshold, notify the user through devd(8). */ - if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) { + if ((newflags & (TZ_THFLAG_CR3 | TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) { sc->tz_validchecks++; if (sc->tz_validchecks == TZ_VALIDCHECKS) { device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds safe limits\n", TZ_KELVTOC(sc->tz_temperature)); - shutdown_nice(RB_POWEROFF); + if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) + shutdown_nice(RB_POWEROFF); + else { + acpi_sc = acpi_device_get_parent_softc(sc->tz_dev); + acpi_ReqSleepState(acpi_sc, ACPI_STATE_S3); + } } else if (sc->tz_validchecks == TZ_NOTIFYCOUNT) acpi_UserNotify("Thermal", sc->tz_handle, TZ_NOTIFY_CRITICAL); } else { @@ -1006,51 +1022,68 @@ acpi_tz_thread(void *arg) static int acpi_tz_cpufreq_restore(struct acpi_tz_softc *sc) { - device_t dev; - int error; + device_t *devs; + int devcount, error, n; if (!sc->tz_cooling_updated) return (0); - if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) + + if ((error = devclass_get_devices(devclass_find("cpufreq"), &devs, &devcount))) { + free(devs, M_TEMP); return (ENXIO); + } + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), "temperature %d.%dC: resuming previous clock speed (%d MHz)\n", TZ_KELVTOC(sc->tz_temperature), sc->tz_cooling_saved_freq); - error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN); + for (n = 0; n < devcount; n++) { + if ((error = CPUFREQ_SET(devs[n], NULL, CPUFREQ_PRIO_KERN))) { + /* + * If you find yourself here: cf_set_method() in kern_cpu.c + * requires CPUFREQ_GET() be run before CPUFREQ_SET(), otherwise on + * a restore sc->curr_level.total_set.freq = CPUFREQ_VAL_UNKNOWN, + * and then if it hasn't been updated the next save/restore will fail. + */ + device_printf(sc->tz_dev, "cpu%d: failed to restore cpufreq priority\n", n); + break; + } + } if (error == 0) sc->tz_cooling_updated = FALSE; + + free(devs, M_TEMP); return (error); } static int acpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req) { - device_t dev; + device_t *devs; struct cf_level *levels; - int num_levels, error, freq, desired_freq, perf, i; + int num_levels, error, freq, desired_freq, perf, devcount, i, n; levels = malloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); if (levels == NULL) return (ENOMEM); /* - * Find the main device, cpufreq0. We don't yet support independent - * CPU frequency control on SMP. + * Find the main device, cpufreq0. We make plans based upon cpu0, and + * change all the cpu frequencies to the same level. */ - if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) { - error = ENXIO; + if ((error = devclass_get_devices(devclass_find("cpufreq"), &devs, &devcount))) { + free(devs, M_TEMP); goto out; } - /* Get the current frequency. */ - error = CPUFREQ_GET(dev, &levels[0]); + /* Get the current frequency for cpu0. */ + error = CPUFREQ_GET(devs[0], &levels[0]); if (error) goto out; freq = levels[0].total_set.freq; - /* Get the current available frequency levels. */ + /* Get the current available frequency levels for cpu0. */ num_levels = CPUFREQ_MAX_LEVELS; - error = CPUFREQ_LEVELS(dev, levels, &num_levels); + error = CPUFREQ_LEVELS(devs[0], levels, &num_levels); if (error) { if (error == E2BIG) printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n"); @@ -1111,8 +1144,22 @@ acpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req) TZ_KELVTOC(sc->tz_temperature), (freq > levels[i].total_set.freq) ? "de" : "in", freq, levels[i].total_set.freq); - error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN); - if (error == 0 && !sc->tz_cooling_updated) { + + /* Change all the cpus to the chosen frequency. */ + for (n = 0; n < devcount; n++) { + /* CPUFREQ_SET() fails occasionally without this. */ + if ((error = CPUFREQ_GET(devs[n], &levels[0]))) + goto out; + + if ((error = CPUFREQ_LEVELS(devs[n], levels, &num_levels))) { + if (error == E2BIG) + printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n"); + goto out; + } + if ((error = CPUFREQ_SET(devs[n], &levels[i], CPUFREQ_PRIO_KERN))) + goto out; + } + if (!sc->tz_cooling_updated) { sc->tz_cooling_saved_freq = freq; sc->tz_cooling_updated = TRUE; } @@ -1121,6 +1168,7 @@ acpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req) out: if (levels) free(levels, M_TEMP); + free(devs, M_TEMP); return (error); }