Power profile script

Nate Lawson nate at root.org
Sat Dec 13 13:12:08 PST 2003


I've implemented a script for changing power profiles in userland when you
go on or off AC power.  The defaults will just change the CPU idle states
to the lowest power ones when off AC power.  Throttling will stay at 100%.
Of course, the user can override these in rc.conf.  In the future, other
power profile features will be maintained by the same script.  You have to
be running devd to use this.

The special values LOW and HIGH refer to the lowest and highest performing
settings for a given sysctl.  So if a system has 8 throttling values, from
1 to 8, HIGH would be 8 and LOW would be 1.  You can also specify a
particular value ("2") if you know what you want.

I'm mostly looking for style input on the /etc/power_profile script since
I'm not familiar with our scripting guidelines.  Note that it's called
from devd (or manually by the user) and is not an rc.d boot-time thing.

-Nate


Index: etc/Makefile
===================================================================
RCS file: /home/ncvs/src/etc/Makefile,v
retrieving revision 1.322
diff -u -r1.322 Makefile
--- etc/Makefile	2 Nov 2003 22:13:36 -0000	1.322
+++ etc/Makefile	13 Dec 2003 20:56:55 -0000
@@ -33,7 +33,7 @@
 .endif

 # -rwxr-xr-x root:wheel, for the new cron root:wheel
-BIN2=	netstart pccard_ether rc.suspend rc.resume
+BIN2=	netstart pccard_ether power_profile rc.suspend rc.resume

 MTREE=	BSD.include.dist BSD.local.dist BSD.root.dist BSD.usr.dist \
 	BSD.var.dist BSD.x11.dist BSD.x11-4.dist
Index: etc/devd.conf
===================================================================
RCS file: /home/ncvs/src/etc/devd.conf,v
retrieving revision 1.9
diff -u -r1.9 devd.conf
--- etc/devd.conf	25 Oct 2003 05:03:25 -0000	1.9
+++ etc/devd.conf	13 Dec 2003 20:59:14 -0000
@@ -70,6 +70,13 @@
 #	action "logger Unknown device: $pnpinfo $location $bus";
 };

