Index: sbin/sysctl/sysctl.c diff -u -p sbin/sysctl/sysctl.c.orig sbin/sysctl/sysctl.c --- sbin/sysctl/sysctl.c.orig Wed Apr 13 15:59:07 2005 +++ sbin/sysctl/sysctl.c Sat Jul 2 14:19:21 2005 @@ -60,6 +60,9 @@ static const char rcsid[] = #include #include +#define ZEROC 2732 +#define KELVTOC(x) (((x) - ZEROC) / 10), (((x) - ZEROC) % 10) + static int aflag, bflag, dflag, eflag, hflag, Nflag, nflag, oflag, xflag; static int oidfmt(int *, int, char *, u_int *); @@ -69,6 +72,7 @@ static int sysctl_all (int *oid, int len static int name2oid(char *, int *); static void set_T_dev_t (char *, void **, size_t *); +static int set_IK(char *, void **, size_t *); static void usage(void) @@ -266,6 +270,12 @@ parse(char *string) if (strcmp(fmt, "T,dev_t") == 0) { set_T_dev_t ((char*)newval, &newval, &newsize); break; + } else if (strcmp(fmt, "IK") == 0) { + if (!set_IK((char*)newval, &newval, + &newsize)) + errx(1, "invalid value '%s'", + newval); + break; } /* FALLTHROUGH */ default: @@ -435,6 +445,39 @@ set_T_dev_t (char *path, void **val, siz *size = sizeof statb.st_rdev; } +static int +set_IK(char *temp, void **val, size_t *size) +{ + static int k; + int d1, d2; + size_t len; + char *p; + + if ((len = strlen(temp)) == 0) + return (0); + if (temp[len - 1] == 'C') { + temp[len - 1] = '\0'; + d1 = (int)strtol(temp, &p, 10); + d2 = 0; + if (p == NULL) + return (0); + if (*p == '.') { + d2 = (int)strtol(p + 1, &p, 10); + if (p == NULL || *p != '\0') + return (0); + } else if (*p != '\0') + return (0); + k = d1 * 10 + d2 + ZEROC; + } else { + k = (int)strtol(temp, &p, 10); + if (p == NULL || *p != '\0') + return (0); + } + *val = (char *)&k; + *size = sizeof k; + return (1); +} + /* * These functions uses a presently undocumented interface to the kernel * to walk the tree and get the type so it can print the value. @@ -575,7 +618,7 @@ show_var(int *oid, int nlen) if (*(int *)p < 0) printf("%d", *(int *)p); else - printf("%d.%dC", (*(int *)p - 2732) / 10, (*(int *)p - 2732) % 10); + printf("%d.%dC", KELVTOC(*(int *)p)); } else printf(hflag ? "%'d" : "%d", *(int *)p); val = " "; @@ -597,7 +640,7 @@ show_var(int *oid, int nlen) if (*(long *)p < 0) printf("%ld", *(long *)p); else - printf("%ld.%ldC", (*(long *)p - 2732) / 10, (*(long *)p - 2732) % 10); + printf("%ld.%ldC", KELVTOC(*(long *)p)); } else printf(hflag ? "%'ld" : "%ld", *(long *)p); val = " "; Index: sys/dev/acpica/acpi_thermal.c diff -u -p sys/dev/acpica/acpi_thermal.c.orig sys/dev/acpica/acpi_thermal.c --- sys/dev/acpica/acpi_thermal.c.orig Tue Feb 22 09:40:13 2005 +++ sys/dev/acpica/acpi_thermal.c Thu Jul 7 17:46:32 2005 @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD: src/sys/dev/acpica/a #include #include #include +#include #include #include #include @@ -42,6 +43,8 @@ __FBSDID("$FreeBSD: src/sys/dev/acpica/a #include #include +#include "cpufreq_if.h" + #include "acpi.h" #include @@ -106,8 +109,16 @@ struct acpi_tz_softc { struct acpi_tz_zone tz_zone; /*Thermal zone parameters*/ int tz_validchecks; + + /* passive cooling thread */ + struct proc *tz_cooling_proc; + int tz_cooling_enabled; + int tz_cooling_active; + int tz_cooling_temp; }; +#define CPUFREQ_MAX_LEVELS 64 + static int acpi_tz_probe(device_t dev); static int acpi_tz_attach(device_t dev); static int acpi_tz_establish(struct acpi_tz_softc *sc); @@ -118,12 +129,14 @@ static void acpi_tz_getparam(struct acpi int *data); static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what); static int acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS); +static int acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context); static void acpi_tz_signal(struct acpi_tz_softc *sc, int flags); static void acpi_tz_timeout(struct acpi_tz_softc *sc, int flags); static void acpi_tz_power_profile(void *arg); static void acpi_tz_thread(void *arg); +static void acpi_tz_cooling_thread(void *arg); static device_method_t acpi_tz_methods[] = { /* Device interface */ @@ -183,6 +196,9 @@ acpi_tz_attach(device_t dev) sc->tz_requested = TZ_ACTIVE_NONE; sc->tz_active = TZ_ACTIVE_NONE; sc->tz_thflags = TZ_THFLAG_NONE; + sc->tz_cooling_proc = NULL; + sc->tz_cooling_enabled = 0; + sc->tz_cooling_active = 0; /* * Parse the current state of the thermal zone and build control @@ -192,6 +208,8 @@ acpi_tz_attach(device_t dev) if ((error = acpi_tz_establish(sc)) != 0) return (error); + sc->tz_cooling_temp = sc->tz_zone.psv; + /* * Register for any Notify events sent to this zone. */ @@ -231,7 +249,17 @@ acpi_tz_attach(device_t dev) SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW, sc, 0, acpi_tz_active_sysctl, "I", ""); - + SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "passive_cooling", CTLTYPE_INT | CTLFLAG_RW, + sc, 0, acpi_tz_cooling_sysctl, "I", ""); + SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "passive_threshold", CTLFLAG_RW, + &sc->tz_cooling_temp, sizeof(sc->tz_cooling_temp), "IK", + ""); + SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "passive_active", CTLFLAG_RD, + &sc->tz_cooling_active, 0, ""); + SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), OID_AUTO, "thermal_flags", CTLFLAG_RD, &sc->tz_thflags, 0, "thermal zone flags"); @@ -248,6 +276,16 @@ acpi_tz_attach(device_t dev) OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac, sizeof(sc->tz_zone.ac), "IK", ""); + SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_TC1", CTLFLAG_RD, + &sc->tz_zone.tc1, 0, "TC1"); + SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_TC2", CTLFLAG_RD, + &sc->tz_zone.tc2, 0, "TC2"); + SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), + OID_AUTO, "_TSP", CTLFLAG_RD, + &sc->tz_zone.tsp, 0, "TSP"); + /* * Create our thread; we only need one, it will service all of the * thermal zones. Register our power profile event handler. @@ -602,6 +640,42 @@ acpi_tz_active_sysctl(SYSCTL_HANDLER_ARG return (0); } +static int +acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_tz_softc *sc; + int enabled; + int error; + char name[20]; + + sc = (struct acpi_tz_softc *)oidp->oid_arg1; + enabled = sc->tz_cooling_enabled; + error = sysctl_handle_int(oidp, &enabled, 0, req); + + /* Error or no new value */ + if (error != 0 || req->newptr == NULL) + return (error); + if (enabled != 0 && enabled != 1) + return (EINVAL); + + sc->tz_cooling_enabled = enabled; + if (enabled && sc->tz_cooling_proc == NULL) { + if (sc->tz_zone.tc1 < 0 || sc->tz_zone.tc2 < 0 || + sc->tz_zone.tsp < 0) + return (ENODEV); + snprintf(name, sizeof(name), "acpi_cooling%d", + device_get_unit(sc->tz_dev)); + error = kthread_create(acpi_tz_cooling_thread, sc, + &sc->tz_cooling_proc, RFHIGHPID, 0, name); + if (error != 0) { + device_printf(sc->tz_dev, "could not create thread - %d", error); + sc->tz_cooling_enabled = 0; + return (error); + } + } + return (0); +} + static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) { @@ -767,4 +841,126 @@ acpi_tz_thread(void *arg) else ACPI_UNLOCK(thermal); } +} + +static int +acpi_tz_cpufreq_decrease(struct acpi_tz_softc *sc, int req) +{ + device_t dev; + struct cf_level *levels; + int num_levels, error, freq, desired_freq, perf, i; + + levels = malloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); + if (levels == NULL) + return (ENOMEM); + + if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) { + error = ENXIO; + goto out; + } + + error = CPUFREQ_GET(dev, &levels[0]); + if (error) + goto out; + freq = levels[0].total_set.freq; + + num_levels = CPUFREQ_MAX_LEVELS; + error = CPUFREQ_LEVELS(dev, levels, &num_levels); + if (error) { + if (error == E2BIG) + printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n"); + goto out; + } + + perf = 100 * freq / levels[0].total_set.freq - req; + if (perf < 0) + perf = 0; + else if (perf > 100) + perf = 100; + desired_freq = levels[0].total_set.freq * perf / 100; + + for (i = 0; i < num_levels; i++) + if (levels[i].total_set.freq <= desired_freq) + break; + if (i == num_levels) + i--; + if (levels[i].total_set.freq != freq) { + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: decreasing clock speed " + "from %d MHz to %d MHz\n", + TZ_KELVTOC(sc->tz_temperature), freq, levels[i].total_set.freq); + error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN); + } + +out: + if (levels) + free(levels, M_TEMP); + return (error); +} + +static int +acpi_tz_cpufreq_restore(struct acpi_tz_softc *sc) +{ + device_t dev; + + if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) + return (ENXIO); + ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: resuming clock speed\n", + TZ_KELVTOC(sc->tz_temperature)); + return (CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN)); +} + +static void +acpi_tz_cooling(struct acpi_tz_softc *sc, int temperature) +{ + int perf; + + ACPI_LOCK(thermal); + if (sc->tz_temperature < sc->tz_cooling_temp) { + ACPI_UNLOCK(thermal); + if (sc->tz_cooling_active) { + acpi_tz_cpufreq_restore(sc); + sc->tz_cooling_active = 0; + } + return; + } + + perf = sc->tz_zone.tc1 * (sc->tz_temperature - temperature) + + sc->tz_zone.tc2 * (sc->tz_temperature - sc->tz_cooling_temp); + ACPI_UNLOCK(thermal); + perf /= 10; + if (perf != 0) { + sc->tz_cooling_active = 1; + acpi_tz_cpufreq_decrease(sc, perf); /* XXX: _PSL? */ + } + return; +} + +/* + * Passive cooling thread. + */ +static void +acpi_tz_cooling_thread(void *arg) +{ + struct acpi_tz_softc *sc; + int temperature; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + sc = (struct acpi_tz_softc *)arg; + + temperature = sc->tz_temperature; + while (sc->tz_cooling_enabled) { + acpi_tz_cooling(sc, temperature); + temperature = sc->tz_temperature; + tsleep(&sc->tz_cooling_proc, PZERO, "cooling", + hz * sc->tz_zone.tsp / 10); + } + if (sc->tz_cooling_active) { + acpi_tz_cpufreq_restore(sc); + sc->tz_cooling_active = 0; + } + sc->tz_cooling_proc = NULL; + kthread_exit(0); } Index: usr.sbin/powerd/powerd.c diff -u -p usr.sbin/powerd/powerd.c.orig usr.sbin/powerd/powerd.c --- usr.sbin/powerd/powerd.c.orig Mon Apr 11 05:42:55 2005 +++ usr.sbin/powerd/powerd.c Thu Jul 7 12:40:26 2005 @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/powerd/powerd.c,v 1.6 2005/04/10 20:42:55 njl Exp $"); #include +#include #include #include #include @@ -83,6 +84,7 @@ static int cp_time_mib[2]; static int freq_mib[4]; static int levels_mib[4]; static int acline_mib[3]; +static int cooling_mib[5]; /* Configuration */ static int cpu_running_mark; @@ -167,8 +169,10 @@ static int set_freq(int freq) { - if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) - return (-1); + if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) { + if (errno != EPERM) + return (-1); + } return (0); } @@ -254,6 +258,7 @@ main(int argc, char * argv[]) long idle, total; int curfreq, *freqs, i, *mwatts, numfreqs; int ch, mode_ac, mode_battery, mode_none, acline, mode, vflag; + int have_passive_cooling, cooling_active; uint64_t mjoules_used; size_t len; @@ -265,6 +270,7 @@ main(int argc, char * argv[]) mjoules_used = 0; vflag = 0; apm_fd = -1; + have_passive_cooling = 0; while ((ch = getopt(argc, argv, "a:b:i:n:p:r:v")) != EOF) switch (ch) { @@ -321,6 +327,11 @@ main(int argc, char * argv[]) if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) err(1, "lookup freq_levels"); + len = 5; + if (!sysctlnametomib("hw.acpi.thermal.tz0.passive_active", cooling_mib, + &len)) + have_passive_cooling = 1; + /* Check if we can read the idle time and supported freqs. */ if (read_usage_times(NULL, NULL)) err(1, "read_usage_times"); @@ -381,6 +392,13 @@ main(int argc, char * argv[]) if (i < numfreqs && mwatts[i] != -1) mjoules_used += (mwatts[i] * (poll_ival / 1000)) / 1000; + } + + if (have_passive_cooling) { + len = sizeof(cooling_active); + if (!sysctl(cooling_mib, 5, &cooling_active, &len, + NULL, 0) && cooling_active) + continue; } /* Always switch to the lowest frequency in min mode. */