xref: /dflybsd-src/usr.sbin/powerd/powerd.c (revision 2c3b1d1bc3a233e4bf80218452e719882a1bb011)
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