powerd - Initial load monitor and cpu frequency adjustment daemon
[dragonfly.git] / usr.sbin / powerd / powerd.c
blobaaec64c64b038b4ef34e05ce8e9e4aaa3a126ae0
1 /*
2 * Copyright (c) 2010 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * 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
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.
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.
36 * The powerd daemon monitors the cpu load and adjusts cpu frequencies
37 * via hw.acpi.cpu.px_dom*.
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>
49 #define STATE_UNKNOWN 0
50 #define STATE_LOW 1
51 #define STATE_HIGH 2
53 static void usage(void);
54 static double getcputime(void);
55 static void acpi_setcpufreq(int ostate, int nstate);
57 int DebugOpt;
58 int PowerState = STATE_UNKNOWN;
59 int PowerFd;
60 double Trigger = 0.25;
62 int
63 main(int ac, char **av)
65 double qavg;
66 double savg;
67 int ch;
68 int nstate;
69 char buf[32];
71 while ((ch = getopt(ac, av, "d")) != -1) {
72 switch(ch) {
73 case 'd':
74 DebugOpt = 1;
75 break;
76 default:
77 usage();
78 /* NOT REACHED */
81 ac -= optind;
82 av -= optind;
85 * Prime delta cputime calculation, make sure at least dom0 exists,
86 * and make sure powerd is not already running.
88 getcputime();
89 savg = 0.0;
91 if (sysctlbyname("hw.acpi.cpu.px_dom0.available", NULL, NULL,
92 NULL, 0) < 0) {
93 fprintf(stderr, "hw.acpi.cpu.px_dom* sysctl not available\n");
94 exit(1);
97 PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644);
98 if (PowerFd < 0) {
99 fprintf(stderr,
100 "Cannot create /var/run/powerd.pid, "
101 "continuing anyway\n");
102 } else {
103 if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) {
104 fprintf(stderr, "powerd is already running\n");
105 exit(1);
110 * Demonize and set pid
112 if (DebugOpt == 0)
113 daemon(0, 0);
115 if (PowerFd >= 0) {
116 ftruncate(PowerFd, 0);
117 snprintf(buf, sizeof(buf), "%d\n", (int)getpid());
118 write(PowerFd, buf, strlen(buf));
122 * Monitoring loop
124 for (;;) {
125 qavg = getcputime();
126 savg = (savg * 7.0 + qavg) / 8.0;
128 if (DebugOpt) {
129 printf("\rqavg=%5.2f savg=%5.2f\r", qavg, savg);
130 fflush(stdout);
133 nstate = PowerState;
134 if (nstate == STATE_UNKNOWN) {
135 if (savg >= Trigger)
136 nstate = STATE_HIGH;
137 else
138 nstate = STATE_LOW;
139 } else if (nstate == STATE_LOW) {
140 if (savg >= Trigger || qavg >= 0.9)
141 nstate = STATE_HIGH;
142 } else {
143 if (savg < Trigger / 2.0 && qavg < Trigger / 2.0)
144 nstate = STATE_LOW;
146 if (PowerState != nstate) {
147 acpi_setcpufreq(PowerState, nstate);
148 PowerState = nstate;
150 sleep(1);
155 * Return the one-second cpu load. One cpu at 100% will return a value
156 * of 1.0. On a SMP system N cpus running at 100% will return a value of N.
158 static
159 double
160 getcputime(void)
162 static struct kinfo_cputime ocpu_time[64];
163 static struct kinfo_cputime ncpu_time[64];
164 int slen;
165 int ncpu;
166 int cpu;
167 uint64_t delta;
169 bcopy(ncpu_time, ocpu_time, sizeof(ncpu_time));
170 slen = sizeof(ncpu_time);
171 if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) {
172 fprintf(stderr, "kern.cputime sysctl not available\n");
173 exit(1);
175 ncpu = slen / sizeof(ncpu_time[0]);
176 delta = 0;
178 for (cpu = 0; cpu < ncpu; ++cpu) {
179 delta += (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys +
180 ncpu_time[cpu].cp_intr) -
181 (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys +
182 ocpu_time[cpu].cp_intr);
184 return((double)delta / 1000000.0);
188 static
189 void
190 acpi_setcpufreq(int ostate, int nstate)
192 int dom;
193 int lowest;
194 int highest;
195 int desired;
196 int v;
197 char *sysid;
198 char *ptr;
199 char buf[256];
200 size_t buflen;
202 dom = 0;
203 for (;;) {
205 * Retrieve availability list
207 asprintf(&sysid, "hw.acpi.cpu.px_dom%d.available", dom);
208 buflen = sizeof(buf) - 1;
209 v = sysctlbyname(sysid, buf, &buflen, NULL, 0);
210 free(sysid);
211 if (v < 0)
212 break;
213 buf[buflen] = 0;
216 * Parse out the highest and lowest cpu frequencies
218 ptr = buf;
219 highest = lowest = 0;
220 while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
221 if (lowest == 0 || lowest > v)
222 lowest = v;
223 if (highest == 0 || highest < v)
224 highest = v;
228 * Calculate the desired cpu frequency, test, and set.
230 desired = (nstate == STATE_LOW) ? lowest : highest;
232 asprintf(&sysid, "hw.acpi.cpu.px_dom%d.select", dom);
233 buflen = sizeof(v);
234 v = 0;
235 sysctlbyname(sysid, &v, &buflen, NULL, 0);
236 if (v != desired || ostate == STATE_UNKNOWN) {
237 if (DebugOpt) {
238 printf("dom%d set frequency %d\n",
239 dom, desired);
241 sysctlbyname(sysid, NULL, NULL,
242 &desired, sizeof(desired));
244 free(sysid);
245 ++dom;
249 static
250 void
251 usage(void)
253 fprintf(stderr, "usage: powerd [-d]\n");
254 exit(1);