1cc537f99SMatthew Dillon /*
2c6434d78SMatthew Dillon * Copyright (c) 2010,2016 The DragonFly Project. All rights reserved.
3cc537f99SMatthew Dillon *
4cc537f99SMatthew Dillon * This code is derived from software contributed to The DragonFly Project
5cc537f99SMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
6cc537f99SMatthew Dillon *
7cc537f99SMatthew Dillon * Redistribution and use in source and binary forms, with or without
8cc537f99SMatthew Dillon * modification, are permitted provided that the following conditions
9cc537f99SMatthew Dillon * are met:
10cc537f99SMatthew Dillon *
11cc537f99SMatthew Dillon * 1. Redistributions of source code must retain the above copyright
12cc537f99SMatthew Dillon * notice, this list of conditions and the following disclaimer.
13cc537f99SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
14cc537f99SMatthew Dillon * notice, this list of conditions and the following disclaimer in
15cc537f99SMatthew Dillon * the documentation and/or other materials provided with the
16cc537f99SMatthew Dillon * distribution.
17cc537f99SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
18cc537f99SMatthew Dillon * contributors may be used to endorse or promote products derived
19cc537f99SMatthew Dillon * from this software without specific, prior written permission.
20cc537f99SMatthew Dillon *
21cc537f99SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22cc537f99SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23cc537f99SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24cc537f99SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25cc537f99SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26cc537f99SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27cc537f99SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28cc537f99SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29cc537f99SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30cc537f99SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31cc537f99SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32cc537f99SMatthew Dillon * SUCH DAMAGE.
33cc537f99SMatthew Dillon */
34cc537f99SMatthew Dillon
35cc537f99SMatthew Dillon /*
36a4c3bf5eSSepherosa Ziehau * The powerd daemon :
37a4c3bf5eSSepherosa Ziehau * - Monitor the cpu load and adjusts cpu and cpu power domain
38a4c3bf5eSSepherosa Ziehau * performance accordingly.
39a4c3bf5eSSepherosa Ziehau * - Monitor battery life. Alarm alerts and shutdown the machine
40a4c3bf5eSSepherosa Ziehau * if battery life goes low.
41cc537f99SMatthew Dillon */
42cc537f99SMatthew Dillon
43c07315c4SMatthew Dillon #define _KERNEL_STRUCTURES
44cc537f99SMatthew Dillon #include <sys/types.h>
45cc537f99SMatthew Dillon #include <sys/sysctl.h>
46cc537f99SMatthew Dillon #include <sys/kinfo.h>
47b818cc9bSSepherosa Ziehau #include <sys/queue.h>
48b1b8502eSSepherosa Ziehau #include <sys/soundcard.h>
49c6434d78SMatthew Dillon #include <sys/sensors.h>
50aa7fc22aSSepherosa Ziehau #include <sys/time.h>
51da82a65aSzrj #include <sys/cpumask.h>
52bd9ff91eSSepherosa Ziehau #include <machine/cpufunc.h>
5325780572SSepherosa Ziehau #include <err.h>
54*2c3b1d1bSSascha Wildner #include <fcntl.h>
55c6434d78SMatthew Dillon #include <signal.h>
56cc537f99SMatthew Dillon #include <stdio.h>
57cc537f99SMatthew Dillon #include <stdlib.h>
58cc537f99SMatthew Dillon #include <unistd.h>
59cc537f99SMatthew Dillon #include <string.h>
6082d1bcc2SSascha Wildner #include <syslog.h>
61cc537f99SMatthew Dillon
62b1b8502eSSepherosa Ziehau #include "alert1.h"
63b1b8502eSSepherosa Ziehau
648b799ad2SSepherosa Ziehau #define MAXDOM MAXCPU /* worst case, 1 cpu per domain */
658b799ad2SSepherosa Ziehau
664afb1280SSepherosa Ziehau #define MAXFREQ 64
67764594cfSSepherosa Ziehau #define CST_STRLEN 16
684afb1280SSepherosa Ziehau
69ed27c2d1SMatthew Dillon #define NFREQ_MONPERF 0x0001
70ed27c2d1SMatthew Dillon #define NFREQ_ADJPERF 0x0002
71ed27c2d1SMatthew Dillon #define NFREQ_CPUTEMP 0x0004
72ed27c2d1SMatthew Dillon
73ed27c2d1SMatthew Dillon #define NFREQ_ALL (NFREQ_MONPERF | NFREQ_ADJPERF | NFREQ_CPUTEMP)
74ed27c2d1SMatthew Dillon
75b818cc9bSSepherosa Ziehau struct cpu_pwrdom {
76b818cc9bSSepherosa Ziehau TAILQ_ENTRY(cpu_pwrdom) dom_link;
77b818cc9bSSepherosa Ziehau int dom_id;
78b818cc9bSSepherosa Ziehau int dom_ncpus;
79bd9ff91eSSepherosa Ziehau cpumask_t dom_cpumask;
80b818cc9bSSepherosa Ziehau };
81a4c3bf5eSSepherosa Ziehau
82a4c3bf5eSSepherosa Ziehau struct cpu_state {
83a4c3bf5eSSepherosa Ziehau double cpu_qavg;
84a4c3bf5eSSepherosa Ziehau double cpu_uavg; /* used for speeding up */
85a4c3bf5eSSepherosa Ziehau double cpu_davg; /* used for slowing down */
86a4c3bf5eSSepherosa Ziehau int cpu_limit;
87a4c3bf5eSSepherosa Ziehau int cpu_count;
88a4c3bf5eSSepherosa Ziehau char cpu_name[8];
89a4c3bf5eSSepherosa Ziehau };
90b818cc9bSSepherosa Ziehau
91cc537f99SMatthew Dillon static void usage(void);
92a4c3bf5eSSepherosa Ziehau static void get_ncpus(void);
93c6434d78SMatthew Dillon static void mon_cputemp(void);
94a4c3bf5eSSepherosa Ziehau
95a4c3bf5eSSepherosa Ziehau /* usched cpumask */
96a4c3bf5eSSepherosa Ziehau static void get_uschedcpus(void);
97a4c3bf5eSSepherosa Ziehau static void set_uschedcpus(void);
98a4c3bf5eSSepherosa Ziehau
99a4c3bf5eSSepherosa Ziehau /* perfbias(4) */
100a4c3bf5eSSepherosa Ziehau static int has_perfbias(void);
101a4c3bf5eSSepherosa Ziehau static void set_perfbias(int, int);
102a4c3bf5eSSepherosa Ziehau
103a4c3bf5eSSepherosa Ziehau /* acpi(4) P-state */
104a4c3bf5eSSepherosa Ziehau static void acpi_getcpufreq_str(int, int *, int *);
105a4c3bf5eSSepherosa Ziehau static int acpi_getcpufreq_bin(int, int *, int *);
106a4c3bf5eSSepherosa Ziehau static void acpi_get_cpufreq(int, int *, int *);
107a4c3bf5eSSepherosa Ziehau static void acpi_set_cpufreq(int, int);
108a4c3bf5eSSepherosa Ziehau static int acpi_get_cpupwrdom(void);
109a4c3bf5eSSepherosa Ziehau
110764594cfSSepherosa Ziehau /* mwait C-state hint */
111764594cfSSepherosa Ziehau static int probe_cstate(void);
112764594cfSSepherosa Ziehau static void set_cstate(int, int);
113764594cfSSepherosa Ziehau
114a4c3bf5eSSepherosa Ziehau /* Performance monitoring */
115a4c3bf5eSSepherosa Ziehau static void init_perf(void);
116a4c3bf5eSSepherosa Ziehau static void mon_perf(double);
117a4c3bf5eSSepherosa Ziehau static void adj_perf(cpumask_t, cpumask_t);
118a4c3bf5eSSepherosa Ziehau static void adj_cpu_pwrdom(int, int);
119a4c3bf5eSSepherosa Ziehau static void adj_cpu_perf(int, int);
120a4c3bf5eSSepherosa Ziehau static void get_cputime(double);
121a4c3bf5eSSepherosa Ziehau static int get_nstate(struct cpu_state *, double);
122a4c3bf5eSSepherosa Ziehau static void add_spare_cpus(const cpumask_t, int);
123a4c3bf5eSSepherosa Ziehau static void restore_perf(void);
124c6434d78SMatthew Dillon static void set_global_freq(int freq);
125a4c3bf5eSSepherosa Ziehau
126a4c3bf5eSSepherosa Ziehau /* Battery monitoring */
127aa7fc22aSSepherosa Ziehau static int has_battery(void);
128aa7fc22aSSepherosa Ziehau static int mon_battery(void);
129a4c3bf5eSSepherosa Ziehau static void low_battery_alert(int);
130cc537f99SMatthew Dillon
131241985a7SSepherosa Ziehau /* Backlight */
132241985a7SSepherosa Ziehau static void restore_backlight(void);
133241985a7SSepherosa Ziehau
134a4c3bf5eSSepherosa Ziehau /* Runtime states for performance monitoring */
135a4c3bf5eSSepherosa Ziehau static int global_pcpu_limit;
136a4c3bf5eSSepherosa Ziehau static struct cpu_state pcpu_state[MAXCPU];
137a4c3bf5eSSepherosa Ziehau static struct cpu_state global_cpu_state;
138a4c3bf5eSSepherosa Ziehau static cpumask_t cpu_used; /* cpus w/ high perf */
139a4c3bf5eSSepherosa Ziehau static cpumask_t cpu_pwrdom_used; /* cpu power domains w/ high perf */
140a4c3bf5eSSepherosa Ziehau static cpumask_t usched_cpu_used; /* cpus for usched */
141b818cc9bSSepherosa Ziehau
142a4c3bf5eSSepherosa Ziehau /* Constants */
143a4c3bf5eSSepherosa Ziehau static cpumask_t cpu_pwrdom_mask; /* usable cpu power domains */
144a4c3bf5eSSepherosa Ziehau static int cpu2pwrdom[MAXCPU]; /* cpu to cpu power domain map */
145a4c3bf5eSSepherosa Ziehau static struct cpu_pwrdom *cpu_pwrdomain[MAXDOM];
146a4c3bf5eSSepherosa Ziehau static int NCpus; /* # of cpus */
147764594cfSSepherosa Ziehau static char orig_global_cx[CST_STRLEN];
148764594cfSSepherosa Ziehau static char cpu_perf_cx[CST_STRLEN];
149764594cfSSepherosa Ziehau static int cpu_perf_cxlen;
150764594cfSSepherosa Ziehau static char cpu_idle_cx[CST_STRLEN];
151764594cfSSepherosa Ziehau static int cpu_idle_cxlen;
152c6434d78SMatthew Dillon static int FreqAry[MAXFREQ];
153c6434d78SMatthew Dillon static int NFreq;
154ed27c2d1SMatthew Dillon static int NFreqChanged = NFREQ_ALL;
155c6434d78SMatthew Dillon static int SavedPXGlobal;
156a4c3bf5eSSepherosa Ziehau
1572e01237bSSepherosa Ziehau static int DebugOpt;
1582e01237bSSepherosa Ziehau static int TurboOpt = 1;
1592e01237bSSepherosa Ziehau static int PowerFd;
1602e01237bSSepherosa Ziehau static int Hysteresis = 10; /* percentage */
1612e01237bSSepherosa Ziehau static double TriggerUp = 0.25; /* single-cpu load to force max freq */
1622e01237bSSepherosa Ziehau static double TriggerDown; /* load per cpu to force the min freq */
1634f23b27fSSepherosa Ziehau static int HasPerfbias = 0;
164c0faca7fSSepherosa Ziehau static int AdjustCpuFreq = 1;
165764594cfSSepherosa Ziehau static int AdjustCstate = 0;
166a2b9b795SSepherosa Ziehau static int HighestCpuFreq;
167a2b9b795SSepherosa Ziehau static int LowestCpuFreq;
16816be03f6SSepherosa Ziehau static int AdjustUsched = 1;
169a4c3bf5eSSepherosa Ziehau
170c6434d78SMatthew Dillon static int AdjustCpuFreqOverride;
171c6434d78SMatthew Dillon
172a4c3bf5eSSepherosa Ziehau static volatile int stopped;
173a4c3bf5eSSepherosa Ziehau
174a4c3bf5eSSepherosa Ziehau /* Battery life monitoring */
175aa7fc22aSSepherosa Ziehau static int BatLifeMin = 2; /* shutdown the box, if low on battery life */
176aa7fc22aSSepherosa Ziehau static struct timespec BatLifePrevT;
177aa7fc22aSSepherosa Ziehau static int BatLifePollIntvl = 5; /* unit: sec */
178b1b8502eSSepherosa Ziehau static struct timespec BatShutdownStartT;
179b1b8502eSSepherosa Ziehau static int BatShutdownLinger = -1;
180b1b8502eSSepherosa Ziehau static int BatShutdownLingerSet = 60; /* unit: sec */
181b1b8502eSSepherosa Ziehau static int BatShutdownLingerCnt;
182b1b8502eSSepherosa Ziehau static int BatShutdownAudioAlert = 1;
183c6434d78SMatthew Dillon static int MinTemp = 75;
184c6434d78SMatthew Dillon static int MaxTemp = 85;
185f1a9ebd2SSepherosa Ziehau static int BackLightPct = 100;
186f1a9ebd2SSepherosa Ziehau static int OldBackLightLevel;
187f1a9ebd2SSepherosa Ziehau static int BackLightDown;
188b1b8502eSSepherosa Ziehau
189323ed682SMatthew Dillon static void sigintr(int signo);
190323ed682SMatthew Dillon
191cc537f99SMatthew Dillon int
main(int ac,char ** av)192cc537f99SMatthew Dillon main(int ac, char **av)
193cc537f99SMatthew Dillon {
194358bf736SMatthew Dillon double srt;
195358bf736SMatthew Dillon double pollrate;
196cc537f99SMatthew Dillon int ch;
197c6434d78SMatthew Dillon int lowest;
198c6434d78SMatthew Dillon int highest;
19965db32a7SMatthew Dillon char buf[64];
200aa7fc22aSSepherosa Ziehau int monbat;
201c6434d78SMatthew Dillon char *p2;
202cc537f99SMatthew Dillon
203358bf736SMatthew Dillon srt = 8.0; /* time for samples - 8 seconds */
204358bf736SMatthew Dillon pollrate = 1.0; /* polling rate in seconds */
205358bf736SMatthew Dillon
20616be03f6SSepherosa Ziehau while ((ch = getopt(ac, av, "b:cdefh:l:p:r:tu:B:H:L:P:QT:U")) != -1) {
207cc537f99SMatthew Dillon switch(ch) {
208f1a9ebd2SSepherosa Ziehau case 'b':
209f1a9ebd2SSepherosa Ziehau BackLightPct = strtol(optarg, NULL, 10);
210f1a9ebd2SSepherosa Ziehau break;
211764594cfSSepherosa Ziehau case 'c':
212764594cfSSepherosa Ziehau AdjustCstate = 1;
213764594cfSSepherosa Ziehau break;
214cc537f99SMatthew Dillon case 'd':
215cc537f99SMatthew Dillon DebugOpt = 1;
216cc537f99SMatthew Dillon break;
21750214825SSepherosa Ziehau case 'e':
2184f23b27fSSepherosa Ziehau HasPerfbias = 1;
21950214825SSepherosa Ziehau break;
220c0faca7fSSepherosa Ziehau case 'f':
221c0faca7fSSepherosa Ziehau AdjustCpuFreq = 0;
222c0faca7fSSepherosa Ziehau break;
223a2b9b795SSepherosa Ziehau case 'h':
224a2b9b795SSepherosa Ziehau HighestCpuFreq = strtol(optarg, NULL, 10);
225a2b9b795SSepherosa Ziehau break;
226a2b9b795SSepherosa Ziehau case 'l':
227a2b9b795SSepherosa Ziehau LowestCpuFreq = strtol(optarg, NULL, 10);
228a2b9b795SSepherosa Ziehau break;
2291306f384SJoris Giovannangeli case 'p':
2301306f384SJoris Giovannangeli Hysteresis = (int)strtol(optarg, NULL, 10);
2311306f384SJoris Giovannangeli break;
232aa7fc22aSSepherosa Ziehau case 'r':
233aa7fc22aSSepherosa Ziehau pollrate = strtod(optarg, NULL);
234aa7fc22aSSepherosa Ziehau break;
2357d82ba6bSSascha Wildner case 't':
2367d82ba6bSSascha Wildner TurboOpt = 0;
2377d82ba6bSSascha Wildner break;
2381306f384SJoris Giovannangeli case 'u':
2391306f384SJoris Giovannangeli TriggerUp = (double)strtol(optarg, NULL, 10) / 100;
2401306f384SJoris Giovannangeli break;
241aa7fc22aSSepherosa Ziehau case 'B':
242aa7fc22aSSepherosa Ziehau BatLifeMin = strtol(optarg, NULL, 10);
243aa7fc22aSSepherosa Ziehau break;
244c6434d78SMatthew Dillon case 'H':
245c6434d78SMatthew Dillon MaxTemp = strtol(optarg, &p2, 0);
246c6434d78SMatthew Dillon if (*p2 == ':') {
247c6434d78SMatthew Dillon MinTemp = MaxTemp;
248c6434d78SMatthew Dillon MaxTemp = strtol(p2 + 1, NULL, 0);
249c6434d78SMatthew Dillon } else {
250c6434d78SMatthew Dillon MinTemp = MaxTemp * 9 / 10;
251c6434d78SMatthew Dillon }
252c6434d78SMatthew Dillon break;
253aa7fc22aSSepherosa Ziehau case 'L':
254b1b8502eSSepherosa Ziehau BatShutdownLingerSet = strtol(optarg, NULL, 10);
255b1b8502eSSepherosa Ziehau if (BatShutdownLingerSet < 0)
256b1b8502eSSepherosa Ziehau BatShutdownLingerSet = 0;
257aa7fc22aSSepherosa Ziehau break;
258aa7fc22aSSepherosa Ziehau case 'P':
259aa7fc22aSSepherosa Ziehau BatLifePollIntvl = strtol(optarg, NULL, 10);
260358bf736SMatthew Dillon break;
261b1b8502eSSepherosa Ziehau case 'Q':
262b1b8502eSSepherosa Ziehau BatShutdownAudioAlert = 0;
263b1b8502eSSepherosa Ziehau break;
264358bf736SMatthew Dillon case 'T':
265358bf736SMatthew Dillon srt = strtod(optarg, NULL);
266358bf736SMatthew Dillon break;
26716be03f6SSepherosa Ziehau case 'U':
26816be03f6SSepherosa Ziehau AdjustUsched = 0;
26916be03f6SSepherosa Ziehau break;
270cc537f99SMatthew Dillon default:
271cc537f99SMatthew Dillon usage();
272cc537f99SMatthew Dillon /* NOT REACHED */
273cc537f99SMatthew Dillon }
274cc537f99SMatthew Dillon }
275cc537f99SMatthew Dillon ac -= optind;
27689a28649SAntonio Huete Jimenez av += optind;
277cc537f99SMatthew Dillon
278a4c3bf5eSSepherosa Ziehau setlinebuf(stdout);
27925780572SSepherosa Ziehau
280a4c3bf5eSSepherosa Ziehau /* Get number of cpus */
281a4c3bf5eSSepherosa Ziehau get_ncpus();
28248c660dcSSepherosa Ziehau
283c6434d78SMatthew Dillon /* Seed FreqAry[] */
284c6434d78SMatthew Dillon acpi_get_cpufreq(0, &lowest, &highest);
285c6434d78SMatthew Dillon
286c6434d78SMatthew Dillon if (Hysteresis < 0 || Hysteresis > 99) {
2871306f384SJoris Giovannangeli fprintf(stderr, "Invalid hysteresis value\n");
2881306f384SJoris Giovannangeli exit(1);
2891306f384SJoris Giovannangeli }
2901306f384SJoris Giovannangeli
291c6434d78SMatthew Dillon if (TriggerUp < 0 || TriggerUp > 1) {
2921306f384SJoris Giovannangeli fprintf(stderr, "Invalid load limit value\n");
2931306f384SJoris Giovannangeli exit(1);
2941306f384SJoris Giovannangeli }
2951306f384SJoris Giovannangeli
296f1a9ebd2SSepherosa Ziehau if (BackLightPct > 100 || BackLightPct <= 0) {
297f1a9ebd2SSepherosa Ziehau fprintf(stderr, "Invalid backlight setting, ignore\n");
298f1a9ebd2SSepherosa Ziehau BackLightPct = 100;
299f1a9ebd2SSepherosa Ziehau }
300f1a9ebd2SSepherosa Ziehau
3011306f384SJoris Giovannangeli TriggerDown = TriggerUp - (TriggerUp * (double) Hysteresis / 100);
3021306f384SJoris Giovannangeli
303cc537f99SMatthew Dillon /*
3048db2b576SSepherosa Ziehau * Make sure powerd is not already running.
305cc537f99SMatthew Dillon */
306cc537f99SMatthew Dillon PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644);
307cc537f99SMatthew Dillon if (PowerFd < 0) {
308cc537f99SMatthew Dillon fprintf(stderr,
309cc537f99SMatthew Dillon "Cannot create /var/run/powerd.pid, "
310cc537f99SMatthew Dillon "continuing anyway\n");
311cc537f99SMatthew Dillon } else {
312c6434d78SMatthew Dillon ssize_t r;
313c6434d78SMatthew Dillon pid_t pid = -1;
314c6434d78SMatthew Dillon
315c6434d78SMatthew Dillon r = read(PowerFd, buf, sizeof(buf) - 1);
316c6434d78SMatthew Dillon if (r > 0) {
317c6434d78SMatthew Dillon buf[r] = 0;
318c6434d78SMatthew Dillon pid = strtol(buf, NULL, 0);
319c6434d78SMatthew Dillon }
320cc537f99SMatthew Dillon if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) {
321c6434d78SMatthew Dillon if (pid > 0) {
322c6434d78SMatthew Dillon kill(pid, SIGTERM);
323c6434d78SMatthew Dillon flock(PowerFd, LOCK_EX);
324c6434d78SMatthew Dillon fprintf(stderr, "restarting powerd\n");
325c6434d78SMatthew Dillon } else {
326c6434d78SMatthew Dillon fprintf(stderr,
327c6434d78SMatthew Dillon "powerd is already running, "
328c6434d78SMatthew Dillon "unable to kill pid for restart\n");
329cc537f99SMatthew Dillon exit(1);
330cc537f99SMatthew Dillon }
331cc537f99SMatthew Dillon }
332c6434d78SMatthew Dillon lseek(PowerFd, 0L, 0);
333c6434d78SMatthew Dillon }
334cc537f99SMatthew Dillon
335cc537f99SMatthew Dillon /*
336cc537f99SMatthew Dillon * Demonize and set pid
337cc537f99SMatthew Dillon */
33882d1bcc2SSascha Wildner if (DebugOpt == 0) {
339cc537f99SMatthew Dillon daemon(0, 0);
34082d1bcc2SSascha Wildner openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON);
34182d1bcc2SSascha Wildner }
342cc537f99SMatthew Dillon
343cc537f99SMatthew Dillon if (PowerFd >= 0) {
344cc537f99SMatthew Dillon ftruncate(PowerFd, 0);
345cc537f99SMatthew Dillon snprintf(buf, sizeof(buf), "%d\n", (int)getpid());
346cc537f99SMatthew Dillon write(PowerFd, buf, strlen(buf));
347cc537f99SMatthew Dillon }
348cc537f99SMatthew Dillon
349aa7fc22aSSepherosa Ziehau /* Do we need to monitor battery life? */
350b1b8502eSSepherosa Ziehau if (BatLifePollIntvl <= 0)
351b1b8502eSSepherosa Ziehau monbat = 0;
352b1b8502eSSepherosa Ziehau else
353aa7fc22aSSepherosa Ziehau monbat = has_battery();
354aa7fc22aSSepherosa Ziehau
355a4c3bf5eSSepherosa Ziehau /* Do we have perfbias(4)? */
35650214825SSepherosa Ziehau if (HasPerfbias)
35750214825SSepherosa Ziehau HasPerfbias = has_perfbias();
35850214825SSepherosa Ziehau
359764594cfSSepherosa Ziehau /* Could we adjust C-state? */
360764594cfSSepherosa Ziehau if (AdjustCstate)
361764594cfSSepherosa Ziehau AdjustCstate = probe_cstate();
362764594cfSSepherosa Ziehau
363cc537f99SMatthew Dillon /*
364a4c3bf5eSSepherosa Ziehau * Wait hw.acpi.cpu.px_dom* sysctl to be created by kernel.
3658db2b576SSepherosa Ziehau *
3668db2b576SSepherosa Ziehau * Since hw.acpi.cpu.px_dom* creation is queued into ACPI
3678db2b576SSepherosa Ziehau * taskqueue and ACPI taskqueue is shared across various
3688db2b576SSepherosa Ziehau * ACPI modules, any delay in other modules may cause
3698db2b576SSepherosa Ziehau * hw.acpi.cpu.px_dom* to be created at quite a later time
3708db2b576SSepherosa Ziehau * (e.g. cmbat module's task could take quite a lot of time).
3718db2b576SSepherosa Ziehau */
3728db2b576SSepherosa Ziehau for (;;) {
373a4c3bf5eSSepherosa Ziehau /* Prime delta cputime calculation. */
374a4c3bf5eSSepherosa Ziehau get_cputime(pollrate);
375a4c3bf5eSSepherosa Ziehau
376a4c3bf5eSSepherosa Ziehau /* Wait for all cpus to appear */
377a4c3bf5eSSepherosa Ziehau if (acpi_get_cpupwrdom())
378dddb241cSSepherosa Ziehau break;
379358bf736SMatthew Dillon usleep((int)(pollrate * 1000000.0));
3808db2b576SSepherosa Ziehau }
3818db2b576SSepherosa Ziehau
382dddb241cSSepherosa Ziehau /*
383a4c3bf5eSSepherosa Ziehau * Catch some signals so that max performance could be restored.
384323ed682SMatthew Dillon */
385323ed682SMatthew Dillon signal(SIGINT, sigintr);
386323ed682SMatthew Dillon signal(SIGTERM, sigintr);
387a4c3bf5eSSepherosa Ziehau
388a4c3bf5eSSepherosa Ziehau /* Initialize performance states */
389a4c3bf5eSSepherosa Ziehau init_perf();
390358bf736SMatthew Dillon
391358bf736SMatthew Dillon srt = srt / pollrate; /* convert to sample count */
392358bf736SMatthew Dillon if (DebugOpt)
393358bf736SMatthew Dillon printf("samples for downgrading: %5.2f\n", srt);
394323ed682SMatthew Dillon
395323ed682SMatthew Dillon /*
396cc537f99SMatthew Dillon * Monitoring loop
397cc537f99SMatthew Dillon */
398a4c3bf5eSSepherosa Ziehau while (!stopped) {
399a4c3bf5eSSepherosa Ziehau /*
400a4c3bf5eSSepherosa Ziehau * Monitor performance
401a4c3bf5eSSepherosa Ziehau */
402a4c3bf5eSSepherosa Ziehau get_cputime(pollrate);
403c6434d78SMatthew Dillon mon_cputemp();
404a4c3bf5eSSepherosa Ziehau mon_perf(srt);
405cc537f99SMatthew Dillon
406a4c3bf5eSSepherosa Ziehau /*
407a4c3bf5eSSepherosa Ziehau * Monitor battery
408a4c3bf5eSSepherosa Ziehau */
409aa7fc22aSSepherosa Ziehau if (monbat)
410aa7fc22aSSepherosa Ziehau monbat = mon_battery();
411cc537f99SMatthew Dillon
412a4c3bf5eSSepherosa Ziehau usleep((int)(pollrate * 1000000.0));
413323ed682SMatthew Dillon }
414323ed682SMatthew Dillon
415cc537f99SMatthew Dillon /*
416a4c3bf5eSSepherosa Ziehau * Set to maximum performance if killed.
417a4c3bf5eSSepherosa Ziehau */
418a4c3bf5eSSepherosa Ziehau syslog(LOG_INFO, "killed, setting max and exiting");
419c6434d78SMatthew Dillon if (SavedPXGlobal)
420c6434d78SMatthew Dillon set_global_freq(SavedPXGlobal);
421a4c3bf5eSSepherosa Ziehau restore_perf();
422241985a7SSepherosa Ziehau restore_backlight();
423a4c3bf5eSSepherosa Ziehau
424a4c3bf5eSSepherosa Ziehau exit(0);
425a4c3bf5eSSepherosa Ziehau }
426a4c3bf5eSSepherosa Ziehau
427a4c3bf5eSSepherosa Ziehau static void
sigintr(int signo __unused)428a4c3bf5eSSepherosa Ziehau sigintr(int signo __unused)
429a4c3bf5eSSepherosa Ziehau {
430a4c3bf5eSSepherosa Ziehau stopped = 1;
431a4c3bf5eSSepherosa Ziehau }
432a4c3bf5eSSepherosa Ziehau
433a4c3bf5eSSepherosa Ziehau /*
434a4c3bf5eSSepherosa Ziehau * Figure out the cpu power domains.
43565db32a7SMatthew Dillon */
436dddb241cSSepherosa Ziehau static int
acpi_get_cpupwrdom(void)437a4c3bf5eSSepherosa Ziehau acpi_get_cpupwrdom(void)
43865db32a7SMatthew Dillon {
439b818cc9bSSepherosa Ziehau struct cpu_pwrdom *dom;
440a4c3bf5eSSepherosa Ziehau cpumask_t pwrdom_mask;
44165db32a7SMatthew Dillon char buf[64];
44265db32a7SMatthew Dillon char members[1024];
44365db32a7SMatthew Dillon char *str;
44465db32a7SMatthew Dillon size_t msize;
445a4c3bf5eSSepherosa Ziehau int n, i, ncpu = 0, dom_id;
44665db32a7SMatthew Dillon
447a4c3bf5eSSepherosa Ziehau memset(cpu2pwrdom, 0, sizeof(cpu2pwrdom));
448a4c3bf5eSSepherosa Ziehau memset(cpu_pwrdomain, 0, sizeof(cpu_pwrdomain));
449a4c3bf5eSSepherosa Ziehau CPUMASK_ASSZERO(cpu_pwrdom_mask);
450dddb241cSSepherosa Ziehau
4518b799ad2SSepherosa Ziehau for (i = 0; i < MAXDOM; ++i) {
45265db32a7SMatthew Dillon snprintf(buf, sizeof(buf),
45365db32a7SMatthew Dillon "hw.acpi.cpu.px_dom%d.available", i);
454b818cc9bSSepherosa Ziehau if (sysctlbyname(buf, NULL, NULL, NULL, 0) < 0)
455b818cc9bSSepherosa Ziehau continue;
45665db32a7SMatthew Dillon
457b818cc9bSSepherosa Ziehau dom = calloc(1, sizeof(*dom));
458b818cc9bSSepherosa Ziehau dom->dom_id = i;
459a4c3bf5eSSepherosa Ziehau
460a4c3bf5eSSepherosa Ziehau if (cpu_pwrdomain[i] != NULL) {
461a4c3bf5eSSepherosa Ziehau fprintf(stderr, "cpu power domain %d exists\n", i);
462a4c3bf5eSSepherosa Ziehau exit(1);
46365db32a7SMatthew Dillon }
464a4c3bf5eSSepherosa Ziehau cpu_pwrdomain[i] = dom;
465a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(cpu_pwrdom_mask, i);
466a4c3bf5eSSepherosa Ziehau }
467a4c3bf5eSSepherosa Ziehau pwrdom_mask = cpu_pwrdom_mask;
46865db32a7SMatthew Dillon
469a4c3bf5eSSepherosa Ziehau while (CPUMASK_TESTNZERO(pwrdom_mask)) {
470a4c3bf5eSSepherosa Ziehau dom_id = BSFCPUMASK(pwrdom_mask);
471a4c3bf5eSSepherosa Ziehau CPUMASK_NANDBIT(pwrdom_mask, dom_id);
472a4c3bf5eSSepherosa Ziehau dom = cpu_pwrdomain[dom_id];
473b818cc9bSSepherosa Ziehau
474bd9ff91eSSepherosa Ziehau CPUMASK_ASSZERO(dom->dom_cpumask);
475b818cc9bSSepherosa Ziehau
47665db32a7SMatthew Dillon snprintf(buf, sizeof(buf),
477b818cc9bSSepherosa Ziehau "hw.acpi.cpu.px_dom%d.members", dom->dom_id);
47865db32a7SMatthew Dillon msize = sizeof(members);
479b818cc9bSSepherosa Ziehau if (sysctlbyname(buf, members, &msize, NULL, 0) < 0) {
480a4c3bf5eSSepherosa Ziehau cpu_pwrdomain[dom_id] = NULL;
481b818cc9bSSepherosa Ziehau free(dom);
482b818cc9bSSepherosa Ziehau continue;
483b818cc9bSSepherosa Ziehau }
484b818cc9bSSepherosa Ziehau
48565db32a7SMatthew Dillon members[msize] = 0;
486b818cc9bSSepherosa Ziehau for (str = strtok(members, " "); str; str = strtok(NULL, " ")) {
48765db32a7SMatthew Dillon n = -1;
48865db32a7SMatthew Dillon sscanf(str, "cpu%d", &n);
48965db32a7SMatthew Dillon if (n >= 0) {
490f1b8fbfeSSepherosa Ziehau ++ncpu;
491b818cc9bSSepherosa Ziehau ++dom->dom_ncpus;
492bd9ff91eSSepherosa Ziehau CPUMASK_ORBIT(dom->dom_cpumask, n);
493a4c3bf5eSSepherosa Ziehau cpu2pwrdom[n] = dom->dom_id;
49465db32a7SMatthew Dillon }
49565db32a7SMatthew Dillon }
496b818cc9bSSepherosa Ziehau if (dom->dom_ncpus == 0) {
497a4c3bf5eSSepherosa Ziehau cpu_pwrdomain[dom_id] = NULL;
498b818cc9bSSepherosa Ziehau free(dom);
499b818cc9bSSepherosa Ziehau continue;
50065db32a7SMatthew Dillon }
501bd9ff91eSSepherosa Ziehau if (DebugOpt) {
502bd9ff91eSSepherosa Ziehau printf("dom%d cpumask: ", dom->dom_id);
503bd9ff91eSSepherosa Ziehau for (i = 0; i < (int)NELEM(dom->dom_cpumask.ary); ++i) {
504bd9ff91eSSepherosa Ziehau printf("%jx ",
505bd9ff91eSSepherosa Ziehau (uintmax_t)dom->dom_cpumask.ary[i]);
506bd9ff91eSSepherosa Ziehau }
507bd9ff91eSSepherosa Ziehau printf("\n");
508bd9ff91eSSepherosa Ziehau }
509b818cc9bSSepherosa Ziehau }
510dddb241cSSepherosa Ziehau
511f1b8fbfeSSepherosa Ziehau if (ncpu != NCpus) {
512a4c3bf5eSSepherosa Ziehau if (DebugOpt)
513f1b8fbfeSSepherosa Ziehau printf("Found %d cpus, expecting %d\n", ncpu, NCpus);
514a4c3bf5eSSepherosa Ziehau
515a4c3bf5eSSepherosa Ziehau pwrdom_mask = cpu_pwrdom_mask;
516a4c3bf5eSSepherosa Ziehau while (CPUMASK_TESTNZERO(pwrdom_mask)) {
517a4c3bf5eSSepherosa Ziehau dom_id = BSFCPUMASK(pwrdom_mask);
518a4c3bf5eSSepherosa Ziehau CPUMASK_NANDBIT(pwrdom_mask, dom_id);
519a4c3bf5eSSepherosa Ziehau dom = cpu_pwrdomain[dom_id];
520a4c3bf5eSSepherosa Ziehau if (dom != NULL)
521a4c3bf5eSSepherosa Ziehau free(dom);
522dddb241cSSepherosa Ziehau }
523dddb241cSSepherosa Ziehau return 0;
524dddb241cSSepherosa Ziehau }
525dddb241cSSepherosa Ziehau return 1;
52665db32a7SMatthew Dillon }
52765db32a7SMatthew Dillon
52865db32a7SMatthew Dillon /*
529a4c3bf5eSSepherosa Ziehau * Save per-cpu load and sum of per-cpu load.
530cc537f99SMatthew Dillon */
531a4c3bf5eSSepherosa Ziehau static void
get_cputime(double pollrate)532a4c3bf5eSSepherosa Ziehau get_cputime(double pollrate)
533cc537f99SMatthew Dillon {
53425780572SSepherosa Ziehau static struct kinfo_cputime ocpu_time[MAXCPU];
53525780572SSepherosa Ziehau static struct kinfo_cputime ncpu_time[MAXCPU];
536572bda79SMatthew Dillon size_t slen;
537cc537f99SMatthew Dillon int ncpu;
538cc537f99SMatthew Dillon int cpu;
539cc537f99SMatthew Dillon uint64_t delta;
540cc537f99SMatthew Dillon
541f1b8fbfeSSepherosa Ziehau bcopy(ncpu_time, ocpu_time, sizeof(struct kinfo_cputime) * NCpus);
542dddb241cSSepherosa Ziehau
543cc537f99SMatthew Dillon slen = sizeof(ncpu_time);
544cc537f99SMatthew Dillon if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) {
545cc537f99SMatthew Dillon fprintf(stderr, "kern.cputime sysctl not available\n");
546cc537f99SMatthew Dillon exit(1);
547cc537f99SMatthew Dillon }
548cc537f99SMatthew Dillon ncpu = slen / sizeof(ncpu_time[0]);
549cc537f99SMatthew Dillon
55025780572SSepherosa Ziehau delta = 0;
551cc537f99SMatthew Dillon for (cpu = 0; cpu < ncpu; ++cpu) {
552a4c3bf5eSSepherosa Ziehau uint64_t d;
553a4c3bf5eSSepherosa Ziehau
554a4c3bf5eSSepherosa Ziehau d = (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys +
5558579bb40SSepherosa Ziehau ncpu_time[cpu].cp_nice + ncpu_time[cpu].cp_intr) -
556cc537f99SMatthew Dillon (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys +
5573eee038fSMatthew Dillon ocpu_time[cpu].cp_nice + ocpu_time[cpu].cp_intr);
558a4c3bf5eSSepherosa Ziehau pcpu_state[cpu].cpu_qavg = (double)d / (pollrate * 1000000.0);
559a4c3bf5eSSepherosa Ziehau
560a4c3bf5eSSepherosa Ziehau delta += d;
561cc537f99SMatthew Dillon }
562a4c3bf5eSSepherosa Ziehau global_cpu_state.cpu_qavg = (double)delta / (pollrate * 1000000.0);
563cc537f99SMatthew Dillon }
564cc537f99SMatthew Dillon
5654afb1280SSepherosa Ziehau static void
acpi_getcpufreq_str(int dom_id,int * highest0,int * lowest0)5664afb1280SSepherosa Ziehau acpi_getcpufreq_str(int dom_id, int *highest0, int *lowest0)
5674afb1280SSepherosa Ziehau {
5684afb1280SSepherosa Ziehau char buf[256], sysid[64];
5694afb1280SSepherosa Ziehau size_t buflen;
5704afb1280SSepherosa Ziehau char *ptr;
5714afb1280SSepherosa Ziehau int v, highest, lowest;
572c6434d78SMatthew Dillon int freqidx;
5734afb1280SSepherosa Ziehau
5744afb1280SSepherosa Ziehau /*
5754afb1280SSepherosa Ziehau * Retrieve availability list
5764afb1280SSepherosa Ziehau */
577c6434d78SMatthew Dillon snprintf(sysid, sizeof(sysid),
578c6434d78SMatthew Dillon "hw.acpi.cpu.px_dom%d.available", dom_id);
5794afb1280SSepherosa Ziehau buflen = sizeof(buf) - 1;
5804afb1280SSepherosa Ziehau if (sysctlbyname(sysid, buf, &buflen, NULL, 0) < 0)
5814afb1280SSepherosa Ziehau return;
5824afb1280SSepherosa Ziehau buf[buflen] = 0;
5834afb1280SSepherosa Ziehau
5844afb1280SSepherosa Ziehau /*
5854afb1280SSepherosa Ziehau * Parse out the highest and lowest cpu frequencies
5864afb1280SSepherosa Ziehau */
5874afb1280SSepherosa Ziehau ptr = buf;
5884afb1280SSepherosa Ziehau highest = lowest = 0;
589c6434d78SMatthew Dillon freqidx = 0;
5904afb1280SSepherosa Ziehau while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
591a2b9b795SSepherosa Ziehau if ((lowest == 0 || lowest > v) &&
592a2b9b795SSepherosa Ziehau (LowestCpuFreq <= 0 || v >= LowestCpuFreq))
5934afb1280SSepherosa Ziehau lowest = v;
594a2b9b795SSepherosa Ziehau if ((highest == 0 || highest < v) &&
595a2b9b795SSepherosa Ziehau (HighestCpuFreq <= 0 || v <= HighestCpuFreq))
5964afb1280SSepherosa Ziehau highest = v;
5974afb1280SSepherosa Ziehau /*
5984afb1280SSepherosa Ziehau * Detect turbo mode
5994afb1280SSepherosa Ziehau */
600e74d6cd2SSepherosa Ziehau if (!TurboOpt && highest - v == 1)
6014afb1280SSepherosa Ziehau highest = v;
602c6434d78SMatthew Dillon ++freqidx;
603c6434d78SMatthew Dillon }
604c6434d78SMatthew Dillon
605c6434d78SMatthew Dillon /*
606c6434d78SMatthew Dillon * Frequency array
607c6434d78SMatthew Dillon */
608ed27c2d1SMatthew Dillon if (freqidx > MAXFREQ)
609ed27c2d1SMatthew Dillon freqidx = MAXFREQ;
610ed27c2d1SMatthew Dillon if (NFreq != freqidx) {
611c6434d78SMatthew Dillon NFreq = freqidx;
612ed27c2d1SMatthew Dillon NFreqChanged = NFREQ_ALL;
613ed27c2d1SMatthew Dillon }
614c6434d78SMatthew Dillon ptr = buf;
615c6434d78SMatthew Dillon while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
616c6434d78SMatthew Dillon if (freqidx == 0)
617c6434d78SMatthew Dillon break;
618ed27c2d1SMatthew Dillon if (FreqAry[freqidx - 1] != v)
619ed27c2d1SMatthew Dillon NFreqChanged = NFREQ_ALL;
620c6434d78SMatthew Dillon FreqAry[--freqidx] = v;
6214afb1280SSepherosa Ziehau }
6224afb1280SSepherosa Ziehau
6234afb1280SSepherosa Ziehau *highest0 = highest;
6244afb1280SSepherosa Ziehau *lowest0 = lowest;
6254afb1280SSepherosa Ziehau }
6264afb1280SSepherosa Ziehau
6274afb1280SSepherosa Ziehau static int
acpi_getcpufreq_bin(int dom_id,int * highest0,int * lowest0)6284afb1280SSepherosa Ziehau acpi_getcpufreq_bin(int dom_id, int *highest0, int *lowest0)
6294afb1280SSepherosa Ziehau {
6304afb1280SSepherosa Ziehau char sysid[64];
6314afb1280SSepherosa Ziehau size_t freqlen;
632a2b9b795SSepherosa Ziehau int freqcnt, i;
633ed27c2d1SMatthew Dillon int freqary[MAXFREQ];
6344afb1280SSepherosa Ziehau
6354afb1280SSepherosa Ziehau /*
6364afb1280SSepherosa Ziehau * Retrieve availability list
6374afb1280SSepherosa Ziehau */
638df29c611SSepherosa Ziehau snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.avail", dom_id);
639c6434d78SMatthew Dillon freqlen = sizeof(FreqAry);
640ed27c2d1SMatthew Dillon bzero(freqary, sizeof(freqary));
641ed27c2d1SMatthew Dillon if (sysctlbyname(sysid, freqary, &freqlen, NULL, 0) < 0)
6424afb1280SSepherosa Ziehau return 0;
6434afb1280SSepherosa Ziehau
644ed27c2d1SMatthew Dillon freqcnt = freqlen / sizeof(freqary[0]);
645ed27c2d1SMatthew Dillon if (NFreq != freqcnt) {
646ed27c2d1SMatthew Dillon NFreq = freqcnt;
647ed27c2d1SMatthew Dillon NFreqChanged = NFREQ_ALL;
648ed27c2d1SMatthew Dillon }
649ed27c2d1SMatthew Dillon if (bcmp(freqary, FreqAry, sizeof(FreqAry)) != 0)
650ed27c2d1SMatthew Dillon NFreqChanged = NFREQ_ALL;
651ed27c2d1SMatthew Dillon bcopy(freqary, FreqAry, sizeof(FreqAry));
6524afb1280SSepherosa Ziehau if (freqcnt == 0)
6534afb1280SSepherosa Ziehau return 0;
6544afb1280SSepherosa Ziehau
655a2b9b795SSepherosa Ziehau for (i = freqcnt - 1; i >= 0; --i) {
656c6434d78SMatthew Dillon *lowest0 = FreqAry[i];
657a2b9b795SSepherosa Ziehau if (LowestCpuFreq <= 0 || *lowest0 >= LowestCpuFreq)
658a2b9b795SSepherosa Ziehau break;
659a2b9b795SSepherosa Ziehau }
6604afb1280SSepherosa Ziehau
661a2b9b795SSepherosa Ziehau i = 0;
662c6434d78SMatthew Dillon *highest0 = FreqAry[0];
663c6434d78SMatthew Dillon if (!TurboOpt && freqcnt > 1 && FreqAry[0] - FreqAry[1] == 1) {
664a2b9b795SSepherosa Ziehau i = 1;
665c6434d78SMatthew Dillon *highest0 = FreqAry[1];
666a2b9b795SSepherosa Ziehau }
667a2b9b795SSepherosa Ziehau for (; i < freqcnt; ++i) {
668a2b9b795SSepherosa Ziehau if (HighestCpuFreq <= 0 || *highest0 <= HighestCpuFreq)
669a2b9b795SSepherosa Ziehau break;
670c6434d78SMatthew Dillon *highest0 = FreqAry[i];
671a2b9b795SSepherosa Ziehau }
6724afb1280SSepherosa Ziehau return 1;
6734afb1280SSepherosa Ziehau }
6744afb1280SSepherosa Ziehau
6754afb1280SSepherosa Ziehau static void
acpi_get_cpufreq(int dom_id,int * highest,int * lowest)676a4c3bf5eSSepherosa Ziehau acpi_get_cpufreq(int dom_id, int *highest, int *lowest)
6774afb1280SSepherosa Ziehau {
6784afb1280SSepherosa Ziehau *highest = 0;
6794afb1280SSepherosa Ziehau *lowest = 0;
6804afb1280SSepherosa Ziehau
6814afb1280SSepherosa Ziehau if (acpi_getcpufreq_bin(dom_id, highest, lowest))
6824afb1280SSepherosa Ziehau return;
6834afb1280SSepherosa Ziehau acpi_getcpufreq_str(dom_id, highest, lowest);
6844afb1280SSepherosa Ziehau }
6854afb1280SSepherosa Ziehau
686cc537f99SMatthew Dillon static
687cc537f99SMatthew Dillon void
usage(void)688cc537f99SMatthew Dillon usage(void)
689cc537f99SMatthew Dillon {
69016be03f6SSepherosa Ziehau fprintf(stderr, "usage: powerd [-cdeftQU] [-p hysteresis] "
691a2b9b795SSepherosa Ziehau "[-h highest_freq] [-l lowest_freq] "
692c0faca7fSSepherosa Ziehau "[-r poll_interval] [-u trigger_up] "
693aa7fc22aSSepherosa Ziehau "[-B min_battery_life] [-L low_battery_linger] "
694f1a9ebd2SSepherosa Ziehau "[-P battery_poll_interval] [-T sample_interval] "
695f1a9ebd2SSepherosa Ziehau "[-b backlight]\n");
696cc537f99SMatthew Dillon exit(1);
697cc537f99SMatthew Dillon }
698aa7fc22aSSepherosa Ziehau
699aa7fc22aSSepherosa Ziehau #define BAT_SYSCTL_TIME_MAX 50000000 /* unit: nanosecond */
700aa7fc22aSSepherosa Ziehau
701aa7fc22aSSepherosa Ziehau static int
has_battery(void)702aa7fc22aSSepherosa Ziehau has_battery(void)
703aa7fc22aSSepherosa Ziehau {
704aa7fc22aSSepherosa Ziehau struct timespec s, e;
705aa7fc22aSSepherosa Ziehau size_t len;
706aa7fc22aSSepherosa Ziehau int val;
707aa7fc22aSSepherosa Ziehau
708aa7fc22aSSepherosa Ziehau clock_gettime(CLOCK_MONOTONIC_FAST, &s);
709aa7fc22aSSepherosa Ziehau BatLifePrevT = s;
710aa7fc22aSSepherosa Ziehau
711aa7fc22aSSepherosa Ziehau len = sizeof(val);
712aa7fc22aSSepherosa Ziehau if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) {
713aa7fc22aSSepherosa Ziehau /* No AC line information */
714aa7fc22aSSepherosa Ziehau return 0;
715aa7fc22aSSepherosa Ziehau }
716aa7fc22aSSepherosa Ziehau clock_gettime(CLOCK_MONOTONIC_FAST, &e);
717aa7fc22aSSepherosa Ziehau
718944cd60cSSascha Wildner timespecsub(&e, &s, &e);
719aa7fc22aSSepherosa Ziehau if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
720aa7fc22aSSepherosa Ziehau /* hw.acpi.acline takes to long to be useful */
721aa7fc22aSSepherosa Ziehau syslog(LOG_NOTICE, "hw.acpi.acline takes too long");
722aa7fc22aSSepherosa Ziehau return 0;
723aa7fc22aSSepherosa Ziehau }
724aa7fc22aSSepherosa Ziehau
725aa7fc22aSSepherosa Ziehau clock_gettime(CLOCK_MONOTONIC_FAST, &s);
726aa7fc22aSSepherosa Ziehau len = sizeof(val);
727aa7fc22aSSepherosa Ziehau if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) {
728aa7fc22aSSepherosa Ziehau /* No battery life */
729aa7fc22aSSepherosa Ziehau return 0;
730aa7fc22aSSepherosa Ziehau }
731aa7fc22aSSepherosa Ziehau clock_gettime(CLOCK_MONOTONIC_FAST, &e);
732aa7fc22aSSepherosa Ziehau
733944cd60cSSascha Wildner timespecsub(&e, &s, &e);
734aa7fc22aSSepherosa Ziehau if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
735aa7fc22aSSepherosa Ziehau /* hw.acpi.battery.life takes to long to be useful */
736aa7fc22aSSepherosa Ziehau syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long");
737aa7fc22aSSepherosa Ziehau return 0;
738aa7fc22aSSepherosa Ziehau }
739aa7fc22aSSepherosa Ziehau return 1;
740aa7fc22aSSepherosa Ziehau }
741aa7fc22aSSepherosa Ziehau
742b1b8502eSSepherosa Ziehau static void
low_battery_alert(int life)743b1b8502eSSepherosa Ziehau low_battery_alert(int life)
744b1b8502eSSepherosa Ziehau {
745b1b8502eSSepherosa Ziehau int fmt, stereo, freq;
746b1b8502eSSepherosa Ziehau int fd;
747b1b8502eSSepherosa Ziehau
748b1b8502eSSepherosa Ziehau syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d",
749b1b8502eSSepherosa Ziehau life, BatShutdownLingerCnt);
750b1b8502eSSepherosa Ziehau ++BatShutdownLingerCnt;
751b1b8502eSSepherosa Ziehau
752b1b8502eSSepherosa Ziehau if (!BatShutdownAudioAlert)
753b1b8502eSSepherosa Ziehau return;
754b1b8502eSSepherosa Ziehau
755b1b8502eSSepherosa Ziehau fd = open("/dev/dsp", O_WRONLY);
756b1b8502eSSepherosa Ziehau if (fd < 0)
757b1b8502eSSepherosa Ziehau return;
758b1b8502eSSepherosa Ziehau
759b1b8502eSSepherosa Ziehau fmt = AFMT_S16_LE;
760b1b8502eSSepherosa Ziehau if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0)
761b1b8502eSSepherosa Ziehau goto done;
762b1b8502eSSepherosa Ziehau
763b1b8502eSSepherosa Ziehau stereo = 0;
764b1b8502eSSepherosa Ziehau if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0)
765b1b8502eSSepherosa Ziehau goto done;
766b1b8502eSSepherosa Ziehau
767b1b8502eSSepherosa Ziehau freq = 44100;
768b1b8502eSSepherosa Ziehau if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0)
769b1b8502eSSepherosa Ziehau goto done;
770b1b8502eSSepherosa Ziehau
771b1b8502eSSepherosa Ziehau write(fd, alert1, sizeof(alert1));
772b1b8502eSSepherosa Ziehau write(fd, alert1, sizeof(alert1));
773b1b8502eSSepherosa Ziehau
774b1b8502eSSepherosa Ziehau done:
775b1b8502eSSepherosa Ziehau close(fd);
776b1b8502eSSepherosa Ziehau }
777b1b8502eSSepherosa Ziehau
778aa7fc22aSSepherosa Ziehau static int
mon_battery(void)779aa7fc22aSSepherosa Ziehau mon_battery(void)
780aa7fc22aSSepherosa Ziehau {
781aa7fc22aSSepherosa Ziehau struct timespec cur, ts;
782aa7fc22aSSepherosa Ziehau int acline, life;
783aa7fc22aSSepherosa Ziehau size_t len;
784aa7fc22aSSepherosa Ziehau
785aa7fc22aSSepherosa Ziehau clock_gettime(CLOCK_MONOTONIC_FAST, &cur);
786944cd60cSSascha Wildner timespecsub(&cur, &BatLifePrevT, &ts);
787aa7fc22aSSepherosa Ziehau if (ts.tv_sec < BatLifePollIntvl)
788aa7fc22aSSepherosa Ziehau return 1;
789aa7fc22aSSepherosa Ziehau BatLifePrevT = cur;
790aa7fc22aSSepherosa Ziehau
791aa7fc22aSSepherosa Ziehau len = sizeof(acline);
792aa7fc22aSSepherosa Ziehau if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0)
793aa7fc22aSSepherosa Ziehau return 1;
794b1b8502eSSepherosa Ziehau if (acline) {
795b1b8502eSSepherosa Ziehau BatShutdownLinger = -1;
796b1b8502eSSepherosa Ziehau BatShutdownLingerCnt = 0;
797241985a7SSepherosa Ziehau restore_backlight();
798aa7fc22aSSepherosa Ziehau return 1;
799b1b8502eSSepherosa Ziehau }
800aa7fc22aSSepherosa Ziehau
801f1a9ebd2SSepherosa Ziehau if (!BackLightDown && BackLightPct != 100) {
802f1a9ebd2SSepherosa Ziehau int backlight_max, backlight;
803f1a9ebd2SSepherosa Ziehau
804f1a9ebd2SSepherosa Ziehau len = sizeof(backlight_max);
805f1a9ebd2SSepherosa Ziehau if (sysctlbyname("hw.backlight_max", &backlight_max, &len,
806f1a9ebd2SSepherosa Ziehau NULL, 0) < 0) {
807f1a9ebd2SSepherosa Ziehau /* No more backlight adjustment */
808f1a9ebd2SSepherosa Ziehau BackLightPct = 100;
809f1a9ebd2SSepherosa Ziehau goto after_backlight;
810f1a9ebd2SSepherosa Ziehau }
811f1a9ebd2SSepherosa Ziehau
812f1a9ebd2SSepherosa Ziehau len = sizeof(OldBackLightLevel);
813f1a9ebd2SSepherosa Ziehau if (sysctlbyname("hw.backlight_level", &OldBackLightLevel, &len,
814f1a9ebd2SSepherosa Ziehau NULL, 0) < 0) {
815f1a9ebd2SSepherosa Ziehau /* No more backlight adjustment */
816f1a9ebd2SSepherosa Ziehau BackLightPct = 100;
817f1a9ebd2SSepherosa Ziehau goto after_backlight;
818f1a9ebd2SSepherosa Ziehau }
819f1a9ebd2SSepherosa Ziehau
820f1a9ebd2SSepherosa Ziehau backlight = (backlight_max * BackLightPct) / 100;
821f1a9ebd2SSepherosa Ziehau if (backlight >= OldBackLightLevel) {
822f1a9ebd2SSepherosa Ziehau /* No more backlight adjustment */
823f1a9ebd2SSepherosa Ziehau BackLightPct = 100;
824f1a9ebd2SSepherosa Ziehau goto after_backlight;
825f1a9ebd2SSepherosa Ziehau }
826f1a9ebd2SSepherosa Ziehau
827f1a9ebd2SSepherosa Ziehau if (sysctlbyname("hw.backlight_level", NULL, NULL,
828f1a9ebd2SSepherosa Ziehau &backlight, sizeof(backlight)) < 0) {
829f1a9ebd2SSepherosa Ziehau /* No more backlight adjustment */
830f1a9ebd2SSepherosa Ziehau BackLightPct = 100;
831f1a9ebd2SSepherosa Ziehau goto after_backlight;
832f1a9ebd2SSepherosa Ziehau }
833f1a9ebd2SSepherosa Ziehau BackLightDown = 1;
834f1a9ebd2SSepherosa Ziehau }
835f1a9ebd2SSepherosa Ziehau after_backlight:
836f1a9ebd2SSepherosa Ziehau
837aa7fc22aSSepherosa Ziehau len = sizeof(life);
838aa7fc22aSSepherosa Ziehau if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0)
839aa7fc22aSSepherosa Ziehau return 1;
840aa7fc22aSSepherosa Ziehau
841b1b8502eSSepherosa Ziehau if (BatShutdownLinger > 0) {
842944cd60cSSascha Wildner timespecsub(&cur, &BatShutdownStartT, &ts);
843b1b8502eSSepherosa Ziehau if (ts.tv_sec > BatShutdownLinger)
844b1b8502eSSepherosa Ziehau BatShutdownLinger = 0;
845aa7fc22aSSepherosa Ziehau }
846aa7fc22aSSepherosa Ziehau
847aa7fc22aSSepherosa Ziehau if (life <= BatLifeMin) {
848b1b8502eSSepherosa Ziehau if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) {
849aa7fc22aSSepherosa Ziehau syslog(LOG_ALERT, "low battery life %d%%, "
850aa7fc22aSSepherosa Ziehau "shutting down", life);
851aa7fc22aSSepherosa Ziehau if (vfork() == 0)
852aa7fc22aSSepherosa Ziehau execlp("poweroff", "poweroff", NULL);
853aa7fc22aSSepherosa Ziehau return 0;
854b1b8502eSSepherosa Ziehau } else if (BatShutdownLinger < 0) {
855b1b8502eSSepherosa Ziehau BatShutdownLinger = BatShutdownLingerSet;
856b1b8502eSSepherosa Ziehau BatShutdownStartT = cur;
857aa7fc22aSSepherosa Ziehau }
858b1b8502eSSepherosa Ziehau low_battery_alert(life);
859aa7fc22aSSepherosa Ziehau }
860aa7fc22aSSepherosa Ziehau return 1;
861aa7fc22aSSepherosa Ziehau }
86225780572SSepherosa Ziehau
86325780572SSepherosa Ziehau static void
get_ncpus(void)864a4c3bf5eSSepherosa Ziehau get_ncpus(void)
86525780572SSepherosa Ziehau {
86625780572SSepherosa Ziehau size_t slen;
86725780572SSepherosa Ziehau
868f1b8fbfeSSepherosa Ziehau slen = sizeof(NCpus);
869f1b8fbfeSSepherosa Ziehau if (sysctlbyname("hw.ncpu", &NCpus, &slen, NULL, 0) < 0)
87025780572SSepherosa Ziehau err(1, "sysctlbyname hw.ncpu failed");
87125780572SSepherosa Ziehau if (DebugOpt)
872f1b8fbfeSSepherosa Ziehau printf("hw.ncpu %d\n", NCpus);
87325780572SSepherosa Ziehau }
87448c660dcSSepherosa Ziehau
87548c660dcSSepherosa Ziehau static void
get_uschedcpus(void)876a4c3bf5eSSepherosa Ziehau get_uschedcpus(void)
87748c660dcSSepherosa Ziehau {
87848c660dcSSepherosa Ziehau size_t slen;
87948c660dcSSepherosa Ziehau
880a4c3bf5eSSepherosa Ziehau slen = sizeof(usched_cpu_used);
881a4c3bf5eSSepherosa Ziehau if (sysctlbyname("kern.usched_global_cpumask", &usched_cpu_used, &slen,
88248c660dcSSepherosa Ziehau NULL, 0) < 0)
88348c660dcSSepherosa Ziehau err(1, "sysctlbyname kern.usched_global_cpumask failed");
88448c660dcSSepherosa Ziehau if (DebugOpt) {
88548c660dcSSepherosa Ziehau int i;
88648c660dcSSepherosa Ziehau
88748c660dcSSepherosa Ziehau printf("usched cpumask was: ");
888a4c3bf5eSSepherosa Ziehau for (i = 0; i < (int)NELEM(usched_cpu_used.ary); ++i)
889a4c3bf5eSSepherosa Ziehau printf("%jx ", (uintmax_t)usched_cpu_used.ary[i]);
89048c660dcSSepherosa Ziehau printf("\n");
89148c660dcSSepherosa Ziehau }
89248c660dcSSepherosa Ziehau }
89350214825SSepherosa Ziehau
894a4c3bf5eSSepherosa Ziehau static void
set_uschedcpus(void)895a4c3bf5eSSepherosa Ziehau set_uschedcpus(void)
896a4c3bf5eSSepherosa Ziehau {
897a4c3bf5eSSepherosa Ziehau if (DebugOpt) {
898a4c3bf5eSSepherosa Ziehau int i;
899a4c3bf5eSSepherosa Ziehau
900a4c3bf5eSSepherosa Ziehau printf("usched cpumask: ");
901a4c3bf5eSSepherosa Ziehau for (i = 0; i < (int)NELEM(usched_cpu_used.ary); ++i) {
902a4c3bf5eSSepherosa Ziehau printf("%jx ",
903a4c3bf5eSSepherosa Ziehau (uintmax_t)usched_cpu_used.ary[i]);
904a4c3bf5eSSepherosa Ziehau }
905a4c3bf5eSSepherosa Ziehau printf("\n");
906a4c3bf5eSSepherosa Ziehau }
907a4c3bf5eSSepherosa Ziehau sysctlbyname("kern.usched_global_cpumask", NULL, 0,
908a4c3bf5eSSepherosa Ziehau &usched_cpu_used, sizeof(usched_cpu_used));
909a4c3bf5eSSepherosa Ziehau }
910a4c3bf5eSSepherosa Ziehau
91150214825SSepherosa Ziehau static int
has_perfbias(void)91250214825SSepherosa Ziehau has_perfbias(void)
91350214825SSepherosa Ziehau {
91450214825SSepherosa Ziehau size_t len;
91550214825SSepherosa Ziehau int hint;
91650214825SSepherosa Ziehau
91750214825SSepherosa Ziehau len = sizeof(hint);
91850214825SSepherosa Ziehau if (sysctlbyname("machdep.perfbias0.hint", &hint, &len, NULL, 0) < 0)
91950214825SSepherosa Ziehau return 0;
92050214825SSepherosa Ziehau return 1;
92150214825SSepherosa Ziehau }
92250214825SSepherosa Ziehau
92350214825SSepherosa Ziehau static void
set_perfbias(int cpu,int inc)924a4c3bf5eSSepherosa Ziehau set_perfbias(int cpu, int inc)
92550214825SSepherosa Ziehau {
926a4c3bf5eSSepherosa Ziehau int hint = inc ? 0 : 15;
92750214825SSepherosa Ziehau char sysid[64];
92850214825SSepherosa Ziehau
92950214825SSepherosa Ziehau if (DebugOpt)
93050214825SSepherosa Ziehau printf("cpu%d set perfbias hint %d\n", cpu, hint);
931a4c3bf5eSSepherosa Ziehau snprintf(sysid, sizeof(sysid), "machdep.perfbias%d.hint", cpu);
932a4c3bf5eSSepherosa Ziehau sysctlbyname(sysid, NULL, NULL, &hint, sizeof(hint));
93350214825SSepherosa Ziehau }
934a4c3bf5eSSepherosa Ziehau
935a4c3bf5eSSepherosa Ziehau static void
init_perf(void)936a4c3bf5eSSepherosa Ziehau init_perf(void)
937a4c3bf5eSSepherosa Ziehau {
938a4c3bf5eSSepherosa Ziehau struct cpu_state *state;
939a4c3bf5eSSepherosa Ziehau int cpu;
940a4c3bf5eSSepherosa Ziehau
941a4c3bf5eSSepherosa Ziehau /* Get usched cpumask */
942a4c3bf5eSSepherosa Ziehau get_uschedcpus();
943a4c3bf5eSSepherosa Ziehau
944a4c3bf5eSSepherosa Ziehau /*
945a4c3bf5eSSepherosa Ziehau * Assume everything are used and are maxed out, before we
946a4c3bf5eSSepherosa Ziehau * start.
947a4c3bf5eSSepherosa Ziehau */
948a4c3bf5eSSepherosa Ziehau CPUMASK_ASSBMASK(cpu_used, NCpus);
949a4c3bf5eSSepherosa Ziehau cpu_pwrdom_used = cpu_pwrdom_mask;
950a4c3bf5eSSepherosa Ziehau global_pcpu_limit = NCpus;
951a4c3bf5eSSepherosa Ziehau
952a4c3bf5eSSepherosa Ziehau for (cpu = 0; cpu < NCpus; ++cpu) {
953a4c3bf5eSSepherosa Ziehau state = &pcpu_state[cpu];
954a4c3bf5eSSepherosa Ziehau
955a4c3bf5eSSepherosa Ziehau state->cpu_uavg = 0.0;
956a4c3bf5eSSepherosa Ziehau state->cpu_davg = 0.0;
957a4c3bf5eSSepherosa Ziehau state->cpu_limit = 1;
958a4c3bf5eSSepherosa Ziehau state->cpu_count = 1;
959a4c3bf5eSSepherosa Ziehau snprintf(state->cpu_name, sizeof(state->cpu_name), "cpu%d",
960a4c3bf5eSSepherosa Ziehau cpu);
961a4c3bf5eSSepherosa Ziehau }
962a4c3bf5eSSepherosa Ziehau
963a4c3bf5eSSepherosa Ziehau state = &global_cpu_state;
964a4c3bf5eSSepherosa Ziehau state->cpu_uavg = 0.0;
965a4c3bf5eSSepherosa Ziehau state->cpu_davg = 0.0;
966a4c3bf5eSSepherosa Ziehau state->cpu_limit = NCpus;
967a4c3bf5eSSepherosa Ziehau state->cpu_count = NCpus;
968a4c3bf5eSSepherosa Ziehau strlcpy(state->cpu_name, "global", sizeof(state->cpu_name));
969a4c3bf5eSSepherosa Ziehau }
970a4c3bf5eSSepherosa Ziehau
971a4c3bf5eSSepherosa Ziehau static int
get_nstate(struct cpu_state * state,double srt)972a4c3bf5eSSepherosa Ziehau get_nstate(struct cpu_state *state, double srt)
973a4c3bf5eSSepherosa Ziehau {
974a4c3bf5eSSepherosa Ziehau int ustate, dstate, nstate;
975a4c3bf5eSSepherosa Ziehau
976a4c3bf5eSSepherosa Ziehau /* speeding up */
977a4c3bf5eSSepherosa Ziehau state->cpu_uavg = (state->cpu_uavg * 2.0 + state->cpu_qavg) / 3.0;
978a4c3bf5eSSepherosa Ziehau /* slowing down */
979a4c3bf5eSSepherosa Ziehau state->cpu_davg = (state->cpu_davg * srt + state->cpu_qavg) / (srt + 1);
980a4c3bf5eSSepherosa Ziehau if (state->cpu_davg < state->cpu_uavg)
981a4c3bf5eSSepherosa Ziehau state->cpu_davg = state->cpu_uavg;
982a4c3bf5eSSepherosa Ziehau
983a4c3bf5eSSepherosa Ziehau ustate = state->cpu_uavg / TriggerUp;
984a4c3bf5eSSepherosa Ziehau if (ustate < state->cpu_limit)
985a4c3bf5eSSepherosa Ziehau ustate = state->cpu_uavg / TriggerDown;
986a4c3bf5eSSepherosa Ziehau dstate = state->cpu_davg / TriggerUp;
987a4c3bf5eSSepherosa Ziehau if (dstate < state->cpu_limit)
988a4c3bf5eSSepherosa Ziehau dstate = state->cpu_davg / TriggerDown;
989a4c3bf5eSSepherosa Ziehau
990a4c3bf5eSSepherosa Ziehau nstate = (ustate > dstate) ? ustate : dstate;
991a4c3bf5eSSepherosa Ziehau if (nstate > state->cpu_count)
992a4c3bf5eSSepherosa Ziehau nstate = state->cpu_count;
993a4c3bf5eSSepherosa Ziehau
994a4c3bf5eSSepherosa Ziehau if (DebugOpt) {
995a4c3bf5eSSepherosa Ziehau printf("%s qavg=%5.2f uavg=%5.2f davg=%5.2f "
996a4c3bf5eSSepherosa Ziehau "%2d ncpus=%d\n", state->cpu_name,
997a4c3bf5eSSepherosa Ziehau state->cpu_qavg, state->cpu_uavg, state->cpu_davg,
998a4c3bf5eSSepherosa Ziehau state->cpu_limit, nstate);
999a4c3bf5eSSepherosa Ziehau }
1000a4c3bf5eSSepherosa Ziehau return nstate;
1001a4c3bf5eSSepherosa Ziehau }
1002a4c3bf5eSSepherosa Ziehau
1003a4c3bf5eSSepherosa Ziehau static void
mon_perf(double srt)1004a4c3bf5eSSepherosa Ziehau mon_perf(double srt)
1005a4c3bf5eSSepherosa Ziehau {
1006a4c3bf5eSSepherosa Ziehau cpumask_t ocpu_used, ocpu_pwrdom_used;
1007a4c3bf5eSSepherosa Ziehau int pnstate = 0, nstate;
1008a4c3bf5eSSepherosa Ziehau int cpu;
1009a4c3bf5eSSepherosa Ziehau
1010a4c3bf5eSSepherosa Ziehau /*
1011a4c3bf5eSSepherosa Ziehau * Find cpus requiring performance and their cooresponding power
1012a4c3bf5eSSepherosa Ziehau * domains. Save the number of cpus requiring performance in
1013a4c3bf5eSSepherosa Ziehau * pnstate.
1014a4c3bf5eSSepherosa Ziehau */
1015a4c3bf5eSSepherosa Ziehau ocpu_used = cpu_used;
1016a4c3bf5eSSepherosa Ziehau ocpu_pwrdom_used = cpu_pwrdom_used;
1017a4c3bf5eSSepherosa Ziehau
1018a4c3bf5eSSepherosa Ziehau CPUMASK_ASSZERO(cpu_used);
1019a4c3bf5eSSepherosa Ziehau CPUMASK_ASSZERO(cpu_pwrdom_used);
1020a4c3bf5eSSepherosa Ziehau
1021a4c3bf5eSSepherosa Ziehau for (cpu = 0; cpu < NCpus; ++cpu) {
1022a4c3bf5eSSepherosa Ziehau struct cpu_state *state = &pcpu_state[cpu];
1023a4c3bf5eSSepherosa Ziehau int s;
1024a4c3bf5eSSepherosa Ziehau
1025a4c3bf5eSSepherosa Ziehau s = get_nstate(state, srt);
1026a4c3bf5eSSepherosa Ziehau if (s) {
1027a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(cpu_used, cpu);
1028a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(cpu_pwrdom_used, cpu2pwrdom[cpu]);
1029a4c3bf5eSSepherosa Ziehau }
1030a4c3bf5eSSepherosa Ziehau pnstate += s;
1031a4c3bf5eSSepherosa Ziehau
1032a4c3bf5eSSepherosa Ziehau state->cpu_limit = s;
1033a4c3bf5eSSepherosa Ziehau }
1034a4c3bf5eSSepherosa Ziehau
1035a4c3bf5eSSepherosa Ziehau /*
1036a4c3bf5eSSepherosa Ziehau * Calculate nstate, the number of cpus we wish to run at max
1037a4c3bf5eSSepherosa Ziehau * performance.
1038a4c3bf5eSSepherosa Ziehau */
1039a4c3bf5eSSepherosa Ziehau nstate = get_nstate(&global_cpu_state, srt);
1040a4c3bf5eSSepherosa Ziehau
1041a4c3bf5eSSepherosa Ziehau if (nstate == global_cpu_state.cpu_limit &&
1042ed27c2d1SMatthew Dillon (NFreqChanged & NFREQ_MONPERF) == 0 &&
1043a4c3bf5eSSepherosa Ziehau (pnstate == global_pcpu_limit || nstate > pnstate)) {
1044a4c3bf5eSSepherosa Ziehau /* Nothing changed; keep the sets */
1045a4c3bf5eSSepherosa Ziehau cpu_used = ocpu_used;
1046a4c3bf5eSSepherosa Ziehau cpu_pwrdom_used = ocpu_pwrdom_used;
1047a4c3bf5eSSepherosa Ziehau
1048a4c3bf5eSSepherosa Ziehau global_pcpu_limit = pnstate;
1049a4c3bf5eSSepherosa Ziehau return;
1050a4c3bf5eSSepherosa Ziehau }
1051ed27c2d1SMatthew Dillon NFreqChanged &= ~NFREQ_MONPERF;
1052a4c3bf5eSSepherosa Ziehau global_pcpu_limit = pnstate;
1053a4c3bf5eSSepherosa Ziehau
1054a4c3bf5eSSepherosa Ziehau if (nstate > pnstate) {
1055a4c3bf5eSSepherosa Ziehau /*
1056a4c3bf5eSSepherosa Ziehau * Add spare cpus to meet global performance requirement.
1057a4c3bf5eSSepherosa Ziehau */
1058a4c3bf5eSSepherosa Ziehau add_spare_cpus(ocpu_used, nstate - pnstate);
1059a4c3bf5eSSepherosa Ziehau }
1060a4c3bf5eSSepherosa Ziehau
1061a4c3bf5eSSepherosa Ziehau global_cpu_state.cpu_limit = nstate;
1062a4c3bf5eSSepherosa Ziehau
1063a4c3bf5eSSepherosa Ziehau /*
1064a4c3bf5eSSepherosa Ziehau * Adjust cpu and cpu power domain performance
1065a4c3bf5eSSepherosa Ziehau */
1066a4c3bf5eSSepherosa Ziehau adj_perf(ocpu_used, ocpu_pwrdom_used);
1067a4c3bf5eSSepherosa Ziehau }
1068a4c3bf5eSSepherosa Ziehau
1069a4c3bf5eSSepherosa Ziehau static void
add_spare_cpus(const cpumask_t ocpu_used,int ncpu)1070a4c3bf5eSSepherosa Ziehau add_spare_cpus(const cpumask_t ocpu_used, int ncpu)
1071a4c3bf5eSSepherosa Ziehau {
1072a4c3bf5eSSepherosa Ziehau cpumask_t saved_pwrdom, xcpu_used;
1073a4c3bf5eSSepherosa Ziehau int done = 0, cpu;
1074a4c3bf5eSSepherosa Ziehau
1075a4c3bf5eSSepherosa Ziehau /*
1076a4c3bf5eSSepherosa Ziehau * Find more cpus in the previous cpu set.
1077a4c3bf5eSSepherosa Ziehau */
1078a4c3bf5eSSepherosa Ziehau xcpu_used = cpu_used;
1079a4c3bf5eSSepherosa Ziehau CPUMASK_XORMASK(xcpu_used, ocpu_used);
1080a4c3bf5eSSepherosa Ziehau while (CPUMASK_TESTNZERO(xcpu_used)) {
1081a4c3bf5eSSepherosa Ziehau cpu = BSFCPUMASK(xcpu_used);
1082a4c3bf5eSSepherosa Ziehau CPUMASK_NANDBIT(xcpu_used, cpu);
1083a4c3bf5eSSepherosa Ziehau
1084a4c3bf5eSSepherosa Ziehau if (CPUMASK_TESTBIT(ocpu_used, cpu)) {
1085a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(cpu_pwrdom_used, cpu2pwrdom[cpu]);
1086a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(cpu_used, cpu);
1087a4c3bf5eSSepherosa Ziehau --ncpu;
1088a4c3bf5eSSepherosa Ziehau if (ncpu == 0)
1089a4c3bf5eSSepherosa Ziehau return;
1090a4c3bf5eSSepherosa Ziehau }
1091a4c3bf5eSSepherosa Ziehau }
1092a4c3bf5eSSepherosa Ziehau
1093a4c3bf5eSSepherosa Ziehau /*
1094a4c3bf5eSSepherosa Ziehau * Find more cpus in the used cpu power domains.
1095a4c3bf5eSSepherosa Ziehau */
1096a4c3bf5eSSepherosa Ziehau saved_pwrdom = cpu_pwrdom_used;
1097a4c3bf5eSSepherosa Ziehau again:
1098a4c3bf5eSSepherosa Ziehau while (CPUMASK_TESTNZERO(saved_pwrdom)) {
1099a4c3bf5eSSepherosa Ziehau cpumask_t unused_cpumask;
1100a4c3bf5eSSepherosa Ziehau int dom;
1101a4c3bf5eSSepherosa Ziehau
1102a4c3bf5eSSepherosa Ziehau dom = BSFCPUMASK(saved_pwrdom);
1103a4c3bf5eSSepherosa Ziehau CPUMASK_NANDBIT(saved_pwrdom, dom);
1104a4c3bf5eSSepherosa Ziehau
1105a4c3bf5eSSepherosa Ziehau unused_cpumask = cpu_pwrdomain[dom]->dom_cpumask;
1106a4c3bf5eSSepherosa Ziehau CPUMASK_NANDMASK(unused_cpumask, cpu_used);
1107a4c3bf5eSSepherosa Ziehau
1108a4c3bf5eSSepherosa Ziehau while (CPUMASK_TESTNZERO(unused_cpumask)) {
1109a4c3bf5eSSepherosa Ziehau cpu = BSFCPUMASK(unused_cpumask);
1110a4c3bf5eSSepherosa Ziehau CPUMASK_NANDBIT(unused_cpumask, cpu);
1111a4c3bf5eSSepherosa Ziehau
1112a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(cpu_pwrdom_used, dom);
1113a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(cpu_used, cpu);
1114a4c3bf5eSSepherosa Ziehau --ncpu;
1115a4c3bf5eSSepherosa Ziehau if (ncpu == 0)
1116a4c3bf5eSSepherosa Ziehau return;
1117a4c3bf5eSSepherosa Ziehau }
1118a4c3bf5eSSepherosa Ziehau }
1119a4c3bf5eSSepherosa Ziehau if (!done) {
1120a4c3bf5eSSepherosa Ziehau done = 1;
1121a4c3bf5eSSepherosa Ziehau /*
1122a4c3bf5eSSepherosa Ziehau * Find more cpus in unused cpu power domains
1123a4c3bf5eSSepherosa Ziehau */
1124a4c3bf5eSSepherosa Ziehau saved_pwrdom = cpu_pwrdom_mask;
1125a4c3bf5eSSepherosa Ziehau CPUMASK_NANDMASK(saved_pwrdom, cpu_pwrdom_used);
1126a4c3bf5eSSepherosa Ziehau goto again;
1127a4c3bf5eSSepherosa Ziehau }
1128a4c3bf5eSSepherosa Ziehau if (DebugOpt)
1129a4c3bf5eSSepherosa Ziehau printf("%d cpus not found\n", ncpu);
1130a4c3bf5eSSepherosa Ziehau }
1131a4c3bf5eSSepherosa Ziehau
1132a4c3bf5eSSepherosa Ziehau static void
acpi_set_cpufreq(int dom,int inc)1133a4c3bf5eSSepherosa Ziehau acpi_set_cpufreq(int dom, int inc)
1134a4c3bf5eSSepherosa Ziehau {
1135a4c3bf5eSSepherosa Ziehau int lowest, highest, desired;
1136a4c3bf5eSSepherosa Ziehau char sysid[64];
1137a4c3bf5eSSepherosa Ziehau
1138a4c3bf5eSSepherosa Ziehau acpi_get_cpufreq(dom, &highest, &lowest);
1139a4c3bf5eSSepherosa Ziehau if (highest == 0 || lowest == 0)
1140a4c3bf5eSSepherosa Ziehau return;
1141a4c3bf5eSSepherosa Ziehau desired = inc ? highest : lowest;
1142a4c3bf5eSSepherosa Ziehau
1143a4c3bf5eSSepherosa Ziehau if (DebugOpt)
1144a4c3bf5eSSepherosa Ziehau printf("dom%d set frequency %d\n", dom, desired);
1145a4c3bf5eSSepherosa Ziehau snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.select", dom);
1146a4c3bf5eSSepherosa Ziehau sysctlbyname(sysid, NULL, NULL, &desired, sizeof(desired));
1147a4c3bf5eSSepherosa Ziehau }
1148a4c3bf5eSSepherosa Ziehau
1149a4c3bf5eSSepherosa Ziehau static void
adj_cpu_pwrdom(int dom,int inc)1150a4c3bf5eSSepherosa Ziehau adj_cpu_pwrdom(int dom, int inc)
1151a4c3bf5eSSepherosa Ziehau {
1152c6434d78SMatthew Dillon if (AdjustCpuFreq && (inc == 0 || AdjustCpuFreqOverride == 0))
1153a4c3bf5eSSepherosa Ziehau acpi_set_cpufreq(dom, inc);
1154a4c3bf5eSSepherosa Ziehau }
1155a4c3bf5eSSepherosa Ziehau
1156a4c3bf5eSSepherosa Ziehau static void
adj_cpu_perf(int cpu,int inc)1157a4c3bf5eSSepherosa Ziehau adj_cpu_perf(int cpu, int inc)
1158a4c3bf5eSSepherosa Ziehau {
1159a4c3bf5eSSepherosa Ziehau if (DebugOpt) {
1160a4c3bf5eSSepherosa Ziehau if (inc)
1161a4c3bf5eSSepherosa Ziehau printf("cpu%d increase perf\n", cpu);
1162a4c3bf5eSSepherosa Ziehau else
1163a4c3bf5eSSepherosa Ziehau printf("cpu%d decrease perf\n", cpu);
1164a4c3bf5eSSepherosa Ziehau }
1165a4c3bf5eSSepherosa Ziehau
1166a4c3bf5eSSepherosa Ziehau if (HasPerfbias)
1167a4c3bf5eSSepherosa Ziehau set_perfbias(cpu, inc);
1168764594cfSSepherosa Ziehau if (AdjustCstate)
1169764594cfSSepherosa Ziehau set_cstate(cpu, inc);
1170a4c3bf5eSSepherosa Ziehau }
1171a4c3bf5eSSepherosa Ziehau
1172a4c3bf5eSSepherosa Ziehau static void
adj_perf(cpumask_t xcpu_used,cpumask_t xcpu_pwrdom_used)1173a4c3bf5eSSepherosa Ziehau adj_perf(cpumask_t xcpu_used, cpumask_t xcpu_pwrdom_used)
1174a4c3bf5eSSepherosa Ziehau {
1175a4c3bf5eSSepherosa Ziehau int cpu, inc;
1176a4c3bf5eSSepherosa Ziehau
117716be03f6SSepherosa Ziehau if (AdjustUsched) {
117816be03f6SSepherosa Ziehau cpumask_t old_usched_used;
117916be03f6SSepherosa Ziehau
1180a4c3bf5eSSepherosa Ziehau /*
1181a4c3bf5eSSepherosa Ziehau * Set cpus requiring performance to the userland process
1182a4c3bf5eSSepherosa Ziehau * scheduler. Leave the rest of cpus unmapped.
1183a4c3bf5eSSepherosa Ziehau */
1184a4c3bf5eSSepherosa Ziehau old_usched_used = usched_cpu_used;
1185a4c3bf5eSSepherosa Ziehau usched_cpu_used = cpu_used;
1186a4c3bf5eSSepherosa Ziehau if (CPUMASK_TESTZERO(usched_cpu_used))
1187a4c3bf5eSSepherosa Ziehau CPUMASK_ORBIT(usched_cpu_used, 0);
1188a4c3bf5eSSepherosa Ziehau if (CPUMASK_CMPMASKNEQ(usched_cpu_used, old_usched_used))
1189a4c3bf5eSSepherosa Ziehau set_uschedcpus();
119016be03f6SSepherosa Ziehau }
1191a4c3bf5eSSepherosa Ziehau
1192a4c3bf5eSSepherosa Ziehau /*
1193ed27c2d1SMatthew Dillon * Adjust per-cpu performance for any cpus which changed.
1194a4c3bf5eSSepherosa Ziehau */
1195a4c3bf5eSSepherosa Ziehau CPUMASK_XORMASK(xcpu_used, cpu_used);
1196ed27c2d1SMatthew Dillon if (NFreqChanged & NFREQ_ADJPERF)
1197ed27c2d1SMatthew Dillon CPUMASK_ASSBMASK(xcpu_used, NCpus);
1198a4c3bf5eSSepherosa Ziehau while (CPUMASK_TESTNZERO(xcpu_used)) {
1199a4c3bf5eSSepherosa Ziehau cpu = BSFCPUMASK(xcpu_used);
1200a4c3bf5eSSepherosa Ziehau CPUMASK_NANDBIT(xcpu_used, cpu);
1201a4c3bf5eSSepherosa Ziehau
1202a4c3bf5eSSepherosa Ziehau if (CPUMASK_TESTBIT(cpu_used, cpu)) {
1203a4c3bf5eSSepherosa Ziehau /* Increase cpu performance */
1204a4c3bf5eSSepherosa Ziehau inc = 1;
1205a4c3bf5eSSepherosa Ziehau } else {
1206a4c3bf5eSSepherosa Ziehau /* Decrease cpu performance */
1207a4c3bf5eSSepherosa Ziehau inc = 0;
1208a4c3bf5eSSepherosa Ziehau }
1209a4c3bf5eSSepherosa Ziehau adj_cpu_perf(cpu, inc);
1210a4c3bf5eSSepherosa Ziehau }
1211a4c3bf5eSSepherosa Ziehau
1212a4c3bf5eSSepherosa Ziehau /*
1213a4c3bf5eSSepherosa Ziehau * Adjust cpu power domain performance. This could affect
1214a4c3bf5eSSepherosa Ziehau * a set of cpus.
1215a4c3bf5eSSepherosa Ziehau */
1216a4c3bf5eSSepherosa Ziehau CPUMASK_XORMASK(xcpu_pwrdom_used, cpu_pwrdom_used);
1217ed27c2d1SMatthew Dillon if (NFreqChanged & NFREQ_ADJPERF)
1218ed27c2d1SMatthew Dillon CPUMASK_ASSBMASK(xcpu_pwrdom_used, NCpus);
1219a4c3bf5eSSepherosa Ziehau while (CPUMASK_TESTNZERO(xcpu_pwrdom_used)) {
1220a4c3bf5eSSepherosa Ziehau int dom;
1221a4c3bf5eSSepherosa Ziehau
1222a4c3bf5eSSepherosa Ziehau dom = BSFCPUMASK(xcpu_pwrdom_used);
1223a4c3bf5eSSepherosa Ziehau CPUMASK_NANDBIT(xcpu_pwrdom_used, dom);
1224a4c3bf5eSSepherosa Ziehau
1225a4c3bf5eSSepherosa Ziehau if (CPUMASK_TESTBIT(cpu_pwrdom_used, dom)) {
1226a4c3bf5eSSepherosa Ziehau /* Increase cpu power domain performance */
1227a4c3bf5eSSepherosa Ziehau inc = 1;
1228a4c3bf5eSSepherosa Ziehau } else {
1229a4c3bf5eSSepherosa Ziehau /* Decrease cpu power domain performance */
1230a4c3bf5eSSepherosa Ziehau inc = 0;
1231a4c3bf5eSSepherosa Ziehau }
1232a4c3bf5eSSepherosa Ziehau adj_cpu_pwrdom(dom, inc);
1233a4c3bf5eSSepherosa Ziehau }
1234ed27c2d1SMatthew Dillon NFreqChanged &= ~NFREQ_ADJPERF;
1235a4c3bf5eSSepherosa Ziehau }
1236a4c3bf5eSSepherosa Ziehau
1237a4c3bf5eSSepherosa Ziehau static void
restore_perf(void)1238a4c3bf5eSSepherosa Ziehau restore_perf(void)
1239a4c3bf5eSSepherosa Ziehau {
1240a4c3bf5eSSepherosa Ziehau cpumask_t ocpu_used, ocpu_pwrdom_used;
1241a4c3bf5eSSepherosa Ziehau
1242a2b9b795SSepherosa Ziehau /* Remove highest cpu frequency limitation */
1243a2b9b795SSepherosa Ziehau HighestCpuFreq = 0;
1244a2b9b795SSepherosa Ziehau
1245a4c3bf5eSSepherosa Ziehau ocpu_used = cpu_used;
1246a4c3bf5eSSepherosa Ziehau ocpu_pwrdom_used = cpu_pwrdom_used;
1247a4c3bf5eSSepherosa Ziehau
1248a4c3bf5eSSepherosa Ziehau /* Max out all cpus and cpu power domains performance */
1249a4c3bf5eSSepherosa Ziehau CPUMASK_ASSBMASK(cpu_used, NCpus);
1250a4c3bf5eSSepherosa Ziehau cpu_pwrdom_used = cpu_pwrdom_mask;
1251a4c3bf5eSSepherosa Ziehau
1252a4c3bf5eSSepherosa Ziehau adj_perf(ocpu_used, ocpu_pwrdom_used);
1253764594cfSSepherosa Ziehau
1254764594cfSSepherosa Ziehau if (AdjustCstate) {
1255764594cfSSepherosa Ziehau /*
1256764594cfSSepherosa Ziehau * Restore the original mwait C-state
1257764594cfSSepherosa Ziehau */
1258764594cfSSepherosa Ziehau if (DebugOpt)
1259764594cfSSepherosa Ziehau printf("global set cstate %s\n", orig_global_cx);
1260764594cfSSepherosa Ziehau sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
1261764594cfSSepherosa Ziehau orig_global_cx, strlen(orig_global_cx) + 1);
1262764594cfSSepherosa Ziehau }
1263764594cfSSepherosa Ziehau }
1264764594cfSSepherosa Ziehau
1265764594cfSSepherosa Ziehau static int
probe_cstate(void)1266764594cfSSepherosa Ziehau probe_cstate(void)
1267764594cfSSepherosa Ziehau {
1268764594cfSSepherosa Ziehau char cx_supported[1024];
1269764594cfSSepherosa Ziehau const char *target;
1270764594cfSSepherosa Ziehau char *ptr;
1271764594cfSSepherosa Ziehau int idle_hlt, deep = 1;
1272764594cfSSepherosa Ziehau size_t len;
1273764594cfSSepherosa Ziehau
1274764594cfSSepherosa Ziehau len = sizeof(idle_hlt);
1275764594cfSSepherosa Ziehau if (sysctlbyname("machdep.cpu_idle_hlt", &idle_hlt, &len, NULL, 0) < 0)
1276764594cfSSepherosa Ziehau return 0;
1277764594cfSSepherosa Ziehau if (idle_hlt != 1)
1278764594cfSSepherosa Ziehau return 0;
1279764594cfSSepherosa Ziehau
1280764594cfSSepherosa Ziehau len = sizeof(cx_supported);
1281764594cfSSepherosa Ziehau if (sysctlbyname("machdep.mwait.CX.supported", cx_supported, &len,
1282764594cfSSepherosa Ziehau NULL, 0) < 0)
1283764594cfSSepherosa Ziehau return 0;
1284764594cfSSepherosa Ziehau
1285764594cfSSepherosa Ziehau len = sizeof(orig_global_cx);
1286764594cfSSepherosa Ziehau if (sysctlbyname("machdep.mwait.CX.idle", orig_global_cx, &len,
1287764594cfSSepherosa Ziehau NULL, 0) < 0)
1288764594cfSSepherosa Ziehau return 0;
1289764594cfSSepherosa Ziehau
1290764594cfSSepherosa Ziehau strlcpy(cpu_perf_cx, "AUTODEEP", sizeof(cpu_perf_cx));
1291764594cfSSepherosa Ziehau cpu_perf_cxlen = strlen(cpu_perf_cx) + 1;
1292764594cfSSepherosa Ziehau if (sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
1293764594cfSSepherosa Ziehau cpu_perf_cx, cpu_perf_cxlen) < 0) {
1294764594cfSSepherosa Ziehau /* AUTODEEP is not supported; try AUTO */
1295764594cfSSepherosa Ziehau deep = 0;
1296764594cfSSepherosa Ziehau strlcpy(cpu_perf_cx, "AUTO", sizeof(cpu_perf_cx));
1297764594cfSSepherosa Ziehau cpu_perf_cxlen = strlen(cpu_perf_cx) + 1;
1298764594cfSSepherosa Ziehau if (sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
1299764594cfSSepherosa Ziehau cpu_perf_cx, cpu_perf_cxlen) < 0)
1300764594cfSSepherosa Ziehau return 0;
1301764594cfSSepherosa Ziehau }
1302764594cfSSepherosa Ziehau
1303764594cfSSepherosa Ziehau if (!deep)
1304764594cfSSepherosa Ziehau target = "C2/0";
1305764594cfSSepherosa Ziehau else
1306764594cfSSepherosa Ziehau target = NULL;
1307764594cfSSepherosa Ziehau for (ptr = strtok(cx_supported, " "); ptr != NULL;
1308764594cfSSepherosa Ziehau ptr = strtok(NULL, " ")) {
1309764594cfSSepherosa Ziehau if (target == NULL ||
1310764594cfSSepherosa Ziehau (target != NULL && strcmp(ptr, target) == 0)) {
1311764594cfSSepherosa Ziehau strlcpy(cpu_idle_cx, ptr, sizeof(cpu_idle_cx));
1312764594cfSSepherosa Ziehau cpu_idle_cxlen = strlen(cpu_idle_cx) + 1;
1313764594cfSSepherosa Ziehau if (target != NULL)
1314764594cfSSepherosa Ziehau break;
1315764594cfSSepherosa Ziehau }
1316764594cfSSepherosa Ziehau }
1317764594cfSSepherosa Ziehau if (cpu_idle_cxlen == 0)
1318764594cfSSepherosa Ziehau return 0;
1319764594cfSSepherosa Ziehau
1320764594cfSSepherosa Ziehau if (DebugOpt) {
1321764594cfSSepherosa Ziehau printf("cstate orig %s, perf %s, idle %s\n",
1322764594cfSSepherosa Ziehau orig_global_cx, cpu_perf_cx, cpu_idle_cx);
1323764594cfSSepherosa Ziehau }
1324764594cfSSepherosa Ziehau return 1;
1325764594cfSSepherosa Ziehau }
1326764594cfSSepherosa Ziehau
1327764594cfSSepherosa Ziehau static void
set_cstate(int cpu,int inc)1328764594cfSSepherosa Ziehau set_cstate(int cpu, int inc)
1329764594cfSSepherosa Ziehau {
1330764594cfSSepherosa Ziehau const char *cst;
1331764594cfSSepherosa Ziehau char sysid[64];
1332764594cfSSepherosa Ziehau size_t len;
1333764594cfSSepherosa Ziehau
1334764594cfSSepherosa Ziehau if (inc) {
1335764594cfSSepherosa Ziehau cst = cpu_perf_cx;
1336764594cfSSepherosa Ziehau len = cpu_perf_cxlen;
1337764594cfSSepherosa Ziehau } else {
1338764594cfSSepherosa Ziehau cst = cpu_idle_cx;
1339764594cfSSepherosa Ziehau len = cpu_idle_cxlen;
1340764594cfSSepherosa Ziehau }
1341764594cfSSepherosa Ziehau
1342764594cfSSepherosa Ziehau if (DebugOpt)
1343764594cfSSepherosa Ziehau printf("cpu%d set cstate %s\n", cpu, cst);
1344764594cfSSepherosa Ziehau snprintf(sysid, sizeof(sysid), "machdep.mwait.CX.idle%d", cpu);
1345764594cfSSepherosa Ziehau sysctlbyname(sysid, NULL, NULL, cst, len);
134650214825SSepherosa Ziehau }
1347241985a7SSepherosa Ziehau
1348241985a7SSepherosa Ziehau static void
restore_backlight(void)1349241985a7SSepherosa Ziehau restore_backlight(void)
1350241985a7SSepherosa Ziehau {
1351241985a7SSepherosa Ziehau if (BackLightDown) {
1352241985a7SSepherosa Ziehau BackLightDown = 0;
1353241985a7SSepherosa Ziehau sysctlbyname("hw.backlight_level", NULL, NULL,
1354241985a7SSepherosa Ziehau &OldBackLightLevel, sizeof(OldBackLightLevel));
1355241985a7SSepherosa Ziehau }
1356241985a7SSepherosa Ziehau }
1357c6434d78SMatthew Dillon
1358c6434d78SMatthew Dillon /*
1359c6434d78SMatthew Dillon * get_cputemp() / mon_cputemp()
1360c6434d78SMatthew Dillon *
1361c6434d78SMatthew Dillon * This enforces the maximum cpu frequency based on temperature
1362c6434d78SMatthew Dillon * verses MinTemp and MaxTemp.
1363c6434d78SMatthew Dillon */
1364c6434d78SMatthew Dillon static int
get_cputemp(void)1365c6434d78SMatthew Dillon get_cputemp(void)
1366c6434d78SMatthew Dillon {
1367c6434d78SMatthew Dillon char sysid[64];
1368c6434d78SMatthew Dillon struct sensor sensor;
1369c6434d78SMatthew Dillon size_t sensor_size;
1370c6434d78SMatthew Dillon int t;
1371c6434d78SMatthew Dillon int mt = -1;
1372c6434d78SMatthew Dillon int n;
1373c6434d78SMatthew Dillon
1374c6434d78SMatthew Dillon for (n = 0; ; ++n) {
1375c6434d78SMatthew Dillon t = 0;
1376c6434d78SMatthew Dillon snprintf(sysid, sizeof(sysid),
1377c6434d78SMatthew Dillon "hw.sensors.cpu_node%d.temp0", n);
1378c6434d78SMatthew Dillon sensor_size = sizeof(sensor);
1379c6434d78SMatthew Dillon if (sysctlbyname(sysid, &sensor, &sensor_size, NULL, 0) < 0)
1380c6434d78SMatthew Dillon break;
1381c6434d78SMatthew Dillon t = -1;
1382c6434d78SMatthew Dillon if ((sensor.flags & (SENSOR_FINVALID | SENSOR_FUNKNOWN)) == 0) {
1383c6434d78SMatthew Dillon t = (int)((sensor.value - 273150000) / 1000000);
1384c6434d78SMatthew Dillon if (mt < t)
1385c6434d78SMatthew Dillon mt = t;
1386c6434d78SMatthew Dillon }
1387c6434d78SMatthew Dillon }
1388c6434d78SMatthew Dillon if (n)
1389c6434d78SMatthew Dillon return mt;
1390c6434d78SMatthew Dillon
1391c6434d78SMatthew Dillon /*
1392c6434d78SMatthew Dillon * Missing nodeN for some reason, try cpuN.
1393c6434d78SMatthew Dillon */
1394c6434d78SMatthew Dillon for (n = 0; ; ++n) {
1395c6434d78SMatthew Dillon t = 0;
1396c6434d78SMatthew Dillon snprintf(sysid, sizeof(sysid),
1397c6434d78SMatthew Dillon "hw.sensors.cpu%d.temp0", n);
1398c6434d78SMatthew Dillon sensor_size = sizeof(sensor);
1399c6434d78SMatthew Dillon if (sysctlbyname(sysid, &sensor, &sensor_size, NULL, 0) < 0)
1400c6434d78SMatthew Dillon break;
1401c6434d78SMatthew Dillon t = -1;
1402c6434d78SMatthew Dillon if ((sensor.flags & (SENSOR_FINVALID | SENSOR_FUNKNOWN)) == 0) {
1403c6434d78SMatthew Dillon t = (int)((sensor.value - 273150000) / 1000000);
1404c6434d78SMatthew Dillon if (mt < t)
1405c6434d78SMatthew Dillon mt = t;
1406c6434d78SMatthew Dillon }
1407c6434d78SMatthew Dillon }
1408c6434d78SMatthew Dillon return mt;
1409c6434d78SMatthew Dillon }
1410c6434d78SMatthew Dillon
1411c6434d78SMatthew Dillon static void
set_global_freq(int freq)1412c6434d78SMatthew Dillon set_global_freq(int freq)
1413c6434d78SMatthew Dillon {
1414c6434d78SMatthew Dillon if (freq > 0)
1415c6434d78SMatthew Dillon sysctlbyname("hw.acpi.cpu.px_global",
1416c6434d78SMatthew Dillon NULL, NULL, &freq, sizeof(freq));
1417c6434d78SMatthew Dillon }
1418c6434d78SMatthew Dillon
1419c6434d78SMatthew Dillon static int
get_global_freq(void)1420c6434d78SMatthew Dillon get_global_freq(void)
1421c6434d78SMatthew Dillon {
1422c6434d78SMatthew Dillon int freq;
1423c6434d78SMatthew Dillon size_t freq_size;
1424c6434d78SMatthew Dillon
1425c6434d78SMatthew Dillon freq = -1;
1426c6434d78SMatthew Dillon freq_size = sizeof(freq);
1427c6434d78SMatthew Dillon sysctlbyname("hw.acpi.cpu.px_global", &freq, &freq_size, NULL, 0);
1428c6434d78SMatthew Dillon
1429c6434d78SMatthew Dillon return freq;
1430c6434d78SMatthew Dillon }
1431c6434d78SMatthew Dillon
1432c6434d78SMatthew Dillon static void
mon_cputemp(void)1433c6434d78SMatthew Dillon mon_cputemp(void)
1434c6434d78SMatthew Dillon {
1435c6434d78SMatthew Dillon static int last_temp = -1;
1436c6434d78SMatthew Dillon static int last_idx = -1;
1437c6434d78SMatthew Dillon int temp = get_cputemp();
1438c6434d78SMatthew Dillon int idx;
1439c6434d78SMatthew Dillon int lowest;
1440c6434d78SMatthew Dillon int highest;
1441c6434d78SMatthew Dillon static int CurPXGlobal __unused;
1442c6434d78SMatthew Dillon
1443c6434d78SMatthew Dillon /*
1444c6434d78SMatthew Dillon * Reseed FreqAry, it can change w/AC power state
1445c6434d78SMatthew Dillon */
1446c6434d78SMatthew Dillon acpi_get_cpufreq(0, &lowest, &highest);
1447c6434d78SMatthew Dillon
1448c6434d78SMatthew Dillon /*
1449c6434d78SMatthew Dillon * Some cpu frequency steps can cause large shifts in cpu temperature,
1450c6434d78SMatthew Dillon * creating an oscillation that min-maxes the temperature in a way
1451c6434d78SMatthew Dillon * that is not desireable. To deal with this, we impose an exponential
1452c6434d78SMatthew Dillon * average for any temperature change.
1453c6434d78SMatthew Dillon *
1454c6434d78SMatthew Dillon * We have to do this in both directions, otherwise (in particular)
1455c6434d78SMatthew Dillon * laptop fan responsiveness and temperature sensor response times
1456c6434d78SMatthew Dillon * can create major frequency oscillations.
1457c6434d78SMatthew Dillon */
1458ed27c2d1SMatthew Dillon if (last_temp < 0 || (NFreqChanged & NFREQ_CPUTEMP)) {
1459ed27c2d1SMatthew Dillon NFreqChanged &= ~NFREQ_CPUTEMP;
1460c6434d78SMatthew Dillon last_temp = temp << 8;
1461c6434d78SMatthew Dillon } else if (temp < last_temp) {
1462c6434d78SMatthew Dillon last_temp = (last_temp * 15 + (temp << 8)) / 16;
1463c6434d78SMatthew Dillon if (DebugOpt) {
1464c6434d78SMatthew Dillon printf("Falling temp %d (use %d)\n",
1465c6434d78SMatthew Dillon temp, (last_temp >> 8));
1466c6434d78SMatthew Dillon }
1467c6434d78SMatthew Dillon } else {
1468c6434d78SMatthew Dillon last_temp = (last_temp * 15 + (temp << 8)) / 16;
1469c6434d78SMatthew Dillon if (DebugOpt) {
1470c6434d78SMatthew Dillon printf("Rising temp %d (use %d)\n",
1471c6434d78SMatthew Dillon temp, (last_temp >> 8));
1472c6434d78SMatthew Dillon }
1473c6434d78SMatthew Dillon }
1474c6434d78SMatthew Dillon temp = last_temp >> 8;
1475c6434d78SMatthew Dillon
1476c6434d78SMatthew Dillon /*
1477c6434d78SMatthew Dillon * CPU Temp not available or available frequencies not yet
1478c6434d78SMatthew Dillon * probed.
1479c6434d78SMatthew Dillon */
1480c6434d78SMatthew Dillon if (DebugOpt)
1481c6434d78SMatthew Dillon printf("Temp %d {%d-%d} NFreq=%d)\n",
1482c6434d78SMatthew Dillon temp, MinTemp, MaxTemp, NFreq);
1483c6434d78SMatthew Dillon if (temp <= 0)
1484c6434d78SMatthew Dillon return;
1485c6434d78SMatthew Dillon if (NFreq == 0)
1486c6434d78SMatthew Dillon return;
1487c6434d78SMatthew Dillon
1488c6434d78SMatthew Dillon /*
1489c6434d78SMatthew Dillon * Return to normal operation if under the minimum
1490c6434d78SMatthew Dillon */
1491c6434d78SMatthew Dillon if (temp <= MinTemp) {
1492c6434d78SMatthew Dillon if (AdjustCpuFreqOverride) {
1493c6434d78SMatthew Dillon AdjustCpuFreqOverride = 0;
1494c6434d78SMatthew Dillon CurPXGlobal = 0;
1495ed27c2d1SMatthew Dillon NFreqChanged = NFREQ_ALL;
1496c6434d78SMatthew Dillon last_idx = -1;
1497c6434d78SMatthew Dillon syslog(LOG_ALERT,
1498c6434d78SMatthew Dillon "Temp below %d, returning to normal operation",
1499c6434d78SMatthew Dillon MinTemp);
1500c6434d78SMatthew Dillon if (SavedPXGlobal)
1501c6434d78SMatthew Dillon set_global_freq(SavedPXGlobal);
1502c6434d78SMatthew Dillon }
1503c6434d78SMatthew Dillon return;
1504c6434d78SMatthew Dillon }
1505c6434d78SMatthew Dillon
1506c6434d78SMatthew Dillon /*
1507c6434d78SMatthew Dillon * Hysteresis before entering temperature control mode
1508c6434d78SMatthew Dillon */
1509c6434d78SMatthew Dillon if (AdjustCpuFreqOverride == 0 &&
1510c6434d78SMatthew Dillon temp <= MinTemp + (MaxTemp - MinTemp) / 10 + 1) {
1511c6434d78SMatthew Dillon return;
1512c6434d78SMatthew Dillon }
1513c6434d78SMatthew Dillon
1514c6434d78SMatthew Dillon /*
1515c6434d78SMatthew Dillon * Override frequency controls (except for idle -> lowest)
1516c6434d78SMatthew Dillon */
1517c6434d78SMatthew Dillon if (AdjustCpuFreqOverride == 0) {
1518c6434d78SMatthew Dillon AdjustCpuFreqOverride = 1;
1519c6434d78SMatthew Dillon SavedPXGlobal = get_global_freq();
1520c6434d78SMatthew Dillon CurPXGlobal = 0;
1521ed27c2d1SMatthew Dillon NFreqChanged = NFREQ_ALL;
1522c6434d78SMatthew Dillon last_idx = -1;
1523c6434d78SMatthew Dillon syslog(LOG_ALERT,
1524c6434d78SMatthew Dillon "Temp %d {%d-%d}, entering temperature control mode",
1525c6434d78SMatthew Dillon temp, MinTemp, MaxTemp);
1526c6434d78SMatthew Dillon }
1527c6434d78SMatthew Dillon if (temp > MaxTemp + (MaxTemp - MinTemp) / 10 + 1) {
1528c6434d78SMatthew Dillon syslog(LOG_ALERT,
1529c6434d78SMatthew Dillon "Temp %d {%d-%d}, TOO HOT!!!",
1530c6434d78SMatthew Dillon temp, MinTemp, MaxTemp);
1531c6434d78SMatthew Dillon }
1532c6434d78SMatthew Dillon idx = (temp - MinTemp) * NFreq / (MaxTemp - MinTemp);
1533c6434d78SMatthew Dillon if (idx < 0 || idx >= NFreq) /* overtemp */
1534c6434d78SMatthew Dillon idx = NFreq - 1;
1535c6434d78SMatthew Dillon
1536c6434d78SMatthew Dillon /*
1537c6434d78SMatthew Dillon * Limit frequency shifts to single steps in both directions.
1538c6434d78SMatthew Dillon * Some fans react very quickly, this will reduce oscillations.
1539c6434d78SMatthew Dillon */
1540c6434d78SMatthew Dillon if (DebugOpt)
1541c6434d78SMatthew Dillon printf("Temp index %d (use %d)\n", idx, last_idx);
1542c6434d78SMatthew Dillon if (last_idx >= 0 && idx < last_idx)
1543c6434d78SMatthew Dillon idx = last_idx - 1;
1544c6434d78SMatthew Dillon else if (last_idx >= 0 && idx > last_idx)
1545c6434d78SMatthew Dillon idx = last_idx + 1;
1546c6434d78SMatthew Dillon last_idx = idx;
1547c6434d78SMatthew Dillon
1548c6434d78SMatthew Dillon /*
1549c6434d78SMatthew Dillon * One last thing, make sure our frequency adheres to
1550c6434d78SMatthew Dillon * HighestCpuFreq. However, override LowestCpuFreq for
1551c6434d78SMatthew Dillon * temperature control purposes.
1552c6434d78SMatthew Dillon */
1553c6434d78SMatthew Dillon while (HighestCpuFreq > 0 && idx < NFreq &&
1554c6434d78SMatthew Dillon FreqAry[idx] > HighestCpuFreq) {
1555c6434d78SMatthew Dillon ++idx;
1556c6434d78SMatthew Dillon }
1557c6434d78SMatthew Dillon #if 0
1558c6434d78SMatthew Dillon /*
1559c6434d78SMatthew Dillon * Currently ignore LowestCpuFreq if temp control thinks it
1560c6434d78SMatthew Dillon * needs to go lower
1561c6434d78SMatthew Dillon */
1562c6434d78SMatthew Dillon while (LowestCpuFreq > 0 && idx > 0 &&
1563c6434d78SMatthew Dillon FreqAry[idx] < LowestCpuFreq) {
1564c6434d78SMatthew Dillon --idx;
1565c6434d78SMatthew Dillon }
1566c6434d78SMatthew Dillon #endif
1567c6434d78SMatthew Dillon
1568c6434d78SMatthew Dillon if (FreqAry[idx] != CurPXGlobal) {
1569c6434d78SMatthew Dillon CurPXGlobal = FreqAry[idx];
1570c6434d78SMatthew Dillon
1571c6434d78SMatthew Dillon #if 0
1572c6434d78SMatthew Dillon /* this can get noisy so don't log for now */
1573c6434d78SMatthew Dillon syslog(LOG_ALERT,
1574c6434d78SMatthew Dillon "Temp %d {%d-%d}, set frequency %d",
1575c6434d78SMatthew Dillon temp, MinTemp, MaxTemp, CurPXGlobal);
1576c6434d78SMatthew Dillon #endif
1577c6434d78SMatthew Dillon }
1578c6434d78SMatthew Dillon set_global_freq(CurPXGlobal);
1579c6434d78SMatthew Dillon }
1580