From owner-freebsd-acpi@FreeBSD.ORG Thu Feb 7 03:19:31 2008 Return-Path: Delivered-To: freebsd-acpi@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 802F516A468 for ; Thu, 7 Feb 2008 03:19:31 +0000 (UTC) (envelope-from alex.kovalenko@verizon.net) Received: from vms173001pub.verizon.net (vms173001pub.verizon.net [206.46.173.1]) by mx1.freebsd.org (Postfix) with ESMTP id 4C84713C442 for ; Thu, 7 Feb 2008 03:19:31 +0000 (UTC) (envelope-from alex.kovalenko@verizon.net) Received: from [10.0.3.231] ([70.111.176.151]) by vms173001.mailsrvcs.net (Sun Java System Messaging Server 6.2-6.01 (built Apr 3 2006)) with ESMTPA id <0JVU00HXIM0C9A12@vms173001.mailsrvcs.net> for freebsd-acpi@freebsd.org; Wed, 06 Feb 2008 21:07:25 -0600 (CST) Date: Wed, 06 Feb 2008 22:19:09 -0500 From: "Alexandre \"Sunny\" Kovalenko" In-reply-to: <1202185241.821.8.camel@RabbitsDen> To: freebsd-acpi Message-id: <1202354349.1157.55.camel@RabbitsDen> MIME-version: 1.0 X-Mailer: Evolution 2.12.3 FreeBSD GNOME Team Port Content-type: multipart/mixed; boundary="Boundary_(ID_ZU9lgbhirRsxebqErOTx4w)" References: <1201733779.902.18.camel@RabbitsDen> <200801310535.15540.jhb@freebsd.org> <1202185241.821.8.camel@RabbitsDen> Cc: Ian Smith , Johannes Dieterich Subject: Re: [RFC] Patch to enable temperature ceiling in powerd X-BeenThere: freebsd-acpi@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: ACPI and power management development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 07 Feb 2008 03:19:31 -0000 --Boundary_(ID_ZU9lgbhirRsxebqErOTx4w) Content-type: text/plain; charset=UTF-8 Content-transfer-encoding: 8BIT On Mon, 2008-02-04 at 23:20 -0500, Alexandre "Sunny" Kovalenko wrote: > On Thu, 2008-01-31 at 05:35 -0500, John Baldwin wrote: > > On Wednesday 30 January 2008 05:56:19 pm Alexandre "Sunny" Kovalenko wrote: > > > Some time ago I have put together patch for powerd, which allows user to > > > specify the temperature threshold at which powerd will lower CPU > > > frequency no matter what the load was at the time. I recently had to > > > adapt it to the 7.0-PRERELEASE for someone with the overheating laptop, > > > which got me to think that it might be useful for someone else yet. > > > > > > Basic idea is fairly simple -- check temperature in TZ0 and, if it has > > > reached certain value, either override frequency with the lowest > > > available (in the case of 'max' setting) or change idle time to 100% and > > > let adaptive algorithm decrease frequency gradually. > > > > > > I imagine it also could be poor man's substitute for the low noise > > > acoustic policy ;) > > > > > > If there is an interest, I will go ahead and submit a PR, otherwise it > > > will live in the mail archives for someone to find. Any comments, > > > suggestions or criticisms are welcome. > > > > > > Temperature threshold (in Celsius) could be set by means of '-T' command > > > line option (as in '-T 60'). > > > > A couple of suggestions: > > > > - I would make the default temperature 0 instead of 200 and just disable the > > feature altogether if it is set to 0 (i.e. don't read the current > > temperature and don't do any checks if it is 0). > > - I would allow the temperature to be specified in either C, K or F with a > > suffix to indicate the scale. (e.g., "80C", "120F", "300K") > > - I would let the thermal zone name be configurable with a default of "tz0". > > (e.g. "-z tz3"). You would then snprintf the sysctl mib name that gets > > passed to sysctlbyname(3). > > I have filed bin/120336: [patch] Enable temperature ceiling in powerd with the patch, implementing what was outlined above including suggestions from John Baldwin. It should apply cleanly to the version 1.21 of powerd.c and version 1.10 of powerd.8. It has been running on my ThinkPad X60 for a while, and some older versions were used by at least two other people. Thanks go to Ian Smith for reviewing it from the functional standpoint and for style(9) compliance. Patch, attached to the PR, is the combination of the two patches, attached to this E-mail due to the fact that PR web form only allows single file to be attached. -- Alexandre "Sunny" Kovalenko (Олександр Коваленко) --Boundary_(ID_ZU9lgbhirRsxebqErOTx4w) Content-type: text/x-patch; name=powerd.8.patch; charset=UTF-8 Content-transfer-encoding: 7BIT Content-disposition: attachment; filename=powerd.8.patch --- 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 , --Boundary_(ID_ZU9lgbhirRsxebqErOTx4w) Content-type: text/x-patch; name=powerd.c.patch; charset=UTF-8 Content-transfer-encoding: 7BIT Content-disposition: attachment; filename=powerd.c.patch --- 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. --Boundary_(ID_ZU9lgbhirRsxebqErOTx4w)--