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