1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2004 Colin Percival 5 * Copyright (c) 2005 Nate Lawson 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted providing that the following conditions 10 * are met: 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 the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/ioctl.h> 32 #include <sys/sysctl.h> 33 #include <sys/resource.h> 34 #include <sys/socket.h> 35 #include <sys/time.h> 36 #include <sys/un.h> 37 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <libutil.h> 42 #include <signal.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <sysexits.h> 47 #include <unistd.h> 48 49 #ifdef __i386__ 50 #define USE_APM 51 #endif 52 53 #ifdef USE_APM 54 #include <machine/apm_bios.h> 55 #endif 56 57 #define DEFAULT_ACTIVE_PERCENT 75 58 #define DEFAULT_IDLE_PERCENT 50 59 #define DEFAULT_POLL_INTERVAL 250 /* Poll interval in milliseconds */ 60 61 typedef enum { 62 MODE_MIN, 63 MODE_ADAPTIVE, 64 MODE_HIADAPTIVE, 65 MODE_MAX, 66 } modes_t; 67 68 typedef enum { 69 SRC_AC, 70 SRC_BATTERY, 71 SRC_UNKNOWN, 72 } power_src_t; 73 74 static const char *modes[] = { 75 "AC", 76 "battery", 77 "unknown" 78 }; 79 80 #define ACPIAC "hw.acpi.acline" 81 #define PMUAC "dev.pmu.0.acline" 82 #define APMDEV "/dev/apm" 83 #define DEVDPIPE "/var/run/devd.pipe" 84 #define DEVCTL_MAXBUF 1024 85 86 static int read_usage_times(int *load, int nonice); 87 static int read_freqs(int *numfreqs, int **freqs, int **power, 88 int minfreq, int maxfreq); 89 static int set_freq(int freq); 90 static void acline_init(void); 91 static void acline_read(void); 92 static int devd_init(void); 93 static void devd_close(void); 94 static void handle_sigs(int sig); 95 static void parse_mode(char *arg, int *mode, int ch); 96 static void usage(void); 97 98 /* Sysctl data structures. */ 99 static int cp_times_mib[2]; 100 static int freq_mib[4]; 101 static int levels_mib[4]; 102 static int acline_mib[4]; 103 static size_t acline_mib_len; 104 105 /* Configuration */ 106 static int cpu_running_mark; 107 static int cpu_idle_mark; 108 static int poll_ival; 109 static int vflag; 110 111 static volatile sig_atomic_t exit_requested; 112 static power_src_t acline_status; 113 typedef enum { 114 ac_none, 115 ac_sysctl, 116 ac_acpi_devd, 117 #ifdef USE_APM 118 ac_apm, 119 #endif 120 } acline_mode_t; 121 static acline_mode_t acline_mode; 122 static acline_mode_t acline_mode_user = ac_none; 123 #ifdef USE_APM 124 static int apm_fd = -1; 125 #endif 126 static int devd_pipe = -1; 127 128 #define DEVD_RETRY_INTERVAL 60 /* seconds */ 129 static struct timeval tried_devd; 130 131 /* 132 * This function returns summary load of all CPUs. It was made so 133 * intentionally to not reduce performance in scenarios when several 134 * threads are processing requests as a pipeline -- running one at 135 * a time on different CPUs and waiting for each other. If nonice 136 * is nonzero, only user+sys+intr time will be counted as load; any 137 * nice time will be treated as if idle. 138 */ 139 static int 140 read_usage_times(int *load, int nonice) 141 { 142 static long *cp_times = NULL, *cp_times_old = NULL; 143 static int ncpus = 0; 144 size_t cp_times_len; 145 int error, cpu, i, total, excl; 146 147 if (cp_times == NULL) { 148 cp_times_len = 0; 149 error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0); 150 if (error) 151 return (error); 152 if ((cp_times = malloc(cp_times_len)) == NULL) 153 return (errno); 154 if ((cp_times_old = malloc(cp_times_len)) == NULL) { 155 free(cp_times); 156 cp_times = NULL; 157 return (errno); 158 } 159 ncpus = cp_times_len / (sizeof(long) * CPUSTATES); 160 } 161 162 cp_times_len = sizeof(long) * CPUSTATES * ncpus; 163 error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0); 164 if (error) 165 return (error); 166 167 if (load) { 168 *load = 0; 169 for (cpu = 0; cpu < ncpus; cpu++) { 170 total = 0; 171 for (i = 0; i < CPUSTATES; i++) { 172 total += cp_times[cpu * CPUSTATES + i] - 173 cp_times_old[cpu * CPUSTATES + i]; 174 } 175 if (total == 0) 176 continue; 177 excl = cp_times[cpu * CPUSTATES + CP_IDLE] - 178 cp_times_old[cpu * CPUSTATES + CP_IDLE]; 179 if (nonice) 180 excl += cp_times[cpu * CPUSTATES + CP_NICE] - 181 cp_times_old[cpu * CPUSTATES + CP_NICE]; 182 *load += 100 - excl * 100 / total; 183 } 184 } 185 186 memcpy(cp_times_old, cp_times, cp_times_len); 187 188 return (0); 189 } 190 191 static int 192 read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq) 193 { 194 char *freqstr, *p, *q; 195 int i, j; 196 size_t len = 0; 197 198 if (sysctl(levels_mib, 4, NULL, &len, NULL, 0)) 199 return (-1); 200 if ((freqstr = malloc(len)) == NULL) 201 return (-1); 202 if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) { 203 free(freqstr); 204 return (-1); 205 } 206 207 *numfreqs = 1; 208 for (p = freqstr; *p != '\0'; p++) 209 if (*p == ' ') 210 (*numfreqs)++; 211 212 if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { 213 free(freqstr); 214 return (-1); 215 } 216 if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) { 217 free(freqstr); 218 free(*freqs); 219 return (-1); 220 } 221 for (i = 0, j = 0, p = freqstr; i < *numfreqs; i++) { 222 q = strchr(p, ' '); 223 if (q != NULL) 224 *q = '\0'; 225 if (sscanf(p, "%d/%d", &(*freqs)[j], &(*power)[i]) != 2) { 226 free(freqstr); 227 free(*freqs); 228 free(*power); 229 return (-1); 230 } 231 if (((*freqs)[j] >= minfreq || minfreq == -1) && 232 ((*freqs)[j] <= maxfreq || maxfreq == -1)) 233 j++; 234 p = q + 1; 235 } 236 237 *numfreqs = j; 238 if ((*freqs = realloc(*freqs, *numfreqs * sizeof(int))) == NULL) { 239 free(freqstr); 240 free(*freqs); 241 free(*power); 242 return (-1); 243 } 244 245 free(freqstr); 246 return (0); 247 } 248 249 static int 250 get_freq(void) 251 { 252 size_t len; 253 int curfreq; 254 255 len = sizeof(curfreq); 256 if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 257 if (vflag) 258 warn("error reading current CPU frequency"); 259 curfreq = 0; 260 } 261 return (curfreq); 262 } 263 264 static int 265 set_freq(int freq) 266 { 267 268 if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) { 269 if (errno != EPERM) 270 return (-1); 271 } 272 273 return (0); 274 } 275 276 static int 277 get_freq_id(int freq, int *freqs, int numfreqs) 278 { 279 int i = 1; 280 281 while (i < numfreqs) { 282 if (freqs[i] < freq) 283 break; 284 i++; 285 } 286 return (i - 1); 287 } 288 289 /* 290 * Try to use ACPI to find the AC line status. If this fails, fall back 291 * to APM. If nothing succeeds, we'll just run in default mode. 292 */ 293 static void 294 acline_init(void) 295 { 296 int skip_source_check; 297 298 acline_mib_len = 4; 299 acline_status = SRC_UNKNOWN; 300 skip_source_check = (acline_mode_user == ac_none || 301 acline_mode_user == ac_acpi_devd); 302 303 if ((skip_source_check || acline_mode_user == ac_sysctl) && 304 sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) { 305 acline_mode = ac_sysctl; 306 if (vflag) 307 warnx("using sysctl for AC line status"); 308 #ifdef __powerpc__ 309 } else if ((skip_source_check || acline_mode_user == ac_sysctl) && 310 sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) { 311 acline_mode = ac_sysctl; 312 if (vflag) 313 warnx("using sysctl for AC line status"); 314 #endif 315 #ifdef USE_APM 316 } else if ((skip_source_check || acline_mode_user == ac_apm) && 317 (apm_fd = open(APMDEV, O_RDONLY)) >= 0) { 318 if (vflag) 319 warnx("using APM for AC line status"); 320 acline_mode = ac_apm; 321 #endif 322 } else { 323 warnx("unable to determine AC line status"); 324 acline_mode = ac_none; 325 } 326 } 327 328 static void 329 acline_read(void) 330 { 331 if (acline_mode == ac_acpi_devd) { 332 char buf[DEVCTL_MAXBUF], *ptr; 333 ssize_t rlen; 334 int notify; 335 336 rlen = read(devd_pipe, buf, sizeof(buf)); 337 if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) { 338 if (vflag) 339 warnx("lost devd connection, switching to sysctl"); 340 devd_close(); 341 acline_mode = ac_sysctl; 342 /* FALLTHROUGH */ 343 } 344 if (rlen > 0 && 345 (ptr = strstr(buf, "system=ACPI")) != NULL && 346 (ptr = strstr(ptr, "subsystem=ACAD")) != NULL && 347 (ptr = strstr(ptr, "notify=")) != NULL && 348 sscanf(ptr, "notify=%x", ¬ify) == 1) 349 acline_status = (notify ? SRC_AC : SRC_BATTERY); 350 } 351 if (acline_mode == ac_sysctl) { 352 int acline; 353 size_t len; 354 355 len = sizeof(acline); 356 if (sysctl(acline_mib, acline_mib_len, &acline, &len, 357 NULL, 0) == 0) 358 acline_status = (acline ? SRC_AC : SRC_BATTERY); 359 else 360 acline_status = SRC_UNKNOWN; 361 } 362 #ifdef USE_APM 363 if (acline_mode == ac_apm) { 364 struct apm_info info; 365 366 if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) { 367 acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY); 368 } else { 369 close(apm_fd); 370 apm_fd = -1; 371 acline_mode = ac_none; 372 acline_status = SRC_UNKNOWN; 373 } 374 } 375 #endif 376 /* try to (re)connect to devd */ 377 #ifdef USE_APM 378 if ((acline_mode == ac_sysctl && 379 (acline_mode_user == ac_none || 380 acline_mode_user == ac_acpi_devd)) || 381 (acline_mode == ac_apm && 382 acline_mode_user == ac_acpi_devd)) { 383 #else 384 if (acline_mode == ac_sysctl && 385 (acline_mode_user == ac_none || 386 acline_mode_user == ac_acpi_devd)) { 387 #endif 388 struct timeval now; 389 390 gettimeofday(&now, NULL); 391 if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) { 392 if (devd_init() >= 0) { 393 if (vflag) 394 warnx("using devd for AC line status"); 395 acline_mode = ac_acpi_devd; 396 } 397 tried_devd = now; 398 } 399 } 400 } 401 402 static int 403 devd_init(void) 404 { 405 struct sockaddr_un devd_addr; 406 407 bzero(&devd_addr, sizeof(devd_addr)); 408 if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0) { 409 if (vflag) 410 warn("%s(): socket()", __func__); 411 return (-1); 412 } 413 414 devd_addr.sun_family = PF_LOCAL; 415 strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); 416 if (connect(devd_pipe, (struct sockaddr *)&devd_addr, 417 sizeof(devd_addr)) == -1) { 418 if (vflag) 419 warn("%s(): connect()", __func__); 420 close(devd_pipe); 421 devd_pipe = -1; 422 return (-1); 423 } 424 425 return (devd_pipe); 426 } 427 428 static void 429 devd_close(void) 430 { 431 432 close(devd_pipe); 433 devd_pipe = -1; 434 } 435 436 static void 437 parse_mode(char *arg, int *mode, int ch) 438 { 439 440 if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0) 441 *mode = MODE_MIN; 442 else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0) 443 *mode = MODE_MAX; 444 else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0) 445 *mode = MODE_ADAPTIVE; 446 else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0) 447 *mode = MODE_HIADAPTIVE; 448 else 449 errx(1, "bad option: -%c %s", (char)ch, optarg); 450 } 451 452 static void 453 parse_acline_mode(char *arg, int ch) 454 { 455 if (strcmp(arg, "sysctl") == 0) 456 acline_mode_user = ac_sysctl; 457 else if (strcmp(arg, "devd") == 0) 458 acline_mode_user = ac_acpi_devd; 459 #ifdef USE_APM 460 else if (strcmp(arg, "apm") == 0) 461 acline_mode_user = ac_apm; 462 #endif 463 else 464 errx(1, "bad option: -%c %s", (char)ch, optarg); 465 } 466 467 static void 468 handle_sigs(int __unused sig) 469 { 470 471 exit_requested = 1; 472 } 473 474 static void 475 usage(void) 476 { 477 478 fprintf(stderr, 479 "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n"); 480 exit(1); 481 } 482 483 int 484 main(int argc, char * argv[]) 485 { 486 struct timeval timeout; 487 fd_set fdset; 488 int nfds; 489 struct pidfh *pfh = NULL; 490 const char *pidfile = NULL; 491 int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load; 492 int minfreq = -1, maxfreq = -1; 493 int ch, mode, mode_ac, mode_battery, mode_none, idle, to; 494 uint64_t mjoules_used; 495 size_t len; 496 int nonice; 497 498 /* Default mode for all AC states is adaptive. */ 499 mode_ac = mode_none = MODE_HIADAPTIVE; 500 mode_battery = MODE_ADAPTIVE; 501 cpu_running_mark = DEFAULT_ACTIVE_PERCENT; 502 cpu_idle_mark = DEFAULT_IDLE_PERCENT; 503 poll_ival = DEFAULT_POLL_INTERVAL; 504 mjoules_used = 0; 505 vflag = 0; 506 nonice = 0; 507 508 /* User must be root to control frequencies. */ 509 if (geteuid() != 0) 510 errx(1, "must be root to run"); 511 512 while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1) 513 switch (ch) { 514 case 'a': 515 parse_mode(optarg, &mode_ac, ch); 516 break; 517 case 'b': 518 parse_mode(optarg, &mode_battery, ch); 519 break; 520 case 's': 521 parse_acline_mode(optarg, ch); 522 break; 523 case 'i': 524 cpu_idle_mark = atoi(optarg); 525 if (cpu_idle_mark < 0 || cpu_idle_mark > 100) { 526 warnx("%d is not a valid percent", 527 cpu_idle_mark); 528 usage(); 529 } 530 break; 531 case 'm': 532 minfreq = atoi(optarg); 533 if (minfreq < 0) { 534 warnx("%d is not a valid CPU frequency", 535 minfreq); 536 usage(); 537 } 538 break; 539 case 'M': 540 maxfreq = atoi(optarg); 541 if (maxfreq < 0) { 542 warnx("%d is not a valid CPU frequency", 543 maxfreq); 544 usage(); 545 } 546 break; 547 case 'N': 548 nonice = 1; 549 break; 550 case 'n': 551 parse_mode(optarg, &mode_none, ch); 552 break; 553 case 'p': 554 poll_ival = atoi(optarg); 555 if (poll_ival < 5) { 556 warnx("poll interval is in units of ms"); 557 usage(); 558 } 559 break; 560 case 'P': 561 pidfile = optarg; 562 break; 563 case 'r': 564 cpu_running_mark = atoi(optarg); 565 if (cpu_running_mark <= 0 || cpu_running_mark > 100) { 566 warnx("%d is not a valid percent", 567 cpu_running_mark); 568 usage(); 569 } 570 break; 571 case 'v': 572 vflag = 1; 573 break; 574 default: 575 usage(); 576 } 577 578 mode = mode_none; 579 580 /* Poll interval is in units of ms. */ 581 poll_ival *= 1000; 582 583 /* Look up various sysctl MIBs. */ 584 len = 2; 585 if (sysctlnametomib("kern.cp_times", cp_times_mib, &len)) 586 err(1, "lookup kern.cp_times"); 587 len = 4; 588 if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len)) 589 err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting"); 590 len = 4; 591 if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) 592 err(1, "lookup freq_levels"); 593 594 /* Check if we can read the load and supported freqs. */ 595 if (read_usage_times(NULL, nonice)) 596 err(1, "read_usage_times"); 597 if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq)) 598 err(1, "error reading supported CPU frequencies"); 599 if (numfreqs == 0) 600 errx(1, "no CPU frequencies in user-specified range"); 601 602 /* Run in the background unless in verbose mode. */ 603 if (!vflag) { 604 pid_t otherpid; 605 606 pfh = pidfile_open(pidfile, 0600, &otherpid); 607 if (pfh == NULL) { 608 if (errno == EEXIST) { 609 errx(1, "powerd already running, pid: %d", 610 otherpid); 611 } 612 warn("cannot open pid file"); 613 } 614 if (daemon(0, 0) != 0) { 615 warn("cannot enter daemon mode, exiting"); 616 pidfile_remove(pfh); 617 exit(EXIT_FAILURE); 618 619 } 620 pidfile_write(pfh); 621 } 622 623 /* Decide whether to use ACPI or APM to read the AC line status. */ 624 acline_init(); 625 626 /* 627 * Exit cleanly on signals. 628 */ 629 signal(SIGINT, handle_sigs); 630 signal(SIGTERM, handle_sigs); 631 632 freq = initfreq = curfreq = get_freq(); 633 i = get_freq_id(curfreq, freqs, numfreqs); 634 if (freq < 1) 635 freq = 1; 636 637 /* 638 * If we are in adaptive mode and the current frequency is outside the 639 * user-defined range, adjust it to be within the user-defined range. 640 */ 641 acline_read(); 642 if (acline_status > SRC_UNKNOWN) 643 errx(1, "invalid AC line status %d", acline_status); 644 if ((acline_status == SRC_AC && 645 (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) || 646 (acline_status == SRC_BATTERY && 647 (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) || 648 (acline_status == SRC_UNKNOWN && 649 (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) { 650 /* Read the current frequency. */ 651 len = sizeof(curfreq); 652 if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 653 if (vflag) 654 warn("error reading current CPU frequency"); 655 } 656 if (curfreq < freqs[numfreqs - 1]) { 657 if (vflag) { 658 printf("CPU frequency is below user-defined " 659 "minimum; changing frequency to %d " 660 "MHz\n", freqs[numfreqs - 1]); 661 } 662 if (set_freq(freqs[numfreqs - 1]) != 0) { 663 warn("error setting CPU freq %d", 664 freqs[numfreqs - 1]); 665 } 666 } else if (curfreq > freqs[0]) { 667 if (vflag) { 668 printf("CPU frequency is above user-defined " 669 "maximum; changing frequency to %d " 670 "MHz\n", freqs[0]); 671 } 672 if (set_freq(freqs[0]) != 0) { 673 warn("error setting CPU freq %d", 674 freqs[0]); 675 } 676 } 677 } 678 679 idle = 0; 680 /* Main loop. */ 681 for (;;) { 682 FD_ZERO(&fdset); 683 if (devd_pipe >= 0) { 684 FD_SET(devd_pipe, &fdset); 685 nfds = devd_pipe + 1; 686 } else { 687 nfds = 0; 688 } 689 if (mode == MODE_HIADAPTIVE || idle < 120) 690 to = poll_ival; 691 else if (idle < 360) 692 to = poll_ival * 2; 693 else 694 to = poll_ival * 4; 695 timeout.tv_sec = to / 1000000; 696 timeout.tv_usec = to % 1000000; 697 select(nfds, &fdset, NULL, &fdset, &timeout); 698 699 /* If the user requested we quit, print some statistics. */ 700 if (exit_requested) { 701 if (vflag && mjoules_used != 0) 702 printf("total joules used: %u.%03u\n", 703 (u_int)(mjoules_used / 1000), 704 (int)mjoules_used % 1000); 705 break; 706 } 707 708 /* Read the current AC status and record the mode. */ 709 acline_read(); 710 switch (acline_status) { 711 case SRC_AC: 712 mode = mode_ac; 713 break; 714 case SRC_BATTERY: 715 mode = mode_battery; 716 break; 717 case SRC_UNKNOWN: 718 mode = mode_none; 719 break; 720 default: 721 errx(1, "invalid AC line status %d", acline_status); 722 } 723 724 /* Read the current frequency. */ 725 if (idle % 32 == 0) { 726 if ((curfreq = get_freq()) == 0) 727 continue; 728 i = get_freq_id(curfreq, freqs, numfreqs); 729 } 730 idle++; 731 if (vflag) { 732 /* Keep a sum of all power actually used. */ 733 if (mwatts[i] != -1) 734 mjoules_used += 735 (mwatts[i] * (poll_ival / 1000)) / 1000; 736 } 737 738 /* Always switch to the lowest frequency in min mode. */ 739 if (mode == MODE_MIN) { 740 freq = freqs[numfreqs - 1]; 741 if (curfreq != freq) { 742 if (vflag) { 743 printf("now operating on %s power; " 744 "changing frequency to %d MHz\n", 745 modes[acline_status], freq); 746 } 747 idle = 0; 748 if (set_freq(freq) != 0) { 749 warn("error setting CPU freq %d", 750 freq); 751 continue; 752 } 753 } 754 continue; 755 } 756 757 /* Always switch to the highest frequency in max mode. */ 758 if (mode == MODE_MAX) { 759 freq = freqs[0]; 760 if (curfreq != freq) { 761 if (vflag) { 762 printf("now operating on %s power; " 763 "changing frequency to %d MHz\n", 764 modes[acline_status], freq); 765 } 766 idle = 0; 767 if (set_freq(freq) != 0) { 768 warn("error setting CPU freq %d", 769 freq); 770 continue; 771 } 772 } 773 continue; 774 } 775 776 /* Adaptive mode; get the current CPU usage times. */ 777 if (read_usage_times(&load, nonice)) { 778 if (vflag) 779 warn("read_usage_times() failed"); 780 continue; 781 } 782 783 if (mode == MODE_ADAPTIVE) { 784 if (load > cpu_running_mark) { 785 if (load > 95 || load > cpu_running_mark * 2) 786 freq *= 2; 787 else 788 freq = freq * load / cpu_running_mark; 789 if (freq > freqs[0]) 790 freq = freqs[0]; 791 } else if (load < cpu_idle_mark && 792 curfreq * load < freqs[get_freq_id( 793 freq * 7 / 8, freqs, numfreqs)] * 794 cpu_running_mark) { 795 freq = freq * 7 / 8; 796 if (freq < freqs[numfreqs - 1]) 797 freq = freqs[numfreqs - 1]; 798 } 799 } else { /* MODE_HIADAPTIVE */ 800 if (load > cpu_running_mark / 2) { 801 if (load > 95 || load > cpu_running_mark) 802 freq *= 4; 803 else 804 freq = freq * load * 2 / cpu_running_mark; 805 if (freq > freqs[0] * 2) 806 freq = freqs[0] * 2; 807 } else if (load < cpu_idle_mark / 2 && 808 curfreq * load < freqs[get_freq_id( 809 freq * 31 / 32, freqs, numfreqs)] * 810 cpu_running_mark / 2) { 811 freq = freq * 31 / 32; 812 if (freq < freqs[numfreqs - 1]) 813 freq = freqs[numfreqs - 1]; 814 } 815 } 816 if (vflag) { 817 printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n", 818 load, curfreq, i, freq); 819 } 820 j = get_freq_id(freq, freqs, numfreqs); 821 if (i != j) { 822 if (vflag) { 823 printf("changing clock" 824 " speed from %d MHz to %d MHz\n", 825 freqs[i], freqs[j]); 826 } 827 idle = 0; 828 if (set_freq(freqs[j])) 829 warn("error setting CPU frequency %d", 830 freqs[j]); 831 } 832 } 833 if (set_freq(initfreq)) 834 warn("error setting CPU frequency %d", initfreq); 835 free(freqs); 836 free(mwatts); 837 devd_close(); 838 if (!vflag) 839 pidfile_remove(pfh); 840 841 exit(0); 842 } 843