1 /*- 2 * Copyright (c) 2004 Colin Percival 3 * Copyright (c) 2005 Nate Lawson 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted providing that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/types.h> 32 #include <sys/param.h> 33 #include <sys/ioctl.h> 34 #include <sys/sysctl.h> 35 #include <sys/resource.h> 36 #include <sys/socket.h> 37 #include <sys/un.h> 38 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <libutil.h> 43 #include <pthread.h> 44 #include <signal.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #ifdef __i386__ 51 #include <machine/apm_bios.h> 52 #endif 53 54 #define DEFAULT_ACTIVE_PERCENT 65 55 #define DEFAULT_IDLE_PERCENT 90 56 #define DEFAULT_POLL_INTERVAL 500 /* Poll interval in milliseconds */ 57 58 enum modes_t { 59 MODE_MIN, 60 MODE_ADAPTIVE, 61 MODE_MAX, 62 }; 63 64 enum power_src_t { 65 SRC_AC, 66 SRC_BATTERY, 67 SRC_UNKNOWN, 68 }; 69 70 const char *modes[] = { 71 "AC", 72 "battery", 73 "unknown" 74 }; 75 76 #define ACPIAC "hw.acpi.acline" 77 #define APMDEV "/dev/apm" 78 #define DEVDPIPE "/var/run/devd.pipe" 79 #define DEVCTL_MAXBUF 1024 80 81 static int read_usage_times(long *idle, long *total); 82 static int read_freqs(int *numfreqs, int **freqs, int **power); 83 static int set_freq(int freq); 84 static void acline_init(void); 85 static int acline_read(void); 86 static int devd_init(void); 87 static void devd_close(void); 88 static void *devd_read(void *arg); 89 static void handle_sigs(int sig); 90 static void parse_mode(char *arg, int *mode, int ch); 91 static void usage(void); 92 93 /* Sysctl data structures. */ 94 static int cp_time_mib[2]; 95 static int freq_mib[4]; 96 static int levels_mib[4]; 97 static int acline_mib[3]; 98 99 /* devd-cached value provided by our thread. */ 100 static int devd_acline; 101 102 /* Configuration */ 103 static int cpu_running_mark; 104 static int cpu_idle_mark; 105 static int poll_ival; 106 static int vflag; 107 108 static int apm_fd; 109 static int devd_pipe; 110 static pthread_t devd_thread; 111 static int exit_requested; 112 113 static int 114 read_usage_times(long *idle, long *total) 115 { 116 static long idle_old, total_old; 117 long cp_time[CPUSTATES], i, total_new; 118 size_t cp_time_len; 119 int error; 120 121 cp_time_len = sizeof(cp_time); 122 error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0); 123 if (error) 124 return (error); 125 for (total_new = 0, i = 0; i < CPUSTATES; i++) 126 total_new += cp_time[i]; 127 128 if (idle) 129 *idle = cp_time[CP_IDLE] - idle_old; 130 if (total) 131 *total = total_new - total_old; 132 133 idle_old = cp_time[CP_IDLE]; 134 total_old = total_new; 135 136 return (0); 137 } 138 139 static int 140 read_freqs(int *numfreqs, int **freqs, int **power) 141 { 142 char *freqstr, *p, *q; 143 int i; 144 size_t len = 0; 145 146 if (sysctl(levels_mib, 4, NULL, &len, NULL, 0)) 147 return (-1); 148 if ((freqstr = malloc(len)) == NULL) 149 return (-1); 150 if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) 151 return (-1); 152 153 *numfreqs = 1; 154 for (p = freqstr; *p != '\0'; p++) 155 if (*p == ' ') 156 (*numfreqs)++; 157 158 if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { 159 free(freqstr); 160 return (-1); 161 } 162 if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) { 163 free(freqstr); 164 free(*freqs); 165 return (-1); 166 } 167 for (i = 0, p = freqstr; i < *numfreqs; i++) { 168 q = strchr(p, ' '); 169 if (q != NULL) 170 *q = '\0'; 171 if (sscanf(p, "%d/%d", &(*freqs)[i], &(*power)[i]) != 2) { 172 free(freqstr); 173 free(*freqs); 174 free(*power); 175 return (-1); 176 } 177 p = q + 1; 178 } 179 180 free(freqstr); 181 return (0); 182 } 183 184 static int 185 set_freq(int freq) 186 { 187 188 if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) { 189 if (errno != EPERM) 190 return (-1); 191 } 192 193 return (0); 194 } 195 196 /* 197 * Try to use ACPI to find the AC line status. If this fails, fall back 198 * to APM. If nothing succeeds, we'll just run in default mode. If we are 199 * using ACPI, try opening a pipe to devd to detect AC line events. 200 */ 201 static void 202 acline_init() 203 { 204 int acline; 205 size_t len; 206 207 apm_fd = -1; 208 devd_pipe = -1; 209 len = sizeof(acline); 210 if (sysctlbyname(ACPIAC, &acline, &len, NULL, 0) == 0) { 211 len = 3; 212 if (sysctlnametomib(ACPIAC, acline_mib, &len)) 213 err(1, "lookup acline"); 214 215 /* Read line status once so that we have an initial value. */ 216 devd_acline = acline_read(); 217 218 /* 219 * Try connecting to the devd pipe and start a read thread 220 * if we succeed. 221 */ 222 if ((devd_pipe = devd_init()) >= 0) { 223 if (pthread_create(&devd_thread, NULL, devd_read, 224 &devd_pipe)) 225 err(1, "pthread_create devd thread"); 226 } else if (vflag) { 227 warnx( 228 "unable to connect to devd pipe, using polling mode instead"); 229 } 230 } else { 231 apm_fd = open(APMDEV, O_RDONLY); 232 if (apm_fd == -1) 233 warnx( 234 "cannot read AC line status, using default settings"); 235 } 236 } 237 238 static int 239 acline_read() 240 { 241 int acline; 242 size_t len; 243 #ifdef __i386__ 244 struct apm_info info; 245 #endif 246 247 acline = SRC_UNKNOWN; 248 len = sizeof(acline); 249 250 /* 251 * Get state from our devd thread, the ACPI sysctl, or APM. We 252 * prefer sources in this order. 253 */ 254 if (devd_pipe >= 0) 255 acline = devd_acline; 256 else if (sysctl(acline_mib, 3, &acline, &len, NULL, 0) == 0) 257 acline = acline ? SRC_AC : SRC_BATTERY; 258 #ifdef __i386__ 259 else if (apm_fd != -1 && ioctl(apm_fd, APMIO_GETINFO, &info) == 0) 260 acline = info.ai_acline ? SRC_AC : SRC_BATTERY; 261 #endif 262 263 return (acline); 264 } 265 266 static int 267 devd_init(void) 268 { 269 struct sockaddr_un devd_addr; 270 int devd_sock; 271 272 bzero(&devd_addr, sizeof(devd_addr)); 273 if ((devd_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { 274 if (vflag) 275 warn("failed to create devd socket"); 276 return (-1); 277 } 278 279 devd_addr.sun_family = PF_LOCAL; 280 strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); 281 if (connect(devd_sock, (struct sockaddr *)&devd_addr, 282 sizeof(devd_addr)) == -1) { 283 close(devd_sock); 284 return (-1); 285 } 286 287 return (devd_sock); 288 } 289 290 static void 291 devd_close(void) 292 { 293 294 if (devd_pipe < 0) 295 return; 296 297 pthread_kill(devd_thread, SIGTERM); 298 close(devd_pipe); 299 } 300 301 /* 302 * This loop runs as a separate thread. It reads events from devd, but 303 * spends most of its time blocked in select(2). 304 */ 305 static void * 306 devd_read(void *arg) 307 { 308 char buf[DEVCTL_MAXBUF], *ptr; 309 fd_set fdset; 310 int fd, notify, rlen; 311 312 fd = *(int *)arg; 313 notify = -1; 314 FD_ZERO(&fdset); 315 while (!exit_requested) { 316 FD_SET(fd, &fdset); 317 if (select(fd + 1, &fdset, NULL, NULL, NULL) < 0) 318 break; 319 if (!FD_ISSET(fd, &fdset)) 320 continue; 321 322 /* Read the notify string, devd NULL-terminates it. */ 323 rlen = read(fd, buf, sizeof(buf)); 324 if (rlen <= 0) { 325 close(devd_pipe); 326 devd_pipe = -1; 327 if (vflag) 328 warnx( 329 "devd disappeared, downgrading to polling mode"); 330 331 /* 332 * Keep trying to reconnect to devd but sleep in 333 * between to avoid wasting CPU cycles. 334 */ 335 while (!exit_requested && (fd = devd_init()) < 0) 336 sleep(300); 337 338 if (fd >= 0) { 339 devd_pipe = fd; 340 if (vflag) 341 warnx( 342 "devd came back, upgrading to event mode"); 343 } 344 continue; 345 } 346 347 /* Loosely match the notify string. */ 348 if ((ptr = strstr(buf, "system=ACPI")) != NULL && 349 (ptr = strstr(ptr, "subsystem=ACAD")) != NULL && 350 (ptr = strstr(ptr, "notify=")) != NULL) { 351 if (sscanf(ptr, "notify=%x", ¬ify) != 1) { 352 warnx("bad devd notify string"); 353 continue; 354 } 355 devd_acline = notify ? SRC_AC : SRC_BATTERY; 356 } 357 } 358 359 return (NULL); 360 } 361 362 static void 363 parse_mode(char *arg, int *mode, int ch) 364 { 365 366 if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0) 367 *mode = MODE_MIN; 368 else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0) 369 *mode = MODE_MAX; 370 else if (strcmp(arg, "adaptive") == 0) 371 *mode = MODE_ADAPTIVE; 372 else 373 errx(1, "bad option: -%c %s", (char)ch, optarg); 374 } 375 376 static void 377 handle_sigs(int __unused sig) 378 { 379 380 exit_requested = 1; 381 } 382 383 static void 384 usage(void) 385 { 386 387 fprintf(stderr, 388 "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%] [-P pidfile]\n"); 389 exit(1); 390 } 391 392 int 393 main(int argc, char * argv[]) 394 { 395 struct pidfh *pfh = NULL; 396 const char *pidfile = NULL; 397 long idle, total; 398 int acline, curfreq, *freqs, i, *mwatts, numfreqs; 399 int ch, mode, mode_ac, mode_battery, mode_none; 400 uint64_t mjoules_used; 401 size_t len; 402 403 /* Default mode for all AC states is adaptive. */ 404 mode_ac = mode_battery = mode_none = MODE_ADAPTIVE; 405 cpu_running_mark = DEFAULT_ACTIVE_PERCENT; 406 cpu_idle_mark = DEFAULT_IDLE_PERCENT; 407 poll_ival = DEFAULT_POLL_INTERVAL; 408 mjoules_used = 0; 409 vflag = 0; 410 apm_fd = -1; 411 412 /* User must be root to control frequencies. */ 413 if (geteuid() != 0) 414 errx(1, "must be root to run"); 415 416 while ((ch = getopt(argc, argv, "a:b:i:n:p:P:r:v")) != EOF) 417 switch (ch) { 418 case 'a': 419 parse_mode(optarg, &mode_ac, ch); 420 break; 421 case 'b': 422 parse_mode(optarg, &mode_battery, ch); 423 break; 424 case 'i': 425 cpu_idle_mark = atoi(optarg); 426 if (cpu_idle_mark < 0 || cpu_idle_mark > 100) { 427 warnx("%d is not a valid percent", 428 cpu_idle_mark); 429 usage(); 430 } 431 break; 432 case 'n': 433 parse_mode(optarg, &mode_none, ch); 434 break; 435 case 'p': 436 poll_ival = atoi(optarg); 437 if (poll_ival < 5) { 438 warnx("poll interval is in units of ms"); 439 usage(); 440 } 441 break; 442 case 'P': 443 pidfile = optarg; 444 break; 445 case 'r': 446 cpu_running_mark = atoi(optarg); 447 if (cpu_running_mark < 0 || cpu_running_mark > 100) { 448 warnx("%d is not a valid percent", 449 cpu_running_mark); 450 usage(); 451 } 452 break; 453 case 'v': 454 vflag = 1; 455 break; 456 default: 457 usage(); 458 } 459 460 mode = mode_none; 461 462 /* Poll interval is in units of ms. */ 463 poll_ival *= 1000; 464 465 /* Look up various sysctl MIBs. */ 466 len = 2; 467 if (sysctlnametomib("kern.cp_time", cp_time_mib, &len)) 468 err(1, "lookup kern.cp_time"); 469 len = 4; 470 if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len)) 471 err(1, "lookup freq"); 472 len = 4; 473 if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) 474 err(1, "lookup freq_levels"); 475 476 /* Check if we can read the idle time and supported freqs. */ 477 if (read_usage_times(NULL, NULL)) 478 err(1, "read_usage_times"); 479 if (read_freqs(&numfreqs, &freqs, &mwatts)) 480 err(1, "error reading supported CPU frequencies"); 481 482 /* 483 * Exit cleanly on signals; devd may send a SIGPIPE if it dies. We 484 * do this before acline_init() since it may create a thread and we 485 * want it to inherit our signal mask. 486 */ 487 signal(SIGINT, handle_sigs); 488 signal(SIGTERM, handle_sigs); 489 signal(SIGPIPE, SIG_IGN); 490 491 /* Run in the background unless in verbose mode. */ 492 if (!vflag) { 493 pid_t otherpid; 494 495 pfh = pidfile_open(pidfile, 0600, &otherpid); 496 if (pfh == NULL) { 497 if (errno == EEXIST) { 498 errx(1, "powerd already running, pid: %d", 499 otherpid); 500 } 501 warn("cannot open pid file"); 502 } 503 if (daemon(0, 0) != 0) { 504 warn("cannot enter daemon mode, exiting"); 505 pidfile_remove(pfh); 506 exit(EXIT_FAILURE); 507 508 } 509 pidfile_write(pfh); 510 } 511 512 /* Decide whether to use ACPI or APM to read the AC line status. */ 513 acline_init(); 514 515 /* Main loop. */ 516 for (;;) { 517 /* Check status every few milliseconds. */ 518 usleep(poll_ival); 519 520 /* If the user requested we quit, print some statistics. */ 521 if (exit_requested) { 522 if (vflag && mjoules_used != 0) 523 printf("total joules used: %u.%03u\n", 524 (u_int)(mjoules_used / 1000), 525 (int)mjoules_used % 1000); 526 break; 527 } 528 529 /* Read the current AC status and record the mode. */ 530 acline = acline_read(); 531 switch (acline) { 532 case SRC_AC: 533 mode = mode_ac; 534 break; 535 case SRC_BATTERY: 536 mode = mode_battery; 537 break; 538 case SRC_UNKNOWN: 539 mode = mode_none; 540 break; 541 default: 542 errx(1, "invalid AC line status %d", acline); 543 } 544 545 /* Read the current frequency. */ 546 len = sizeof(curfreq); 547 if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 548 if (vflag) 549 warn("error reading current CPU frequency"); 550 continue; 551 } 552 553 if (vflag) { 554 for (i = 0; i < numfreqs; i++) { 555 if (freqs[i] == curfreq) 556 break; 557 } 558 559 /* Keep a sum of all power actually used. */ 560 if (i < numfreqs && mwatts[i] != -1) 561 mjoules_used += 562 (mwatts[i] * (poll_ival / 1000)) / 1000; 563 } 564 565 /* Always switch to the lowest frequency in min mode. */ 566 if (mode == MODE_MIN) { 567 if (curfreq != freqs[numfreqs - 1]) { 568 if (vflag) { 569 printf("now operating on %s power; " 570 "changing frequency to %d MHz\n", 571 modes[acline], freqs[numfreqs - 1]); 572 } 573 if (set_freq(freqs[numfreqs - 1]) != 0) { 574 warn("error setting CPU freq %d", 575 freqs[numfreqs - 1]); 576 continue; 577 } 578 } 579 continue; 580 } 581 582 /* Always switch to the highest frequency in max mode. */ 583 if (mode == MODE_MAX) { 584 if (curfreq != freqs[0]) { 585 if (vflag) { 586 printf("now operating on %s power; " 587 "changing frequency to %d MHz\n", 588 modes[acline], freqs[0]); 589 } 590 if (set_freq(freqs[0]) != 0) { 591 warn("error setting CPU freq %d", 592 freqs[0]); 593 continue; 594 } 595 } 596 continue; 597 } 598 599 /* Adaptive mode; get the current CPU usage times. */ 600 if (read_usage_times(&idle, &total)) { 601 if (vflag) 602 warn("read_usage_times() failed"); 603 continue; 604 } 605 606 /* 607 * If we're idle less than the active mark, bump up two levels. 608 * If we're idle more than the idle mark, drop down one level. 609 */ 610 for (i = 0; i < numfreqs - 1; i++) { 611 if (freqs[i] == curfreq) 612 break; 613 } 614 if (idle < (total * cpu_running_mark) / 100 && 615 curfreq < freqs[0]) { 616 i -= 2; 617 if (i < 0) 618 i = 0; 619 if (vflag) { 620 printf("idle time < %d%%, increasing clock" 621 " speed from %d MHz to %d MHz\n", 622 cpu_running_mark, curfreq, freqs[i]); 623 } 624 if (set_freq(freqs[i])) 625 err(1, "error setting CPU frequency %d", 626 freqs[i]); 627 } else if (idle > (total * cpu_idle_mark) / 100 && 628 curfreq > freqs[numfreqs - 1]) { 629 i++; 630 if (vflag) { 631 printf("idle time > %d%%, decreasing clock" 632 " speed from %d MHz to %d MHz\n", 633 cpu_idle_mark, curfreq, freqs[i]); 634 } 635 if (set_freq(freqs[i]) != 0) 636 warn("error setting CPU frequency %d", 637 freqs[i]); 638 } 639 } 640 free(freqs); 641 free(mwatts); 642 devd_close(); 643 if (!vflag) 644 pidfile_remove(pfh); 645 646 exit(0); 647 } 648