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