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