xref: /dflybsd-src/usr.sbin/powerd/powerd.c (revision e8721bf4b6e6b12c2025fbba27e7b9214bb19756)
1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*
36  * The powerd daemon monitors the cpu load and adjusts cpu frequencies
37  * via hw.acpi.cpu.px_dom*.
38  */
39 
40 #define _KERNEL_STRUCTURES
41 #include <sys/types.h>
42 #include <sys/sysctl.h>
43 #include <sys/kinfo.h>
44 #include <sys/file.h>
45 #include <sys/queue.h>
46 #include <sys/soundcard.h>
47 #include <sys/time.h>
48 #include <machine/cpufunc.h>
49 #include <err.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <syslog.h>
55 
56 #include "alert1.h"
57 
58 #define MAXDOM		MAXCPU	/* worst case, 1 cpu per domain */
59 
60 #define MAXFREQ		64
61 
62 struct cpu_pwrdom {
63 	TAILQ_ENTRY(cpu_pwrdom)	dom_link;
64 	int			dom_id;
65 	int			dom_ncpus;
66 	cpumask_t		dom_cpumask;
67 };
68 TAILQ_HEAD(cpu_pwrdom_list, cpu_pwrdom);
69 
70 static void usage(void);
71 static double getcputime(double);
72 static void acpi_setcpufreq(int nstate);
73 static int setupdominfo(void);
74 static int has_battery(void);
75 static int mon_battery(void);
76 static void getncpus(void);
77 static void getuschedmask(void);
78 static int has_perfbias(void);
79 static void setperfbias(cpumask_t, int);
80 
81 static struct cpu_pwrdom_list CpuPwrDomain;
82 static struct cpu_pwrdom *CpuPwrDomLimit;
83 static struct cpu_pwrdom CpuPwrDomLast;
84 static int NCpuPwrDomUsed;
85 
86 static int NCpus;
87 static cpumask_t UschedCpumask;
88 static int DebugOpt;
89 static int TurboOpt = 1;
90 static int CpuLimit;		/* # of cpus at max frequency */
91 static int PowerFd;
92 static int Hysteresis = 10;	/* percentage */
93 static double TriggerUp = 0.25;	/* single-cpu load to force max freq */
94 static double TriggerDown;	/* load per cpu to force the min freq */
95 static int BatLifeMin = 2;	/* shutdown the box, if low on battery life */
96 static struct timespec BatLifePrevT;
97 static int BatLifePollIntvl = 5; /* unit: sec */
98 static int HasPerfbias = 1;
99 
100 static struct timespec BatShutdownStartT;
101 static int BatShutdownLinger = -1;
102 static int BatShutdownLingerSet = 60; /* unit: sec */
103 static int BatShutdownLingerCnt;
104 static int BatShutdownAudioAlert = 1;
105 
106 static void sigintr(int signo);
107 
108 int
109 main(int ac, char **av)
110 {
111 	double qavg;
112 	double uavg;	/* uavg - used for speeding up */
113 	double davg;	/* davg - used for slowing down */
114 	double srt;
115 	double pollrate;
116 	int ch;
117 	int ustate;
118 	int dstate;
119 	int nstate;
120 	char buf[64];
121 	int monbat;
122 
123 	srt = 8.0;	/* time for samples - 8 seconds */
124 	pollrate = 1.0;	/* polling rate in seconds */
125 
126 	while ((ch = getopt(ac, av, "dep:r:tu:B:L:P:QT:")) != -1) {
127 		switch(ch) {
128 		case 'd':
129 			DebugOpt = 1;
130 			break;
131 		case 'e':
132 			HasPerfbias = 0;
133 			break;
134 		case 'p':
135 			Hysteresis = (int)strtol(optarg, NULL, 10);
136 			break;
137 		case 'r':
138 			pollrate = strtod(optarg, NULL);
139 			break;
140 		case 't':
141 			TurboOpt = 0;
142 			break;
143 		case 'u':
144 			TriggerUp = (double)strtol(optarg, NULL, 10) / 100;
145 			break;
146 		case 'B':
147 			BatLifeMin = strtol(optarg, NULL, 10);
148 			break;
149 		case 'L':
150 			BatShutdownLingerSet = strtol(optarg, NULL, 10);
151 			if (BatShutdownLingerSet < 0)
152 				BatShutdownLingerSet = 0;
153 			break;
154 		case 'P':
155 			BatLifePollIntvl = strtol(optarg, NULL, 10);
156 			break;
157 		case 'Q':
158 			BatShutdownAudioAlert = 0;
159 			break;
160 		case 'T':
161 			srt = strtod(optarg, NULL);
162 			break;
163 		default:
164 			usage();
165 			/* NOT REACHED */
166 		}
167 	}
168 	ac -= optind;
169 	av += optind;
170 
171 	/* Get the number of cpus */
172 	getncpus();
173 
174 	/* Get usched cpumask */
175 	getuschedmask();
176 
177 	if (0 > Hysteresis || Hysteresis > 99) {
178 		fprintf(stderr, "Invalid hysteresis value\n");
179 		exit(1);
180 	}
181 
182 	if (0 > TriggerUp || TriggerUp > 1) {
183 		fprintf(stderr, "Invalid load limit value\n");
184 		exit(1);
185 	}
186 
187 	TriggerDown = TriggerUp - (TriggerUp * (double) Hysteresis / 100);
188 
189 	/*
190 	 * Make sure powerd is not already running.
191 	 */
192 	PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644);
193 	if (PowerFd < 0) {
194 		fprintf(stderr,
195 			"Cannot create /var/run/powerd.pid, "
196 			"continuing anyway\n");
197 	} else {
198 		if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) {
199 			fprintf(stderr, "powerd is already running\n");
200 			exit(1);
201 		}
202 	}
203 
204 	/*
205 	 * Demonize and set pid
206 	 */
207 	if (DebugOpt == 0) {
208 		daemon(0, 0);
209 		openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON);
210 	}
211 
212 	if (PowerFd >= 0) {
213 		ftruncate(PowerFd, 0);
214 		snprintf(buf, sizeof(buf), "%d\n", (int)getpid());
215 		write(PowerFd, buf, strlen(buf));
216 	}
217 
218 	/* Do we need to monitor battery life? */
219 	if (BatLifePollIntvl <= 0)
220 		monbat = 0;
221 	else
222 		monbat = has_battery();
223 
224 	if (HasPerfbias)
225 		HasPerfbias = has_perfbias();
226 
227 	/*
228 	 * Wait hw.acpi.cpu.px_dom* sysctl to be created by kernel
229 	 *
230 	 * Since hw.acpi.cpu.px_dom* creation is queued into ACPI
231 	 * taskqueue and ACPI taskqueue is shared across various
232 	 * ACPI modules, any delay in other modules may cause
233 	 * hw.acpi.cpu.px_dom* to be created at quite a later time
234 	 * (e.g. cmbat module's task could take quite a lot of time).
235 	 */
236 	for (;;) {
237 		/*
238 		 * Prime delta cputime calculation, make sure at least
239 		 * dom0 exists.
240 		 */
241 		getcputime(pollrate);
242 		if (setupdominfo())
243 			break;
244 		usleep((int)(pollrate * 1000000.0));
245 	}
246 
247 	/*
248 	 * Assume everything are used and are maxed out, before we
249 	 * start.
250 	 */
251 	CpuPwrDomLimit = &CpuPwrDomLast;
252 	CpuLimit = NCpus;
253 
254 	/*
255 	 * Set to maximum performance if killed.
256 	 */
257 	signal(SIGINT, sigintr);
258 	signal(SIGTERM, sigintr);
259 	uavg = 0.0;
260 	davg = 0.0;
261 
262 	srt = srt / pollrate;	/* convert to sample count */
263 
264 	if (DebugOpt)
265 		printf("samples for downgrading: %5.2f\n", srt);
266 
267 	/*
268 	 * Monitoring loop
269 	 *
270 	 * Calculate nstate, the number of cpus we wish to run at max
271 	 * frequency.  All remaining cpus will be set to their lowest
272 	 * frequency and mapped out of the user process scheduler.
273 	 */
274 	for (;;) {
275 		qavg = getcputime(pollrate);
276 		uavg = (uavg * 2.0 + qavg) / 3.0;	/* speeding up */
277 		davg = (davg * srt + qavg) / (srt + 1);	/* slowing down */
278 		if (davg < uavg)
279 			davg = uavg;
280 
281 		ustate = uavg / TriggerUp;
282 		if (ustate < CpuLimit)
283 			ustate = uavg / TriggerDown;
284 		dstate = davg / TriggerUp;
285 		if (dstate < CpuLimit)
286 			dstate = davg / TriggerDown;
287 
288 		nstate = (ustate > dstate) ? ustate : dstate;
289 		if (nstate > NCpus)
290 			nstate = NCpus;
291 
292 		if (DebugOpt) {
293 			printf("\rqavg=%5.2f uavg=%5.2f davg=%5.2f "
294 			       "%2d/%2d ncpus=%d\r",
295 				qavg, uavg, davg,
296 				CpuLimit, NCpuPwrDomUsed, nstate);
297 			fflush(stdout);
298 		}
299 		if (nstate != CpuLimit)
300 			acpi_setcpufreq(nstate);
301 		if (monbat)
302 			monbat = mon_battery();
303 		usleep((int)(pollrate * 1000000.0));
304 	}
305 }
306 
307 static
308 void
309 sigintr(int signo __unused)
310 {
311 	syslog(LOG_INFO, "killed, setting max and exiting");
312 	acpi_setcpufreq(NCpus);
313 	exit(1);
314 }
315 
316 /*
317  * Figure out the CPU power domains.
318  */
319 static int
320 setupdominfo(void)
321 {
322 	struct cpu_pwrdom *dom;
323 	struct cpu_pwrdom_list tmp_list;
324 	char buf[64];
325 	char members[1024];
326 	char *str;
327 	size_t msize;
328 	int n, i, ncpu = 0;
329 
330 	TAILQ_INIT(&CpuPwrDomain);
331 	NCpuPwrDomUsed = 0;
332 
333 	TAILQ_INIT(&tmp_list);
334 	for (i = 0; i < MAXDOM; ++i) {
335 		snprintf(buf, sizeof(buf),
336 			 "hw.acpi.cpu.px_dom%d.available", i);
337 		if (sysctlbyname(buf, NULL, NULL, NULL, 0) < 0)
338 			continue;
339 
340 		dom = calloc(1, sizeof(*dom));
341 		dom->dom_id = i;
342 		TAILQ_INSERT_TAIL(&tmp_list, dom, dom_link);
343 	}
344 
345 	while ((dom = TAILQ_FIRST(&tmp_list)) != NULL) {
346 		int bsp_domain = 0;
347 
348 		TAILQ_REMOVE(&tmp_list, dom, dom_link);
349 		CPUMASK_ASSZERO(dom->dom_cpumask);
350 
351 		snprintf(buf, sizeof(buf),
352 			 "hw.acpi.cpu.px_dom%d.members", dom->dom_id);
353 		msize = sizeof(members);
354 		if (sysctlbyname(buf, members, &msize, NULL, 0) < 0) {
355 			free(dom);
356 			continue;
357 		}
358 
359 		members[msize] = 0;
360 		for (str = strtok(members, " "); str; str = strtok(NULL, " ")) {
361 			n = -1;
362 			sscanf(str, "cpu%d", &n);
363 			if (n >= 0) {
364 				++ncpu;
365 				++dom->dom_ncpus;
366 				if (n == 0)
367 					bsp_domain = 1;
368 				CPUMASK_ORBIT(dom->dom_cpumask, n);
369 			}
370 		}
371 		if (dom->dom_ncpus == 0) {
372 			free(dom);
373 			continue;
374 		}
375 		if (DebugOpt) {
376 			printf("dom%d cpumask: ", dom->dom_id);
377 			for (i = 0; i < (int)NELEM(dom->dom_cpumask.ary); ++i) {
378 				printf("%jx ",
379 				    (uintmax_t)dom->dom_cpumask.ary[i]);
380 			}
381 			printf("\n");
382 			fflush(stdout);
383 		}
384 
385 		if (bsp_domain) {
386 			/*
387 			 * Use the power domain containing the BSP as the first
388 			 * power domain.  So if all CPUs are idle, we could
389 			 * leave BSP to the usched without too much trouble.
390 			 */
391 			TAILQ_INSERT_HEAD(&CpuPwrDomain, dom, dom_link);
392 		} else {
393 			TAILQ_INSERT_TAIL(&CpuPwrDomain, dom, dom_link);
394 		}
395 		++NCpuPwrDomUsed;
396 	}
397 
398 	if (ncpu != NCpus) {
399 		while ((dom = TAILQ_FIRST(&CpuPwrDomain)) != NULL) {
400 			TAILQ_REMOVE(&CpuPwrDomain, dom, dom_link);
401 			free(dom);
402 		}
403 		if (DebugOpt) {
404 			printf("Found %d cpus, expecting %d\n", ncpu, NCpus);
405 			fflush(stdout);
406 		}
407 		return 0;
408 	}
409 
410 	/* Install sentinel */
411 	CpuPwrDomLast.dom_id = -1;
412 	TAILQ_INSERT_TAIL(&CpuPwrDomain, &CpuPwrDomLast, dom_link);
413 
414 	return 1;
415 }
416 
417 /*
418  * Return the one-second cpu load.  One cpu at 100% will return a value
419  * of 1.0.  On a SMP system N cpus running at 100% will return a value of N.
420  */
421 static
422 double
423 getcputime(double pollrate)
424 {
425 	static struct kinfo_cputime ocpu_time[MAXCPU];
426 	static struct kinfo_cputime ncpu_time[MAXCPU];
427 	size_t slen;
428 	int ncpu;
429 	int cpu;
430 	uint64_t delta;
431 
432 	bcopy(ncpu_time, ocpu_time, sizeof(struct kinfo_cputime) * NCpus);
433 
434 	slen = sizeof(ncpu_time);
435 	if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) {
436 		fprintf(stderr, "kern.cputime sysctl not available\n");
437 		exit(1);
438 	}
439 	ncpu = slen / sizeof(ncpu_time[0]);
440 
441 	delta = 0;
442 	for (cpu = 0; cpu < ncpu; ++cpu) {
443 		delta += (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys +
444 			  ncpu_time[cpu].cp_nice + ncpu_time[cpu].cp_intr) -
445 			 (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys +
446 			  ocpu_time[cpu].cp_nice + ocpu_time[cpu].cp_intr);
447 	}
448 	return((double)delta / (pollrate * 1000000.0));
449 }
450 
451 static void
452 acpi_getcpufreq_str(int dom_id, int *highest0, int *lowest0)
453 {
454 	char buf[256], sysid[64];
455 	size_t buflen;
456 	char *ptr;
457 	int v, highest, lowest;
458 
459 	/*
460 	 * Retrieve availability list
461 	 */
462 	snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.available",
463 	    dom_id);
464 	buflen = sizeof(buf) - 1;
465 	if (sysctlbyname(sysid, buf, &buflen, NULL, 0) < 0)
466 		return;
467 	buf[buflen] = 0;
468 
469 	/*
470 	 * Parse out the highest and lowest cpu frequencies
471 	 */
472 	ptr = buf;
473 	highest = lowest = 0;
474 	while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
475 		if (lowest == 0 || lowest > v)
476 			lowest = v;
477 		if (highest == 0 || highest < v)
478 			highest = v;
479 		/*
480 		 * Detect turbo mode
481 		 */
482 		if (!TurboOpt && highest - v == 1)
483 			highest = v;
484 	}
485 
486 	*highest0 = highest;
487 	*lowest0 = lowest;
488 }
489 
490 static int
491 acpi_getcpufreq_bin(int dom_id, int *highest0, int *lowest0)
492 {
493 	char sysid[64];
494 	int freq[MAXFREQ];
495 	size_t freqlen;
496 	int freqcnt;
497 
498 	/*
499 	 * Retrieve availability list
500 	 */
501 	snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.avail", dom_id);
502 	freqlen = sizeof(freq);
503 	if (sysctlbyname(sysid, freq, &freqlen, NULL, 0) < 0)
504 		return 0;
505 
506 	freqcnt = freqlen / sizeof(freq[0]);
507 	if (freqcnt == 0)
508 		return 0;
509 
510 	*lowest0 = freq[freqcnt - 1];
511 
512 	*highest0 = freq[0];
513 	if (!TurboOpt && freqcnt > 1 && freq[0] - freq[1] == 1)
514 		*highest0 = freq[1];
515 	return 1;
516 }
517 
518 static void
519 acpi_getcpufreq(int dom_id, int *highest, int *lowest)
520 {
521 	*highest = 0;
522 	*lowest = 0;
523 
524 	if (acpi_getcpufreq_bin(dom_id, highest, lowest))
525 		return;
526 	acpi_getcpufreq_str(dom_id, highest, lowest);
527 }
528 
529 /*
530  * nstate is the requested number of cpus that we wish to run at full
531  * frequency.  We calculate how many domains we have to adjust to reach
532  * this goal.
533  *
534  * This function also sets the user scheduler global cpu mask.
535  */
536 static void
537 acpi_setcpufreq(int nstate)
538 {
539 	int ncpus = 0;
540 	int increasing = (nstate > CpuLimit);
541 	struct cpu_pwrdom *dom, *domBeg, *domEnd;
542 	int lowest;
543 	int highest;
544 	int desired;
545 	char sysid[64];
546 	int force_uschedbsp = 0;
547 	cpumask_t old_cpumask;
548 
549 	old_cpumask = UschedCpumask;
550 
551 	/*
552 	 * Calculate the ending domain if the number of operating cpus
553 	 * has increased.
554 	 *
555 	 * Calculate the starting domain if the number of operating cpus
556 	 * has decreased.
557 	 *
558 	 * Calculate the mask of cpus the userland scheduler is allowed
559 	 * to use.
560 	 */
561 	NCpuPwrDomUsed = 0;
562 	CPUMASK_ASSZERO(UschedCpumask);
563 	for (dom = TAILQ_FIRST(&CpuPwrDomain); dom != &CpuPwrDomLast;
564 	     dom = TAILQ_NEXT(dom, dom_link)) {
565 		cpumask_t mask;
566 
567 		if (ncpus >= nstate)
568 			break;
569 		ncpus += dom->dom_ncpus;
570 		++NCpuPwrDomUsed;
571 
572 		mask = dom->dom_cpumask;
573 		if (ncpus > nstate) {
574 			int i, diff;
575 
576 			diff = ncpus - nstate;
577 			for (i = 0; i < diff; ++i) {
578 				int c;
579 
580 				c = BSRCPUMASK(mask);
581 				CPUMASK_NANDBIT(mask, c);
582 			}
583 		}
584 		CPUMASK_ORMASK(UschedCpumask, mask);
585 	}
586 
587 	syslog(LOG_INFO, "using %d cpus", nstate);
588 
589 	/*
590 	 * Set the mask of cpus the userland scheduler is allowed to use.
591 	 *
592 	 * Make sure that userland scheduler has at least one cpu.
593 	 */
594 	if (CPUMASK_TESTZERO(UschedCpumask)) {
595 		CPUMASK_ORBIT(UschedCpumask, 0);
596 		force_uschedbsp = 1;
597 	}
598 	if (DebugOpt) {
599 		int i;
600 
601 		printf("\nusched cpumask: ");
602 		for (i = 0; i < (int)NELEM(UschedCpumask.ary); ++i)
603 			printf("%jx ", (uintmax_t)UschedCpumask.ary[i]);
604 		printf("\n");
605 		fflush(stdout);
606 	}
607 	sysctlbyname("kern.usched_global_cpumask", NULL, 0,
608 		     &UschedCpumask, sizeof(UschedCpumask));
609 	if (force_uschedbsp)
610 		CPUMASK_NANDBIT(UschedCpumask, 0);
611 
612 	CPUMASK_XORMASK(old_cpumask, UschedCpumask);
613 
614 	/*
615 	 * Set performance-energy bias
616 	 */
617 	if (HasPerfbias)
618 		setperfbias(old_cpumask, increasing);
619 
620 	if (increasing) {
621 		domBeg = CpuPwrDomLimit;
622 		domEnd = dom;
623 	} else {
624 		domBeg = dom;
625 		domEnd = CpuPwrDomLimit;
626 	}
627 	CpuPwrDomLimit = dom;
628 	CpuLimit = nstate;
629 
630 	/*
631 	 * Adjust the cpu frequency
632 	 */
633 	for (dom = domBeg; dom != domEnd; dom = TAILQ_NEXT(dom, dom_link)) {
634 		acpi_getcpufreq(dom->dom_id, &highest, &lowest);
635 		if (highest == 0 || lowest == 0)
636 			continue;
637 
638 		/*
639 		 * Calculate the desired cpu frequency, test, and set.
640 		 */
641 		desired = increasing ? highest : lowest;
642 
643 		snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.select",
644 		    dom->dom_id);
645 		if (DebugOpt) {
646 			printf("dom%d set frequency %d\n",
647 			       dom->dom_id, desired);
648 		}
649 		sysctlbyname(sysid, NULL, NULL, &desired, sizeof(desired));
650 	}
651 }
652 
653 static
654 void
655 usage(void)
656 {
657 	fprintf(stderr, "usage: powerd [-dt] [-p hysteresis] "
658 	    "[-u trigger_up] [-T sample_interval] [-r poll_interval] "
659 	    "[-B min_battery_life] [-L low_battery_linger] "
660 	    "[-P battery_poll_interval] [-Q] [-e]\n");
661 	exit(1);
662 }
663 
664 #ifndef timespecsub
665 #define timespecsub(vvp, uvp)						\
666 	do {								\
667 		(vvp)->tv_sec -= (uvp)->tv_sec;				\
668 		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
669 		if ((vvp)->tv_nsec < 0) {				\
670 			(vvp)->tv_sec--;				\
671 			(vvp)->tv_nsec += 1000000000;			\
672 		}							\
673 	} while (0)
674 #endif
675 
676 #define BAT_SYSCTL_TIME_MAX	50000000 /* unit: nanosecond */
677 
678 static int
679 has_battery(void)
680 {
681 	struct timespec s, e;
682 	size_t len;
683 	int val;
684 
685 	clock_gettime(CLOCK_MONOTONIC_FAST, &s);
686 	BatLifePrevT = s;
687 
688 	len = sizeof(val);
689 	if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) {
690 		/* No AC line information */
691 		return 0;
692 	}
693 	clock_gettime(CLOCK_MONOTONIC_FAST, &e);
694 
695 	timespecsub(&e, &s);
696 	if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
697 		/* hw.acpi.acline takes to long to be useful */
698 		syslog(LOG_NOTICE, "hw.acpi.acline takes too long");
699 		return 0;
700 	}
701 
702 	clock_gettime(CLOCK_MONOTONIC_FAST, &s);
703 	len = sizeof(val);
704 	if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) {
705 		/* No battery life */
706 		return 0;
707 	}
708 	clock_gettime(CLOCK_MONOTONIC_FAST, &e);
709 
710 	timespecsub(&e, &s);
711 	if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
712 		/* hw.acpi.battery.life takes to long to be useful */
713 		syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long");
714 		return 0;
715 	}
716 	return 1;
717 }
718 
719 static void
720 low_battery_alert(int life)
721 {
722 	int fmt, stereo, freq;
723 	int fd;
724 
725 	syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d",
726 	    life, BatShutdownLingerCnt);
727 	++BatShutdownLingerCnt;
728 
729 	if (!BatShutdownAudioAlert)
730 		return;
731 
732 	fd = open("/dev/dsp", O_WRONLY);
733 	if (fd < 0)
734 		return;
735 
736 	fmt = AFMT_S16_LE;
737 	if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0)
738 		goto done;
739 
740 	stereo = 0;
741 	if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0)
742 		goto done;
743 
744 	freq = 44100;
745 	if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0)
746 		goto done;
747 
748 	write(fd, alert1, sizeof(alert1));
749 	write(fd, alert1, sizeof(alert1));
750 
751 done:
752 	close(fd);
753 }
754 
755 static int
756 mon_battery(void)
757 {
758 	struct timespec cur, ts;
759 	int acline, life;
760 	size_t len;
761 
762 	clock_gettime(CLOCK_MONOTONIC_FAST, &cur);
763 	ts = cur;
764 	timespecsub(&ts, &BatLifePrevT);
765 	if (ts.tv_sec < BatLifePollIntvl)
766 		return 1;
767 	BatLifePrevT = cur;
768 
769 	len = sizeof(acline);
770 	if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0)
771 		return 1;
772 	if (acline) {
773 		BatShutdownLinger = -1;
774 		BatShutdownLingerCnt = 0;
775 		return 1;
776 	}
777 
778 	len = sizeof(life);
779 	if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0)
780 		return 1;
781 
782 	if (BatShutdownLinger > 0) {
783 		ts = cur;
784 		timespecsub(&ts, &BatShutdownStartT);
785 		if (ts.tv_sec > BatShutdownLinger)
786 			BatShutdownLinger = 0;
787 	}
788 
789 	if (life <= BatLifeMin) {
790 		if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) {
791 			syslog(LOG_ALERT, "low battery life %d%%, "
792 			    "shutting down", life);
793 			if (vfork() == 0)
794 				execlp("poweroff", "poweroff", NULL);
795 			return 0;
796 		} else if (BatShutdownLinger < 0) {
797 			BatShutdownLinger = BatShutdownLingerSet;
798 			BatShutdownStartT = cur;
799 		}
800 		low_battery_alert(life);
801 	}
802 	return 1;
803 }
804 
805 static void
806 getncpus(void)
807 {
808 	size_t slen;
809 
810 	slen = sizeof(NCpus);
811 	if (sysctlbyname("hw.ncpu", &NCpus, &slen, NULL, 0) < 0)
812 		err(1, "sysctlbyname hw.ncpu failed");
813 	if (DebugOpt)
814 		printf("hw.ncpu %d\n", NCpus);
815 }
816 
817 static void
818 getuschedmask(void)
819 {
820 	size_t slen;
821 
822 	slen = sizeof(UschedCpumask);
823 	if (sysctlbyname("kern.usched_global_cpumask", &UschedCpumask, &slen,
824 	    NULL, 0) < 0)
825 		err(1, "sysctlbyname kern.usched_global_cpumask failed");
826 	if (DebugOpt) {
827 		int i;
828 
829 		printf("usched cpumask was: ");
830 		for (i = 0; i < (int)NELEM(UschedCpumask.ary); ++i)
831 			printf("%jx ", (uintmax_t)UschedCpumask.ary[i]);
832 		printf("\n");
833 		fflush(stdout);
834 	}
835 }
836 
837 static int
838 has_perfbias(void)
839 {
840 	size_t len;
841 	int hint;
842 
843 	len = sizeof(hint);
844 	if (sysctlbyname("machdep.perfbias0.hint", &hint, &len, NULL, 0) < 0)
845 		return 0;
846 	return 1;
847 }
848 
849 static void
850 setperfbias(cpumask_t mask, int increasing)
851 {
852 	int hint = increasing ? 0 : 15;
853 
854 	while (CPUMASK_TESTNZERO(mask)) {
855 		char sysid[64];
856 		int cpu;
857 
858 		cpu = BSFCPUMASK(mask);
859 		CPUMASK_NANDBIT(mask, cpu);
860 
861 		snprintf(sysid, sizeof(sysid), "machdep.perfbias%d.hint", cpu);
862 		sysctlbyname(sysid, NULL, NULL, &hint, sizeof(hint));
863 		if (DebugOpt)
864 			printf("cpu%d set perfbias hint %d\n", cpu, hint);
865 	}
866 }
867