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