From owner-freebsd-bugs@FreeBSD.ORG Thu Feb 7 02:50:00 2008 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id ED12816A419 for ; Thu, 7 Feb 2008 02:50:00 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id BF13913C467 for ; Thu, 7 Feb 2008 02:50:00 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.2/8.14.2) with ESMTP id m172o0hM019674 for ; Thu, 7 Feb 2008 02:50:00 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.2/8.14.1/Submit) id m172o05V019673; Thu, 7 Feb 2008 02:50:00 GMT (envelope-from gnats) Resent-Date: Thu, 7 Feb 2008 02:50:00 GMT Resent-Message-Id: <200802070250.m172o05V019673@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Alexandre Kovalenko Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id BD37E16A468 for ; Thu, 7 Feb 2008 02:41:32 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id 956AE13C457 for ; Thu, 7 Feb 2008 02:41:32 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.2/8.14.2) with ESMTP id m172dSo3094390 for ; Thu, 7 Feb 2008 02:39:28 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.2/8.14.1/Submit) id m172dSS9094389; Thu, 7 Feb 2008 02:39:28 GMT (envelope-from nobody) Message-Id: <200802070239.m172dSS9094389@www.freebsd.org> Date: Thu, 7 Feb 2008 02:39:28 GMT From: Alexandre Kovalenko To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: bin/120336: [patch] Enable temperature ceiling in powerd X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 07 Feb 2008 02:50:01 -0000 >Number: 120336 >Category: bin >Synopsis: [patch] Enable temperature ceiling in powerd >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Thu Feb 07 02:50:00 UTC 2008 >Closed-Date: >Last-Modified: >Originator: Alexandre Kovalenko >Release: 7.0-PRERELEASE >Organization: Home >Environment: FreeBSD RabbitsDen.RabbitsLawn.verizon.net 7.0-PRERELEASE FreeBSD 7.0-PRERELEASE #0: Thu Jan 31 23:37:32 EST 2008 root@RabbitsDen.RabbitsLawn.verizon.net:/usr/obj/usr/src/sys/TPX60 i386 >Description: This patch adds two command line options to powerd: -T [CFK] sets temperature at which powerd will revert to the lowes available frequency in the maximum mode or start lowering frequency stepwise in the adaptive mode. -z sets the name of the thermal zone used to monitor temperature above. Patch combines changes to /usr/src/usr.sbin/powerd/powerd.c and /usr/src/usr.sbin/powerd/powerd.8 into the single file due to the limitation of send-pr web form. Patch has been tested on several machines, running recent versions of the 7.0. >How-To-Repeat: This patch has proven to be useful on the machines which have frequency governors, but could not complete make buildworld without overheating. >Fix: See attached patch. Patch attached with submission follows: --- powerd.8.orig 2008-02-04 22:48:14.000000000 -0500 +++ powerd.8 2008-02-06 16:33:42.000000000 -0500 @@ -39,7 +39,9 @@ .Op Fl p Ar ival .Op Fl P Ar pidfile .Op Fl r Ar percent +.Op Fl T Ar temperature .Op Fl v +.Op Fl z Ar thermal zone .Sh DESCRIPTION The .Nm @@ -92,11 +94,25 @@ adaptive mode should consider the CPU running and increase performance. The default is 65% or lower. +.It Fl T Ar temperature +Specifies temperature which will cause powerd to switch to the lowest +available frequency in the maximum mode or to reduce frequency in the +adaptive mode. Temperature could be specified using qualifiers C, F and K, +for Celsius, Fahrenheit and Kelvin respectively. Number without the qualifier +will be treated as the number with the qualifier C. Please, note that +negative temperature values and values in the excess of the equivalent of +150C are considered invalid. .It Fl v Verbose mode. Messages about power changes will be printed to stdout and .Nm will operate in the foreground. +.It Fl z Ar thermal zone +Specifies the name of the thermal zone, used to monitor temperature for the 'T' +option above. This will be used as the part of the mib name, e.g. '-z tz2' will +result in 'hw.acpi.thermal.tz2.temperature' being monitored. If no thermal zone +name was specified on the command line, 'tz0' is assumed. In the absence of the 'T' +option, this option is ignored. .El .Sh SEE ALSO .Xr acpi 4 , --- powerd.c.orig 2008-02-06 16:03:10.000000000 -0500 +++ powerd.c 2008-02-06 21:13:53.000000000 -0500 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -87,18 +88,22 @@ static void handle_sigs(int sig); static void parse_mode(char *arg, int *mode, int ch); static void usage(void); +static int convert_temperature_to_acpi(const char *temp); /* Sysctl data structures. */ static int cp_time_mib[2]; static int freq_mib[4]; static int levels_mib[4]; static int acline_mib[3]; +static int temp_mib[5]; /* Configuration */ static int cpu_running_mark; static int cpu_idle_mark; static int poll_ival; +static int passive_cooling_mark; static int vflag; +static int tflag; static volatile sig_atomic_t exit_requested; static power_src_t acline_status; @@ -357,10 +362,80 @@ { fprintf(stderr, -"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%] [-P pidfile]\n"); +"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%] [-P pidfile] [-T temperature] [-z thermal zone]\n"); exit(1); } +/* Convert temperature in the form of nnC, nnK and nnF into tenths + * of the K as used by ACPI subsystem. Temperatures without qualifier + * are assumed to be in Celsius. Temperatures, longer then three + * digits or having qualifiers other then C, K or F are considered + * invalid. Function will return negative value if invalid temperature + * is encountered as well as upon reaching error condition. + */ +static int +convert_temperature_to_acpi(const char *temp) +{ + regex_t preg; + regmatch_t pmatch[3]; + int result = 0; + char temp_value[4]; + /* If no qualifier is specified, defaulting to Celsius */ + char qualifier = 'C'; + + /* That would be an internal error -- return -1 */ + if (regcomp(&preg, "^([0-9]+)([CKF]?)$", REG_EXTENDED)) + result = -1; + /* If it looks like nothing we expect -- return -2 */ + if (!result && (regexec(&preg, temp, 3, pmatch, 0) == REG_NOMATCH)) + result = -2; + /* If we were able to successfully allocate 'preg' we need to free it */ + if (result != -1) + regfree(&preg); + /* If there were no problems so far, let's interpret the string */ + if (!result) { + if (pmatch[2].rm_so != pmatch[2].rm_eo) + qualifier = temp[pmatch[2].rm_so]; + /* + * Three digits of the temperature are enough for practical + * purposes + */ + if ((pmatch[1].rm_eo - pmatch[1].rm_so) <= 3) { + memcpy(temp_value, &temp[pmatch[1].rm_so], + pmatch[1].rm_eo - pmatch[1].rm_so); + temp_value[pmatch[1].rm_eo - pmatch[1].rm_so] = '\0'; + result = atoi(temp_value); + } + else + result = -3; + + if (result >= 0) { + switch (qualifier) { + case 'F': + result = ((result - 32) * 5) / 9; + /* Fallthrough is intentional */ + case 'C': + result += 273; + /* Fallthrough is intentional */ + case 'K': + result *= 10; + /* + * 150C (which equals to 4230 units + * here) should be more than modern + * electronics could endure. + */ + if (result > 4230) + result = -5; + break; + default: + result = -4; + break; + } + } + } + return(result); +} + int main(int argc, char * argv[]) { @@ -371,6 +446,7 @@ const char *pidfile = NULL; long idle, total; int curfreq, *freqs, i, *mwatts, numfreqs; + int temperature; int ch, mode, mode_ac, mode_battery, mode_none; uint64_t mjoules_used; size_t len; @@ -382,12 +458,17 @@ poll_ival = DEFAULT_POLL_INTERVAL; mjoules_used = 0; vflag = 0; + tflag = temperature = passive_cooling_mark = 0; + char tz_mib_name[40]; /* This should be sufficient to hold "hw.acpi.thermal.%s.temperature" */ /* User must be root to control frequencies. */ if (geteuid() != 0) errx(1, "must be root to run"); - while ((ch = getopt(argc, argv, "a:b:i:n:p:P:r:v")) != EOF) + /* Set default mib name for the thermal zone */ + snprintf(tz_mib_name, sizeof(tz_mib_name), "hw.acpi.thermal.%s.temperature", "tz0"); + + while ((ch = getopt(argc, argv, "a:b:i:n:p:P:r:T:v:z:")) != EOF) switch (ch) { case 'a': parse_mode(optarg, &mode_ac, ch); @@ -424,9 +505,27 @@ usage(); } break; + case 'T': + passive_cooling_mark = + convert_temperature_to_acpi(optarg); + if (passive_cooling_mark < 0) { + warnx("%s is not valid temperature for passive cooling", + optarg); + usage(); + } else if (passive_cooling_mark > 0) + tflag = 1; + break; case 'v': vflag = 1; break; + case 'z': + /* + * We will decipher thermal zone here but it will not + * be used unless -T was also present + */ + snprintf(tz_mib_name, sizeof(tz_mib_name), + "hw.acpi.thermal.%s.temperature", optarg); + break; default: usage(); } @@ -446,6 +545,11 @@ len = 4; if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) err(1, "lookup freq_levels"); + if (tflag) { /* if no -T option don't fail if temp not available */ + len = 5; + if (sysctlnametomib(tz_mib_name, temp_mib, &len)) + err(1, "lookup temperature"); + } /* Check if we can read the idle time and supported freqs. */ if (read_usage_times(NULL, NULL)) @@ -528,6 +632,12 @@ warn("error reading current CPU frequency"); continue; } + /* Read current temperature if -T option is set */ + if (tflag) { + len = sizeof(temperature); + if (sysctl(temp_mib, 5, &temperature, &len, NULL, 0)) + err(1, "error reading current temperature"); + } if (vflag) { for (i = 0; i < numfreqs; i++) { @@ -559,20 +669,34 @@ continue; } - /* Always switch to the highest frequency in max mode. */ if (mode == MODE_MAX) { - if (curfreq != freqs[0]) { + /* Unless passive cooling override is in effect... */ + if (tflag && (temperature > passive_cooling_mark)) { + if (curfreq != freqs[numfreqs - 1]) { + if (vflag) { + printf("passive cooling override; " + "changing frequency to %d MHz\n", + freqs[numfreqs - 1]); + } + if (set_freq(freqs[numfreqs - 1])) { + warn("error setting CPU freq %d", + freqs[numfreqs - 1]); + continue; + } + } + /* ... always switch to the highest frequency in max mode. */ + } else if (curfreq != freqs[0]) { if (vflag) { printf("now operating on %s power; " - "changing frequency to %d MHz\n", - modes[acline_status], - freqs[0]); + "changing frequency to %d MHz\n", + modes[acline_status], + freqs[0]); } if (set_freq(freqs[0]) != 0) { warn("error setting CPU freq %d", - freqs[0]); + freqs[0]); continue; - } + } } continue; } @@ -583,6 +707,14 @@ warn("read_usage_times() failed"); continue; } + /* + * If temperature has risen over passive cooling mark, we + * would want to decrease frequency regardless of the load, + * Simplest way to go about this would be to report 100% + * idle CPU and let adaptive algorithm do its job. + */ + if (tflag && (temperature > passive_cooling_mark)) + idle = total; /* * If we're idle less than the active mark, bump up two levels. >Release-Note: >Audit-Trail: >Unformatted: