powerd: Set backlight to the specified value, if AC line is unplugged.
[dragonfly.git] / usr.sbin / powerd / powerd.c
CommitLineData
cc537f99
MD
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/*
a4c3bf5e
SZ
36 * The powerd daemon :
37 * - Monitor the cpu load and adjusts cpu and cpu power domain
38 * performance accordingly.
39 * - Monitor battery life. Alarm alerts and shutdown the machine
40 * if battery life goes low.
cc537f99
MD
41 */
42
c07315c4 43#define _KERNEL_STRUCTURES
cc537f99
MD
44#include <sys/types.h>
45#include <sys/sysctl.h>
46#include <sys/kinfo.h>
47#include <sys/file.h>
b818cc9b 48#include <sys/queue.h>
b1b8502e 49#include <sys/soundcard.h>
aa7fc22a 50#include <sys/time.h>
bd9ff91e 51#include <machine/cpufunc.h>
25780572 52#include <err.h>
cc537f99
MD
53#include <stdio.h>
54#include <stdlib.h>
55#include <unistd.h>
56#include <string.h>
82d1bcc2 57#include <syslog.h>
cc537f99 58
b1b8502e
SZ
59#include "alert1.h"
60
8b799ad2
SZ
61#define MAXDOM MAXCPU /* worst case, 1 cpu per domain */
62
4afb1280 63#define MAXFREQ 64
764594cf 64#define CST_STRLEN 16
4afb1280 65
b818cc9b
SZ
66struct cpu_pwrdom {
67 TAILQ_ENTRY(cpu_pwrdom) dom_link;
68 int dom_id;
69 int dom_ncpus;
bd9ff91e 70 cpumask_t dom_cpumask;
b818cc9b 71};
a4c3bf5e
SZ
72
73struct cpu_state {
74 double cpu_qavg;
75 double cpu_uavg; /* used for speeding up */
76 double cpu_davg; /* used for slowing down */
77 int cpu_limit;
78 int cpu_count;
79 char cpu_name[8];
80};
b818cc9b 81
cc537f99 82static void usage(void);
a4c3bf5e
SZ
83static void get_ncpus(void);
84
85/* usched cpumask */
86static void get_uschedcpus(void);
87static void set_uschedcpus(void);
88
89/* perfbias(4) */
90static int has_perfbias(void);
91static void set_perfbias(int, int);
92
93/* acpi(4) P-state */
94static void acpi_getcpufreq_str(int, int *, int *);
95static int acpi_getcpufreq_bin(int, int *, int *);
96static void acpi_get_cpufreq(int, int *, int *);
97static void acpi_set_cpufreq(int, int);
98static int acpi_get_cpupwrdom(void);
99
764594cf
SZ
100/* mwait C-state hint */
101static int probe_cstate(void);
102static void set_cstate(int, int);
103
a4c3bf5e
SZ
104/* Performance monitoring */
105static void init_perf(void);
106static void mon_perf(double);
107static void adj_perf(cpumask_t, cpumask_t);
108static void adj_cpu_pwrdom(int, int);
109static void adj_cpu_perf(int, int);
110static void get_cputime(double);
111static int get_nstate(struct cpu_state *, double);
112static void add_spare_cpus(const cpumask_t, int);
113static void restore_perf(void);
114
115/* Battery monitoring */
aa7fc22a
SZ
116static int has_battery(void);
117static int mon_battery(void);
a4c3bf5e
SZ
118static void low_battery_alert(int);
119
120/* Runtime states for performance monitoring */
121static int global_pcpu_limit;
122static struct cpu_state pcpu_state[MAXCPU];
123static struct cpu_state global_cpu_state;
124static cpumask_t cpu_used; /* cpus w/ high perf */
125static cpumask_t cpu_pwrdom_used; /* cpu power domains w/ high perf */
126static cpumask_t usched_cpu_used; /* cpus for usched */
127
128/* Constants */
129static cpumask_t cpu_pwrdom_mask; /* usable cpu power domains */
130static int cpu2pwrdom[MAXCPU]; /* cpu to cpu power domain map */
131static struct cpu_pwrdom *cpu_pwrdomain[MAXDOM];
132static int NCpus; /* # of cpus */
764594cf
SZ
133static char orig_global_cx[CST_STRLEN];
134static char cpu_perf_cx[CST_STRLEN];
135static int cpu_perf_cxlen;
136static char cpu_idle_cx[CST_STRLEN];
137static int cpu_idle_cxlen;
cc537f99 138
2e01237b
SZ
139static int DebugOpt;
140static int TurboOpt = 1;
2e01237b
SZ
141static int PowerFd;
142static int Hysteresis = 10; /* percentage */
143static double TriggerUp = 0.25; /* single-cpu load to force max freq */
144static double TriggerDown; /* load per cpu to force the min freq */
4f23b27f 145static int HasPerfbias = 0;
c0faca7f 146static int AdjustCpuFreq = 1;
764594cf 147static int AdjustCstate = 0;
a2b9b795
SZ
148static int HighestCpuFreq;
149static int LowestCpuFreq;
a4c3bf5e
SZ
150
151static volatile int stopped;
152
153/* Battery life monitoring */
2e01237b 154static int BatLifeMin = 2; /* shutdown the box, if low on battery life */
aa7fc22a 155static struct timespec BatLifePrevT;
aa7fc22a 156static int BatLifePollIntvl = 5; /* unit: sec */
b1b8502e
SZ
157static struct timespec BatShutdownStartT;
158static int BatShutdownLinger = -1;
159static int BatShutdownLingerSet = 60; /* unit: sec */
160static int BatShutdownLingerCnt;
161static int BatShutdownAudioAlert = 1;
f1a9ebd2
SZ
162static int BackLightPct = 100;
163static int OldBackLightLevel;
164static int BackLightDown;
b1b8502e 165
323ed682
MD
166static void sigintr(int signo);
167
cc537f99
MD
168int
169main(int ac, char **av)
170{
358bf736
MD
171 double srt;
172 double pollrate;
cc537f99 173 int ch;
65db32a7 174 char buf[64];
aa7fc22a 175 int monbat;
cc537f99 176
358bf736
MD
177 srt = 8.0; /* time for samples - 8 seconds */
178 pollrate = 1.0; /* polling rate in seconds */
179
f1a9ebd2 180 while ((ch = getopt(ac, av, "b:cdefh:l:p:r:tu:B:L:P:QT:")) != -1) {
cc537f99 181 switch(ch) {
f1a9ebd2
SZ
182 case 'b':
183 BackLightPct = strtol(optarg, NULL, 10);
184 break;
764594cf
SZ
185 case 'c':
186 AdjustCstate = 1;
187 break;
cc537f99
MD
188 case 'd':
189 DebugOpt = 1;
190 break;
50214825 191 case 'e':
4f23b27f 192 HasPerfbias = 1;
50214825 193 break;
c0faca7f
SZ
194 case 'f':
195 AdjustCpuFreq = 0;
196 break;
a2b9b795
SZ
197 case 'h':
198 HighestCpuFreq = strtol(optarg, NULL, 10);
199 break;
200 case 'l':
201 LowestCpuFreq = strtol(optarg, NULL, 10);
202 break;
1306f384
JG
203 case 'p':
204 Hysteresis = (int)strtol(optarg, NULL, 10);
205 break;
aa7fc22a
SZ
206 case 'r':
207 pollrate = strtod(optarg, NULL);
208 break;
7d82ba6b
SW
209 case 't':
210 TurboOpt = 0;
211 break;
1306f384 212 case 'u':
7d82ba6b 213 TriggerUp = (double)strtol(optarg, NULL, 10) / 100;
1306f384 214 break;
aa7fc22a
SZ
215 case 'B':
216 BatLifeMin = strtol(optarg, NULL, 10);
217 break;
218 case 'L':
b1b8502e
SZ
219 BatShutdownLingerSet = strtol(optarg, NULL, 10);
220 if (BatShutdownLingerSet < 0)
221 BatShutdownLingerSet = 0;
aa7fc22a
SZ
222 break;
223 case 'P':
224 BatLifePollIntvl = strtol(optarg, NULL, 10);
358bf736 225 break;
b1b8502e
SZ
226 case 'Q':
227 BatShutdownAudioAlert = 0;
228 break;
358bf736
MD
229 case 'T':
230 srt = strtod(optarg, NULL);
231 break;
cc537f99
MD
232 default:
233 usage();
234 /* NOT REACHED */
235 }
236 }
237 ac -= optind;
89a28649 238 av += optind;
cc537f99 239
a4c3bf5e 240 setlinebuf(stdout);
25780572 241
a4c3bf5e
SZ
242 /* Get number of cpus */
243 get_ncpus();
48c660dc 244
1306f384
JG
245 if (0 > Hysteresis || Hysteresis > 99) {
246 fprintf(stderr, "Invalid hysteresis value\n");
247 exit(1);
248 }
249
250 if (0 > TriggerUp || TriggerUp > 1) {
251 fprintf(stderr, "Invalid load limit value\n");
252 exit(1);
253 }
254
f1a9ebd2
SZ
255 if (BackLightPct > 100 || BackLightPct <= 0) {
256 fprintf(stderr, "Invalid backlight setting, ignore\n");
257 BackLightPct = 100;
258 }
259
1306f384
JG
260 TriggerDown = TriggerUp - (TriggerUp * (double) Hysteresis / 100);
261
cc537f99 262 /*
8db2b576 263 * Make sure powerd is not already running.
cc537f99 264 */
cc537f99
MD
265 PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644);
266 if (PowerFd < 0) {
267 fprintf(stderr,
268 "Cannot create /var/run/powerd.pid, "
269 "continuing anyway\n");
270 } else {
271 if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) {
272 fprintf(stderr, "powerd is already running\n");
273 exit(1);
274 }
275 }
276
277 /*
278 * Demonize and set pid
279 */
82d1bcc2 280 if (DebugOpt == 0) {
cc537f99 281 daemon(0, 0);
82d1bcc2
SW
282 openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON);
283 }
cc537f99
MD
284
285 if (PowerFd >= 0) {
286 ftruncate(PowerFd, 0);
287 snprintf(buf, sizeof(buf), "%d\n", (int)getpid());
288 write(PowerFd, buf, strlen(buf));
289 }
290
aa7fc22a 291 /* Do we need to monitor battery life? */
b1b8502e
SZ
292 if (BatLifePollIntvl <= 0)
293 monbat = 0;
294 else
295 monbat = has_battery();
aa7fc22a 296
a4c3bf5e 297 /* Do we have perfbias(4)? */
50214825
SZ
298 if (HasPerfbias)
299 HasPerfbias = has_perfbias();
300
764594cf
SZ
301 /* Could we adjust C-state? */
302 if (AdjustCstate)
303 AdjustCstate = probe_cstate();
304
8db2b576 305 /*
a4c3bf5e 306 * Wait hw.acpi.cpu.px_dom* sysctl to be created by kernel.
8db2b576
SZ
307 *
308 * Since hw.acpi.cpu.px_dom* creation is queued into ACPI
309 * taskqueue and ACPI taskqueue is shared across various
310 * ACPI modules, any delay in other modules may cause
311 * hw.acpi.cpu.px_dom* to be created at quite a later time
312 * (e.g. cmbat module's task could take quite a lot of time).
313 */
314 for (;;) {
a4c3bf5e
SZ
315 /* Prime delta cputime calculation. */
316 get_cputime(pollrate);
317
318 /* Wait for all cpus to appear */
319 if (acpi_get_cpupwrdom())
dddb241c
SZ
320 break;
321 usleep((int)(pollrate * 1000000.0));
8db2b576
SZ
322 }
323
dddb241c 324 /*
a4c3bf5e 325 * Catch some signals so that max performance could be restored.
323ed682
MD
326 */
327 signal(SIGINT, sigintr);
328 signal(SIGTERM, sigintr);
358bf736 329
a4c3bf5e
SZ
330 /* Initialize performance states */
331 init_perf();
358bf736 332
a4c3bf5e 333 srt = srt / pollrate; /* convert to sample count */
358bf736
MD
334 if (DebugOpt)
335 printf("samples for downgrading: %5.2f\n", srt);
323ed682 336
cc537f99
MD
337 /*
338 * Monitoring loop
339 */
a4c3bf5e
SZ
340 while (!stopped) {
341 /*
342 * Monitor performance
343 */
344 get_cputime(pollrate);
345 mon_perf(srt);
358bf736 346
a4c3bf5e
SZ
347 /*
348 * Monitor battery
349 */
aa7fc22a
SZ
350 if (monbat)
351 monbat = mon_battery();
a4c3bf5e 352
358bf736 353 usleep((int)(pollrate * 1000000.0));
65db32a7 354 }
a4c3bf5e
SZ
355
356 /*
357 * Set to maximum performance if killed.
358 */
359 syslog(LOG_INFO, "killed, setting max and exiting");
360 restore_perf();
361
362 exit(0);
65db32a7
MD
363}
364
a4c3bf5e 365static void
323ed682
MD
366sigintr(int signo __unused)
367{
a4c3bf5e 368 stopped = 1;
323ed682
MD
369}
370
65db32a7 371/*
a4c3bf5e 372 * Figure out the cpu power domains.
65db32a7 373 */
dddb241c 374static int
a4c3bf5e 375acpi_get_cpupwrdom(void)
65db32a7 376{
b818cc9b 377 struct cpu_pwrdom *dom;
a4c3bf5e 378 cpumask_t pwrdom_mask;
65db32a7
MD
379 char buf[64];
380 char members[1024];
381 char *str;
382 size_t msize;
a4c3bf5e 383 int n, i, ncpu = 0, dom_id;
cc537f99 384
a4c3bf5e
SZ
385 memset(cpu2pwrdom, 0, sizeof(cpu2pwrdom));
386 memset(cpu_pwrdomain, 0, sizeof(cpu_pwrdomain));
387 CPUMASK_ASSZERO(cpu_pwrdom_mask);
dddb241c 388
8b799ad2 389 for (i = 0; i < MAXDOM; ++i) {
65db32a7
MD
390 snprintf(buf, sizeof(buf),
391 "hw.acpi.cpu.px_dom%d.available", i);
b818cc9b
SZ
392 if (sysctlbyname(buf, NULL, NULL, NULL, 0) < 0)
393 continue;
65db32a7 394
b818cc9b
SZ
395 dom = calloc(1, sizeof(*dom));
396 dom->dom_id = i;
a4c3bf5e
SZ
397
398 if (cpu_pwrdomain[i] != NULL) {
399 fprintf(stderr, "cpu power domain %d exists\n", i);
400 exit(1);
401 }
402 cpu_pwrdomain[i] = dom;
403 CPUMASK_ORBIT(cpu_pwrdom_mask, i);
65db32a7 404 }
a4c3bf5e 405 pwrdom_mask = cpu_pwrdom_mask;
65db32a7 406
a4c3bf5e
SZ
407 while (CPUMASK_TESTNZERO(pwrdom_mask)) {
408 dom_id = BSFCPUMASK(pwrdom_mask);
409 CPUMASK_NANDBIT(pwrdom_mask, dom_id);
410 dom = cpu_pwrdomain[dom_id];
b818cc9b 411
bd9ff91e 412 CPUMASK_ASSZERO(dom->dom_cpumask);
b818cc9b 413
65db32a7 414 snprintf(buf, sizeof(buf),
b818cc9b 415 "hw.acpi.cpu.px_dom%d.members", dom->dom_id);
65db32a7 416 msize = sizeof(members);
b818cc9b 417 if (sysctlbyname(buf, members, &msize, NULL, 0) < 0) {
a4c3bf5e 418 cpu_pwrdomain[dom_id] = NULL;
b818cc9b
SZ
419 free(dom);
420 continue;
421 }
422
423 members[msize] = 0;
424 for (str = strtok(members, " "); str; str = strtok(NULL, " ")) {
425 n = -1;
426 sscanf(str, "cpu%d", &n);
427 if (n >= 0) {
f1b8fbfe 428 ++ncpu;
b818cc9b 429 ++dom->dom_ncpus;
bd9ff91e 430 CPUMASK_ORBIT(dom->dom_cpumask, n);
a4c3bf5e 431 cpu2pwrdom[n] = dom->dom_id;
65db32a7 432 }
cc537f99 433 }
b818cc9b 434 if (dom->dom_ncpus == 0) {
a4c3bf5e 435 cpu_pwrdomain[dom_id] = NULL;
b818cc9b
SZ
436 free(dom);
437 continue;
438 }
bd9ff91e
SZ
439 if (DebugOpt) {
440 printf("dom%d cpumask: ", dom->dom_id);
441 for (i = 0; i < (int)NELEM(dom->dom_cpumask.ary); ++i) {
442 printf("%jx ",
443 (uintmax_t)dom->dom_cpumask.ary[i]);
444 }
445 printf("\n");
b818cc9b 446 }
b818cc9b 447 }
dddb241c 448
f1b8fbfe 449 if (ncpu != NCpus) {
a4c3bf5e 450 if (DebugOpt)
f1b8fbfe 451 printf("Found %d cpus, expecting %d\n", ncpu, NCpus);
a4c3bf5e
SZ
452
453 pwrdom_mask = cpu_pwrdom_mask;
454 while (CPUMASK_TESTNZERO(pwrdom_mask)) {
455 dom_id = BSFCPUMASK(pwrdom_mask);
456 CPUMASK_NANDBIT(pwrdom_mask, dom_id);
457 dom = cpu_pwrdomain[dom_id];
458 if (dom != NULL)
459 free(dom);
dddb241c
SZ
460 }
461 return 0;
cc537f99 462 }
dddb241c 463 return 1;
cc537f99
MD
464}
465
466/*
a4c3bf5e 467 * Save per-cpu load and sum of per-cpu load.
cc537f99 468 */
a4c3bf5e
SZ
469static void
470get_cputime(double pollrate)
cc537f99 471{
25780572
SZ
472 static struct kinfo_cputime ocpu_time[MAXCPU];
473 static struct kinfo_cputime ncpu_time[MAXCPU];
572bda79 474 size_t slen;
cc537f99
MD
475 int ncpu;
476 int cpu;
477 uint64_t delta;
478
f1b8fbfe 479 bcopy(ncpu_time, ocpu_time, sizeof(struct kinfo_cputime) * NCpus);
dddb241c 480
cc537f99
MD
481 slen = sizeof(ncpu_time);
482 if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) {
483 fprintf(stderr, "kern.cputime sysctl not available\n");
484 exit(1);
485 }
486 ncpu = slen / sizeof(ncpu_time[0]);
cc537f99 487
25780572 488 delta = 0;
cc537f99 489 for (cpu = 0; cpu < ncpu; ++cpu) {
a4c3bf5e
SZ
490 uint64_t d;
491
492 d = (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys +
493 ncpu_time[cpu].cp_nice + ncpu_time[cpu].cp_intr) -
494 (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys +
495 ocpu_time[cpu].cp_nice + ocpu_time[cpu].cp_intr);
496 pcpu_state[cpu].cpu_qavg = (double)d / (pollrate * 1000000.0);
497
498 delta += d;
cc537f99 499 }
a4c3bf5e 500 global_cpu_state.cpu_qavg = (double)delta / (pollrate * 1000000.0);
cc537f99
MD
501}
502
4afb1280
SZ
503static void
504acpi_getcpufreq_str(int dom_id, int *highest0, int *lowest0)
505{
506 char buf[256], sysid[64];
507 size_t buflen;
508 char *ptr;
509 int v, highest, lowest;
510
511 /*
512 * Retrieve availability list
513 */
514 snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.available",
515 dom_id);
516 buflen = sizeof(buf) - 1;
517 if (sysctlbyname(sysid, buf, &buflen, NULL, 0) < 0)
518 return;
519 buf[buflen] = 0;
520
521 /*
522 * Parse out the highest and lowest cpu frequencies
523 */
524 ptr = buf;
525 highest = lowest = 0;
526 while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
a2b9b795
SZ
527 if ((lowest == 0 || lowest > v) &&
528 (LowestCpuFreq <= 0 || v >= LowestCpuFreq))
4afb1280 529 lowest = v;
a2b9b795
SZ
530 if ((highest == 0 || highest < v) &&
531 (HighestCpuFreq <= 0 || v <= HighestCpuFreq))
4afb1280
SZ
532 highest = v;
533 /*
534 * Detect turbo mode
535 */
e74d6cd2 536 if (!TurboOpt && highest - v == 1)
4afb1280
SZ
537 highest = v;
538 }
539
540 *highest0 = highest;
541 *lowest0 = lowest;
542}
543
544static int
545acpi_getcpufreq_bin(int dom_id, int *highest0, int *lowest0)
546{
547 char sysid[64];
548 int freq[MAXFREQ];
549 size_t freqlen;
a2b9b795 550 int freqcnt, i;
4afb1280
SZ
551
552 /*
553 * Retrieve availability list
554 */
df29c611 555 snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.avail", dom_id);
4afb1280
SZ
556 freqlen = sizeof(freq);
557 if (sysctlbyname(sysid, freq, &freqlen, NULL, 0) < 0)
558 return 0;
559
560 freqcnt = freqlen / sizeof(freq[0]);
561 if (freqcnt == 0)
562 return 0;
563
a2b9b795
SZ
564 for (i = freqcnt - 1; i >= 0; --i) {
565 *lowest0 = freq[i];
566 if (LowestCpuFreq <= 0 || *lowest0 >= LowestCpuFreq)
567 break;
568 }
4afb1280 569
a2b9b795 570 i = 0;
4afb1280 571 *highest0 = freq[0];
a2b9b795
SZ
572 if (!TurboOpt && freqcnt > 1 && freq[0] - freq[1] == 1) {
573 i = 1;
4afb1280 574 *highest0 = freq[1];
a2b9b795
SZ
575 }
576 for (; i < freqcnt; ++i) {
577 if (HighestCpuFreq <= 0 || *highest0 <= HighestCpuFreq)
578 break;
579 *highest0 = freq[i];
580 }
4afb1280
SZ
581 return 1;
582}
583
584static void
a4c3bf5e 585acpi_get_cpufreq(int dom_id, int *highest, int *lowest)
4afb1280
SZ
586{
587 *highest = 0;
588 *lowest = 0;
589
590 if (acpi_getcpufreq_bin(dom_id, highest, lowest))
591 return;
592 acpi_getcpufreq_str(dom_id, highest, lowest);
593}
594
cc537f99
MD
595static
596void
597usage(void)
598{
764594cf 599 fprintf(stderr, "usage: powerd [-cdeftQ] [-p hysteresis] "
a2b9b795 600 "[-h highest_freq] [-l lowest_freq] "
c0faca7f 601 "[-r poll_interval] [-u trigger_up] "
aa7fc22a 602 "[-B min_battery_life] [-L low_battery_linger] "
f1a9ebd2
SZ
603 "[-P battery_poll_interval] [-T sample_interval] "
604 "[-b backlight]\n");
cc537f99
MD
605 exit(1);
606}
aa7fc22a
SZ
607
608#ifndef timespecsub
609#define timespecsub(vvp, uvp) \
610 do { \
611 (vvp)->tv_sec -= (uvp)->tv_sec; \
612 (vvp)->tv_nsec -= (uvp)->tv_nsec; \
613 if ((vvp)->tv_nsec < 0) { \
614 (vvp)->tv_sec--; \
615 (vvp)->tv_nsec += 1000000000; \
616 } \
617 } while (0)
618#endif
619
620#define BAT_SYSCTL_TIME_MAX 50000000 /* unit: nanosecond */
621
622static int
623has_battery(void)
624{
625 struct timespec s, e;
626 size_t len;
627 int val;
628
629 clock_gettime(CLOCK_MONOTONIC_FAST, &s);
630 BatLifePrevT = s;
aa7fc22a
SZ
631
632 len = sizeof(val);
633 if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) {
634 /* No AC line information */
635 return 0;
636 }
637 clock_gettime(CLOCK_MONOTONIC_FAST, &e);
638
639 timespecsub(&e, &s);
640 if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
641 /* hw.acpi.acline takes to long to be useful */
642 syslog(LOG_NOTICE, "hw.acpi.acline takes too long");
643 return 0;
644 }
645
646 clock_gettime(CLOCK_MONOTONIC_FAST, &s);
647 len = sizeof(val);
648 if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) {
649 /* No battery life */
650 return 0;
651 }
652 clock_gettime(CLOCK_MONOTONIC_FAST, &e);
653
654 timespecsub(&e, &s);
655 if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
656 /* hw.acpi.battery.life takes to long to be useful */
657 syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long");
658 return 0;
659 }
660 return 1;
661}
662
b1b8502e
SZ
663static void
664low_battery_alert(int life)
665{
666 int fmt, stereo, freq;
667 int fd;
668
669 syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d",
670 life, BatShutdownLingerCnt);
671 ++BatShutdownLingerCnt;
672
673 if (!BatShutdownAudioAlert)
674 return;
675
676 fd = open("/dev/dsp", O_WRONLY);
677 if (fd < 0)
678 return;
679
680 fmt = AFMT_S16_LE;
681 if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0)
682 goto done;
683
684 stereo = 0;
685 if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0)
686 goto done;
687
688 freq = 44100;
689 if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0)
690 goto done;
691
692 write(fd, alert1, sizeof(alert1));
693 write(fd, alert1, sizeof(alert1));
694
695done:
696 close(fd);
697}
698
aa7fc22a
SZ
699static int
700mon_battery(void)
701{
702 struct timespec cur, ts;
703 int acline, life;
704 size_t len;
705
706 clock_gettime(CLOCK_MONOTONIC_FAST, &cur);
707 ts = cur;
708 timespecsub(&ts, &BatLifePrevT);
709 if (ts.tv_sec < BatLifePollIntvl)
710 return 1;
711 BatLifePrevT = cur;
712
713 len = sizeof(acline);
714 if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0)
715 return 1;
b1b8502e
SZ
716 if (acline) {
717 BatShutdownLinger = -1;
718 BatShutdownLingerCnt = 0;
f1a9ebd2
SZ
719 if (BackLightDown) {
720 BackLightDown = 0;
721 sysctlbyname("hw.backlight_level", NULL, NULL,
722 &OldBackLightLevel, sizeof(OldBackLightLevel));
723 }
aa7fc22a 724 return 1;
b1b8502e 725 }
aa7fc22a 726
f1a9ebd2
SZ
727 if (!BackLightDown && BackLightPct != 100) {
728 int backlight_max, backlight;
729
730 len = sizeof(backlight_max);
731 if (sysctlbyname("hw.backlight_max", &backlight_max, &len,
732 NULL, 0) < 0) {
733 /* No more backlight adjustment */
734 BackLightPct = 100;
735 goto after_backlight;
736 }
737
738 len = sizeof(OldBackLightLevel);
739 if (sysctlbyname("hw.backlight_level", &OldBackLightLevel, &len,
740 NULL, 0) < 0) {
741 /* No more backlight adjustment */
742 BackLightPct = 100;
743 goto after_backlight;
744 }
745
746 backlight = (backlight_max * BackLightPct) / 100;
747 if (backlight >= OldBackLightLevel) {
748 /* No more backlight adjustment */
749 BackLightPct = 100;
750 goto after_backlight;
751 }
752
753 if (sysctlbyname("hw.backlight_level", NULL, NULL,
754 &backlight, sizeof(backlight)) < 0) {
755 /* No more backlight adjustment */
756 BackLightPct = 100;
757 goto after_backlight;
758 }
759 BackLightDown = 1;
760 }
761after_backlight:
762
aa7fc22a
SZ
763 len = sizeof(life);
764 if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0)
765 return 1;
766
b1b8502e 767 if (BatShutdownLinger > 0) {
aa7fc22a 768 ts = cur;
b1b8502e
SZ
769 timespecsub(&ts, &BatShutdownStartT);
770 if (ts.tv_sec > BatShutdownLinger)
771 BatShutdownLinger = 0;
aa7fc22a
SZ
772 }
773
774 if (life <= BatLifeMin) {
b1b8502e 775 if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) {
aa7fc22a
SZ
776 syslog(LOG_ALERT, "low battery life %d%%, "
777 "shutting down", life);
778 if (vfork() == 0)
779 execlp("poweroff", "poweroff", NULL);
780 return 0;
b1b8502e
SZ
781 } else if (BatShutdownLinger < 0) {
782 BatShutdownLinger = BatShutdownLingerSet;
783 BatShutdownStartT = cur;
aa7fc22a 784 }
b1b8502e 785 low_battery_alert(life);
aa7fc22a
SZ
786 }
787 return 1;
788}
25780572
SZ
789
790static void
a4c3bf5e 791get_ncpus(void)
25780572
SZ
792{
793 size_t slen;
794
f1b8fbfe
SZ
795 slen = sizeof(NCpus);
796 if (sysctlbyname("hw.ncpu", &NCpus, &slen, NULL, 0) < 0)
25780572
SZ
797 err(1, "sysctlbyname hw.ncpu failed");
798 if (DebugOpt)
f1b8fbfe 799 printf("hw.ncpu %d\n", NCpus);
25780572 800}
48c660dc
SZ
801
802static void
a4c3bf5e 803get_uschedcpus(void)
48c660dc
SZ
804{
805 size_t slen;
806
a4c3bf5e
SZ
807 slen = sizeof(usched_cpu_used);
808 if (sysctlbyname("kern.usched_global_cpumask", &usched_cpu_used, &slen,
48c660dc
SZ
809 NULL, 0) < 0)
810 err(1, "sysctlbyname kern.usched_global_cpumask failed");
811 if (DebugOpt) {
812 int i;
813
814 printf("usched cpumask was: ");
a4c3bf5e
SZ
815 for (i = 0; i < (int)NELEM(usched_cpu_used.ary); ++i)
816 printf("%jx ", (uintmax_t)usched_cpu_used.ary[i]);
48c660dc 817 printf("\n");
48c660dc
SZ
818 }
819}
50214825 820
a4c3bf5e
SZ
821static void
822set_uschedcpus(void)
823{
824 if (DebugOpt) {
825 int i;
826
827 printf("usched cpumask: ");
828 for (i = 0; i < (int)NELEM(usched_cpu_used.ary); ++i) {
829 printf("%jx ",
830 (uintmax_t)usched_cpu_used.ary[i]);
831 }
832 printf("\n");
833 }
834 sysctlbyname("kern.usched_global_cpumask", NULL, 0,
835 &usched_cpu_used, sizeof(usched_cpu_used));
836}
837
50214825
SZ
838static int
839has_perfbias(void)
840{
841 size_t len;
842 int hint;
843
844 len = sizeof(hint);
845 if (sysctlbyname("machdep.perfbias0.hint", &hint, &len, NULL, 0) < 0)
846 return 0;
847 return 1;
848}
849
850static void
a4c3bf5e
SZ
851set_perfbias(int cpu, int inc)
852{
853 int hint = inc ? 0 : 15;
854 char sysid[64];
855
856 if (DebugOpt)
857 printf("cpu%d set perfbias hint %d\n", cpu, hint);
858 snprintf(sysid, sizeof(sysid), "machdep.perfbias%d.hint", cpu);
859 sysctlbyname(sysid, NULL, NULL, &hint, sizeof(hint));
860}
861
862static void
863init_perf(void)
50214825 864{
a4c3bf5e
SZ
865 struct cpu_state *state;
866 int cpu;
50214825 867
a4c3bf5e
SZ
868 /* Get usched cpumask */
869 get_uschedcpus();
50214825 870
a4c3bf5e
SZ
871 /*
872 * Assume everything are used and are maxed out, before we
873 * start.
874 */
50214825 875
a4c3bf5e
SZ
876 CPUMASK_ASSBMASK(cpu_used, NCpus);
877 cpu_pwrdom_used = cpu_pwrdom_mask;
878 global_pcpu_limit = NCpus;
879
880 for (cpu = 0; cpu < NCpus; ++cpu) {
881 state = &pcpu_state[cpu];
882
883 state->cpu_uavg = 0.0;
884 state->cpu_davg = 0.0;
885 state->cpu_limit = 1;
886 state->cpu_count = 1;
887 snprintf(state->cpu_name, sizeof(state->cpu_name), "cpu%d",
888 cpu);
889 }
890
891 state = &global_cpu_state;
892 state->cpu_uavg = 0.0;
893 state->cpu_davg = 0.0;
894 state->cpu_limit = NCpus;
895 state->cpu_count = NCpus;
896 strlcpy(state->cpu_name, "global", sizeof(state->cpu_name));
897}
898
899static int
900get_nstate(struct cpu_state *state, double srt)
901{
902 int ustate, dstate, nstate;
903
904 /* speeding up */
905 state->cpu_uavg = (state->cpu_uavg * 2.0 + state->cpu_qavg) / 3.0;
906 /* slowing down */
907 state->cpu_davg = (state->cpu_davg * srt + state->cpu_qavg) / (srt + 1);
908 if (state->cpu_davg < state->cpu_uavg)
909 state->cpu_davg = state->cpu_uavg;
910
911 ustate = state->cpu_uavg / TriggerUp;
912 if (ustate < state->cpu_limit)
913 ustate = state->cpu_uavg / TriggerDown;
914 dstate = state->cpu_davg / TriggerUp;
915 if (dstate < state->cpu_limit)
916 dstate = state->cpu_davg / TriggerDown;
917
918 nstate = (ustate > dstate) ? ustate : dstate;
919 if (nstate > state->cpu_count)
920 nstate = state->cpu_count;
921
922 if (DebugOpt) {
923 printf("%s qavg=%5.2f uavg=%5.2f davg=%5.2f "
924 "%2d ncpus=%d\n", state->cpu_name,
925 state->cpu_qavg, state->cpu_uavg, state->cpu_davg,
926 state->cpu_limit, nstate);
927 }
928 return nstate;
929}
930
931static void
932mon_perf(double srt)
933{
934 cpumask_t ocpu_used, ocpu_pwrdom_used;
935 int pnstate = 0, nstate;
936 int cpu;
937
938 /*
939 * Find cpus requiring performance and their cooresponding power
940 * domains. Save the number of cpus requiring performance in
941 * pnstate.
942 */
943 ocpu_used = cpu_used;
944 ocpu_pwrdom_used = cpu_pwrdom_used;
945
946 CPUMASK_ASSZERO(cpu_used);
947 CPUMASK_ASSZERO(cpu_pwrdom_used);
948
949 for (cpu = 0; cpu < NCpus; ++cpu) {
950 struct cpu_state *state = &pcpu_state[cpu];
951 int s;
952
953 s = get_nstate(state, srt);
954 if (s) {
955 CPUMASK_ORBIT(cpu_used, cpu);
956 CPUMASK_ORBIT(cpu_pwrdom_used, cpu2pwrdom[cpu]);
957 }
958 pnstate += s;
959
960 state->cpu_limit = s;
50214825 961 }
a4c3bf5e
SZ
962
963 /*
964 * Calculate nstate, the number of cpus we wish to run at max
965 * performance.
966 */
967 nstate = get_nstate(&global_cpu_state, srt);
968
969 if (nstate == global_cpu_state.cpu_limit &&
970 (pnstate == global_pcpu_limit || nstate > pnstate)) {
971 /* Nothing changed; keep the sets */
972 cpu_used = ocpu_used;
973 cpu_pwrdom_used = ocpu_pwrdom_used;
974
975 global_pcpu_limit = pnstate;
976 return;
977 }
978 global_pcpu_limit = pnstate;
979
980 if (nstate > pnstate) {
981 /*
982 * Add spare cpus to meet global performance requirement.
983 */
984 add_spare_cpus(ocpu_used, nstate - pnstate);
985 }
986
987 global_cpu_state.cpu_limit = nstate;
988
989 /*
990 * Adjust cpu and cpu power domain performance
991 */
992 adj_perf(ocpu_used, ocpu_pwrdom_used);
993}
994
995static void
996add_spare_cpus(const cpumask_t ocpu_used, int ncpu)
997{
998 cpumask_t saved_pwrdom, xcpu_used;
999 int done = 0, cpu;
1000
1001 /*
1002 * Find more cpus in the previous cpu set.
1003 */
1004 xcpu_used = cpu_used;
1005 CPUMASK_XORMASK(xcpu_used, ocpu_used);
1006 while (CPUMASK_TESTNZERO(xcpu_used)) {
1007 cpu = BSFCPUMASK(xcpu_used);
1008 CPUMASK_NANDBIT(xcpu_used, cpu);
1009
1010 if (CPUMASK_TESTBIT(ocpu_used, cpu)) {
1011 CPUMASK_ORBIT(cpu_pwrdom_used, cpu2pwrdom[cpu]);
1012 CPUMASK_ORBIT(cpu_used, cpu);
1013 --ncpu;
1014 if (ncpu == 0)
1015 return;
1016 }
1017 }
1018
1019 /*
1020 * Find more cpus in the used cpu power domains.
1021 */
1022 saved_pwrdom = cpu_pwrdom_used;
1023again:
1024 while (CPUMASK_TESTNZERO(saved_pwrdom)) {
1025 cpumask_t unused_cpumask;
1026 int dom;
1027
1028 dom = BSFCPUMASK(saved_pwrdom);
1029 CPUMASK_NANDBIT(saved_pwrdom, dom);
1030
1031 unused_cpumask = cpu_pwrdomain[dom]->dom_cpumask;
1032 CPUMASK_NANDMASK(unused_cpumask, cpu_used);
1033
1034 while (CPUMASK_TESTNZERO(unused_cpumask)) {
1035 cpu = BSFCPUMASK(unused_cpumask);
1036 CPUMASK_NANDBIT(unused_cpumask, cpu);
1037
1038 CPUMASK_ORBIT(cpu_pwrdom_used, dom);
1039 CPUMASK_ORBIT(cpu_used, cpu);
1040 --ncpu;
1041 if (ncpu == 0)
1042 return;
1043 }
1044 }
1045 if (!done) {
1046 done = 1;
1047 /*
1048 * Find more cpus in unused cpu power domains
1049 */
1050 saved_pwrdom = cpu_pwrdom_mask;
1051 CPUMASK_NANDMASK(saved_pwrdom, cpu_pwrdom_used);
1052 goto again;
1053 }
1054 if (DebugOpt)
1055 printf("%d cpus not found\n", ncpu);
1056}
1057
1058static void
1059acpi_set_cpufreq(int dom, int inc)
1060{
1061 int lowest, highest, desired;
1062 char sysid[64];
1063
1064 acpi_get_cpufreq(dom, &highest, &lowest);
1065 if (highest == 0 || lowest == 0)
1066 return;
1067 desired = inc ? highest : lowest;
1068
1069 if (DebugOpt)
1070 printf("dom%d set frequency %d\n", dom, desired);
1071 snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.select", dom);
1072 sysctlbyname(sysid, NULL, NULL, &desired, sizeof(desired));
1073}
1074
1075static void
1076adj_cpu_pwrdom(int dom, int inc)
1077{
c0faca7f
SZ
1078 if (AdjustCpuFreq)
1079 acpi_set_cpufreq(dom, inc);
a4c3bf5e
SZ
1080}
1081
1082static void
1083adj_cpu_perf(int cpu, int inc)
1084{
1085 if (DebugOpt) {
1086 if (inc)
1087 printf("cpu%d increase perf\n", cpu);
1088 else
1089 printf("cpu%d decrease perf\n", cpu);
1090 }
1091
1092 if (HasPerfbias)
1093 set_perfbias(cpu, inc);
764594cf
SZ
1094 if (AdjustCstate)
1095 set_cstate(cpu, inc);
a4c3bf5e
SZ
1096}
1097
1098static void
1099adj_perf(cpumask_t xcpu_used, cpumask_t xcpu_pwrdom_used)
1100{
1101 cpumask_t old_usched_used;
1102 int cpu, inc;
1103
1104 /*
1105 * Set cpus requiring performance to the userland process
1106 * scheduler. Leave the rest of cpus unmapped.
1107 */
1108 old_usched_used = usched_cpu_used;
1109 usched_cpu_used = cpu_used;
1110 if (CPUMASK_TESTZERO(usched_cpu_used))
1111 CPUMASK_ORBIT(usched_cpu_used, 0);
1112 if (CPUMASK_CMPMASKNEQ(usched_cpu_used, old_usched_used))
1113 set_uschedcpus();
1114
1115 /*
1116 * Adjust per-cpu performance.
1117 */
1118 CPUMASK_XORMASK(xcpu_used, cpu_used);
1119 while (CPUMASK_TESTNZERO(xcpu_used)) {
1120 cpu = BSFCPUMASK(xcpu_used);
1121 CPUMASK_NANDBIT(xcpu_used, cpu);
1122
1123 if (CPUMASK_TESTBIT(cpu_used, cpu)) {
1124 /* Increase cpu performance */
1125 inc = 1;
1126 } else {
1127 /* Decrease cpu performance */
1128 inc = 0;
1129 }
1130 adj_cpu_perf(cpu, inc);
1131 }
1132
1133 /*
1134 * Adjust cpu power domain performance. This could affect
1135 * a set of cpus.
1136 */
1137 CPUMASK_XORMASK(xcpu_pwrdom_used, cpu_pwrdom_used);
1138 while (CPUMASK_TESTNZERO(xcpu_pwrdom_used)) {
1139 int dom;
1140
1141 dom = BSFCPUMASK(xcpu_pwrdom_used);
1142 CPUMASK_NANDBIT(xcpu_pwrdom_used, dom);
1143
1144 if (CPUMASK_TESTBIT(cpu_pwrdom_used, dom)) {
1145 /* Increase cpu power domain performance */
1146 inc = 1;
1147 } else {
1148 /* Decrease cpu power domain performance */
1149 inc = 0;
1150 }
1151 adj_cpu_pwrdom(dom, inc);
1152 }
1153}
1154
1155static void
1156restore_perf(void)
1157{
1158 cpumask_t ocpu_used, ocpu_pwrdom_used;
1159
a2b9b795
SZ
1160 /* Remove highest cpu frequency limitation */
1161 HighestCpuFreq = 0;
1162
a4c3bf5e
SZ
1163 ocpu_used = cpu_used;
1164 ocpu_pwrdom_used = cpu_pwrdom_used;
1165
1166 /* Max out all cpus and cpu power domains performance */
1167 CPUMASK_ASSBMASK(cpu_used, NCpus);
1168 cpu_pwrdom_used = cpu_pwrdom_mask;
1169
1170 adj_perf(ocpu_used, ocpu_pwrdom_used);
764594cf
SZ
1171
1172 if (AdjustCstate) {
1173 /*
1174 * Restore the original mwait C-state
1175 */
1176 if (DebugOpt)
1177 printf("global set cstate %s\n", orig_global_cx);
1178 sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
1179 orig_global_cx, strlen(orig_global_cx) + 1);
1180 }
1181}
1182
1183static int
1184probe_cstate(void)
1185{
1186 char cx_supported[1024];
1187 const char *target;
1188 char *ptr;
1189 int idle_hlt, deep = 1;
1190 size_t len;
1191
1192 len = sizeof(idle_hlt);
1193 if (sysctlbyname("machdep.cpu_idle_hlt", &idle_hlt, &len, NULL, 0) < 0)
1194 return 0;
1195 if (idle_hlt != 1)
1196 return 0;
1197
1198 len = sizeof(cx_supported);
1199 if (sysctlbyname("machdep.mwait.CX.supported", cx_supported, &len,
1200 NULL, 0) < 0)
1201 return 0;
1202
1203 len = sizeof(orig_global_cx);
1204 if (sysctlbyname("machdep.mwait.CX.idle", orig_global_cx, &len,
1205 NULL, 0) < 0)
1206 return 0;
1207
1208 strlcpy(cpu_perf_cx, "AUTODEEP", sizeof(cpu_perf_cx));
1209 cpu_perf_cxlen = strlen(cpu_perf_cx) + 1;
1210 if (sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
1211 cpu_perf_cx, cpu_perf_cxlen) < 0) {
1212 /* AUTODEEP is not supported; try AUTO */
1213 deep = 0;
1214 strlcpy(cpu_perf_cx, "AUTO", sizeof(cpu_perf_cx));
1215 cpu_perf_cxlen = strlen(cpu_perf_cx) + 1;
1216 if (sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
1217 cpu_perf_cx, cpu_perf_cxlen) < 0)
1218 return 0;
1219 }
1220
1221 if (!deep)
1222 target = "C2/0";
1223 else
1224 target = NULL;
1225 for (ptr = strtok(cx_supported, " "); ptr != NULL;
1226 ptr = strtok(NULL, " ")) {
1227 if (target == NULL ||
1228 (target != NULL && strcmp(ptr, target) == 0)) {
1229 strlcpy(cpu_idle_cx, ptr, sizeof(cpu_idle_cx));
1230 cpu_idle_cxlen = strlen(cpu_idle_cx) + 1;
1231 if (target != NULL)
1232 break;
1233 }
1234 }
1235 if (cpu_idle_cxlen == 0)
1236 return 0;
1237
1238 if (DebugOpt) {
1239 printf("cstate orig %s, perf %s, idle %s\n",
1240 orig_global_cx, cpu_perf_cx, cpu_idle_cx);
1241 }
1242 return 1;
1243}
1244
1245static void
1246set_cstate(int cpu, int inc)
1247{
1248 const char *cst;
1249 char sysid[64];
1250 size_t len;
1251
1252 if (inc) {
1253 cst = cpu_perf_cx;
1254 len = cpu_perf_cxlen;
1255 } else {
1256 cst = cpu_idle_cx;
1257 len = cpu_idle_cxlen;
1258 }
1259
1260 if (DebugOpt)
1261 printf("cpu%d set cstate %s\n", cpu, cst);
1262 snprintf(sysid, sizeof(sysid), "machdep.mwait.CX.idle%d", cpu);
1263 sysctlbyname(sysid, NULL, NULL, cst, len);
50214825 1264}