powerd.c revision 151628
1192595Sdes/*- 260573Skris * Copyright (c) 2004 Colin Percival 360573Skris * Copyright (c) 2005 Nate Lawson 460573Skris * All rights reserved. 560573Skris * 660573Skris * Redistribution and use in source and binary forms, with or without 760573Skris * modification, are permitted providing that the following conditions 860573Skris * are met: 960573Skris * 1. Redistributions of source code must retain the above copyright 1060573Skris * notice, this list of conditions and the following disclaimer. 1160573Skris * 2. Redistributions in binary form must reproduce the above copyright 1260573Skris * notice, this list of conditions and the following disclaimer in the 1360573Skris * documentation and/or other materials provided with the distribution. 1460573Skris * 1560573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 1660573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1760573Skris * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1860573Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 1960573Skris * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2060573Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2160573Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2260573Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 2360573Skris * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 2460573Skris * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2565674Skris * POSSIBILITY OF SUCH DAMAGE. 2660573Skris */ 27162856Sdes 2860573Skris#include <sys/cdefs.h> 29162856Sdes__FBSDID("$FreeBSD: head/usr.sbin/powerd/powerd.c 151628 2005-10-24 18:34:54Z njl $"); 30181111Sdes 31181111Sdes#include <sys/types.h> 32162856Sdes#include <sys/param.h> 33181111Sdes#include <sys/ioctl.h> 34162856Sdes#include <sys/sysctl.h> 35162856Sdes#include <sys/resource.h> 36162856Sdes#include <sys/socket.h> 37181111Sdes#include <sys/un.h> 38162856Sdes 39162856Sdes#include <err.h> 40181111Sdes#include <errno.h> 4176262Sgreen#include <fcntl.h> 4260573Skris#include <libutil.h> 4376262Sgreen#include <pthread.h> 44162856Sdes#include <signal.h> 4560573Skris#include <stdio.h> 4660573Skris#include <stdlib.h> 47162856Sdes#include <string.h> 48162856Sdes#include <unistd.h> 4960573Skris 5060573Skris#ifdef __i386__ 5176262Sgreen#include <machine/apm_bios.h> 52147005Sdes#endif 53162856Sdes 5460573Skris#define DEFAULT_ACTIVE_PERCENT 65 55124211Sdes#define DEFAULT_IDLE_PERCENT 90 56124211Sdes#define DEFAULT_POLL_INTERVAL 500 /* Poll interval in milliseconds */ 57124211Sdes 58162856Sdesenum modes_t { 59124211Sdes MODE_MIN, 6060573Skris MODE_ADAPTIVE, 6160573Skris MODE_MAX, 6276262Sgreen}; 63124211Sdes 64147005Sdesenum power_src_t { 6560573Skris SRC_AC, 6698684Sdes SRC_BATTERY, 6798684Sdes SRC_UNKNOWN, 6898684Sdes}; 6998684Sdes 7098684Sdesconst char *modes[] = { 7198684Sdes "AC", 7298684Sdes "battery", 73124211Sdes "unknown" 74124211Sdes}; 75124211Sdes 76192595Sdes#define ACPIAC "hw.acpi.acline" 77192595Sdes#define APMDEV "/dev/apm" 78192595Sdes#define DEVDPIPE "/var/run/devd.pipe" 7998684Sdes#define DEVCTL_MAXBUF 1024 8098684Sdes 8198684Sdesstatic int read_usage_times(long *idle, long *total); 8298684Sdesstatic int read_freqs(int *numfreqs, int **freqs, int **power); 83124211Sdesstatic int set_freq(int freq); 84124211Sdesstatic void acline_init(void); 85124211Sdesstatic int acline_read(void); 86192595Sdesstatic int devd_init(void); 87192595Sdesstatic void devd_close(void); 88192595Sdesstatic void *devd_read(void *arg); 8998684Sdesstatic void handle_sigs(int sig); 9098684Sdesstatic void parse_mode(char *arg, int *mode, int ch); 9198684Sdesstatic void usage(void); 9298684Sdes 9369591Sgreen/* Sysctl data structures. */ 9469591Sgreenstatic int cp_time_mib[2]; 9560573Skrisstatic int freq_mib[4]; 9660573Skrisstatic int levels_mib[4]; 9792559Sdesstatic int acline_mib[3]; 9892559Sdes 9960573Skris/* devd-cached value provided by our thread. */ 10060573Skrisstatic int devd_acline; 10192559Sdes 10292559Sdes/* Configuration */ 10360573Skrisstatic int cpu_running_mark; 104181111Sdesstatic int cpu_idle_mark; 105181111Sdesstatic int poll_ival; 106181111Sdesstatic int vflag; 107181111Sdes 108181111Sdesstatic int apm_fd; 109181111Sdesstatic int devd_pipe; 110181111Sdesstatic pthread_t devd_thread; 111181111Sdesstatic int exit_requested; 112181111Sdes 113181111Sdesstatic int 114181111Sdesread_usage_times(long *idle, long *total) 115181111Sdes{ 116181111Sdes static long idle_old, total_old; 117181111Sdes long cp_time[CPUSTATES], i, total_new; 118181111Sdes size_t cp_time_len; 119181111Sdes int error; 120181111Sdes 121181111Sdes cp_time_len = sizeof(cp_time); 122181111Sdes error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0); 123181111Sdes if (error) 124181111Sdes return (error); 125181111Sdes for (total_new = 0, i = 0; i < CPUSTATES; i++) 126181111Sdes total_new += cp_time[i]; 127181111Sdes 128181111Sdes if (idle) 129181111Sdes *idle = cp_time[CP_IDLE] - idle_old; 130181111Sdes if (total) 131181111Sdes *total = total_new - total_old; 132181111Sdes 133181111Sdes idle_old = cp_time[CP_IDLE]; 134181111Sdes total_old = total_new; 135181111Sdes 136181111Sdes return (0); 137181111Sdes} 138181111Sdes 139181111Sdesstatic int 140181111Sdesread_freqs(int *numfreqs, int **freqs, int **power) 141181111Sdes{ 142181111Sdes char *freqstr, *p, *q; 143181111Sdes int i; 144181111Sdes size_t len = 0; 145181111Sdes 146181111Sdes if (sysctl(levels_mib, 4, NULL, &len, NULL, 0)) 147181111Sdes return (-1); 148181111Sdes if ((freqstr = malloc(len)) == NULL) 149181111Sdes return (-1); 150181111Sdes if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) 151181111Sdes return (-1); 152181111Sdes 153181111Sdes *numfreqs = 1; 154181111Sdes for (p = freqstr; *p != '\0'; p++) 155181111Sdes if (*p == ' ') 156181111Sdes (*numfreqs)++; 157181111Sdes 158181111Sdes if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { 159181111Sdes free(freqstr); 160181111Sdes return (-1); 161181111Sdes } 162181111Sdes if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) { 163181111Sdes free(freqstr); 164181111Sdes free(*freqs); 165181111Sdes return (-1); 166181111Sdes } 167181111Sdes for (i = 0, p = freqstr; i < *numfreqs; i++) { 168181111Sdes q = strchr(p, ' '); 16960573Skris if (q != NULL) 17069591Sgreen *q = '\0'; 17160573Skris if (sscanf(p, "%d/%d", &(*freqs)[i], &(*power)[i]) != 2) { 172126277Sdes free(freqstr); 173126277Sdes free(*freqs); 17460573Skris free(*power); 17592559Sdes return (-1); 17660573Skris } 17769591Sgreen p = q + 1; 17860573Skris } 17960573Skris 180162856Sdes free(freqstr); 18192559Sdes return (0); 18292559Sdes} 18360573Skris 18469591Sgreenstatic int 18576262Sgreenset_freq(int freq) 186106130Sdes{ 18760573Skris 18892559Sdes if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) { 18960573Skris if (errno != EPERM) 19069591Sgreen return (-1); 19169591Sgreen } 19269591Sgreen 19360573Skris return (0); 19469591Sgreen} 195106130Sdes 19660573Skris/* 19760573Skris * Try to use ACPI to find the AC line status. If this fails, fall back 19860573Skris * to APM. If nothing succeeds, we'll just run in default mode. If we are 19960573Skris * using ACPI, try opening a pipe to devd to detect AC line events. 20060573Skris */ 20160573Skrisstatic void 202106130Sdesacline_init() 20360573Skris{ 20460573Skris int acline; 20560573Skris size_t len; 20660573Skris 20760573Skris apm_fd = -1; 20860573Skris devd_pipe = -1; 20960573Skris len = sizeof(acline); 21060573Skris if (sysctlbyname(ACPIAC, &acline, &len, NULL, 0) == 0) { 21160573Skris len = 3; 21260573Skris if (sysctlnametomib(ACPIAC, acline_mib, &len)) 21360573Skris err(1, "lookup acline"); 214162856Sdes 21592559Sdes /* Read line status once so that we have an initial value. */ 21692559Sdes devd_acline = acline_read(); 21760573Skris 21869591Sgreen /* 21969591Sgreen * Try connecting to the devd pipe and start a read thread 22076262Sgreen * if we succeed. 22160573Skris */ 22299053Sdes if ((devd_pipe = devd_init()) >= 0) { 22399053Sdes if (pthread_create(&devd_thread, NULL, devd_read, 22499053Sdes &devd_pipe)) 22560573Skris err(1, "pthread_create devd thread"); 226124211Sdes } else if (vflag) { 22799053Sdes warnx( 22899053Sdes "unable to connect to devd pipe, using polling mode instead"); 22999053Sdes } 23069591Sgreen } else { 23169591Sgreen apm_fd = open(APMDEV, O_RDONLY); 23260573Skris if (apm_fd == -1) 23369591Sgreen warnx( 23469591Sgreen "cannot read AC line status, using default settings"); 23569591Sgreen } 23660573Skris} 23776262Sgreen 23860573Skrisstatic int 23976262Sgreenacline_read() 24076262Sgreen{ 24176262Sgreen int acline; 24276262Sgreen size_t len; 24369591Sgreen#ifdef __i386__ 24498684Sdes struct apm_info info; 245128460Sdes#endif 24698684Sdes 24769591Sgreen acline = SRC_UNKNOWN; 24869591Sgreen len = sizeof(acline); 24969591Sgreen 250137019Sdes /* 251124211Sdes * Get state from our devd thread, the ACPI sysctl, or APM. We 252147005Sdes * prefer sources in this order. 253147005Sdes */ 254147005Sdes if (devd_pipe >= 0) 25560573Skris acline = devd_acline; 256157019Sdes else if (sysctl(acline_mib, 3, &acline, &len, NULL, 0) == 0) 257157019Sdes acline = acline ? SRC_AC : SRC_BATTERY; 258157019Sdes#ifdef __i386__ 259157019Sdes else if (apm_fd != -1 && ioctl(apm_fd, APMIO_GETINFO, &info) == 0) 260137019Sdes acline = info.ai_acline ? SRC_AC : SRC_BATTERY; 26198684Sdes#endif 26269591Sgreen 26392559Sdes return (acline); 26498684Sdes} 26598684Sdes 266181111Sdesstatic int 26792559Sdesdevd_init(void) 26892559Sdes{ 26992559Sdes struct sockaddr_un devd_addr; 27092559Sdes int devd_sock; 27192559Sdes 27260573Skris bzero(&devd_addr, sizeof(devd_addr)); 27399053Sdes if ((devd_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { 27499053Sdes if (vflag) 27599053Sdes warn("failed to create devd socket"); 27699053Sdes return (-1); 27799053Sdes } 27899053Sdes 27999053Sdes devd_addr.sun_family = PF_LOCAL; 280124211Sdes strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); 28199053Sdes if (connect(devd_sock, (struct sockaddr *)&devd_addr, 28299053Sdes sizeof(devd_addr)) == -1) { 28399053Sdes close(devd_sock); 28499053Sdes return (-1); 285124211Sdes } 28699053Sdes 28799053Sdes return (devd_sock); 28899053Sdes} 28999053Sdes 29099053Sdesstatic void 29199053Sdesdevd_close(void) 29299053Sdes{ 29399053Sdes 29476262Sgreen if (devd_pipe < 0) 29592559Sdes return; 296192595Sdes 297192595Sdes pthread_kill(devd_thread, SIGTERM); 298192595Sdes close(devd_pipe); 299124211Sdes} 300124211Sdes 301192595Sdes/* 302124211Sdes * This loop runs as a separate thread. It reads events from devd, but 303124211Sdes * spends most of its time blocked in select(2). 304124211Sdes */ 305124211Sdesstatic void * 30676262Sgreendevd_read(void *arg) 30768704Sgreen{ 30876262Sgreen char buf[DEVCTL_MAXBUF], *ptr; 30969591Sgreen fd_set fdset; 310181111Sdes int fd, notify, rlen; 31169591Sgreen 31269591Sgreen fd = *(int *)arg; 31369591Sgreen notify = -1; 31476262Sgreen FD_ZERO(&fdset); 31569591Sgreen while (!exit_requested) { 31669591Sgreen FD_SET(fd, &fdset); 31769591Sgreen if (select(fd + 1, &fdset, NULL, NULL, NULL) < 0) 31869591Sgreen break; 31969591Sgreen if (!FD_ISSET(fd, &fdset)) 32069591Sgreen continue; 32169591Sgreen 32276262Sgreen /* Read the notify string, devd NULL-terminates it. */ 32369591Sgreen rlen = read(fd, buf, sizeof(buf)); 32492559Sdes if (rlen <= 0) { 32592559Sdes close(devd_pipe); 32676262Sgreen devd_pipe = -1; 32776262Sgreen if (vflag) 32876262Sgreen warnx( 32969591Sgreen "devd disappeared, downgrading to polling mode"); 33076262Sgreen 331113911Sdes /* 332147005Sdes * Keep trying to reconnect to devd but sleep in 33376262Sgreen * between to avoid wasting CPU cycles. 334147005Sdes */ 335147005Sdes while (!exit_requested && (fd = devd_init()) < 0) 336147005Sdes sleep(300); 337147005Sdes 33860573Skris if (fd >= 0) { 33998941Sdes devd_pipe = fd; 340147005Sdes if (vflag) 341147005Sdes warnx( 342147005Sdes "devd came back, upgrading to event mode"); 343147005Sdes } 344147005Sdes continue; 345147005Sdes } 346147005Sdes 347147005Sdes /* Loosely match the notify string. */ 348147005Sdes if ((ptr = strstr(buf, "system=ACPI")) != NULL && 349149753Sdes (ptr = strstr(ptr, "subsystem=ACAD")) != NULL && 350147005Sdes (ptr = strstr(ptr, "notify=")) != NULL) { 351147005Sdes if (sscanf(ptr, "notify=%x", ¬ify) != 1) { 352124211Sdes warnx("bad devd notify string"); 35398941Sdes continue; 354106130Sdes } 355106130Sdes devd_acline = notify ? SRC_AC : SRC_BATTERY; 356106130Sdes } 357106130Sdes } 358106130Sdes 359106130Sdes return (NULL); 360106130Sdes} 36176262Sgreen 36276262Sgreenstatic void 36369591Sgreenparse_mode(char *arg, int *mode, int ch) 36492559Sdes{ 36592559Sdes 36692559Sdes if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0) 36792559Sdes *mode = MODE_MIN; 36892559Sdes else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0) 36992559Sdes *mode = MODE_MAX; 37092559Sdes else if (strcmp(arg, "adaptive") == 0) 37192559Sdes *mode = MODE_ADAPTIVE; 37292559Sdes else 37392559Sdes errx(1, "bad option: -%c %s", (char)ch, optarg); 37492559Sdes} 37592559Sdes 37692559Sdesstatic void 377181111Sdeshandle_sigs(int __unused sig) 378181111Sdes{ 379181111Sdes 380181111Sdes exit_requested = 1; 381181111Sdes} 382147005Sdes 383147005Sdesstatic void 384147005Sdesusage(void) 38592559Sdes{ 386147005Sdes 38792559Sdes fprintf(stderr, 38892559Sdes"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%] [-P pidfile]\n"); 38992559Sdes exit(1); 39092559Sdes} 39192559Sdes 39292559Sdesint 39392559Sdesmain(int argc, char * argv[]) 39492559Sdes{ 39576262Sgreen struct pidfh *pfh = NULL; 39669591Sgreen const char *pidfile = NULL; 39792559Sdes long idle, total; 39869591Sgreen int acline, curfreq, *freqs, i, *mwatts, numfreqs; 39960573Skris int ch, mode, mode_ac, mode_battery, mode_none; 40092559Sdes uint64_t mjoules_used; 40169591Sgreen size_t len; 40298684Sdes 40360573Skris /* Default mode for all AC states is adaptive. */ 40492559Sdes mode_ac = mode_battery = mode_none = MODE_ADAPTIVE; 40598684Sdes cpu_running_mark = DEFAULT_ACTIVE_PERCENT; 40698684Sdes cpu_idle_mark = DEFAULT_IDLE_PERCENT; 40769591Sgreen poll_ival = DEFAULT_POLL_INTERVAL; 40898684Sdes mjoules_used = 0; 40998684Sdes vflag = 0; 41092559Sdes apm_fd = -1; 41192559Sdes 41298684Sdes /* User must be root to control frequencies. */ 41398684Sdes if (geteuid() != 0) 41460573Skris errx(1, "must be root to run"); 41569591Sgreen 41692559Sdes while ((ch = getopt(argc, argv, "a:b:i:n:p:P:r:v")) != EOF) 41792559Sdes switch (ch) { 41892559Sdes case 'a': 41969591Sgreen parse_mode(optarg, &mode_ac, ch); 42060573Skris break; 42160573Skris case 'b': 42292559Sdes parse_mode(optarg, &mode_battery, ch); 42369591Sgreen break; 42469591Sgreen case 'i': 42598684Sdes cpu_idle_mark = atoi(optarg); 42698684Sdes if (cpu_idle_mark < 0 || cpu_idle_mark > 100) { 42769591Sgreen warnx("%d is not a valid percent", 42898684Sdes cpu_idle_mark); 42998684Sdes usage(); 43098684Sdes } 43198684Sdes break; 43298684Sdes case 'n': 43398684Sdes parse_mode(optarg, &mode_none, ch); 43498684Sdes break; 43569591Sgreen case 'p': 43669591Sgreen poll_ival = atoi(optarg); 437181111Sdes 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