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