50fc6bba97fcebc8e561e98fd846d59a0c6186d8
[dragonfly.git] / usr.sbin / powerd / powerd.c
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 }