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