bin/120336: [patch] Enable temperature ceiling in powerd
Alexandre Kovalenko
alex.kovalenko at verizon.net
Thu Feb 7 02:50:01 UTC 2008
>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 at RabbitsDen.RabbitsLawn.verizon.net:/usr/obj/usr/src/sys/TPX60 i386
>Description:
This patch adds two command line options to powerd:
-T <temperature>[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 <thermal zone name> 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 <errno.h>
#include <fcntl.h>
#include <libutil.h>
+#include <regex.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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:
More information about the freebsd-bugs
mailing list