powerd: Support Intel Performance and Energy Bias Hint
[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 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 }