1142577Snjl/*- 2142577Snjl * Copyright (c) 2004 Colin Percival 3142577Snjl * Copyright (c) 2005 Nate Lawson 4142577Snjl * All rights reserved. 5142577Snjl * 6142577Snjl * Redistribution and use in source and binary forms, with or without 7142577Snjl * modification, are permitted providing that the following conditions 8142577Snjl * are met: 9142577Snjl * 1. Redistributions of source code must retain the above copyright 10142577Snjl * notice, this list of conditions and the following disclaimer. 11142577Snjl * 2. Redistributions in binary form must reproduce the above copyright 12142577Snjl * notice, this list of conditions and the following disclaimer in the 13142577Snjl * documentation and/or other materials provided with the distribution. 14142577Snjl * 15151461Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 16151461Snjl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17142577Snjl * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18151461Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19142577Snjl * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20142577Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21142577Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22142577Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23151461Snjl * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24151461Snjl * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25142577Snjl * POSSIBILITY OF SUCH DAMAGE. 26142577Snjl */ 27142577Snjl 28142577Snjl#include <sys/cdefs.h> 29142577Snjl__FBSDID("$FreeBSD: stable/10/usr.sbin/powerd/powerd.c 342983 2019-01-13 02:27:10Z avos $"); 30142577Snjl 31149428Spjd#include <sys/param.h> 32149428Spjd#include <sys/ioctl.h> 33149428Spjd#include <sys/sysctl.h> 34149428Spjd#include <sys/resource.h> 35151461Snjl#include <sys/socket.h> 36153999Sdes#include <sys/time.h> 37151461Snjl#include <sys/un.h> 38149428Spjd 39142577Snjl#include <err.h> 40148139Sume#include <errno.h> 41142577Snjl#include <fcntl.h> 42149428Spjd#include <libutil.h> 43144873Snjl#include <signal.h> 44142577Snjl#include <stdio.h> 45142577Snjl#include <stdlib.h> 46142577Snjl#include <string.h> 47234070Srmh#include <sysexits.h> 48142577Snjl#include <unistd.h> 49142577Snjl 50203482Simp#ifdef __i386__ 51203482Simp#define USE_APM 52203482Simp#endif 53203482Simp 54153999Sdes#ifdef USE_APM 55142577Snjl#include <machine/apm_bios.h> 56142602Smarcel#endif 57142577Snjl 58185050Smav#define DEFAULT_ACTIVE_PERCENT 75 59185050Smav#define DEFAULT_IDLE_PERCENT 50 60185050Smav#define DEFAULT_POLL_INTERVAL 250 /* Poll interval in milliseconds */ 61142577Snjl 62153999Sdestypedef enum { 63142577Snjl MODE_MIN, 64142577Snjl MODE_ADAPTIVE, 65185050Smav MODE_HIADAPTIVE, 66142577Snjl MODE_MAX, 67153999Sdes} modes_t; 68142577Snjl 69153999Sdestypedef enum { 70142577Snjl SRC_AC, 71142577Snjl SRC_BATTERY, 72142577Snjl SRC_UNKNOWN, 73153999Sdes} power_src_t; 74142577Snjl 75228416Sedstatic const char *modes[] = { 76142577Snjl "AC", 77142577Snjl "battery", 78142577Snjl "unknown" 79142577Snjl}; 80142577Snjl 81142577Snjl#define ACPIAC "hw.acpi.acline" 82193161Snwhitehorn#define PMUAC "dev.pmu.0.acline" 83142577Snjl#define APMDEV "/dev/apm" 84151461Snjl#define DEVDPIPE "/var/run/devd.pipe" 85151461Snjl#define DEVCTL_MAXBUF 1024 86142577Snjl 87185050Smavstatic int read_usage_times(int *load); 88211415Sbrucecstatic int read_freqs(int *numfreqs, int **freqs, int **power, 89211415Sbrucec int minfreq, int maxfreq); 90142577Snjlstatic int set_freq(int freq); 91142605Snjlstatic void acline_init(void); 92153999Sdesstatic void acline_read(void); 93151461Snjlstatic int devd_init(void); 94151461Snjlstatic void devd_close(void); 95144873Snjlstatic void handle_sigs(int sig); 96142577Snjlstatic void parse_mode(char *arg, int *mode, int ch); 97142577Snjlstatic void usage(void); 98142577Snjl 99142577Snjl/* Sysctl data structures. */ 100185050Smavstatic int cp_times_mib[2]; 101142577Snjlstatic int freq_mib[4]; 102142577Snjlstatic int levels_mib[4]; 103193161Snwhitehornstatic int acline_mib[4]; 104193161Snwhitehornstatic size_t acline_mib_len; 105142577Snjl 106142577Snjl/* Configuration */ 107142577Snjlstatic int cpu_running_mark; 108142577Snjlstatic int cpu_idle_mark; 109142577Snjlstatic int poll_ival; 110151461Snjlstatic int vflag; 111142577Snjl 112153999Sdesstatic volatile sig_atomic_t exit_requested; 113153999Sdesstatic power_src_t acline_status; 114342983Savostypedef enum { 115153999Sdes ac_none, 116193161Snwhitehorn ac_sysctl, 117153999Sdes ac_acpi_devd, 118153999Sdes#ifdef USE_APM 119153999Sdes ac_apm, 120153999Sdes#endif 121342983Savos} acline_mode_t; 122342983Savosstatic acline_mode_t acline_mode; 123342983Savosstatic acline_mode_t acline_mode_user = ac_none; 124153999Sdes#ifdef USE_APM 125153999Sdesstatic int apm_fd = -1; 126153999Sdes#endif 127153999Sdesstatic int devd_pipe = -1; 128142605Snjl 129153999Sdes#define DEVD_RETRY_INTERVAL 60 /* seconds */ 130153999Sdesstatic struct timeval tried_devd; 131153999Sdes 132280751Smav/* 133280751Smav * This function returns summary load of all CPUs. It was made so 134280751Smav * intentionally to not reduce performance in scenarios when several 135280751Smav * threads are processing requests as a pipeline -- running one at 136280751Smav * a time on different CPUs and waiting for each other. 137280751Smav */ 138142577Snjlstatic int 139185050Smavread_usage_times(int *load) 140142577Snjl{ 141185050Smav static long *cp_times = NULL, *cp_times_old = NULL; 142185050Smav static int ncpus = 0; 143185050Smav size_t cp_times_len; 144185050Smav int error, cpu, i, total; 145142577Snjl 146185050Smav if (cp_times == NULL) { 147185050Smav cp_times_len = 0; 148185050Smav error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0); 149185050Smav if (error) 150185050Smav return (error); 151185050Smav if ((cp_times = malloc(cp_times_len)) == NULL) 152185050Smav return (errno); 153185050Smav if ((cp_times_old = malloc(cp_times_len)) == NULL) { 154185050Smav free(cp_times); 155185050Smav cp_times = NULL; 156185050Smav return (errno); 157185050Smav } 158185050Smav ncpus = cp_times_len / (sizeof(long) * CPUSTATES); 159185050Smav } 160185050Smav 161185050Smav cp_times_len = sizeof(long) * CPUSTATES * ncpus; 162185050Smav error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0); 163142577Snjl if (error) 164142577Snjl return (error); 165252713Swblock 166185050Smav if (load) { 167185050Smav *load = 0; 168185050Smav for (cpu = 0; cpu < ncpus; cpu++) { 169185050Smav total = 0; 170185050Smav for (i = 0; i < CPUSTATES; i++) { 171185050Smav total += cp_times[cpu * CPUSTATES + i] - 172185050Smav cp_times_old[cpu * CPUSTATES + i]; 173185050Smav } 174185050Smav if (total == 0) 175185050Smav continue; 176252713Swblock *load += 100 - (cp_times[cpu * CPUSTATES + CP_IDLE] - 177185050Smav cp_times_old[cpu * CPUSTATES + CP_IDLE]) * 100 / total; 178185050Smav } 179185050Smav } 180142577Snjl 181185050Smav memcpy(cp_times_old, cp_times, cp_times_len); 182142577Snjl 183142577Snjl return (0); 184142577Snjl} 185142577Snjl 186142577Snjlstatic int 187211415Sbrucecread_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq) 188142577Snjl{ 189142577Snjl char *freqstr, *p, *q; 190211415Sbrucec int i, j; 191142577Snjl size_t len = 0; 192142577Snjl 193142577Snjl if (sysctl(levels_mib, 4, NULL, &len, NULL, 0)) 194142577Snjl return (-1); 195142577Snjl if ((freqstr = malloc(len)) == NULL) 196142577Snjl return (-1); 197142577Snjl if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) 198142577Snjl return (-1); 199142577Snjl 200142577Snjl *numfreqs = 1; 201142577Snjl for (p = freqstr; *p != '\0'; p++) 202142577Snjl if (*p == ' ') 203142577Snjl (*numfreqs)++; 204142577Snjl 205142577Snjl if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { 206142577Snjl free(freqstr); 207142577Snjl return (-1); 208142577Snjl } 209144873Snjl if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) { 210144873Snjl free(freqstr); 211144873Snjl free(*freqs); 212144873Snjl return (-1); 213144873Snjl } 214211415Sbrucec for (i = 0, j = 0, p = freqstr; i < *numfreqs; i++) { 215142577Snjl q = strchr(p, ' '); 216142577Snjl if (q != NULL) 217142577Snjl *q = '\0'; 218211415Sbrucec if (sscanf(p, "%d/%d", &(*freqs)[j], &(*power)[i]) != 2) { 219142577Snjl free(freqstr); 220142577Snjl free(*freqs); 221144873Snjl free(*power); 222142577Snjl return (-1); 223142577Snjl } 224211415Sbrucec if (((*freqs)[j] >= minfreq || minfreq == -1) && 225211415Sbrucec ((*freqs)[j] <= maxfreq || maxfreq == -1)) 226211415Sbrucec j++; 227142577Snjl p = q + 1; 228142577Snjl } 229142577Snjl 230211415Sbrucec *numfreqs = j; 231211415Sbrucec if ((*freqs = realloc(*freqs, *numfreqs * sizeof(int))) == NULL) { 232211415Sbrucec free(freqstr); 233211415Sbrucec free(*freqs); 234211415Sbrucec free(*power); 235211415Sbrucec return (-1); 236211415Sbrucec } 237211415Sbrucec 238142577Snjl free(freqstr); 239142577Snjl return (0); 240142577Snjl} 241142577Snjl 242142577Snjlstatic int 243185050Smavget_freq(void) 244185050Smav{ 245185050Smav size_t len; 246185050Smav int curfreq; 247252713Swblock 248185050Smav len = sizeof(curfreq); 249185050Smav if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 250185050Smav if (vflag) 251185050Smav warn("error reading current CPU frequency"); 252185050Smav curfreq = 0; 253185050Smav } 254185050Smav return (curfreq); 255185050Smav} 256185050Smav 257185050Smavstatic int 258142577Snjlset_freq(int freq) 259142577Snjl{ 260142577Snjl 261148139Sume if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) { 262148139Sume if (errno != EPERM) 263148139Sume return (-1); 264148139Sume } 265142577Snjl 266142577Snjl return (0); 267142577Snjl} 268142577Snjl 269185050Smavstatic int 270185050Smavget_freq_id(int freq, int *freqs, int numfreqs) 271185050Smav{ 272185050Smav int i = 1; 273252713Swblock 274185050Smav while (i < numfreqs) { 275185050Smav if (freqs[i] < freq) 276185050Smav break; 277185050Smav i++; 278185050Smav } 279185050Smav return (i - 1); 280185050Smav} 281185050Smav 282142605Snjl/* 283142605Snjl * Try to use ACPI to find the AC line status. If this fails, fall back 284153999Sdes * to APM. If nothing succeeds, we'll just run in default mode. 285142605Snjl */ 286142577Snjlstatic void 287201227Sedacline_init(void) 288142605Snjl{ 289342983Savos int skip_source_check; 290342983Savos 291193161Snwhitehorn acline_mib_len = 4; 292262474Sbrueffer acline_status = SRC_UNKNOWN; 293342983Savos skip_source_check = (acline_mode_user == ac_none || 294342983Savos acline_mode_user == ac_acpi_devd); 295142605Snjl 296342983Savos if ((skip_source_check || acline_mode_user == ac_sysctl) && 297342983Savos sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) { 298193161Snwhitehorn acline_mode = ac_sysctl; 299153999Sdes if (vflag) 300153999Sdes warnx("using sysctl for AC line status"); 301193161Snwhitehorn#if __powerpc__ 302342983Savos } else if ((skip_source_check || acline_mode_user == ac_sysctl) && 303342983Savos sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) { 304193161Snwhitehorn acline_mode = ac_sysctl; 305193161Snwhitehorn if (vflag) 306193161Snwhitehorn warnx("using sysctl for AC line status"); 307193161Snwhitehorn#endif 308153999Sdes#ifdef USE_APM 309342983Savos } else if ((skip_source_check || acline_mode_user == ac_apm) && 310342983Savos (apm_fd = open(APMDEV, O_RDONLY)) >= 0) { 311153999Sdes if (vflag) 312153999Sdes warnx("using APM for AC line status"); 313153999Sdes acline_mode = ac_apm; 314153999Sdes#endif 315142605Snjl } else { 316153999Sdes warnx("unable to determine AC line status"); 317153999Sdes acline_mode = ac_none; 318142605Snjl } 319142605Snjl} 320142605Snjl 321153999Sdesstatic void 322153999Sdesacline_read(void) 323142605Snjl{ 324153999Sdes if (acline_mode == ac_acpi_devd) { 325153999Sdes char buf[DEVCTL_MAXBUF], *ptr; 326153999Sdes ssize_t rlen; 327153999Sdes int notify; 328142605Snjl 329153999Sdes rlen = read(devd_pipe, buf, sizeof(buf)); 330153999Sdes if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) { 331153999Sdes if (vflag) 332153999Sdes warnx("lost devd connection, switching to sysctl"); 333153999Sdes devd_close(); 334193161Snwhitehorn acline_mode = ac_sysctl; 335153999Sdes /* FALLTHROUGH */ 336153999Sdes } 337153999Sdes if (rlen > 0 && 338153999Sdes (ptr = strstr(buf, "system=ACPI")) != NULL && 339153999Sdes (ptr = strstr(ptr, "subsystem=ACAD")) != NULL && 340153999Sdes (ptr = strstr(ptr, "notify=")) != NULL && 341153999Sdes sscanf(ptr, "notify=%x", ¬ify) == 1) 342153999Sdes acline_status = (notify ? SRC_AC : SRC_BATTERY); 343153999Sdes } 344193161Snwhitehorn if (acline_mode == ac_sysctl) { 345153999Sdes int acline; 346153999Sdes size_t len; 347151461Snjl 348153999Sdes len = sizeof(acline); 349193161Snwhitehorn if (sysctl(acline_mib, acline_mib_len, &acline, &len, 350193161Snwhitehorn NULL, 0) == 0) 351153999Sdes acline_status = (acline ? SRC_AC : SRC_BATTERY); 352153999Sdes else 353153999Sdes acline_status = SRC_UNKNOWN; 354153999Sdes } 355153999Sdes#ifdef USE_APM 356153999Sdes if (acline_mode == ac_apm) { 357153999Sdes struct apm_info info; 358153999Sdes 359153999Sdes if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) { 360153999Sdes acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY); 361153999Sdes } else { 362153999Sdes close(apm_fd); 363153999Sdes apm_fd = -1; 364153999Sdes acline_mode = ac_none; 365153999Sdes acline_status = SRC_UNKNOWN; 366153999Sdes } 367153999Sdes } 368142605Snjl#endif 369153999Sdes /* try to (re)connect to devd */ 370342983Savos#ifdef USE_APM 371342983Savos if ((acline_mode == ac_sysctl && 372342983Savos (acline_mode_user == ac_none || 373342983Savos acline_mode_user == ac_acpi_devd)) || 374342983Savos (acline_mode == ac_apm && 375342983Savos acline_mode_user == ac_acpi_devd)) { 376342983Savos#else 377342983Savos if (acline_mode == ac_sysctl && 378342983Savos (acline_mode_user == ac_none || 379342983Savos acline_mode_user == ac_acpi_devd)) { 380342983Savos#endif 381153999Sdes struct timeval now; 382142605Snjl 383153999Sdes gettimeofday(&now, NULL); 384153999Sdes if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) { 385153999Sdes if (devd_init() >= 0) { 386153999Sdes if (vflag) 387153999Sdes warnx("using devd for AC line status"); 388153999Sdes acline_mode = ac_acpi_devd; 389153999Sdes } 390153999Sdes tried_devd = now; 391153999Sdes } 392153999Sdes } 393151461Snjl} 394151461Snjl 395151461Snjlstatic int 396151461Snjldevd_init(void) 397151461Snjl{ 398151461Snjl struct sockaddr_un devd_addr; 399151461Snjl 400151461Snjl bzero(&devd_addr, sizeof(devd_addr)); 401153999Sdes if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { 402151461Snjl if (vflag) 403153999Sdes warn("%s(): socket()", __func__); 404151461Snjl return (-1); 405142605Snjl } 406142605Snjl 407151461Snjl devd_addr.sun_family = PF_LOCAL; 408151461Snjl strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path)); 409153999Sdes if (connect(devd_pipe, (struct sockaddr *)&devd_addr, 410151461Snjl sizeof(devd_addr)) == -1) { 411153999Sdes if (vflag) 412153999Sdes warn("%s(): connect()", __func__); 413153999Sdes close(devd_pipe); 414153999Sdes devd_pipe = -1; 415151461Snjl return (-1); 416151461Snjl } 417151461Snjl 418153999Sdes if (fcntl(devd_pipe, F_SETFL, O_NONBLOCK) == -1) { 419153999Sdes if (vflag) 420153999Sdes warn("%s(): fcntl()", __func__); 421153999Sdes close(devd_pipe); 422153999Sdes return (-1); 423153999Sdes } 424153999Sdes 425153999Sdes return (devd_pipe); 426142605Snjl} 427142605Snjl 428142605Snjlstatic void 429151461Snjldevd_close(void) 430151461Snjl{ 431151461Snjl 432151461Snjl close(devd_pipe); 433153999Sdes devd_pipe = -1; 434151461Snjl} 435151461Snjl 436151461Snjlstatic void 437142577Snjlparse_mode(char *arg, int *mode, int ch) 438142577Snjl{ 439142577Snjl 440150564Sdes if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0) 441142577Snjl *mode = MODE_MIN; 442150564Sdes else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0) 443142577Snjl *mode = MODE_MAX; 444179932Sobrien else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0) 445142577Snjl *mode = MODE_ADAPTIVE; 446185050Smav else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0) 447185050Smav *mode = MODE_HIADAPTIVE; 448142577Snjl else 449142577Snjl errx(1, "bad option: -%c %s", (char)ch, optarg); 450142577Snjl} 451142577Snjl 452142577Snjlstatic void 453342983Savosparse_acline_mode(char *arg, int ch) 454342983Savos{ 455342983Savos if (strcmp(arg, "sysctl") == 0) 456342983Savos acline_mode_user = ac_sysctl; 457342983Savos else if (strcmp(arg, "devd") == 0) 458342983Savos acline_mode_user = ac_acpi_devd; 459342983Savos#ifdef USE_APM 460342983Savos else if (strcmp(arg, "apm") == 0) 461342983Savos acline_mode_user = ac_apm; 462342983Savos#endif 463342983Savos else 464342983Savos errx(1, "bad option: -%c %s", (char)ch, optarg); 465342983Savos} 466342983Savos 467342983Savosstatic void 468144873Snjlhandle_sigs(int __unused sig) 469144873Snjl{ 470151461Snjl 471144873Snjl exit_requested = 1; 472144873Snjl} 473144873Snjl 474144873Snjlstatic void 475142577Snjlusage(void) 476142577Snjl{ 477142577Snjl 478142577Snjl fprintf(stderr, 479342983Savos"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n"); 480142577Snjl exit(1); 481142577Snjl} 482142577Snjl 483142577Snjlint 484142577Snjlmain(int argc, char * argv[]) 485142577Snjl{ 486153999Sdes struct timeval timeout; 487153999Sdes fd_set fdset; 488153999Sdes int nfds; 489149437Spjd struct pidfh *pfh = NULL; 490149428Spjd const char *pidfile = NULL; 491185053Smav int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load; 492211415Sbrucec int minfreq = -1, maxfreq = -1; 493209234Smav int ch, mode, mode_ac, mode_battery, mode_none, idle, to; 494144873Snjl uint64_t mjoules_used; 495142577Snjl size_t len; 496142577Snjl 497142577Snjl /* Default mode for all AC states is adaptive. */ 498185050Smav mode_ac = mode_none = MODE_HIADAPTIVE; 499185050Smav mode_battery = MODE_ADAPTIVE; 500142577Snjl cpu_running_mark = DEFAULT_ACTIVE_PERCENT; 501142577Snjl cpu_idle_mark = DEFAULT_IDLE_PERCENT; 502142577Snjl poll_ival = DEFAULT_POLL_INTERVAL; 503144873Snjl mjoules_used = 0; 504142577Snjl vflag = 0; 505142577Snjl 506148395Snjl /* User must be root to control frequencies. */ 507148395Snjl if (geteuid() != 0) 508148395Snjl errx(1, "must be root to run"); 509148395Snjl 510342983Savos while ((ch = getopt(argc, argv, "a:b:i:m:M:n:p:P:r:s:v")) != -1) 511142577Snjl switch (ch) { 512142577Snjl case 'a': 513142577Snjl parse_mode(optarg, &mode_ac, ch); 514142577Snjl break; 515142577Snjl case 'b': 516142577Snjl parse_mode(optarg, &mode_battery, ch); 517142577Snjl break; 518342983Savos case 's': 519342983Savos parse_acline_mode(optarg, ch); 520342983Savos break; 521142577Snjl case 'i': 522142577Snjl cpu_idle_mark = atoi(optarg); 523142577Snjl if (cpu_idle_mark < 0 || cpu_idle_mark > 100) { 524142577Snjl warnx("%d is not a valid percent", 525142577Snjl cpu_idle_mark); 526142577Snjl usage(); 527142577Snjl } 528142577Snjl break; 529211415Sbrucec case 'm': 530211415Sbrucec minfreq = atoi(optarg); 531211415Sbrucec if (minfreq < 0) { 532211415Sbrucec warnx("%d is not a valid CPU frequency", 533211415Sbrucec minfreq); 534211415Sbrucec usage(); 535211415Sbrucec } 536211415Sbrucec break; 537211415Sbrucec case 'M': 538211415Sbrucec maxfreq = atoi(optarg); 539211415Sbrucec if (maxfreq < 0) { 540211415Sbrucec warnx("%d is not a valid CPU frequency", 541211415Sbrucec maxfreq); 542211415Sbrucec usage(); 543211415Sbrucec } 544211415Sbrucec break; 545142577Snjl case 'n': 546142577Snjl parse_mode(optarg, &mode_none, ch); 547142577Snjl break; 548142577Snjl case 'p': 549142577Snjl poll_ival = atoi(optarg); 550142577Snjl if (poll_ival < 5) { 551142577Snjl warnx("poll interval is in units of ms"); 552142577Snjl usage(); 553142577Snjl } 554142577Snjl break; 555149428Spjd case 'P': 556149428Spjd pidfile = optarg; 557149428Spjd break; 558142577Snjl case 'r': 559142577Snjl cpu_running_mark = atoi(optarg); 560185050Smav if (cpu_running_mark <= 0 || cpu_running_mark > 100) { 561142577Snjl warnx("%d is not a valid percent", 562142577Snjl cpu_running_mark); 563142577Snjl usage(); 564142577Snjl } 565142577Snjl break; 566142577Snjl case 'v': 567142577Snjl vflag = 1; 568142577Snjl break; 569142577Snjl default: 570142577Snjl usage(); 571142577Snjl } 572142577Snjl 573151461Snjl mode = mode_none; 574151461Snjl 575142577Snjl /* Poll interval is in units of ms. */ 576142577Snjl poll_ival *= 1000; 577142577Snjl 578142577Snjl /* Look up various sysctl MIBs. */ 579142577Snjl len = 2; 580185050Smav if (sysctlnametomib("kern.cp_times", cp_times_mib, &len)) 581185050Smav err(1, "lookup kern.cp_times"); 582142577Snjl len = 4; 583142577Snjl if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len)) 584234070Srmh err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting"); 585142577Snjl len = 4; 586142577Snjl if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len)) 587142577Snjl err(1, "lookup freq_levels"); 588142577Snjl 589185050Smav /* Check if we can read the load and supported freqs. */ 590185050Smav if (read_usage_times(NULL)) 591142577Snjl err(1, "read_usage_times"); 592211415Sbrucec if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq)) 593142577Snjl err(1, "error reading supported CPU frequencies"); 594211415Sbrucec if (numfreqs == 0) 595211415Sbrucec errx(1, "no CPU frequencies in user-specified range"); 596142577Snjl 597142577Snjl /* Run in the background unless in verbose mode. */ 598149428Spjd if (!vflag) { 599149428Spjd pid_t otherpid; 600149428Spjd 601149428Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 602149428Spjd if (pfh == NULL) { 603149428Spjd if (errno == EEXIST) { 604149428Spjd errx(1, "powerd already running, pid: %d", 605149428Spjd otherpid); 606149428Spjd } 607149428Spjd warn("cannot open pid file"); 608149428Spjd } 609151491Snjl if (daemon(0, 0) != 0) { 610151491Snjl warn("cannot enter daemon mode, exiting"); 611151491Snjl pidfile_remove(pfh); 612151491Snjl exit(EXIT_FAILURE); 613151491Snjl 614151491Snjl } 615149428Spjd pidfile_write(pfh); 616149428Spjd } 617142577Snjl 618151628Snjl /* Decide whether to use ACPI or APM to read the AC line status. */ 619151628Snjl acline_init(); 620151628Snjl 621153999Sdes /* 622153999Sdes * Exit cleanly on signals. 623153999Sdes */ 624153999Sdes signal(SIGINT, handle_sigs); 625153999Sdes signal(SIGTERM, handle_sigs); 626153999Sdes 627209234Smav freq = initfreq = curfreq = get_freq(); 628209234Smav i = get_freq_id(curfreq, freqs, numfreqs); 629185050Smav if (freq < 1) 630185050Smav freq = 1; 631211415Sbrucec 632211415Sbrucec /* 633211415Sbrucec * If we are in adaptive mode and the current frequency is outside the 634211415Sbrucec * user-defined range, adjust it to be within the user-defined range. 635211415Sbrucec */ 636211415Sbrucec acline_read(); 637211415Sbrucec if (acline_status > SRC_UNKNOWN) 638211415Sbrucec errx(1, "invalid AC line status %d", acline_status); 639211415Sbrucec if ((acline_status == SRC_AC && 640211415Sbrucec (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) || 641211415Sbrucec (acline_status == SRC_BATTERY && 642211415Sbrucec (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) || 643211415Sbrucec (acline_status == SRC_UNKNOWN && 644211415Sbrucec (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) { 645211415Sbrucec /* Read the current frequency. */ 646211415Sbrucec len = sizeof(curfreq); 647211415Sbrucec if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) { 648211415Sbrucec if (vflag) 649211415Sbrucec warn("error reading current CPU frequency"); 650211415Sbrucec } 651211415Sbrucec if (curfreq < freqs[numfreqs - 1]) { 652211415Sbrucec if (vflag) { 653211415Sbrucec printf("CPU frequency is below user-defined " 654211415Sbrucec "minimum; changing frequency to %d " 655211415Sbrucec "MHz\n", freqs[numfreqs - 1]); 656211415Sbrucec } 657211415Sbrucec if (set_freq(freqs[numfreqs - 1]) != 0) { 658211415Sbrucec warn("error setting CPU freq %d", 659211415Sbrucec freqs[numfreqs - 1]); 660211415Sbrucec } 661211415Sbrucec } else if (curfreq > freqs[0]) { 662211415Sbrucec if (vflag) { 663211415Sbrucec printf("CPU frequency is above user-defined " 664211415Sbrucec "maximum; changing frequency to %d " 665211415Sbrucec "MHz\n", freqs[0]); 666211415Sbrucec } 667211415Sbrucec if (set_freq(freqs[0]) != 0) { 668211415Sbrucec warn("error setting CPU freq %d", 669211415Sbrucec freqs[0]); 670211415Sbrucec } 671211415Sbrucec } 672211415Sbrucec } 673211415Sbrucec 674209234Smav idle = 0; 675142577Snjl /* Main loop. */ 676142577Snjl for (;;) { 677153999Sdes FD_ZERO(&fdset); 678153999Sdes if (devd_pipe >= 0) { 679153999Sdes FD_SET(devd_pipe, &fdset); 680153999Sdes nfds = devd_pipe + 1; 681153999Sdes } else { 682153999Sdes nfds = 0; 683153999Sdes } 684209234Smav if (mode == MODE_HIADAPTIVE || idle < 120) 685209234Smav to = poll_ival; 686209234Smav else if (idle < 360) 687209234Smav to = poll_ival * 2; 688209234Smav else 689209234Smav to = poll_ival * 4; 690209234Smav timeout.tv_sec = to / 1000000; 691209234Smav timeout.tv_usec = to % 1000000; 692153999Sdes select(nfds, &fdset, NULL, &fdset, &timeout); 693142577Snjl 694144873Snjl /* If the user requested we quit, print some statistics. */ 695144873Snjl if (exit_requested) { 696144873Snjl if (vflag && mjoules_used != 0) 697144873Snjl printf("total joules used: %u.%03u\n", 698144873Snjl (u_int)(mjoules_used / 1000), 699144873Snjl (int)mjoules_used % 1000); 700144873Snjl break; 701144873Snjl } 702144873Snjl 703142577Snjl /* Read the current AC status and record the mode. */ 704153999Sdes acline_read(); 705153999Sdes switch (acline_status) { 706142577Snjl case SRC_AC: 707142577Snjl mode = mode_ac; 708142577Snjl break; 709142577Snjl case SRC_BATTERY: 710142577Snjl mode = mode_battery; 711142577Snjl break; 712142577Snjl case SRC_UNKNOWN: 713142577Snjl mode = mode_none; 714142577Snjl break; 715142577Snjl default: 716153999Sdes errx(1, "invalid AC line status %d", acline_status); 717142577Snjl } 718142577Snjl 719142577Snjl /* Read the current frequency. */ 720209234Smav if (idle % 32 == 0) { 721209234Smav if ((curfreq = get_freq()) == 0) 722209234Smav continue; 723209234Smav i = get_freq_id(curfreq, freqs, numfreqs); 724209234Smav } 725209234Smav idle++; 726144873Snjl if (vflag) { 727144873Snjl /* Keep a sum of all power actually used. */ 728185050Smav if (mwatts[i] != -1) 729144873Snjl mjoules_used += 730144873Snjl (mwatts[i] * (poll_ival / 1000)) / 1000; 731144873Snjl } 732144873Snjl 733142577Snjl /* Always switch to the lowest frequency in min mode. */ 734142577Snjl if (mode == MODE_MIN) { 735185050Smav freq = freqs[numfreqs - 1]; 736185050Smav if (curfreq != freq) { 737142577Snjl if (vflag) { 738142577Snjl printf("now operating on %s power; " 739142577Snjl "changing frequency to %d MHz\n", 740185050Smav modes[acline_status], freq); 741142577Snjl } 742209234Smav idle = 0; 743185050Smav if (set_freq(freq) != 0) { 744151461Snjl warn("error setting CPU freq %d", 745185050Smav freq); 746151461Snjl continue; 747151461Snjl } 748142577Snjl } 749142577Snjl continue; 750142577Snjl } 751142577Snjl 752142577Snjl /* Always switch to the highest frequency in max mode. */ 753142577Snjl if (mode == MODE_MAX) { 754185050Smav freq = freqs[0]; 755185050Smav if (curfreq != freq) { 756142577Snjl if (vflag) { 757142605Snjl printf("now operating on %s power; " 758142577Snjl "changing frequency to %d MHz\n", 759185050Smav modes[acline_status], freq); 760142577Snjl } 761209234Smav idle = 0; 762185050Smav if (set_freq(freq) != 0) { 763151461Snjl warn("error setting CPU freq %d", 764252713Swblock freq); 765151461Snjl continue; 766151461Snjl } 767142577Snjl } 768142577Snjl continue; 769142577Snjl } 770142577Snjl 771142577Snjl /* Adaptive mode; get the current CPU usage times. */ 772185050Smav if (read_usage_times(&load)) { 773151461Snjl if (vflag) 774151461Snjl warn("read_usage_times() failed"); 775151461Snjl continue; 776151461Snjl } 777252713Swblock 778185050Smav if (mode == MODE_ADAPTIVE) { 779185050Smav if (load > cpu_running_mark) { 780185050Smav if (load > 95 || load > cpu_running_mark * 2) 781185050Smav freq *= 2; 782185050Smav else 783185050Smav freq = freq * load / cpu_running_mark; 784185050Smav if (freq > freqs[0]) 785185050Smav freq = freqs[0]; 786185050Smav } else if (load < cpu_idle_mark && 787185050Smav curfreq * load < freqs[get_freq_id( 788252713Swblock freq * 7 / 8, freqs, numfreqs)] * 789185050Smav cpu_running_mark) { 790185050Smav freq = freq * 7 / 8; 791185050Smav if (freq < freqs[numfreqs - 1]) 792185050Smav freq = freqs[numfreqs - 1]; 793185050Smav } 794185050Smav } else { /* MODE_HIADAPTIVE */ 795185050Smav if (load > cpu_running_mark / 2) { 796185050Smav if (load > 95 || load > cpu_running_mark) 797185050Smav freq *= 4; 798185050Smav else 799185050Smav freq = freq * load * 2 / cpu_running_mark; 800185050Smav if (freq > freqs[0] * 2) 801185050Smav freq = freqs[0] * 2; 802185050Smav } else if (load < cpu_idle_mark / 2 && 803185050Smav curfreq * load < freqs[get_freq_id( 804252713Swblock freq * 31 / 32, freqs, numfreqs)] * 805185050Smav cpu_running_mark / 2) { 806185050Smav freq = freq * 31 / 32; 807185050Smav if (freq < freqs[numfreqs - 1]) 808185050Smav freq = freqs[numfreqs - 1]; 809185050Smav } 810149410Sbruno } 811185050Smav if (vflag) { 812185050Smav printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n", 813185050Smav load, curfreq, i, freq); 814185050Smav } 815185050Smav j = get_freq_id(freq, freqs, numfreqs); 816185050Smav if (i != j) { 817142577Snjl if (vflag) { 818185050Smav printf("changing clock" 819142577Snjl " speed from %d MHz to %d MHz\n", 820185050Smav freqs[i], freqs[j]); 821142577Snjl } 822209234Smav idle = 0; 823185050Smav if (set_freq(freqs[j])) 824170682Smarck warn("error setting CPU frequency %d", 825185050Smav freqs[j]); 826142577Snjl } 827142577Snjl } 828185053Smav if (set_freq(initfreq)) 829185053Smav warn("error setting CPU frequency %d", initfreq); 830144873Snjl free(freqs); 831144873Snjl free(mwatts); 832151461Snjl devd_close(); 833149428Spjd if (!vflag) 834149428Spjd pidfile_remove(pfh); 835142577Snjl 836142577Snjl exit(0); 837142577Snjl} 838