+# Switch power profiles when the AC line state changes
+notify 10 {
+	match "system"		"ACPI";
+	match "subsystem"	"ACAD";
+	action			"/etc/power_profile $notify";
+};
+
 /* EXAMPLES TO END OF FILE

 # The following might be an example of something that a vendor might
Index: etc/power_profile
===================================================================
RCS file: etc/power_profile
diff -N etc/power_profile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ etc/power_profile	13 Dec 2003 20:58:00 -0000
@@ -0,0 +1,87 @@
+#!/bin/sh -
+#
+# Modify the power profile based on AC line state.  This script is
+# usually called from devd(8).
+#
+# Arguments: 0x00 (AC offline, economy) or 0x01 (AC online, performance)
+#
+# $FreeBSD$
+#
+
+LOGGER="logger -t power_profile -p daemon.notice"
+
+# Set a given sysctl node to a value.
+#
+# Variables:
+# $node: sysctl node to set with the new value
+# $value: HIGH for the highest performance value, LOW for the best
+#	  economy value, or the value itself.
+# $highest_value: maximum value for this sysctl, when $value is "HIGH"
+# $lowest_value: minimum value for this sysctl, when $value is "LOW"
+#
+sysctl_set () {
+	# Check if the node exists
+	if [ -z "`sysctl -n ${node} 2> /dev/null`" ]; then
+		return
+	fi
+
+	# Get the new value, checking for special types HIGH or LOW
+	case ${value} in
+	[Hh][Ii][Gg][Hh])
+		value=${highest_value}
+		;;
+	[Ll][Oo][Ww])
+		value=${lowest_value}
+		;;
+	*)
+		;;
+	esac
+
+	# Set the desired value
+	[ -n "${value}" ] && sysctl ${node}=${value}
+}
+
+# Pull in default values.
+if [ -r /etc/defaults/rc.conf ]; then
+	. /etc/defaults/rc.conf
+	source_rc_confs
+elif [ -r /etc/rc.conf ]; then
+	. /etc/rc.conf
+fi
+
+if [ $# -ne 1 ]; then
+	echo "Usage: $0 [0x00|0x01]"
+	exit 1
+fi
+
+# Find the next state (performance or economy).
+state=$1
+case ${state} in
+0x01 | '')
+	${LOGGER} "changed to 'performance'"
+	profile="performance"
+	;;
+0x00)
+	${LOGGER} "changed to 'economy'"
+	profile="economy"
+	;;
+*)
+	echo "Usage: $0 [0x00|0x01]"
+	exit 1
+esac
+
+# Set the various sysctls based on the profile's values.
+node="hw.acpi.cpu.cx_lowest"
+highest_value=0
+lowest_value="`sysctl -n hw.acpi.cpu.cx_supported | \
+	awk '{ print split($0, a) - 1 }' - 2> /dev/null`"
+eval value=\$${profile}_cx_lowest
+sysctl_set
+
+node="hw.acpi.cpu.throttle_state"
+highest_value="`sysctl -n hw.acpi.cpu.throttle_max 2> /dev/null`"
+lowest_value="1"
+eval value=\$${profile}_throttle_state
+sysctl_set
+
+exit 0
Index: etc/defaults/rc.conf
===================================================================
RCS file: /home/ncvs/src/etc/defaults/rc.conf,v
retrieving revision 1.191
diff -u -r1.191 rc.conf
--- etc/defaults/rc.conf	28 Nov 2003 17:28:42 -0000	1.191
+++ etc/defaults/rc.conf	13 Dec 2003 20:42:24 -0000
@@ -441,6 +441,11 @@
 devfs_rulesets="/etc/defaults/devfs.rules /etc/devfs.rules" # Files containing
 							    # devfs(8) rules.
 devfs_system_ruleset=""	# The name of a ruleset to apply to /dev
+performance_cx_lowest="HIGH"		# Online CPU idle state
+performance_throttle_state="HIGH"	# Online throttling state
+economy_cx_lowest="LOW"			# Offline CPU idle state
+economy_throttle_state="HIGH"		# Offline throttling state
+

 ##############################################################
 ### Jail Configuration #######################################

Index: sys/dev/acpica/acpi_cpu.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/acpica/acpi_cpu.c,v
retrieving revision 1.26
diff -u -r1.26 acpi_cpu.c
--- sys/dev/acpica/acpi_cpu.c	12 Dec 2003 19:42:16 -0000	1.26
+++ sys/dev/acpica/acpi_cpu.c	13 Dec 2003 04:27:46 -0000
@@ -134,10 +134,8 @@
 static int		 cpu_idle_busy;	/* Count of CPUs in acpi_cpu_idle. */

 /* Values for sysctl. */
-static uint32_t		 cpu_current_state;
-static uint32_t		 cpu_performance_state;
-static uint32_t		 cpu_economy_state;
-static uint32_t		 cpu_max_state;
+static uint32_t		 cpu_throttle_state;
+static uint32_t		 cpu_throttle_max;
 static int		 cpu_cx_lowest;
 static char 		 cpu_cx_supported[64];

@@ -165,7 +163,6 @@
 static void	acpi_pm_ticksub(uint32_t *end, const uint32_t *start);
 static void	acpi_cpu_notify(ACPI_HANDLE h, UINT32 notify, void *context);
 static int	acpi_cpu_quirks(struct acpi_cpu_softc *sc);
-static void	acpi_cpu_power_profile(void *arg);
 static int	acpi_cpu_throttle_sysctl(SYSCTL_HANDLER_ARGS);
 static int	acpi_cpu_history_sysctl(SYSCTL_HANDLER_ARGS);
 static int	acpi_cpu_cx_lowest_sysctl(SYSCTL_HANDLER_ARGS);
