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