From nobody@FreeBSD.org  Thu Feb  7 02:41:32 2008
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id BD37E16A468
	for <freebsd-gnats-submit@FreeBSD.org>; 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 <freebsd-gnats-submit@FreeBSD.org>; 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 <freebsd-gnats-submit@FreeBSD.org>; 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 <alex.kovalenko@verizon.net>
To: freebsd-gnats-submit@FreeBSD.org
Subject: [patch] Enable temperature ceiling in powerd
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         120336
>Category:       bin
>Synopsis:       [patch] Enable temperature ceiling in powerd(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Thu Feb 07 02:50:00 UTC 2008
>Closed-Date:    Wed Jun 18 09:43:36 UTC 2008
>Last-Modified:  Wed Jun 18 09:43:36 UTC 2008
>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 <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:
State-Changed-From-To: open->closed 
State-Changed-By: gavin 
State-Changed-When: Wed Jun 18 09:40:39 UTC 2008 
State-Changed-Why:  
Submitter requests that this be closed, citing 
http://docs.FreeBSD.org/cgi/mid.cgi?1203126071.833.19.camel 
It looks like the problem this PR attempted to fix actually lied 
elsewhere. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=120336 
>Unformatted:
