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
64typedef enum {
65	MODE_MIN,
66	MODE_ADAPTIVE,
67	MODE_HIADAPTIVE,
68	MODE_MAX,
69} modes_t;
70
71typedef enum {
72	SRC_AC,
73	SRC_BATTERY,
74	SRC_UNKNOWN,
75} power_src_t;
76
77static 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
89static int	read_usage_times(int *load, int nonice);
90static int	read_freqs(int *numfreqs, int **freqs, int **power,
91		    int minfreq, int maxfreq);
92static int	set_freq(int freq);
93static void	acline_init(void);
94static void	acline_read(void);
95static int	devd_init(void);
96static void	devd_close(void);
97static void	handle_sigs(int sig);
98static void	parse_mode(char *arg, int *mode, int ch);
99static void	usage(void);
100
101/* Sysctl data structures. */
102static int	cp_times_mib[2];
103static int	freq_mib[4];
104static int	levels_mib[4];
105static int	acline_mib[4];
106static size_t	acline_mib_len;
107
108/* Configuration */
109static int	cpu_running_mark;
110static int	cpu_idle_mark;
111static int	poll_ival;
112static int	vflag;
113
114static volatile sig_atomic_t exit_requested;
115static power_src_t acline_status;
116typedef enum {
117	ac_none,
118	ac_sysctl,
119	ac_acpi_devd,
120#ifdef USE_APM
121	ac_apm,
122#endif
123} acline_mode_t;
124static acline_mode_t acline_mode;
125static acline_mode_t acline_mode_user = ac_none;
126#ifdef USE_APM
127static int	apm_fd = -1;
128#endif
129static int	devd_pipe = -1;
130
131#define DEVD_RETRY_INTERVAL 60 /* seconds */
132static 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 */
142static int
143read_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
194static int
195read_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
252static int
253get_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
267static int
268set_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
279static int
280get_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 */
296static void
297acline_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
331static void
332acline_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
405static int
406devd_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
431static void
432devd_close(void)
433{
434
435	close(devd_pipe);
436	devd_pipe = -1;
437}
438
439static void
440parse_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
455static void
456parse_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
470static void
471handle_sigs(int __unused sig)
472{
473
474	exit_requested = 1;
475}
476
477static void
478usage(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
486int
487main(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