1 /* 2 * Copyright (c) 2010 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 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 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * The powerd daemon monitors the cpu load and adjusts cpu frequencies 37 * via hw.acpi.cpu.px_dom*. 38 */ 39 40 #include <sys/types.h> 41 #include <sys/sysctl.h> 42 #include <sys/kinfo.h> 43 #include <sys/file.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 #include <string.h> 48 #include <syslog.h> 49 50 #define STATE_UNKNOWN 0 51 #define STATE_LOW 1 52 #define STATE_HIGH 2 53 54 static void usage(void); 55 static double getcputime(void); 56 static void acpi_setcpufreq(int ostate, int nstate); 57 58 int DebugOpt; 59 int PowerState = STATE_UNKNOWN; 60 int PowerFd; 61 double Trigger = 0.25; 62 63 int 64 main(int ac, char **av) 65 { 66 double qavg; 67 double savg; 68 int ch; 69 int nstate; 70 char buf[32]; 71 72 while ((ch = getopt(ac, av, "d")) != -1) { 73 switch(ch) { 74 case 'd': 75 DebugOpt = 1; 76 break; 77 default: 78 usage(); 79 /* NOT REACHED */ 80 } 81 } 82 ac -= optind; 83 av += optind; 84 85 /* 86 * Prime delta cputime calculation, make sure at least dom0 exists, 87 * and make sure powerd is not already running. 88 */ 89 getcputime(); 90 savg = 0.0; 91 92 if (sysctlbyname("hw.acpi.cpu.px_dom0.available", NULL, NULL, 93 NULL, 0) < 0) { 94 fprintf(stderr, "hw.acpi.cpu.px_dom* sysctl not available\n"); 95 exit(1); 96 } 97 98 PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644); 99 if (PowerFd < 0) { 100 fprintf(stderr, 101 "Cannot create /var/run/powerd.pid, " 102 "continuing anyway\n"); 103 } else { 104 if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) { 105 fprintf(stderr, "powerd is already running\n"); 106 exit(1); 107 } 108 } 109 110 /* 111 * Demonize and set pid 112 */ 113 if (DebugOpt == 0) { 114 daemon(0, 0); 115 openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON); 116 } 117 118 if (PowerFd >= 0) { 119 ftruncate(PowerFd, 0); 120 snprintf(buf, sizeof(buf), "%d\n", (int)getpid()); 121 write(PowerFd, buf, strlen(buf)); 122 } 123 124 /* 125 * Monitoring loop 126 */ 127 for (;;) { 128 qavg = getcputime(); 129 savg = (savg * 7.0 + qavg) / 8.0; 130 131 if (DebugOpt) { 132 printf("\rqavg=%5.2f savg=%5.2f\r", qavg, savg); 133 fflush(stdout); 134 } 135 136 nstate = PowerState; 137 if (nstate == STATE_UNKNOWN) { 138 if (savg >= Trigger) 139 nstate = STATE_HIGH; 140 else 141 nstate = STATE_LOW; 142 } else if (nstate == STATE_LOW) { 143 if (savg >= Trigger || qavg >= 0.9) 144 nstate = STATE_HIGH; 145 } else { 146 if (savg < Trigger / 2.0 && qavg < Trigger / 2.0) 147 nstate = STATE_LOW; 148 } 149 if (PowerState != nstate) { 150 acpi_setcpufreq(PowerState, nstate); 151 PowerState = nstate; 152 } 153 sleep(1); 154 } 155 } 156 157 /* 158 * Return the one-second cpu load. One cpu at 100% will return a value 159 * of 1.0. On a SMP system N cpus running at 100% will return a value of N. 160 */ 161 static 162 double 163 getcputime(void) 164 { 165 static struct kinfo_cputime ocpu_time[64]; 166 static struct kinfo_cputime ncpu_time[64]; 167 size_t slen; 168 int ncpu; 169 int cpu; 170 uint64_t delta; 171 172 bcopy(ncpu_time, ocpu_time, sizeof(ncpu_time)); 173 slen = sizeof(ncpu_time); 174 if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) { 175 fprintf(stderr, "kern.cputime sysctl not available\n"); 176 exit(1); 177 } 178 ncpu = slen / sizeof(ncpu_time[0]); 179 delta = 0; 180 181 for (cpu = 0; cpu < ncpu; ++cpu) { 182 delta += (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys + 183 ncpu_time[cpu].cp_intr) - 184 (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys + 185 ocpu_time[cpu].cp_intr); 186 } 187 return((double)delta / 1000000.0); 188 } 189 190 191 static 192 void 193 acpi_setcpufreq(int ostate, int nstate) 194 { 195 int dom; 196 int lowest; 197 int highest; 198 int desired; 199 int v; 200 char *sysid; 201 char *ptr; 202 char buf[256]; 203 size_t buflen; 204 205 dom = 0; 206 for (;;) { 207 /* 208 * Retrieve availability list 209 */ 210 asprintf(&sysid, "hw.acpi.cpu.px_dom%d.available", dom); 211 buflen = sizeof(buf) - 1; 212 v = sysctlbyname(sysid, buf, &buflen, NULL, 0); 213 free(sysid); 214 if (v < 0) 215 break; 216 buf[buflen] = 0; 217 218 /* 219 * Parse out the highest and lowest cpu frequencies 220 */ 221 ptr = buf; 222 highest = lowest = 0; 223 while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) { 224 if (lowest == 0 || lowest > v) 225 lowest = v; 226 if (highest == 0 || highest < v) 227 highest = v; 228 } 229 230 /* 231 * Calculate the desired cpu frequency, test, and set. 232 */ 233 desired = (nstate == STATE_LOW) ? lowest : highest; 234 235 asprintf(&sysid, "hw.acpi.cpu.px_dom%d.select", dom); 236 buflen = sizeof(v); 237 v = 0; 238 sysctlbyname(sysid, &v, &buflen, NULL, 0); 239 if (v != desired || ostate == STATE_UNKNOWN) { 240 if (DebugOpt) { 241 printf("dom%d set frequency %d\n", 242 dom, desired); 243 } else { 244 syslog(LOG_INFO, "dom%d set frequency %d\n", 245 dom, desired); 246 } 247 sysctlbyname(sysid, NULL, NULL, 248 &desired, sizeof(desired)); 249 } 250 free(sysid); 251 ++dom; 252 } 253 } 254 255 static 256 void 257 usage(void) 258 { 259 fprintf(stderr, "usage: powerd [-d]\n"); 260 exit(1); 261 } 262