xref: /freebsd/usr.sbin/powerd/powerd.c (revision 25ecdc7d52770caf1c9b44b5ec11f468f6b636f3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004 Colin Percival
5  * Copyright (c) 2005 Nate Lawson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted providing that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/ioctl.h>
35 #include <sys/sysctl.h>
36 #include <sys/resource.h>
37 #include <sys/socket.h>
38 #include <sys/time.h>
39 #include <sys/un.h>
40 
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <libutil.h>
45 #include <signal.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sysexits.h>
50 #include <unistd.h>
51 
52 #ifdef __i386__
53 #define USE_APM
54 #endif
55 
56 #ifdef USE_APM
57 #include <machine/apm_bios.h>
58 #endif
59 
60 #define DEFAULT_ACTIVE_PERCENT	75
61 #define DEFAULT_IDLE_PERCENT	50
62 #define DEFAULT_POLL_INTERVAL	250	/* Poll interval in milliseconds */
63 
64 typedef enum {
65 	MODE_MIN,
66 	MODE_ADAPTIVE,
67 	MODE_HIADAPTIVE,
68 	MODE_MAX,
69 } modes_t;
70 
71 typedef enum {
72 	SRC_AC,
73 	SRC_BATTERY,
74 	SRC_UNKNOWN,
75 } power_src_t;
76 
77 static const char *modes[] = {
78 	"AC",
79 	"battery",
80 	"unknown"
81 };
82 
83 #define ACPIAC		"hw.acpi.acline"
84 #define PMUAC		"dev.pmu.0.acline"
85 #define APMDEV		"/dev/apm"
86 #define DEVDPIPE	"/var/run/devd.pipe"
87 #define DEVCTL_MAXBUF	1024
88 
89 static int	read_usage_times(int *load, int nonice);
90 static int	read_freqs(int *numfreqs, int **freqs, int **power,
91 		    int minfreq, int maxfreq);
92 static int	set_freq(int freq);
93 static void	acline_init(void);
94 static void	acline_read(void);
95 static int	devd_init(void);
96 static void	devd_close(void);
97 static void	handle_sigs(int sig);
98 static void	parse_mode(char *arg, int *mode, int ch);
99 static void	usage(void);
100 
101 /* Sysctl data structures. */
102 static int	cp_times_mib[2];
103 static int	freq_mib[4];
104 static int	levels_mib[4];
105 static int	acline_mib[4];
106 static size_t	acline_mib_len;
107 
108 /* Configuration */
109 static int	cpu_running_mark;
110 static int	cpu_idle_mark;
111 static int	poll_ival;
112 static int	vflag;
113 
114 static volatile sig_atomic_t exit_requested;
115 static power_src_t acline_status;
116 typedef enum {
117 	ac_none,
118 	ac_sysctl,
119 	ac_acpi_devd,
120 #ifdef USE_APM
121 	ac_apm,
122 #endif
123 } acline_mode_t;
124 static acline_mode_t acline_mode;
125 static acline_mode_t acline_mode_user = ac_none;
126 #ifdef USE_APM
127 static int	apm_fd = -1;
128 #endif
129 static int	devd_pipe = -1;
130 
131 #define DEVD_RETRY_INTERVAL 60 /* seconds */
132 static struct timeval tried_devd;
133 
134 /*
135  * This function returns summary load of all CPUs.  It was made so
136  * intentionally to not reduce performance in scenarios when several
137  * threads are processing requests as a pipeline -- running one at
138  * a time on different CPUs and waiting for each other.  If nonice
139  * is nonzero, only user+sys+intr time will be counted as load; any
140  * nice time will be treated as if idle.
141  */
142 static int
143 read_usage_times(int *load, int nonice)
144 {
145 	static long *cp_times = NULL, *cp_times_old = NULL;
146 	static int ncpus = 0;
147 	size_t cp_times_len;
148 	int error, cpu, i, total, excl;
149 
150 	if (cp_times == NULL) {
151 		cp_times_len = 0;
152 		error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0);
153 		if (error)
154 			return (error);
155 		if ((cp_times = malloc(cp_times_len)) == NULL)
156 			return (errno);
157 		if ((cp_times_old = malloc(cp_times_len)) == NULL) {
158 			free(cp_times);
159 			cp_times = NULL;
160 			return (errno);
161 		}
162 		ncpus = cp_times_len / (sizeof(long) * CPUSTATES);
163 	}
164 
165 	cp_times_len = sizeof(long) * CPUSTATES * ncpus;
166 	error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
167 	if (error)
168 		return (error);
169 
170 	if (load) {
171 		*load = 0;
172 		for (cpu = 0; cpu < ncpus; cpu++) {
173 			total = 0;
174 			for (i = 0; i < CPUSTATES; i++) {
175 			    total += cp_times[cpu * CPUSTATES + i] -
176 				cp_times_old[cpu * CPUSTATES + i];
177 			}
178 			if (total == 0)
179 				continue;
180 			excl = cp_times[cpu * CPUSTATES + CP_IDLE] -
181 			    cp_times_old[cpu * CPUSTATES + CP_IDLE];
182 			if (nonice)
183 				excl += cp_times[cpu * CPUSTATES + CP_NICE] -
184 				    cp_times_old[cpu * CPUSTATES + CP_NICE];
185 			*load += 100 - excl * 100 / total;
186 		}
187 	}
188 
189 	memcpy(cp_times_old, cp_times, cp_times_len);
190 
191 	return (0);
192 }
193 
194 static int
195 read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq)
196 {
197 	char *freqstr, *p, *q;
198 	int i, j;
199 	size_t len = 0;
200 
201 	if (sysctl(levels_mib, 4, NULL, &len, NULL, 0))
202 		return (-1);
203 	if ((freqstr = malloc(len)) == NULL)
204 		return (-1);
205 	if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) {
206 		free(freqstr);
207 		return (-1);
208 	}
209 
210 	*numfreqs = 1;
211 	for (p = freqstr; *p != '\0'; p++)
212 		if (*p == ' ')
213 			(*numfreqs)++;
214 
215 	if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) {
216 		free(freqstr);
217 		return (-1);
218 	}
219 	if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
220 		free(freqstr);
221 		free(*freqs);
222 		return (-1);
223 	}
224 	for (i = 0, j = 0, p = freqstr; i < *numfreqs; i++) {
225 		q = strchr(p, ' ');
226 		if (q != NULL)
227 			*q = '\0';
228 		if (sscanf(p, "%d/%d", &(*freqs)[j], &(*power)[i]) != 2) {
229 			free(freqstr);
230 			free(*freqs);
231 			free(*power);
232 			return (-1);
233 		}
234 		if (((*freqs)[j] >= minfreq || minfreq == -1) &&
235 		    ((*freqs)[j] <= maxfreq || maxfreq == -1))
236 			j++;
237 		p = q + 1;
238 	}
239 
240 	*numfreqs = j;
241 	if ((*freqs = realloc(*freqs, *numfreqs * sizeof(int))) == NULL) {
242 		free(freqstr);
243 		free(*freqs);
244 		free(*power);
245 		return (-1);
246 	}
247 
248 	free(freqstr);
249 	return (0);
250 }
251 
252 static int
253 get_freq(void)
254 {
255 	size_t len;
256 	int curfreq;
257 
258 	len = sizeof(curfreq);
259 	if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
260 		if (vflag)
261 			warn("error reading current CPU frequency");
262 		curfreq = 0;
263 	}
264 	return (curfreq);
265 }
266 
267 static int
268 set_freq(int freq)
269 {
270 
271 	if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) {
272 		if (errno != EPERM)
273 			return (-1);
274 	}
275 
276 	return (0);
277 }
278 
279 static int
280 get_freq_id(int freq, int *freqs, int numfreqs)
281 {
282 	int i = 1;
283 
284 	while (i < numfreqs) {
285 		if (freqs[i] < freq)
286 			break;
287 		i++;
288 	}
289 	return (i - 1);
290 }
291 
292 /*
293  * Try to use ACPI to find the AC line status.  If this fails, fall back
294  * to APM.  If nothing succeeds, we'll just run in default mode.
295  */
296 static void
297 acline_init(void)
298 {
299 	int skip_source_check;
300 
301 	acline_mib_len = 4;
302 	acline_status = SRC_UNKNOWN;
303 	skip_source_check = (acline_mode_user == ac_none ||
304 			     acline_mode_user == ac_acpi_devd);
305 
306 	if ((skip_source_check || acline_mode_user == ac_sysctl) &&
307 	    sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) {
308 		acline_mode = ac_sysctl;
309 		if (vflag)
310 			warnx("using sysctl for AC line status");
311 #ifdef __powerpc__
312 	} else if ((skip_source_check || acline_mode_user == ac_sysctl) &&
313 		   sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) {
314 		acline_mode = ac_sysctl;
315 		if (vflag)
316 			warnx("using sysctl for AC line status");
317 #endif
318 #ifdef USE_APM
319 	} else if ((skip_source_check || acline_mode_user == ac_apm) &&
320 		   (apm_fd = open(APMDEV, O_RDONLY)) >= 0) {
321 		if (vflag)
322 			warnx("using APM for AC line status");
323 		acline_mode = ac_apm;
324 #endif
325 	} else {
326 		warnx("unable to determine AC line status");
327 		acline_mode = ac_none;
328 	}
329 }
330 
331 static void
332 acline_read(void)
333 {
334 	if (acline_mode == ac_acpi_devd) {
335 		char buf[DEVCTL_MAXBUF], *ptr;
336 		ssize_t rlen;
337 		int notify;
338 
339 		rlen = read(devd_pipe, buf, sizeof(buf));
340 		if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) {
341 			if (vflag)
342 				warnx("lost devd connection, switching to sysctl");
343 			devd_close();
344 			acline_mode = ac_sysctl;
345 			/* FALLTHROUGH */
346 		}
347 		if (rlen > 0 &&
348 		    (ptr = strstr(buf, "system=ACPI")) != NULL &&
349 		    (ptr = strstr(ptr, "subsystem=ACAD")) != NULL &&
350 		    (ptr = strstr(ptr, "notify=")) != NULL &&
351 		    sscanf(ptr, "notify=%x", &notify) == 1)
352 			acline_status = (notify ? SRC_AC : SRC_BATTERY);
353 	}
354 	if (acline_mode == ac_sysctl) {
355 		int acline;
356 		size_t len;
357 
358 		len = sizeof(acline);
359 		if (sysctl(acline_mib, acline_mib_len, &acline, &len,
360 		    NULL, 0) == 0)
361 			acline_status = (acline ? SRC_AC : SRC_BATTERY);
362 		else
363 			acline_status = SRC_UNKNOWN;
364 	}
365 #ifdef USE_APM
366 	if (acline_mode == ac_apm) {
367 		struct apm_info info;
368 
369 		if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) {
370 			acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY);
371 		} else {
372 			close(apm_fd);
373 			apm_fd = -1;
374 			acline_mode = ac_none;
375 			acline_status = SRC_UNKNOWN;
376 		}
377 	}
378 #endif
379 	/* try to (re)connect to devd */
380 #ifdef USE_APM
381 	if ((acline_mode == ac_sysctl &&
382 	    (acline_mode_user == ac_none ||
383 	     acline_mode_user == ac_acpi_devd)) ||
384 	    (acline_mode == ac_apm &&
385 	     acline_mode_user == ac_acpi_devd)) {
386 #else
387 	if (acline_mode == ac_sysctl &&
388 	    (acline_mode_user == ac_none ||
389 	     acline_mode_user == ac_acpi_devd)) {
390 #endif
391 		struct timeval now;
392 
393 		gettimeofday(&now, NULL);
394 		if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) {
395 			if (devd_init() >= 0) {
396 				if (vflag)
397 					warnx("using devd for AC line status");
398 				acline_mode = ac_acpi_devd;
399 			}
400 			tried_devd = now;
401 		}
402 	}
403 }
404 
405 static int
406 devd_init(void)
407 {
408 	struct sockaddr_un devd_addr;
409 
410 	bzero(&devd_addr, sizeof(devd_addr));
411 	if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0) {
412 		if (vflag)
413 			warn("%s(): socket()", __func__);
414 		return (-1);
415 	}
416 
417 	devd_addr.sun_family = PF_LOCAL;
418 	strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
419 	if (connect(devd_pipe, (struct sockaddr *)&devd_addr,
420 	    sizeof(devd_addr)) == -1) {
421 		if (vflag)
422 			warn("%s(): connect()", __func__);
423 		close(devd_pipe);
424 		devd_pipe = -1;
425 		return (-1);
426 	}
427 
428 	return (devd_pipe);
429 }
430 
431 static void
432 devd_close(void)
433 {
434 
435 	close(devd_pipe);
436 	devd_pipe = -1;
437 }
438 
439 static void
440 parse_mode(char *arg, int *mode, int ch)
441 {
442 
443 	if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0)
444 		*mode = MODE_MIN;
445 	else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0)
446 		*mode = MODE_MAX;
447 	else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
448 		*mode = MODE_ADAPTIVE;
449 	else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
450 		*mode = MODE_HIADAPTIVE;
451 	else
452 		errx(1, "bad option: -%c %s", (char)ch, optarg);
453 }
454 
455 static void
456 parse_acline_mode(char *arg, int ch)
457 {
458 	if (strcmp(arg, "sysctl") == 0)
459 		acline_mode_user = ac_sysctl;
460 	else if (strcmp(arg, "devd") == 0)
461 		acline_mode_user = ac_acpi_devd;
462 #ifdef USE_APM
463 	else if (strcmp(arg, "apm") == 0)
464 		acline_mode_user = ac_apm;
465 #endif
466 	else
467 		errx(1, "bad option: -%c %s", (char)ch, optarg);
468 }
469 
470 static void
471 handle_sigs(int __unused sig)
472 {
473 
474 	exit_requested = 1;
475 }
476 
477 static void
478 usage(void)
479 {
480 
481 	fprintf(stderr,
482 "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n");
483 	exit(1);
484 }
485 
486 int
487 main(int argc, char * argv[])
488 {
489 	struct timeval timeout;
490 	fd_set fdset;
491 	int nfds;
492 	struct pidfh *pfh = NULL;
493 	const char *pidfile = NULL;
494 	int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load;
495 	int minfreq = -1, maxfreq = -1;
496 	int ch, mode, mode_ac, mode_battery, mode_none, idle, to;
497 	uint64_t mjoules_used;
498 	size_t len;
499 	int nonice;
500 
501 	/* Default mode for all AC states is adaptive. */
502 	mode_ac = mode_none = MODE_HIADAPTIVE;
503 	mode_battery = MODE_ADAPTIVE;
504 	cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
505 	cpu_idle_mark = DEFAULT_IDLE_PERCENT;
506 	poll_ival = DEFAULT_POLL_INTERVAL;
507 	mjoules_used = 0;
508 	vflag = 0;
509 	nonice = 0;
510 
511 	/* User must be root to control frequencies. */
512 	if (geteuid() != 0)
513 		errx(1, "must be root to run");
514 
515 	while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1)
516 		switch (ch) {
517 		case 'a':
518 			parse_mode(optarg, &mode_ac, ch);
519 			break;
520 		case 'b':
521 			parse_mode(optarg, &mode_battery, ch);
522 			break;
523 		case 's':
524 			parse_acline_mode(optarg, ch);
525 			break;
526 		case 'i':
527 			cpu_idle_mark = atoi(optarg);
528 			if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
529 				warnx("%d is not a valid percent",
530 				    cpu_idle_mark);
531 				usage();
532 			}
533 			break;
534 		case 'm':
535 			minfreq = atoi(optarg);
536 			if (minfreq < 0) {
537 				warnx("%d is not a valid CPU frequency",
538 				    minfreq);
539 				usage();
540 			}
541 			break;
542 		case 'M':
543 			maxfreq = atoi(optarg);
544 			if (maxfreq < 0) {
545 				warnx("%d is not a valid CPU frequency",
546 				    maxfreq);
547 				usage();
548 			}
549 			break;
550 		case 'N':
551 			nonice = 1;
552 			break;
553 		case 'n':
554 			parse_mode(optarg, &mode_none, ch);
555 			break;
556 		case 'p':
557 			poll_ival = atoi(optarg);
558 			if (poll_ival < 5) {
559 				warnx("poll interval is in units of ms");
560 				usage();
561 			}
562 			break;
563 		case 'P':
564 			pidfile = optarg;
565 			break;
566 		case 'r':
567 			cpu_running_mark = atoi(optarg);
568 			if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
569 				warnx("%d is not a valid percent",
570 				    cpu_running_mark);
571 				usage();
572 			}
573 			break;
574 		case 'v':
575 			vflag = 1;
576 			break;
577 		default:
578 			usage();
579 		}
580 
581 	mode = mode_none;
582 
583 	/* Poll interval is in units of ms. */
584 	poll_ival *= 1000;
585 
586 	/* Look up various sysctl MIBs. */
587 	len = 2;
588 	if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
589 		err(1, "lookup kern.cp_times");
590 	len = 4;
591 	if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
592 		err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting");
593 	len = 4;
594 	if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
595 		err(1, "lookup freq_levels");
596 
597 	/* Check if we can read the load and supported freqs. */
598 	if (read_usage_times(NULL, nonice))
599 		err(1, "read_usage_times");
600 	if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq))
601 		err(1, "error reading supported CPU frequencies");
602 	if (numfreqs == 0)
603 		errx(1, "no CPU frequencies in user-specified range");
604 
605 	/* Run in the background unless in verbose mode. */
606 	if (!vflag) {
607 		pid_t otherpid;
608 
609 		pfh = pidfile_open(pidfile, 0600, &otherpid);
610 		if (pfh == NULL) {
611 			if (errno == EEXIST) {
612 				errx(1, "powerd already running, pid: %d",
613 				    otherpid);
614 			}
615 			warn("cannot open pid file");
616 		}
617 		if (daemon(0, 0) != 0) {
618 			warn("cannot enter daemon mode, exiting");
619 			pidfile_remove(pfh);
620 			exit(EXIT_FAILURE);
621 
622 		}
623 		pidfile_write(pfh);
624 	}
625 
626 	/* Decide whether to use ACPI or APM to read the AC line status. */
627 	acline_init();
628 
629 	/*
630 	 * Exit cleanly on signals.
631 	 */
632 	signal(SIGINT, handle_sigs);
633 	signal(SIGTERM, handle_sigs);
634 
635 	freq = initfreq = curfreq = get_freq();
636 	i = get_freq_id(curfreq, freqs, numfreqs);
637 	if (freq < 1)
638 		freq = 1;
639 
640 	/*
641 	 * If we are in adaptive mode and the current frequency is outside the
642 	 * user-defined range, adjust it to be within the user-defined range.
643 	 */
644 	acline_read();
645 	if (acline_status > SRC_UNKNOWN)
646 		errx(1, "invalid AC line status %d", acline_status);
647 	if ((acline_status == SRC_AC &&
648 	    (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) ||
649 	    (acline_status == SRC_BATTERY &&
650 	    (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) ||
651 	    (acline_status == SRC_UNKNOWN &&
652 	    (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) {
653 		/* Read the current frequency. */
654 		len = sizeof(curfreq);
655 		if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
656 			if (vflag)
657 				warn("error reading current CPU frequency");
658 		}
659 		if (curfreq < freqs[numfreqs - 1]) {
660 			if (vflag) {
661 				printf("CPU frequency is below user-defined "
662 				    "minimum; changing frequency to %d "
663 				    "MHz\n", freqs[numfreqs - 1]);
664 			}
665 			if (set_freq(freqs[numfreqs - 1]) != 0) {
666 				warn("error setting CPU freq %d",
667 				    freqs[numfreqs - 1]);
668 			}
669 		} else if (curfreq > freqs[0]) {
670 			if (vflag) {
671 				printf("CPU frequency is above user-defined "
672 				    "maximum; changing frequency to %d "
673 				    "MHz\n", freqs[0]);
674 			}
675 			if (set_freq(freqs[0]) != 0) {
676 				warn("error setting CPU freq %d",
677 				    freqs[0]);
678 			}
679 		}
680 	}
681 
682 	idle = 0;
683 	/* Main loop. */
684 	for (;;) {
685 		FD_ZERO(&fdset);
686 		if (devd_pipe >= 0) {
687 			FD_SET(devd_pipe, &fdset);
688 			nfds = devd_pipe + 1;
689 		} else {
690 			nfds = 0;
691 		}
692 		if (mode == MODE_HIADAPTIVE || idle < 120)
693 			to = poll_ival;
694 		else if (idle < 360)
695 			to = poll_ival * 2;
696 		else
697 			to = poll_ival * 4;
698 		timeout.tv_sec = to / 1000000;
699 		timeout.tv_usec = to % 1000000;
700 		select(nfds, &fdset, NULL, &fdset, &timeout);
701 
702 		/* If the user requested we quit, print some statistics. */
703 		if (exit_requested) {
704 			if (vflag && mjoules_used != 0)
705 				printf("total joules used: %u.%03u\n",
706 				    (u_int)(mjoules_used / 1000),
707 				    (int)mjoules_used % 1000);
708 			break;
709 		}
710 
711 		/* Read the current AC status and record the mode. */
712 		acline_read();
713 		switch (acline_status) {
714 		case SRC_AC:
715 			mode = mode_ac;
716 			break;
717 		case SRC_BATTERY:
718 			mode = mode_battery;
719 			break;
720 		case SRC_UNKNOWN:
721 			mode = mode_none;
722 			break;
723 		default:
724 			errx(1, "invalid AC line status %d", acline_status);
725 		}
726 
727 		/* Read the current frequency. */
728 		if (idle % 32 == 0) {
729 			if ((curfreq = get_freq()) == 0)
730 				continue;
731 			i = get_freq_id(curfreq, freqs, numfreqs);
732 		}
733 		idle++;
734 		if (vflag) {
735 			/* Keep a sum of all power actually used. */
736 			if (mwatts[i] != -1)
737 				mjoules_used +=
738 				    (mwatts[i] * (poll_ival / 1000)) / 1000;
739 		}
740 
741 		/* Always switch to the lowest frequency in min mode. */
742 		if (mode == MODE_MIN) {
743 			freq = freqs[numfreqs - 1];
744 			if (curfreq != freq) {
745 				if (vflag) {
746 					printf("now operating on %s power; "
747 					    "changing frequency to %d MHz\n",
748 					    modes[acline_status], freq);
749 				}
750 				idle = 0;
751 				if (set_freq(freq) != 0) {
752 					warn("error setting CPU freq %d",
753 					    freq);
754 					continue;
755 				}
756 			}
757 			continue;
758 		}
759 
760 		/* Always switch to the highest frequency in max mode. */
761 		if (mode == MODE_MAX) {
762 			freq = freqs[0];
763 			if (curfreq != freq) {
764 				if (vflag) {
765 					printf("now operating on %s power; "
766 					    "changing frequency to %d MHz\n",
767 					    modes[acline_status], freq);
768 				}
769 				idle = 0;
770 				if (set_freq(freq) != 0) {
771 					warn("error setting CPU freq %d",
772 					    freq);
773 					continue;
774 				}
775 			}
776 			continue;
777 		}
778 
779 		/* Adaptive mode; get the current CPU usage times. */
780 		if (read_usage_times(&load, nonice)) {
781 			if (vflag)
782 				warn("read_usage_times() failed");
783 			continue;
784 		}
785 
786 		if (mode == MODE_ADAPTIVE) {
787 			if (load > cpu_running_mark) {
788 				if (load > 95 || load > cpu_running_mark * 2)
789 					freq *= 2;
790 				else
791 					freq = freq * load / cpu_running_mark;
792 				if (freq > freqs[0])
793 					freq = freqs[0];
794 			} else if (load < cpu_idle_mark &&
795 			    curfreq * load < freqs[get_freq_id(
796 			    freq * 7 / 8, freqs, numfreqs)] *
797 			    cpu_running_mark) {
798 				freq = freq * 7 / 8;
799 				if (freq < freqs[numfreqs - 1])
800 					freq = freqs[numfreqs - 1];
801 			}
802 		} else { /* MODE_HIADAPTIVE */
803 			if (load > cpu_running_mark / 2) {
804 				if (load > 95 || load > cpu_running_mark)
805 					freq *= 4;
806 				else
807 					freq = freq * load * 2 / cpu_running_mark;
808 				if (freq > freqs[0] * 2)
809 					freq = freqs[0] * 2;
810 			} else if (load < cpu_idle_mark / 2 &&
811 			    curfreq * load < freqs[get_freq_id(
812 			    freq * 31 / 32, freqs, numfreqs)] *
813 			    cpu_running_mark / 2) {
814 				freq = freq * 31 / 32;
815 				if (freq < freqs[numfreqs - 1])
816 					freq = freqs[numfreqs - 1];
817 			}
818 		}
819 		if (vflag) {
820 		    printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
821 			load, curfreq, i, freq);
822 		}
823 		j = get_freq_id(freq, freqs, numfreqs);
824 		if (i != j) {
825 			if (vflag) {
826 				printf("changing clock"
827 				    " speed from %d MHz to %d MHz\n",
828 				    freqs[i], freqs[j]);
829 			}
830 			idle = 0;
831 			if (set_freq(freqs[j]))
832 				warn("error setting CPU frequency %d",
833 				    freqs[j]);
834 		}
835 	}
836 	if (set_freq(initfreq))
837 		warn("error setting CPU frequency %d", initfreq);
838 	free(freqs);
839 	free(mwatts);
840 	devd_close();
841 	if (!vflag)
842 		pidfile_remove(pfh);
843 
844 	exit(0);
845 }
846