powerd.c revision 259065
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: releng/10.0/usr.sbin/powerd/powerd.c 252713 2013-07-04 18:59:58Z wblock $");
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
62typedef enum {
63	MODE_MIN,
64	MODE_ADAPTIVE,
65	MODE_HIADAPTIVE,
66	MODE_MAX,
67} modes_t;
68
69typedef enum {
70	SRC_AC,
71	SRC_BATTERY,
72	SRC_UNKNOWN,
73} power_src_t;
74
75static 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
87static int	read_usage_times(int *load);
88static int	read_freqs(int *numfreqs, int **freqs, int **power,
89		    int minfreq, int maxfreq);
90static int	set_freq(int freq);
91static void	acline_init(void);
92static void	acline_read(void);
93static int	devd_init(void);
94static void	devd_close(void);
95static void	handle_sigs(int sig);
96static void	parse_mode(char *arg, int *mode, int ch);
97static void	usage(void);
98
99/* Sysctl data structures. */
100static int	cp_times_mib[2];
101static int	freq_mib[4];
102static int	levels_mib[4];
103static int	acline_mib[4];
104static size_t	acline_mib_len;
105
106/* Configuration */
107static int	cpu_running_mark;
108static int	cpu_idle_mark;
109static int	poll_ival;
110static int	vflag;
111
112static volatile sig_atomic_t exit_requested;
113static power_src_t acline_status;
114static 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
123static int	apm_fd = -1;
124#endif
125static int	devd_pipe = -1;
126
127#define DEVD_RETRY_INTERVAL 60 /* seconds */
128static struct timeval tried_devd;
129
130static int
131read_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
178static int
179read_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
234static int
235get_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
249static int
250set_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
261static int
262get_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 */
278static void
279acline_init(void)
280{
281	acline_mib_len = 4;
282
283	if (sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) {
284		acline_mode = ac_sysctl;
285		if (vflag)
286			warnx("using sysctl for AC line status");
287#if __powerpc__
288	} else if (sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) {
289		acline_mode = ac_sysctl;
290		if (vflag)
291			warnx("using sysctl for AC line status");
292#endif
293#ifdef USE_APM
294	} else if ((apm_fd = open(APMDEV, O_RDONLY)) >= 0) {
295		if (vflag)
296			warnx("using APM for AC line status");
297		acline_mode = ac_apm;
298#endif
299	} else {
300		warnx("unable to determine AC line status");
301		acline_mode = ac_none;
302	}
303}
304
305static void
306acline_read(void)
307{
308	if (acline_mode == ac_acpi_devd) {
309		char buf[DEVCTL_MAXBUF], *ptr;
310		ssize_t rlen;
311		int notify;
312
313		rlen = read(devd_pipe, buf, sizeof(buf));
314		if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) {
315			if (vflag)
316				warnx("lost devd connection, switching to sysctl");
317			devd_close();
318			acline_mode = ac_sysctl;
319			/* FALLTHROUGH */
320		}
321		if (rlen > 0 &&
322		    (ptr = strstr(buf, "system=ACPI")) != NULL &&
323		    (ptr = strstr(ptr, "subsystem=ACAD")) != NULL &&
324		    (ptr = strstr(ptr, "notify=")) != NULL &&
325		    sscanf(ptr, "notify=%x", &notify) == 1)
326			acline_status = (notify ? SRC_AC : SRC_BATTERY);
327	}
328	if (acline_mode == ac_sysctl) {
329		int acline;
330		size_t len;
331
332		len = sizeof(acline);
333		if (sysctl(acline_mib, acline_mib_len, &acline, &len,
334		    NULL, 0) == 0)
335			acline_status = (acline ? SRC_AC : SRC_BATTERY);
336		else
337			acline_status = SRC_UNKNOWN;
338	}
339#ifdef USE_APM
340	if (acline_mode == ac_apm) {
341		struct apm_info info;
342
343		if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) {
344			acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY);
345		} else {
346			close(apm_fd);
347			apm_fd = -1;
348			acline_mode = ac_none;
349			acline_status = SRC_UNKNOWN;
350		}
351	}
352#endif
353	/* try to (re)connect to devd */
354	if (acline_mode == ac_sysctl) {
355		struct timeval now;
356
357		gettimeofday(&now, NULL);
358		if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) {
359			if (devd_init() >= 0) {
360				if (vflag)
361					warnx("using devd for AC line status");
362				acline_mode = ac_acpi_devd;
363			}
364			tried_devd = now;
365		}
366	}
367}
368
369static int
370devd_init(void)
371{
372	struct sockaddr_un devd_addr;
373
374	bzero(&devd_addr, sizeof(devd_addr));
375	if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
376		if (vflag)
377			warn("%s(): socket()", __func__);
378		return (-1);
379	}
380
381	devd_addr.sun_family = PF_LOCAL;
382	strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
383	if (connect(devd_pipe, (struct sockaddr *)&devd_addr,
384	    sizeof(devd_addr)) == -1) {
385		if (vflag)
386			warn("%s(): connect()", __func__);
387		close(devd_pipe);
388		devd_pipe = -1;
389		return (-1);
390	}
391
392	if (fcntl(devd_pipe, F_SETFL, O_NONBLOCK) == -1) {
393		if (vflag)
394			warn("%s(): fcntl()", __func__);
395		close(devd_pipe);
396		return (-1);
397	}
398
399	return (devd_pipe);
400}
401
402static void
403devd_close(void)
404{
405
406	close(devd_pipe);
407	devd_pipe = -1;
408}
409
410static void
411parse_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
426static void
427handle_sigs(int __unused sig)
428{
429
430	exit_requested = 1;
431}
432
433static void
434usage(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
442int
443main(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