2 * Copyright (c) 2004 Colin Percival
3 * Copyright (c) 2005 Nate Lawson
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted providing that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
41 #include <machine/apm_bios.h>
44 #include <sys/ioctl.h>
45 #include <sys/sysctl.h>
46 #include <sys/resource.h>
48 #define DEFAULT_ACTIVE_PERCENT 65
49 #define DEFAULT_IDLE_PERCENT 90
50 #define DEFAULT_POLL_INTERVAL 500 /* Poll interval in milliseconds */
64 const char *modes[] = {
70 #define ACPIAC "hw.acpi.acline"
71 #define APMDEV "/dev/apm"
73 static int read_usage_times(long *idle, long *total);
74 static int read_freqs(int *numfreqs, int **freqs, int **power);
75 static int set_freq(int freq);
76 static void acline_init(void);
77 static int acline_read(void);
78 static void handle_sigs(int sig);
79 static void parse_mode(char *arg, int *mode, int ch);
80 static void usage(void);
82 /* Sysctl data structures. */
83 static int cp_time_mib[2];
84 static int freq_mib[4];
85 static int levels_mib[4];
86 static int acline_mib[3];
89 static int cpu_running_mark;
90 static int cpu_idle_mark;
94 static int exit_requested;
97 read_usage_times(long *idle, long *total)
99 static long idle_old, total_old;
100 long cp_time[CPUSTATES], i, total_new;
104 cp_time_len = sizeof(cp_time);
105 error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0);
108 for (total_new = 0, i = 0; i < CPUSTATES; i++)
109 total_new += cp_time[i];
112 *idle = cp_time[CP_IDLE] - idle_old;
114 *total = total_new - total_old;
116 idle_old = cp_time[CP_IDLE];
117 total_old = total_new;
123 read_freqs(int *numfreqs, int **freqs, int **power)
125 char *freqstr, *p, *q;
129 if (sysctl(levels_mib, 4, NULL, &len, NULL, 0))
131 if ((freqstr = malloc(len)) == NULL)
133 if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0))
137 for (p = freqstr; *p != '\0'; p++)
141 if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) {
145 if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
150 for (i = 0, p = freqstr; i < *numfreqs; i++) {
154 if (sscanf(p, "%d/%d", &(*freqs)[i], &(*power)[i]) != 2) {
171 if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) {
180 * Try to use ACPI to find the AC line status. If this fails, fall back
181 * to APM. If nothing succeeds, we'll just run in default mode.
190 len = sizeof(acline);
191 if (sysctlbyname(ACPIAC, &acline, &len, NULL, 0) == 0) {
193 if (sysctlnametomib(ACPIAC, acline_mib, &len))
194 err(1, "lookup acline");
196 apm_fd = open(APMDEV, O_RDONLY);
199 "cannot read AC line status, using default settings");
206 int acline = SRC_UNKNOWN;
210 struct apm_info info;
212 if (apm_fd != -1 && ioctl(apm_fd, APMIO_GETINFO, &info) == 0)
213 acline = info.ai_acline ? SRC_AC : SRC_BATTERY;
216 if (acline == SRC_UNKNOWN) {
217 len = sizeof(acline);
218 if (sysctl(acline_mib, 3, &acline, &len, NULL, 0) == 0)
219 acline = acline ? SRC_AC : SRC_BATTERY;
226 parse_mode(char *arg, int *mode, int ch)
229 if (strcmp(arg, "min") == 0)
231 else if (strcmp(arg, "max") == 0)
233 else if (strcmp(arg, "adaptive") == 0)
234 *mode = MODE_ADAPTIVE;
236 errx(1, "bad option: -%c %s", (char)ch, optarg);
240 handle_sigs(int __unused sig)
250 "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%]\n");
255 main(int argc, char * argv[])
258 int curfreq, *freqs, i, *mwatts, numfreqs;
259 int ch, mode_ac, mode_battery, mode_none, acline, mode, vflag;
260 uint64_t mjoules_used;
263 /* Default mode for all AC states is adaptive. */
264 mode_ac = mode_battery = mode_none = MODE_ADAPTIVE;
265 cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
266 cpu_idle_mark = DEFAULT_IDLE_PERCENT;
267 poll_ival = DEFAULT_POLL_INTERVAL;
272 while ((ch = getopt(argc, argv, "a:b:i:n:p:r:v")) != EOF)
275 parse_mode(optarg, &mode_ac, ch);
278 parse_mode(optarg, &mode_battery, ch);
281 cpu_idle_mark = atoi(optarg);
282 if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
283 warnx("%d is not a valid percent",
289 parse_mode(optarg, &mode_none, ch);
292 poll_ival = atoi(optarg);
294 warnx("poll interval is in units of ms");
299 cpu_running_mark = atoi(optarg);
300 if (cpu_running_mark < 0 || cpu_running_mark > 100) {
301 warnx("%d is not a valid percent",
313 /* Poll interval is in units of ms. */
316 /* Look up various sysctl MIBs. */
318 if (sysctlnametomib("kern.cp_time", cp_time_mib, &len))
319 err(1, "lookup kern.cp_time");
321 if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
322 err(1, "lookup freq");
324 if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
325 err(1, "lookup freq_levels");
327 /* Check if we can read the idle time and supported freqs. */
328 if (read_usage_times(NULL, NULL))
329 err(1, "read_usage_times");
330 if (read_freqs(&numfreqs, &freqs, &mwatts))
331 err(1, "error reading supported CPU frequencies");
333 /* Decide whether to use ACPI or APM to read the AC line status. */
336 /* Run in the background unless in verbose mode. */
339 signal(SIGINT, handle_sigs);
340 signal(SIGTERM, handle_sigs);
344 /* Check status every few milliseconds. */
347 /* If the user requested we quit, print some statistics. */
348 if (exit_requested) {
349 if (vflag && mjoules_used != 0)
350 printf("total joules used: %u.%03u\n",
351 (u_int)(mjoules_used / 1000),
352 (int)mjoules_used % 1000);
356 /* Read the current AC status and record the mode. */
357 acline = acline_read();
369 errx(1, "invalid AC line status %d", acline);
372 /* Read the current frequency. */
373 len = sizeof(curfreq);
374 if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0))
375 err(1, "error reading current CPU frequency");
378 for (i = 0; i < numfreqs; i++) {
379 if (freqs[i] == curfreq)
383 /* Keep a sum of all power actually used. */
384 if (i < numfreqs && mwatts[i] != -1)
386 (mwatts[i] * (poll_ival / 1000)) / 1000;
389 /* Always switch to the lowest frequency in min mode. */
390 if (mode == MODE_MIN) {
391 if (curfreq != freqs[numfreqs - 1]) {
393 printf("now operating on %s power; "
394 "changing frequency to %d MHz\n",
395 modes[acline], freqs[numfreqs - 1]);
397 if (set_freq(freqs[numfreqs - 1]))
398 err(1, "error setting CPU freq %d",
399 freqs[numfreqs - 1]);
404 /* Always switch to the highest frequency in max mode. */
405 if (mode == MODE_MAX) {
406 if (curfreq != freqs[0]) {
408 printf("now operating on %s power; "
409 "changing frequency to %d MHz\n",
410 modes[acline], freqs[0]);
412 if (set_freq(freqs[0]))
413 err(1, "error setting CPU freq %d",
419 /* Adaptive mode; get the current CPU usage times. */
420 if (read_usage_times(&idle, &total))
421 err(1, "read_usage_times");
424 * If we're idle less than the active mark, jump the CPU to
425 * its fastest speed if we're not there yet. If we're idle
426 * more than the idle mark, drop down to the first setting
427 * that is half the current speed (exponential backoff).
429 if (idle < (total * cpu_running_mark) / 100 &&
430 curfreq < freqs[0]) {
432 printf("idle time < %d%%, increasing clock"
433 " speed from %d MHz to %d MHz\n",
434 cpu_running_mark, curfreq, freqs[0]);
436 if (set_freq(freqs[0]))
437 err(1, "error setting CPU frequency %d",
439 } else if (idle > (total * cpu_idle_mark) / 100 &&
440 curfreq > freqs[numfreqs - 1]) {
441 for (i = 0; i < numfreqs - 1; i++) {
442 if (freqs[i] <= curfreq / 2)
446 printf("idle time > %d%%, decreasing clock"
447 " speed from %d MHz to %d MHz\n",
448 cpu_idle_mark, curfreq, freqs[i]);
450 if (set_freq(freqs[i]))
451 err(1, "error setting CPU frequency %d",