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/soundcard.h> 46 #include <sys/time.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <unistd.h> 50 #include <string.h> 51 #include <syslog.h> 52 53 #include "alert1.h" 54 55 static void usage(void); 56 static double getcputime(double); 57 static void acpi_setcpufreq(int nstate); 58 static void setupdominfo(void); 59 static int has_battery(void); 60 static int mon_battery(void); 61 62 int DebugOpt; 63 int TurboOpt = 1; 64 int CpuLimit; /* # of cpus at max frequency */ 65 int DomLimit; /* # of domains at max frequency */ 66 int PowerFd; 67 int DomBeg; 68 int DomEnd; 69 int NCpus; 70 int CpuCount[256]; /* # of cpus in any given domain */ 71 int CpuToDom[256]; /* domain a particular cpu belongs to */ 72 int Hysteresis = 10; /* percentage */ 73 double TriggerUp = 0.25;/* single-cpu load to force max freq */ 74 double TriggerDown; /* load per cpu to force the min freq */ 75 static int BatLifeMin = 2; /* shutdown the box, if low on battery life */ 76 static struct timespec BatLifePrevT; 77 static int BatLifePollIntvl = 5; /* unit: sec */ 78 79 static struct timespec BatShutdownStartT; 80 static int BatShutdownLinger = -1; 81 static int BatShutdownLingerSet = 60; /* unit: sec */ 82 static int BatShutdownLingerCnt; 83 static int BatShutdownAudioAlert = 1; 84 85 static void sigintr(int signo); 86 87 int 88 main(int ac, char **av) 89 { 90 double qavg; 91 double uavg; /* uavg - used for speeding up */ 92 double davg; /* davg - used for slowing down */ 93 double srt; 94 double pollrate; 95 int ch; 96 int ustate; 97 int dstate; 98 int nstate; 99 char buf[64]; 100 int monbat; 101 102 srt = 8.0; /* time for samples - 8 seconds */ 103 pollrate = 1.0; /* polling rate in seconds */ 104 105 while ((ch = getopt(ac, av, "dp:r:tu:B:L:P:QT:")) != -1) { 106 switch(ch) { 107 case 'd': 108 DebugOpt = 1; 109 break; 110 case 'p': 111 Hysteresis = (int)strtol(optarg, NULL, 10); 112 break; 113 case 'r': 114 pollrate = strtod(optarg, NULL); 115 break; 116 case 't': 117 TurboOpt = 0; 118 break; 119 case 'u': 120 TriggerUp = (double)strtol(optarg, NULL, 10) / 100; 121 break; 122 case 'B': 123 BatLifeMin = strtol(optarg, NULL, 10); 124 break; 125 case 'L': 126 BatShutdownLingerSet = strtol(optarg, NULL, 10); 127 if (BatShutdownLingerSet < 0) 128 BatShutdownLingerSet = 0; 129 break; 130 case 'P': 131 BatLifePollIntvl = strtol(optarg, NULL, 10); 132 break; 133 case 'Q': 134 BatShutdownAudioAlert = 0; 135 break; 136 case 'T': 137 srt = strtod(optarg, NULL); 138 break; 139 default: 140 usage(); 141 /* NOT REACHED */ 142 } 143 } 144 ac -= optind; 145 av += optind; 146 147 if (0 > Hysteresis || Hysteresis > 99) { 148 fprintf(stderr, "Invalid hysteresis value\n"); 149 exit(1); 150 } 151 152 if (0 > TriggerUp || TriggerUp > 1) { 153 fprintf(stderr, "Invalid load limit value\n"); 154 exit(1); 155 } 156 157 TriggerDown = TriggerUp - (TriggerUp * (double) Hysteresis / 100); 158 159 /* 160 * Make sure powerd is not already running. 161 */ 162 PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644); 163 if (PowerFd < 0) { 164 fprintf(stderr, 165 "Cannot create /var/run/powerd.pid, " 166 "continuing anyway\n"); 167 } else { 168 if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) { 169 fprintf(stderr, "powerd is already running\n"); 170 exit(1); 171 } 172 } 173 174 /* 175 * Demonize and set pid 176 */ 177 if (DebugOpt == 0) { 178 daemon(0, 0); 179 openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON); 180 } 181 182 if (PowerFd >= 0) { 183 ftruncate(PowerFd, 0); 184 snprintf(buf, sizeof(buf), "%d\n", (int)getpid()); 185 write(PowerFd, buf, strlen(buf)); 186 } 187 188 /* Do we need to monitor battery life? */ 189 if (BatLifePollIntvl <= 0) 190 monbat = 0; 191 else 192 monbat = has_battery(); 193 194 /* 195 * Wait hw.acpi.cpu.px_dom* sysctl to be created by kernel 196 * 197 * Since hw.acpi.cpu.px_dom* creation is queued into ACPI 198 * taskqueue and ACPI taskqueue is shared across various 199 * ACPI modules, any delay in other modules may cause 200 * hw.acpi.cpu.px_dom* to be created at quite a later time 201 * (e.g. cmbat module's task could take quite a lot of time). 202 */ 203 for (;;) { 204 /* 205 * Prime delta cputime calculation, make sure at least 206 * dom0 exists. 207 */ 208 getcputime(pollrate); 209 210 setupdominfo(); 211 if (DomBeg >= DomEnd) { 212 usleep((int)(pollrate * 1000000.0)); 213 continue; 214 } 215 216 DomLimit = DomEnd; 217 CpuLimit = NCpus; 218 break; 219 } 220 221 /* 222 * Set to maximum performance if killed. 223 */ 224 signal(SIGINT, sigintr); 225 signal(SIGTERM, sigintr); 226 uavg = 0.0; 227 davg = 0.0; 228 229 srt = srt / pollrate; /* convert to sample count */ 230 231 if (DebugOpt) 232 printf("samples for downgrading: %5.2f\n", srt); 233 234 /* 235 * Monitoring loop 236 * 237 * Calculate nstate, the number of cpus we wish to run at max 238 * frequency. All remaining cpus will be set to their lowest 239 * frequency and mapped out of the user process scheduler. 240 */ 241 for (;;) { 242 qavg = getcputime(pollrate); 243 uavg = (uavg * 2.0 + qavg) / 3.0; /* speeding up */ 244 davg = (davg * srt + qavg) / (srt + 1); /* slowing down */ 245 if (davg < uavg) 246 davg = uavg; 247 248 ustate = uavg / TriggerUp; 249 if (ustate < CpuLimit) 250 ustate = uavg / TriggerDown; 251 dstate = davg / TriggerUp; 252 if (dstate < CpuLimit) 253 dstate = davg / TriggerDown; 254 255 nstate = (ustate > dstate) ? ustate : dstate; 256 if (nstate > NCpus) 257 nstate = NCpus; 258 259 if (DebugOpt) { 260 printf("\rqavg=%5.2f uavg=%5.2f davg=%5.2f " 261 "%2d/%2d ncpus=%d\r", 262 qavg, uavg, davg, 263 CpuLimit, DomLimit, nstate); 264 fflush(stdout); 265 } 266 if (nstate != CpuLimit) 267 acpi_setcpufreq(nstate); 268 if (monbat) 269 monbat = mon_battery(); 270 usleep((int)(pollrate * 1000000.0)); 271 } 272 } 273 274 static 275 void 276 sigintr(int signo __unused) 277 { 278 syslog(LOG_INFO, "killed, setting max and exiting"); 279 acpi_setcpufreq(NCpus); 280 exit(1); 281 } 282 283 /* 284 * Figure out the domains and calculate the CpuCount[] and CpuToDom[] 285 * arrays. 286 */ 287 static 288 void 289 setupdominfo(void) 290 { 291 char buf[64]; 292 char members[1024]; 293 char *str; 294 size_t msize; 295 int i; 296 int n; 297 298 for (i = 0; i < 256; ++i) { 299 snprintf(buf, sizeof(buf), 300 "hw.acpi.cpu.px_dom%d.available", i); 301 if (sysctlbyname(buf, NULL, NULL, NULL, 0) >= 0) 302 break; 303 } 304 DomBeg = i; 305 306 for (i = 255; i >= DomBeg; --i) { 307 snprintf(buf, sizeof(buf), 308 "hw.acpi.cpu.px_dom%d.available", i); 309 if (sysctlbyname(buf, NULL, NULL, NULL, 0) >= 0) { 310 ++i; 311 break; 312 } 313 } 314 DomEnd = i; 315 316 for (i = DomBeg; i < DomEnd; ++i) { 317 snprintf(buf, sizeof(buf), 318 "hw.acpi.cpu.px_dom%d.members", i); 319 msize = sizeof(members); 320 if (sysctlbyname(buf, members, &msize, NULL, 0) == 0) { 321 members[msize] = 0; 322 for (str = strtok(members, " "); str; 323 str = strtok(NULL, " ")) { 324 n = -1; 325 sscanf(str, "cpu%d", &n); 326 if (n >= 0) { 327 ++NCpus; 328 ++CpuCount[i]; 329 CpuToDom[n]= i; 330 } 331 } 332 } 333 } 334 } 335 336 /* 337 * Return the one-second cpu load. One cpu at 100% will return a value 338 * of 1.0. On a SMP system N cpus running at 100% will return a value of N. 339 */ 340 static 341 double 342 getcputime(double pollrate) 343 { 344 static struct kinfo_cputime ocpu_time[64]; 345 static struct kinfo_cputime ncpu_time[64]; 346 size_t slen; 347 int ncpu; 348 int cpu; 349 uint64_t delta; 350 351 bcopy(ncpu_time, ocpu_time, sizeof(ncpu_time)); 352 slen = sizeof(ncpu_time); 353 if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) { 354 fprintf(stderr, "kern.cputime sysctl not available\n"); 355 exit(1); 356 } 357 ncpu = slen / sizeof(ncpu_time[0]); 358 delta = 0; 359 360 for (cpu = 0; cpu < ncpu; ++cpu) { 361 delta += (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys + 362 ncpu_time[cpu].cp_nice + ncpu_time[cpu].cp_intr) - 363 (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys + 364 ocpu_time[cpu].cp_nice + ocpu_time[cpu].cp_intr); 365 } 366 return((double)delta / (pollrate * 1000000.0)); 367 } 368 369 /* 370 * nstate is the requested number of cpus that we wish to run at full 371 * frequency. We calculate how many domains we have to adjust to reach 372 * this goal. 373 * 374 * This function also sets the user scheduler global cpu mask. 375 */ 376 static 377 void 378 acpi_setcpufreq(int nstate) 379 { 380 int ncpus = 0; 381 int increasing = (nstate > CpuLimit); 382 int dom; 383 int domBeg; 384 int domEnd; 385 int lowest; 386 int highest; 387 int desired; 388 int v; 389 char *sysid; 390 char *ptr; 391 char buf[256]; 392 size_t buflen; 393 cpumask_t global_cpumask; 394 395 /* 396 * Calculate the ending domain if the number of operating cpus 397 * has increased. 398 * 399 * Calculate the starting domain if the number of operating cpus 400 * has decreased. 401 */ 402 for (dom = DomBeg; dom < DomEnd; ++dom) { 403 if (ncpus >= nstate) 404 break; 405 ncpus += CpuCount[dom]; 406 } 407 408 syslog(LOG_INFO, "using %d cpus", nstate); 409 410 /* 411 * Set the mask of cpus the userland scheduler is allowed to use. 412 */ 413 CPUMASK_ASSBMASK(global_cpumask, nstate); 414 sysctlbyname("kern.usched_global_cpumask", NULL, 0, 415 &global_cpumask, sizeof(global_cpumask)); 416 417 if (increasing) { 418 domBeg = DomLimit; 419 domEnd = dom; 420 } else { 421 domBeg = dom; 422 domEnd = DomLimit; 423 } 424 DomLimit = dom; 425 CpuLimit = nstate; 426 427 /* 428 * Adjust the cpu frequency 429 */ 430 if (DebugOpt) 431 printf("\n"); 432 for (dom = domBeg; dom < domEnd; ++dom) { 433 /* 434 * Retrieve availability list 435 */ 436 asprintf(&sysid, "hw.acpi.cpu.px_dom%d.available", dom); 437 buflen = sizeof(buf) - 1; 438 v = sysctlbyname(sysid, buf, &buflen, NULL, 0); 439 free(sysid); 440 if (v < 0) 441 continue; 442 buf[buflen] = 0; 443 444 /* 445 * Parse out the highest and lowest cpu frequencies 446 */ 447 ptr = buf; 448 highest = lowest = 0; 449 while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) { 450 if (lowest == 0 || lowest > v) 451 lowest = v; 452 if (highest == 0 || highest < v) 453 highest = v; 454 /* 455 * Detect turbo mode 456 */ 457 if ((highest - v == 1) && ! TurboOpt) 458 highest = v; 459 460 } 461 462 /* 463 * Calculate the desired cpu frequency, test, and set. 464 */ 465 desired = increasing ? highest : lowest; 466 467 asprintf(&sysid, "hw.acpi.cpu.px_dom%d.select", dom); 468 buflen = sizeof(v); 469 v = 0; 470 sysctlbyname(sysid, &v, &buflen, NULL, 0); 471 { 472 if (DebugOpt) { 473 printf("dom%d set frequency %d\n", 474 dom, desired); 475 } 476 sysctlbyname(sysid, NULL, NULL, 477 &desired, sizeof(desired)); 478 } 479 free(sysid); 480 } 481 } 482 483 static 484 void 485 usage(void) 486 { 487 fprintf(stderr, "usage: powerd [-dt] [-p hysteresis] " 488 "[-u trigger_up] [-T sample_interval] [-r poll_interval] " 489 "[-B min_battery_life] [-L low_battery_linger] " 490 "[-P battery_poll_interval] [-Q]\n"); 491 exit(1); 492 } 493 494 #ifndef timespecsub 495 #define timespecsub(vvp, uvp) \ 496 do { \ 497 (vvp)->tv_sec -= (uvp)->tv_sec; \ 498 (vvp)->tv_nsec -= (uvp)->tv_nsec; \ 499 if ((vvp)->tv_nsec < 0) { \ 500 (vvp)->tv_sec--; \ 501 (vvp)->tv_nsec += 1000000000; \ 502 } \ 503 } while (0) 504 #endif 505 506 #define BAT_SYSCTL_TIME_MAX 50000000 /* unit: nanosecond */ 507 508 static int 509 has_battery(void) 510 { 511 struct timespec s, e; 512 size_t len; 513 int val; 514 515 clock_gettime(CLOCK_MONOTONIC_FAST, &s); 516 BatLifePrevT = s; 517 518 len = sizeof(val); 519 if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) { 520 /* No AC line information */ 521 return 0; 522 } 523 clock_gettime(CLOCK_MONOTONIC_FAST, &e); 524 525 timespecsub(&e, &s); 526 if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) { 527 /* hw.acpi.acline takes to long to be useful */ 528 syslog(LOG_NOTICE, "hw.acpi.acline takes too long"); 529 return 0; 530 } 531 532 clock_gettime(CLOCK_MONOTONIC_FAST, &s); 533 len = sizeof(val); 534 if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) { 535 /* No battery life */ 536 return 0; 537 } 538 clock_gettime(CLOCK_MONOTONIC_FAST, &e); 539 540 timespecsub(&e, &s); 541 if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) { 542 /* hw.acpi.battery.life takes to long to be useful */ 543 syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long"); 544 return 0; 545 } 546 return 1; 547 } 548 549 static void 550 low_battery_alert(int life) 551 { 552 int fmt, stereo, freq; 553 int fd; 554 555 syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d", 556 life, BatShutdownLingerCnt); 557 ++BatShutdownLingerCnt; 558 559 if (!BatShutdownAudioAlert) 560 return; 561 562 fd = open("/dev/dsp", O_WRONLY); 563 if (fd < 0) 564 return; 565 566 fmt = AFMT_S16_LE; 567 if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0) 568 goto done; 569 570 stereo = 0; 571 if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0) 572 goto done; 573 574 freq = 44100; 575 if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0) 576 goto done; 577 578 write(fd, alert1, sizeof(alert1)); 579 write(fd, alert1, sizeof(alert1)); 580 581 done: 582 close(fd); 583 } 584 585 static int 586 mon_battery(void) 587 { 588 struct timespec cur, ts; 589 int acline, life; 590 size_t len; 591 592 clock_gettime(CLOCK_MONOTONIC_FAST, &cur); 593 ts = cur; 594 timespecsub(&ts, &BatLifePrevT); 595 if (ts.tv_sec < BatLifePollIntvl) 596 return 1; 597 BatLifePrevT = cur; 598 599 len = sizeof(acline); 600 if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0) 601 return 1; 602 if (acline) { 603 BatShutdownLinger = -1; 604 BatShutdownLingerCnt = 0; 605 return 1; 606 } 607 608 len = sizeof(life); 609 if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0) 610 return 1; 611 612 if (BatShutdownLinger > 0) { 613 ts = cur; 614 timespecsub(&ts, &BatShutdownStartT); 615 if (ts.tv_sec > BatShutdownLinger) 616 BatShutdownLinger = 0; 617 } 618 619 if (life <= BatLifeMin) { 620 if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) { 621 syslog(LOG_ALERT, "low battery life %d%%, " 622 "shutting down", life); 623 if (vfork() == 0) 624 execlp("poweroff", "poweroff", NULL); 625 return 0; 626 } else if (BatShutdownLinger < 0) { 627 BatShutdownLinger = BatShutdownLingerSet; 628 BatShutdownStartT = cur; 629 } 630 low_battery_alert(life); 631 } 632 return 1; 633 } 634