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