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