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 Sat Jul 2 14:19:21 2005 @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD: src/sys/dev/acpica/a #include #include #include +#include #include #include #include @@ -106,6 +107,12 @@ 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; }; static int acpi_tz_probe(device_t dev); @@ -118,12 +125,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 +192,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 +204,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 +245,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 +272,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 +636,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 +837,68 @@ acpi_tz_thread(void *arg) else ACPI_UNLOCK(thermal); } +} + +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_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: resuming performance\n", + TZ_KELVTOC(sc->tz_temperature)); + sc->tz_cooling_active = 0; + cpufreq_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) { + ACPI_VPRINT(sc->tz_dev, + acpi_device_get_parent_softc(sc->tz_dev), + "temperature %d.%dC: decreasing performance by %d%%\n", + TZ_KELVTOC(sc->tz_temperature), perf); + if (!sc->tz_cooling_active) { + sc->tz_cooling_active = 1; + cpufreq_cooling_active(1); + } + cpufreq_perf_decr(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) { + cpufreq_cooling_active(0); + sc->tz_cooling_active = 0; + } + sc->tz_cooling_proc = NULL; + kthread_exit(0); } Index: sys/kern/kern_cpu.c diff -u -p sys/kern/kern_cpu.c.orig sys/kern/kern_cpu.c --- sys/kern/kern_cpu.c.orig Mon Apr 11 04:11:23 2005 +++ sys/kern/kern_cpu.c Sun Jul 3 08:16:46 2005 @@ -102,7 +102,10 @@ static int cpufreq_expand_set(struct cpu struct cf_setting_array *set_arr); static struct cf_level *cpufreq_dup_set(struct cpufreq_softc *sc, struct cf_level *dup, struct cf_setting *set); +static int cpufreq_getfreq(void); +static int cpufreq_setfreq(int, struct cf_level *, int); static int cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS); +static int cpufreq_real_sysctl(SYSCTL_HANDLER_ARGS); static int cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS); static int cpufreq_settings_sysctl(SYSCTL_HANDLER_ARGS); @@ -134,6 +137,9 @@ SYSCTL_INT(_debug_cpufreq, OID_AUTO, low SYSCTL_INT(_debug_cpufreq, OID_AUTO, verbose, CTLFLAG_RW, &cf_verbose, 1, "Print verbose debugging messages"); +static int cooling_active; +static int cooling_reqfreq = -1; + static int cpufreq_attach(device_t dev) { @@ -169,6 +175,10 @@ cpufreq_attach(device_t dev) cpufreq_curr_sysctl, "I", "Current CPU frequency"); SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(parent)), + OID_AUTO, "realfreq", CTLTYPE_INT | CTLFLAG_RD, sc, 0, + cpufreq_real_sysctl, "I", "Current actual CPU frequency"); + SYSCTL_ADD_PROC(&sc->sysctl_ctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(parent)), OID_AUTO, "freq_levels", CTLTYPE_STRING | CTLFLAG_RD, sc, 0, cpufreq_levels_sysctl, "A", "CPU frequency levels"); cf_ev_tag = EVENTHANDLER_REGISTER(cpufreq_changed, cpufreq_evaluate, @@ -204,6 +214,17 @@ cpufreq_evaluate(void *arg) } static int +cpufreq_match(struct cf_level *levels, int count, int freq) +{ + int i; + + for (i = 0; i < count; i++) + if (CPUFREQ_CMP(levels[i].total_set.freq, freq)) + return (i); + return (-1); +} + +static int cf_set_method(device_t dev, const struct cf_level *level, int priority) { struct cpufreq_softc *sc; @@ -783,26 +804,39 @@ cpufreq_dup_set(struct cpufreq_softc *sc } static int -cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS) +cpufreq_getfreq(void) { - struct cpufreq_softc *sc; - struct cf_level *levels; - int count, devcount, error, freq, i, n; + struct cf_level level; + int devcount, error, freq; device_t *devs; devs = NULL; - sc = oidp->oid_arg1; - levels = malloc(CF_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); - if (levels == NULL) - return (ENOMEM); + freq = -1; - error = CPUFREQ_GET(sc->dev, &levels[0]); + error = devclass_get_devices(cpufreq_dc, &devs, &devcount); if (error) goto out; - freq = levels[0].total_set.freq; - error = sysctl_handle_int(oidp, &freq, 0, req); - if (error != 0 || req->newptr == NULL) + if (devcount < 1) + goto out; + + error = CPUFREQ_GET(devs[0], &level); + if (error) goto out; + freq = level.total_set.freq; + +out: + if (devs) + free(devs, M_TEMP); + return (freq); +} + +static int +cpufreq_setfreq(int freq, struct cf_level *levels, int max_levels) +{ + int count, devcount, error, i, n; + device_t *devs; + + devs = NULL; /* * While we only call cpufreq_get() on one device (assuming all @@ -813,7 +847,7 @@ cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS) if (error) goto out; for (n = 0; n < devcount; n++) { - count = CF_MAX_LEVELS; + count = max_levels; error = CPUFREQ_LEVELS(devs[n], levels, &count); if (error) { if (error == E2BIG) @@ -821,24 +855,81 @@ cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS) "cpufreq: need to increase CF_MAX_LEVELS\n"); break; } - for (i = 0; i < count; i++) { - if (CPUFREQ_CMP(levels[i].total_set.freq, freq)) { - error = CPUFREQ_SET(devs[n], &levels[i], - CPUFREQ_PRIO_USER); - break; - } - } - if (i == count) { + if ((i = cpufreq_match(levels, count, freq)) < 0) { error = EINVAL; break; } + error = CPUFREQ_SET(devs[n], &levels[i], CPUFREQ_PRIO_USER); } out: if (devs) free(devs, M_TEMP); - if (levels) - free(levels, M_TEMP); + return (error); +} + +static int +cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct cpufreq_softc *sc; + struct cf_level *levels; + int count, error, freq, curfreq, i; + + sc = oidp->oid_arg1; + levels = malloc(CF_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); + if (levels == NULL) + return (ENOMEM); + + if (req->newptr == NULL && cooling_active) + freq = cooling_reqfreq; + else { + error = CPUFREQ_GET(sc->dev, &levels[0]); + if (error) + goto out; + freq = levels[0].total_set.freq; + } + curfreq = freq; + error = sysctl_handle_int(oidp, &freq, 0, req); + if (error != 0 || req->newptr == NULL) + goto out; + + count = CF_MAX_LEVELS; + error = CPUFREQ_LEVELS(sc->dev, levels, &count); + if (error) { + if (error == E2BIG) + printf("cpufreq: need to increase CF_MAX_LEVELS\n"); + goto out; + } + if ((i = cpufreq_match(levels, count, freq)) < 0) { + error = EINVAL; + goto out; + } + cooling_reqfreq = levels[i].total_set.freq; + + if (cooling_active && freq > curfreq) + goto out; + + error = cpufreq_setfreq(freq, levels, CF_MAX_LEVELS); + +out: + free(levels, M_TEMP); + return (error); +} + +static int +cpufreq_real_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct cpufreq_softc *sc; + struct cf_level level; + int error, freq; + + sc = oidp->oid_arg1; + + error = CPUFREQ_GET(sc->dev, &level); + if (error) + return (error); + freq = level.total_set.freq; + error = sysctl_handle_int(oidp, &freq, 0, req); return (error); } @@ -913,6 +1004,84 @@ cpufreq_settings_sysctl(SYSCTL_HANDLER_A out: free(sets, M_TEMP); sbuf_delete(&sb); + return (error); +} + +int +cpufreq_cooling_active(int flag) +{ + struct cf_level *levels; + int error = 0; + + cooling_active = (flag > 0); + if (cooling_active) + cooling_reqfreq = cpufreq_getfreq(); + else if (cooling_reqfreq > 0) { + levels = malloc(CF_MAX_LEVELS * sizeof(*levels), M_TEMP, + M_NOWAIT); + if (levels == NULL) + return (ENOMEM); + error = cpufreq_setfreq(cooling_reqfreq, levels, + CF_MAX_LEVELS); + free(levels, M_TEMP); + } + return (error); +} + +int +cpufreq_perf_decr(int req) +{ + struct cf_level *levels; + int count, devcount, error, freq, perf, i, n; + device_t *devs; + + levels = malloc(CF_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT); + if (levels == NULL) + return (ENOMEM); + + devs = NULL; + error = devclass_get_devices(cpufreq_dc, &devs, &devcount); + if (error) + goto out; + if (devcount < 1) + goto out; + + error = CPUFREQ_GET(devs[0], &levels[0]); + if (error) + goto out; + freq = levels[0].total_set.freq; + + for (n = 0; n < devcount; n++) { + count = CF_MAX_LEVELS; + error = CPUFREQ_LEVELS(devs[n], levels, &count); + if (error) { + if (error == E2BIG) + printf( + "cpufreq: need to increase CF_MAX_LEVELS\n"); + break; + } + + perf = 100 * freq / levels[0].total_set.freq - req; + if (perf < 0) + perf = 0; + else if (perf > 100) + perf = 100; + + for (i = 0; i < count; i++) + if (levels[i].total_set.freq <= freq * perf / 100) + break; + if (i == count) + i--; + if (levels[i].total_set.freq != freq) + error = CPUFREQ_SET(devs[n], &levels[i], + CPUFREQ_PRIO_USER); + } + +out: + if (devs) + free(devs, M_TEMP); + if (levels) + free(levels, M_TEMP); return (error); } Index: sys/sys/cpu.h diff -u sys/sys/cpu.h.orig sys/sys/cpu.h --- sys/sys/cpu.h.orig Sat Feb 19 15:13:25 2005 +++ sys/sys/cpu.h Sun Jul 3 07:10:08 2005 @@ -118,6 +118,10 @@ int cpufreq_register(device_t dev); int cpufreq_unregister(device_t dev); +/* passive cooling stuff */ +int cpufreq_cooling_active(int); +int cpufreq_perf_decr(int); + /* Allow values to be +/- a bit since sometimes we have to estimate. */ #define CPUFREQ_CMP(x, y) (abs((x) - (y)) < 25)