@@ -616,10 +613,6 @@
     /* Get set of CPU devices */
     devclass_get_devices(acpi_cpu_devclass, &cpu_devices, &cpu_ndevices);

-    /* Register performance profile change handler */
-    EVENTHANDLER_REGISTER(power_profile_change, acpi_cpu_power_profile,
-			  NULL, 0);
-
     /*
      * Make sure all the processors' Cx counts match.  We should probably
      * also check the contents of each.  However, no known systems have
@@ -647,60 +640,34 @@
 static void
 acpi_cpu_startup_throttling()
 {
-    int cpu_temp_speed;
     ACPI_LOCK_DECL;

     /* Initialise throttling states */
-    cpu_max_state = CPU_MAX_SPEED;
-    cpu_performance_state = cpu_max_state;
-    cpu_economy_state = cpu_performance_state / 2;
-
-    /* 0 is 'reserved' */
-    if (cpu_economy_state == 0)
-	cpu_economy_state++;
-    if (TUNABLE_INT_FETCH("hw.acpi.cpu.performance_speed", &cpu_temp_speed) &&
-	cpu_temp_speed > 0 && cpu_temp_speed <= cpu_max_state) {
-
-	cpu_performance_state = cpu_temp_speed;
-    }
-    if (TUNABLE_INT_FETCH("hw.acpi.cpu.economy_speed", &cpu_temp_speed) &&
-	cpu_temp_speed > 0 && cpu_temp_speed <= cpu_max_state) {
-
-	cpu_economy_state = cpu_temp_speed;
-    }
+    cpu_throttle_max = CPU_MAX_SPEED;
+    cpu_throttle_state = CPU_MAX_SPEED;

     SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx,
 		   SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
-		   OID_AUTO, "max_speed", CTLFLAG_RD,
-		   &cpu_max_state, 0, "maximum CPU speed");
-    SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx,
-		   SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
-		   OID_AUTO, "current_speed", CTLFLAG_RD,
-		   &cpu_current_state, 0, "current CPU speed");
-    SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx,
-		    SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
-		    OID_AUTO, "performance_speed",
-		    CTLTYPE_INT | CTLFLAG_RW, &cpu_performance_state,
-		    0, acpi_cpu_throttle_sysctl, "I", "");
+		   OID_AUTO, "throttle_max", CTLFLAG_RD,
+		   &cpu_throttle_max, 0, "maximum CPU speed");
     SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx,
 		    SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
-		    OID_AUTO, "economy_speed",
-		    CTLTYPE_INT | CTLFLAG_RW, &cpu_economy_state,
-		    0, acpi_cpu_throttle_sysctl, "I", "");
+		    OID_AUTO, "throttle_state",
+		    CTLTYPE_INT | CTLFLAG_RW, &cpu_throttle_state,
+		    0, acpi_cpu_throttle_sysctl, "I", "current CPU speed");

     /* If ACPI 2.0+, signal platform that we are taking over throttling. */
-    if (cpu_pstate_cnt != 0) {
-	ACPI_LOCK;
+    ACPI_LOCK;
+    if (cpu_pstate_cnt != 0)
 	AcpiOsWritePort(cpu_smi_cmd, cpu_pstate_cnt, 8);
-	ACPI_UNLOCK;
-    }

-    /* Set initial speed */
-    acpi_cpu_power_profile(NULL);
+    /* Set initial speed to maximum. */
+    acpi_cpu_throttle_set(cpu_throttle_max);
+    ACPI_UNLOCK;

     printf("acpi_cpu: throttling enabled, %d steps (100%% to %d.%d%%), "
 	   "currently %d.%d%%\n", CPU_MAX_SPEED, CPU_SPEED_PRINTABLE(1),
-	   CPU_SPEED_PRINTABLE(cpu_current_state));
+	   CPU_SPEED_PRINTABLE(cpu_throttle_state));
 }

 static void
@@ -787,7 +754,7 @@
 	ACPI_VPRINT(sc->cpu_dev, acpi_device_get_parent_softc(sc->cpu_dev),
 		    "set speed to %d.%d%%\n", CPU_SPEED_PRINTABLE(speed));
     }
-    cpu_current_state = speed;
+    cpu_throttle_state = speed;
 }

 /*
@@ -1026,54 +993,14 @@
     return (0);
 }

-/*
- * Power profile change hook.
- *
- * Uses the ACPI lock to avoid reentrancy.
- */
-static void
-acpi_cpu_power_profile(void *arg)
-{
-    int		state;
-    uint32_t	new;
-    ACPI_LOCK_DECL;
-
-    state = power_profile_get_state();
-    if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
-	return;
-
-    ACPI_LOCK;
-
-    switch (state) {
-    case POWER_PROFILE_PERFORMANCE:
-	new = cpu_performance_state;
-	break;
-    case POWER_PROFILE_ECONOMY:
-	new = cpu_economy_state;
-	break;
-    default:
-	new = cpu_current_state;
-	break;
-    }
-
-    if (cpu_current_state != new)
-	acpi_cpu_throttle_set(new);
-
-    ACPI_UNLOCK;
-}
-
-/*
- * Handle changes in the performance/ecomony CPU settings.
- *
- * Does not need the ACPI lock (although setting *argp should
- * probably be atomic).
- */
+/* Handle changes in the CPU throttling setting. */
 static int
 acpi_cpu_throttle_sysctl(SYSCTL_HANDLER_ARGS)
 {
     uint32_t	*argp;
     uint32_t	 arg;
     int		 error;
+    ACPI_LOCK_DECL;

     argp = (uint32_t *)oidp->oid_arg1;
     arg = *argp;
@@ -1082,12 +1009,16 @@
     /* Error or no new value */
     if (error != 0 || req->newptr == NULL)
 	return (error);
-    if (arg < 1 || arg > cpu_max_state)
+    if (arg < 1 || arg > cpu_throttle_max)
 	return (EINVAL);

-    /* Set new value and possibly switch */
-    *argp = arg;
-    acpi_cpu_power_profile(NULL);
+    /* If throttling changed, notify the BIOS of the new rate. */
+    ACPI_LOCK;
+    if (*argp != arg) {
+	*argp = arg;
+	acpi_cpu_throttle_set(arg);
+    }
+    ACPI_UNLOCK;

     return (0);
 }
Index: share/man/man4/acpi.4
===================================================================
RCS file: /home/ncvs/src/share/man/man4/acpi.4,v
retrieving revision 1.20
diff -u -r1.20 acpi.4
--- share/man/man4/acpi.4	19 Nov 2003 20:37:15 -0000	1.20
+++ share/man/man4/acpi.4	13 Dec 2003 05:04:22 -0000
@@ -328,12 +328,12 @@
 .El
 .Sh SYSCTLS
 .Bl -tag -width indent
-.It Va hw.acpi.cpu.performance_speed
-Sets the speed of the CPU, if it supports multiple speeds, while in
-the performance power profile.
-.It Va hw.acpi.cpu.economy_speed
-Sets the speed of the CPU, if it supports multiple speeds, while in
-the economy power profile.
+.It Va hw.acpi.cpu.throttle_max
+Maximum value for CPU throttling, equal to 100% of the clock rate.
+.It Va hw.acpi.cpu.throttle_state
+Get or set the current throttling state, from 1 to
+.Va hw.acpi.cpu.throttle_max .
+This scales back the CPU clock rate and the corresponding power consumption.
 .It Va hw.acpi.cpu.cx_history
 Debugging information listing all sleep states and the number of
 long and short sleeps for each one.


More information about the freebsd-arch mailing list