#define _KERNEL_STRUCTURES
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/kinfo.h>
#include <sys/queue.h>
#include <sys/soundcard.h>
#include <sys/sensors.h>
#include <sys/time.h>
#include <sys/cpumask.h>
#include <machine/cpufunc.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include "alert1.h"
#define MAXDOM MAXCPU
#define MAXFREQ 64
#define CST_STRLEN 16
#define NFREQ_MONPERF 0x0001
#define NFREQ_ADJPERF 0x0002
#define NFREQ_CPUTEMP 0x0004
#define NFREQ_ALL (NFREQ_MONPERF | NFREQ_ADJPERF | NFREQ_CPUTEMP)
struct cpu_pwrdom {
TAILQ_ENTRY(cpu_pwrdom) dom_link;
int dom_id;
int dom_ncpus;
cpumask_t dom_cpumask;
};
struct cpu_state {
double cpu_qavg;
double cpu_uavg;
double cpu_davg;
int cpu_limit;
int cpu_count;
char cpu_name[8];
};
static void usage(void);
static void get_ncpus(void);
static void mon_cputemp(void);
static void get_uschedcpus(void);
static void set_uschedcpus(void);
static int has_perfbias(void);
static void set_perfbias(int, int);
static void acpi_getcpufreq_str(int, int *, int *);
static int acpi_getcpufreq_bin(int, int *, int *);
static void acpi_get_cpufreq(int, int *, int *);
static void acpi_set_cpufreq(int, int);
static int acpi_get_cpupwrdom(void);
static int probe_cstate(void);
static void set_cstate(int, int);
static void init_perf(void);
static void mon_perf(double);
static void adj_perf(cpumask_t, cpumask_t);
static void adj_cpu_pwrdom(int, int);
static void adj_cpu_perf(int, int);
static void get_cputime(double);
static int get_nstate(struct cpu_state *, double);
static void add_spare_cpus(const cpumask_t, int);
static void restore_perf(void);
static void set_global_freq(int freq);
static int has_battery(void);
static int mon_battery(void);
static void low_battery_alert(int);
static void restore_backlight(void);
static int global_pcpu_limit;
static struct cpu_state pcpu_state[MAXCPU];
static struct cpu_state global_cpu_state;
static cpumask_t cpu_used;
static cpumask_t cpu_pwrdom_used;
static cpumask_t usched_cpu_used;
static cpumask_t cpu_pwrdom_mask;
static int cpu2pwrdom[MAXCPU];
static struct cpu_pwrdom *cpu_pwrdomain[MAXDOM];
static int NCpus;
static char orig_global_cx[CST_STRLEN];
static char cpu_perf_cx[CST_STRLEN];
static int cpu_perf_cxlen;
static char cpu_idle_cx[CST_STRLEN];
static int cpu_idle_cxlen;
static int FreqAry[MAXFREQ];
static int NFreq;
static int NFreqChanged = NFREQ_ALL;
static int SavedPXGlobal;
static int DebugOpt;
static int TurboOpt = 1;
static int PowerFd;
static int Hysteresis = 10;
static double TriggerUp = 0.25;
static double TriggerDown;
static int HasPerfbias = 0;
static int AdjustCpuFreq = 1;
static int AdjustCstate = 0;
static int HighestCpuFreq;
static int LowestCpuFreq;
static int AdjustUsched = 1;
static int AdjustCpuFreqOverride;
static volatile int stopped;
static int BatLifeMin = 2;
static struct timespec BatLifePrevT;
static int BatLifePollIntvl = 5;
static struct timespec BatShutdownStartT;
static int BatShutdownLinger = -1;
static int BatShutdownLingerSet = 60;
static int BatShutdownLingerCnt;
static int BatShutdownAudioAlert = 1;
static int MinTemp = 75;
static int MaxTemp = 85;
static int BackLightPct = 100;
static int OldBackLightLevel;
static int BackLightDown;
static void sigintr(int signo);
int
main(int ac, char **av)
{
double srt;
double pollrate;
int ch;
int lowest;
int highest;
char buf[64];
int monbat;
char *p2;
srt = 8.0;
pollrate = 1.0;
while ((ch = getopt(ac, av, "b:cdefh:l:p:r:tu:B:H:L:P:QT:U")) != -1) {
switch(ch) {
case 'b':
BackLightPct = strtol(optarg, NULL, 10);
break;
case 'c':
AdjustCstate = 1;
break;
case 'd':
DebugOpt = 1;
break;
case 'e':
HasPerfbias = 1;
break;
case 'f':
AdjustCpuFreq = 0;
break;
case 'h':
HighestCpuFreq = strtol(optarg, NULL, 10);
break;
case 'l':
LowestCpuFreq = strtol(optarg, NULL, 10);
break;
case 'p':
Hysteresis = (int)strtol(optarg, NULL, 10);
break;
case 'r':
pollrate = strtod(optarg, NULL);
break;
case 't':
TurboOpt = 0;
break;
case 'u':
TriggerUp = (double)strtol(optarg, NULL, 10) / 100;
break;
case 'B':
BatLifeMin = strtol(optarg, NULL, 10);
break;
case 'H':
MaxTemp = strtol(optarg, &p2, 0);
if (*p2 == ':') {
MinTemp = MaxTemp;
MaxTemp = strtol(p2 + 1, NULL, 0);
} else {
MinTemp = MaxTemp * 9 / 10;
}
break;
case 'L':
BatShutdownLingerSet = strtol(optarg, NULL, 10);
if (BatShutdownLingerSet < 0)
BatShutdownLingerSet = 0;
break;
case 'P':
BatLifePollIntvl = strtol(optarg, NULL, 10);
break;
case 'Q':
BatShutdownAudioAlert = 0;
break;
case 'T':
srt = strtod(optarg, NULL);
break;
case 'U':
AdjustUsched = 0;
break;
default:
usage();
}
}
ac -= optind;
av += optind;
setlinebuf(stdout);
get_ncpus();
acpi_get_cpufreq(0, &lowest, &highest);
if (Hysteresis < 0 || Hysteresis > 99) {
fprintf(stderr, "Invalid hysteresis value\n");
exit(1);
}
if (TriggerUp < 0 || TriggerUp > 1) {
fprintf(stderr, "Invalid load limit value\n");
exit(1);
}
if (BackLightPct > 100 || BackLightPct <= 0) {
fprintf(stderr, "Invalid backlight setting, ignore\n");
BackLightPct = 100;
}
TriggerDown = TriggerUp - (TriggerUp * (double) Hysteresis / 100);
PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644);
if (PowerFd < 0) {
fprintf(stderr,
"Cannot create /var/run/powerd.pid, "
"continuing anyway\n");
} else {
ssize_t r;
pid_t pid = -1;
r = read(PowerFd, buf, sizeof(buf) - 1);
if (r > 0) {
buf[r] = 0;
pid = strtol(buf, NULL, 0);
}
if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) {
if (pid > 0) {
kill(pid, SIGTERM);
flock(PowerFd, LOCK_EX);
fprintf(stderr, "restarting powerd\n");
} else {
fprintf(stderr,
"powerd is already running, "
"unable to kill pid for restart\n");
exit(1);
}
}
lseek(PowerFd, 0L, 0);
}
if (DebugOpt == 0) {
daemon(0, 0);
openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON);
}
if (PowerFd >= 0) {
ftruncate(PowerFd, 0);
snprintf(buf, sizeof(buf), "%d\n", (int)getpid());
write(PowerFd, buf, strlen(buf));
}
if (BatLifePollIntvl <= 0)
monbat = 0;
else
monbat = has_battery();
if (HasPerfbias)
HasPerfbias = has_perfbias();
if (AdjustCstate)
AdjustCstate = probe_cstate();
for (;;) {
get_cputime(pollrate);
if (acpi_get_cpupwrdom())
break;
usleep((int)(pollrate * 1000000.0));
}
signal(SIGINT, sigintr);
signal(SIGTERM, sigintr);
init_perf();
srt = srt / pollrate;
if (DebugOpt)
printf("samples for downgrading: %5.2f\n", srt);
while (!stopped) {
get_cputime(pollrate);
mon_cputemp();
mon_perf(srt);
if (monbat)
monbat = mon_battery();
usleep((int)(pollrate * 1000000.0));
}
syslog(LOG_INFO, "killed, setting max and exiting");
if (SavedPXGlobal)
set_global_freq(SavedPXGlobal);
restore_perf();
restore_backlight();
exit(0);
}
static void
sigintr(int signo __unused)
{
stopped = 1;
}
static int
acpi_get_cpupwrdom(void)
{
struct cpu_pwrdom *dom;
cpumask_t pwrdom_mask;
char buf[64];
char members[1024];
char *str;
size_t msize;
int n, i, ncpu = 0, dom_id;
memset(cpu2pwrdom, 0, sizeof(cpu2pwrdom));
memset(cpu_pwrdomain, 0, sizeof(cpu_pwrdomain));
CPUMASK_ASSZERO(cpu_pwrdom_mask);
for (i = 0; i < MAXDOM; ++i) {
snprintf(buf, sizeof(buf),
"hw.acpi.cpu.px_dom%d.available", i);
if (sysctlbyname(buf, NULL, NULL, NULL, 0) < 0)
continue;
dom = calloc(1, sizeof(*dom));
dom->dom_id = i;
if (cpu_pwrdomain[i] != NULL) {
fprintf(stderr, "cpu power domain %d exists\n", i);
exit(1);
}
cpu_pwrdomain[i] = dom;
CPUMASK_ORBIT(cpu_pwrdom_mask, i);
}
pwrdom_mask = cpu_pwrdom_mask;
while (CPUMASK_TESTNZERO(pwrdom_mask)) {
dom_id = BSFCPUMASK(pwrdom_mask);
CPUMASK_NANDBIT(pwrdom_mask, dom_id);
dom = cpu_pwrdomain[dom_id];
CPUMASK_ASSZERO(dom->dom_cpumask);
snprintf(buf, sizeof(buf),
"hw.acpi.cpu.px_dom%d.members", dom->dom_id);
msize = sizeof(members);
if (sysctlbyname(buf, members, &msize, NULL, 0) < 0) {
cpu_pwrdomain[dom_id] = NULL;
free(dom);
continue;
}
members[msize] = 0;
for (str = strtok(members, " "); str; str = strtok(NULL, " ")) {
n = -1;
sscanf(str, "cpu%d", &n);
if (n >= 0) {
++ncpu;
++dom->dom_ncpus;
CPUMASK_ORBIT(dom->dom_cpumask, n);
cpu2pwrdom[n] = dom->dom_id;
}
}
if (dom->dom_ncpus == 0) {
cpu_pwrdomain[dom_id] = NULL;
free(dom);
continue;
}
if (DebugOpt) {
printf("dom%d cpumask: ", dom->dom_id);
for (i = 0; i < (int)NELEM(dom->dom_cpumask.ary); ++i) {
printf("%jx ",
(uintmax_t)dom->dom_cpumask.ary[i]);
}
printf("\n");
}
}
if (ncpu != NCpus) {
if (DebugOpt)
printf("Found %d cpus, expecting %d\n", ncpu, NCpus);
pwrdom_mask = cpu_pwrdom_mask;
while (CPUMASK_TESTNZERO(pwrdom_mask)) {
dom_id = BSFCPUMASK(pwrdom_mask);
CPUMASK_NANDBIT(pwrdom_mask, dom_id);
dom = cpu_pwrdomain[dom_id];
if (dom != NULL)
free(dom);
}
return 0;
}
return 1;
}
static void
get_cputime(double pollrate)
{
static struct kinfo_cputime ocpu_time[MAXCPU];
static struct kinfo_cputime ncpu_time[MAXCPU];
size_t slen;
int ncpu;
int cpu;
uint64_t delta;
bcopy(ncpu_time, ocpu_time, sizeof(struct kinfo_cputime) * NCpus);
slen = sizeof(ncpu_time);
if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) {
fprintf(stderr, "kern.cputime sysctl not available\n");
exit(1);
}
ncpu = slen / sizeof(ncpu_time[0]);
delta = 0;
for (cpu = 0; cpu < ncpu; ++cpu) {
uint64_t d;
d = (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys +
ncpu_time[cpu].cp_nice + ncpu_time[cpu].cp_intr) -
(ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys +
ocpu_time[cpu].cp_nice + ocpu_time[cpu].cp_intr);
pcpu_state[cpu].cpu_qavg = (double)d / (pollrate * 1000000.0);
delta += d;
}
global_cpu_state.cpu_qavg = (double)delta / (pollrate * 1000000.0);
}
static void
acpi_getcpufreq_str(int dom_id, int *highest0, int *lowest0)
{
char buf[256], sysid[64];
size_t buflen;
char *ptr;
int v, highest, lowest;
int freqidx;
snprintf(sysid, sizeof(sysid),
"hw.acpi.cpu.px_dom%d.available", dom_id);
buflen = sizeof(buf) - 1;
if (sysctlbyname(sysid, buf, &buflen, NULL, 0) < 0)
return;
buf[buflen] = 0;
ptr = buf;
highest = lowest = 0;
freqidx = 0;
while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
if ((lowest == 0 || lowest > v) &&
(LowestCpuFreq <= 0 || v >= LowestCpuFreq))
lowest = v;
if ((highest == 0 || highest < v) &&
(HighestCpuFreq <= 0 || v <= HighestCpuFreq))
highest = v;
if (!TurboOpt && highest - v == 1)
highest = v;
++freqidx;
}
if (freqidx > MAXFREQ)
freqidx = MAXFREQ;
if (NFreq != freqidx) {
NFreq = freqidx;
NFreqChanged = NFREQ_ALL;
}
ptr = buf;
while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
if (freqidx == 0)
break;
if (FreqAry[freqidx - 1] != v)
NFreqChanged = NFREQ_ALL;
FreqAry[--freqidx] = v;
}
*highest0 = highest;
*lowest0 = lowest;
}
static int
acpi_getcpufreq_bin(int dom_id, int *highest0, int *lowest0)
{
char sysid[64];
size_t freqlen;
int freqcnt, i;
int freqary[MAXFREQ];
snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.avail", dom_id);
freqlen = sizeof(FreqAry);
bzero(freqary, sizeof(freqary));
if (sysctlbyname(sysid, freqary, &freqlen, NULL, 0) < 0)
return 0;
freqcnt = freqlen / sizeof(freqary[0]);
if (NFreq != freqcnt) {
NFreq = freqcnt;
NFreqChanged = NFREQ_ALL;
}
if (bcmp(freqary, FreqAry, sizeof(FreqAry)) != 0)
NFreqChanged = NFREQ_ALL;
bcopy(freqary, FreqAry, sizeof(FreqAry));
if (freqcnt == 0)
return 0;
for (i = freqcnt - 1; i >= 0; --i) {
*lowest0 = FreqAry[i];
if (LowestCpuFreq <= 0 || *lowest0 >= LowestCpuFreq)
break;
}
i = 0;
*highest0 = FreqAry[0];
if (!TurboOpt && freqcnt > 1 && FreqAry[0] - FreqAry[1] == 1) {
i = 1;
*highest0 = FreqAry[1];
}
for (; i < freqcnt; ++i) {
if (HighestCpuFreq <= 0 || *highest0 <= HighestCpuFreq)
break;
*highest0 = FreqAry[i];
}
return 1;
}
static void
acpi_get_cpufreq(int dom_id, int *highest, int *lowest)
{
*highest = 0;
*lowest = 0;
if (acpi_getcpufreq_bin(dom_id, highest, lowest))
return;
acpi_getcpufreq_str(dom_id, highest, lowest);
}
static
void
usage(void)
{
fprintf(stderr, "usage: powerd [-cdeftQU] [-p hysteresis] "
"[-h highest_freq] [-l lowest_freq] "
"[-r poll_interval] [-u trigger_up] "
"[-B min_battery_life] [-L low_battery_linger] "
"[-P battery_poll_interval] [-T sample_interval] "
"[-b backlight]\n");
exit(1);
}
#define BAT_SYSCTL_TIME_MAX 50000000
static int
has_battery(void)
{
struct timespec s, e;
size_t len;
int val;
clock_gettime(CLOCK_MONOTONIC_FAST, &s);
BatLifePrevT = s;
len = sizeof(val);
if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) {
return 0;
}
clock_gettime(CLOCK_MONOTONIC_FAST, &e);
timespecsub(&e, &s, &e);
if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
syslog(LOG_NOTICE, "hw.acpi.acline takes too long");
return 0;
}
clock_gettime(CLOCK_MONOTONIC_FAST, &s);
len = sizeof(val);
if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) {
return 0;
}
clock_gettime(CLOCK_MONOTONIC_FAST, &e);
timespecsub(&e, &s, &e);
if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long");
return 0;
}
return 1;
}
static void
low_battery_alert(int life)
{
int fmt, stereo, freq;
int fd;
syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d",
life, BatShutdownLingerCnt);
++BatShutdownLingerCnt;
if (!BatShutdownAudioAlert)
return;
fd = open("/dev/dsp", O_WRONLY);
if (fd < 0)
return;
fmt = AFMT_S16_LE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0)
goto done;
stereo = 0;
if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0)
goto done;
freq = 44100;
if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0)
goto done;
write(fd, alert1, sizeof(alert1));
write(fd, alert1, sizeof(alert1));
done:
close(fd);
}
static int
mon_battery(void)
{
struct timespec cur, ts;
int acline, life;
size_t len;
clock_gettime(CLOCK_MONOTONIC_FAST, &cur);
timespecsub(&cur, &BatLifePrevT, &ts);
if (ts.tv_sec < BatLifePollIntvl)
return 1;
BatLifePrevT = cur;
len = sizeof(acline);
if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0)
return 1;
if (acline) {
BatShutdownLinger = -1;
BatShutdownLingerCnt = 0;
restore_backlight();
return 1;
}
if (!BackLightDown && BackLightPct != 100) {
int backlight_max, backlight;
len = sizeof(backlight_max);
if (sysctlbyname("hw.backlight_max", &backlight_max, &len,
NULL, 0) < 0) {
BackLightPct = 100;
goto after_backlight;
}
len = sizeof(OldBackLightLevel);
if (sysctlbyname("hw.backlight_level", &OldBackLightLevel, &len,
NULL, 0) < 0) {
BackLightPct = 100;
goto after_backlight;
}
backlight = (backlight_max * BackLightPct) / 100;
if (backlight >= OldBackLightLevel) {
BackLightPct = 100;
goto after_backlight;
}
if (sysctlbyname("hw.backlight_level", NULL, NULL,
&backlight, sizeof(backlight)) < 0) {
BackLightPct = 100;
goto after_backlight;
}
BackLightDown = 1;
}
after_backlight:
len = sizeof(life);
if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0)
return 1;
if (BatShutdownLinger > 0) {
timespecsub(&cur, &BatShutdownStartT, &ts);
if (ts.tv_sec > BatShutdownLinger)
BatShutdownLinger = 0;
}
if (life <= BatLifeMin) {
if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) {
syslog(LOG_ALERT, "low battery life %d%%, "
"shutting down", life);
if (vfork() == 0)
execlp("poweroff", "poweroff", NULL);
return 0;
} else if (BatShutdownLinger < 0) {
BatShutdownLinger = BatShutdownLingerSet;
BatShutdownStartT = cur;
}
low_battery_alert(life);
}
return 1;
}
static void
get_ncpus(void)
{
size_t slen;
slen = sizeof(NCpus);
if (sysctlbyname("hw.ncpu", &NCpus, &slen, NULL, 0) < 0)
err(1, "sysctlbyname hw.ncpu failed");
if (DebugOpt)
printf("hw.ncpu %d\n", NCpus);
}
static void
get_uschedcpus(void)
{
size_t slen;
slen = sizeof(usched_cpu_used);
if (sysctlbyname("kern.usched_global_cpumask", &usched_cpu_used, &slen,
NULL, 0) < 0)
err(1, "sysctlbyname kern.usched_global_cpumask failed");
if (DebugOpt) {
int i;
printf("usched cpumask was: ");
for (i = 0; i < (int)NELEM(usched_cpu_used.ary); ++i)
printf("%jx ", (uintmax_t)usched_cpu_used.ary[i]);
printf("\n");
}
}
static void
set_uschedcpus(void)
{
if (DebugOpt) {
int i;
printf("usched cpumask: ");
for (i = 0; i < (int)NELEM(usched_cpu_used.ary); ++i) {
printf("%jx ",
(uintmax_t)usched_cpu_used.ary[i]);
}
printf("\n");
}
sysctlbyname("kern.usched_global_cpumask", NULL, 0,
&usched_cpu_used, sizeof(usched_cpu_used));
}
static int
has_perfbias(void)
{
size_t len;
int hint;
len = sizeof(hint);
if (sysctlbyname("machdep.perfbias0.hint", &hint, &len, NULL, 0) < 0)
return 0;
return 1;
}
static void
set_perfbias(int cpu, int inc)
{
int hint = inc ? 0 : 15;
char sysid[64];
if (DebugOpt)
printf("cpu%d set perfbias hint %d\n", cpu, hint);
snprintf(sysid, sizeof(sysid), "machdep.perfbias%d.hint", cpu);
sysctlbyname(sysid, NULL, NULL, &hint, sizeof(hint));
}
static void
init_perf(void)
{
struct cpu_state *state;
int cpu;
get_uschedcpus();
CPUMASK_ASSBMASK(cpu_used, NCpus);
cpu_pwrdom_used = cpu_pwrdom_mask;
global_pcpu_limit = NCpus;
for (cpu = 0; cpu < NCpus; ++cpu) {
state = &pcpu_state[cpu];
state->cpu_uavg = 0.0;
state->cpu_davg = 0.0;
state->cpu_limit = 1;
state->cpu_count = 1;
snprintf(state->cpu_name, sizeof(state->cpu_name), "cpu%d",
cpu);
}
state = &global_cpu_state;
state->cpu_uavg = 0.0;
state->cpu_davg = 0.0;
state->cpu_limit = NCpus;
state->cpu_count = NCpus;
strlcpy(state->cpu_name, "global", sizeof(state->cpu_name));
}
static int
get_nstate(struct cpu_state *state, double srt)
{
int ustate, dstate, nstate;
state->cpu_uavg = (state->cpu_uavg * 2.0 + state->cpu_qavg) / 3.0;
state->cpu_davg = (state->cpu_davg * srt + state->cpu_qavg) / (srt + 1);
if (state->cpu_davg < state->cpu_uavg)
state->cpu_davg = state->cpu_uavg;
ustate = state->cpu_uavg / TriggerUp;
if (ustate < state->cpu_limit)
ustate = state->cpu_uavg / TriggerDown;
dstate = state->cpu_davg / TriggerUp;
if (dstate < state->cpu_limit)
dstate = state->cpu_davg / TriggerDown;
nstate = (ustate > dstate) ? ustate : dstate;
if (nstate > state->cpu_count)
nstate = state->cpu_count;
if (DebugOpt) {
printf("%s qavg=%5.2f uavg=%5.2f davg=%5.2f "
"%2d ncpus=%d\n", state->cpu_name,
state->cpu_qavg, state->cpu_uavg, state->cpu_davg,
state->cpu_limit, nstate);
}
return nstate;
}
static void
mon_perf(double srt)
{
cpumask_t ocpu_used, ocpu_pwrdom_used;
int pnstate = 0, nstate;
int cpu;
ocpu_used = cpu_used;
ocpu_pwrdom_used = cpu_pwrdom_used;
CPUMASK_ASSZERO(cpu_used);
CPUMASK_ASSZERO(cpu_pwrdom_used);
for (cpu = 0; cpu < NCpus; ++cpu) {
struct cpu_state *state = &pcpu_state[cpu];
int s;
s = get_nstate(state, srt);
if (s) {
CPUMASK_ORBIT(cpu_used, cpu);
CPUMASK_ORBIT(cpu_pwrdom_used, cpu2pwrdom[cpu]);
}
pnstate += s;
state->cpu_limit = s;
}
nstate = get_nstate(&global_cpu_state, srt);
if (nstate == global_cpu_state.cpu_limit &&
(NFreqChanged & NFREQ_MONPERF) == 0 &&
(pnstate == global_pcpu_limit || nstate > pnstate)) {
cpu_used = ocpu_used;
cpu_pwrdom_used = ocpu_pwrdom_used;
global_pcpu_limit = pnstate;
return;
}
NFreqChanged &= ~NFREQ_MONPERF;
global_pcpu_limit = pnstate;
if (nstate > pnstate) {
add_spare_cpus(ocpu_used, nstate - pnstate);
}
global_cpu_state.cpu_limit = nstate;
adj_perf(ocpu_used, ocpu_pwrdom_used);
}
static void
add_spare_cpus(const cpumask_t ocpu_used, int ncpu)
{
cpumask_t saved_pwrdom, xcpu_used;
int done = 0, cpu;
xcpu_used = cpu_used;
CPUMASK_XORMASK(xcpu_used, ocpu_used);
while (CPUMASK_TESTNZERO(xcpu_used)) {
cpu = BSFCPUMASK(xcpu_used);
CPUMASK_NANDBIT(xcpu_used, cpu);
if (CPUMASK_TESTBIT(ocpu_used, cpu)) {
CPUMASK_ORBIT(cpu_pwrdom_used, cpu2pwrdom[cpu]);
CPUMASK_ORBIT(cpu_used, cpu);
--ncpu;
if (ncpu == 0)
return;
}
}
saved_pwrdom = cpu_pwrdom_used;
again:
while (CPUMASK_TESTNZERO(saved_pwrdom)) {
cpumask_t unused_cpumask;
int dom;
dom = BSFCPUMASK(saved_pwrdom);
CPUMASK_NANDBIT(saved_pwrdom, dom);
unused_cpumask = cpu_pwrdomain[dom]->dom_cpumask;
CPUMASK_NANDMASK(unused_cpumask, cpu_used);
while (CPUMASK_TESTNZERO(unused_cpumask)) {
cpu = BSFCPUMASK(unused_cpumask);
CPUMASK_NANDBIT(unused_cpumask, cpu);
CPUMASK_ORBIT(cpu_pwrdom_used, dom);
CPUMASK_ORBIT(cpu_used, cpu);
--ncpu;
if (ncpu == 0)
return;
}
}
if (!done) {
done = 1;
saved_pwrdom = cpu_pwrdom_mask;
CPUMASK_NANDMASK(saved_pwrdom, cpu_pwrdom_used);
goto again;
}
if (DebugOpt)
printf("%d cpus not found\n", ncpu);
}
static void
acpi_set_cpufreq(int dom, int inc)
{
int lowest, highest, desired;
char sysid[64];
acpi_get_cpufreq(dom, &highest, &lowest);
if (highest == 0 || lowest == 0)
return;
desired = inc ? highest : lowest;
if (DebugOpt)
printf("dom%d set frequency %d\n", dom, desired);
snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.select", dom);
sysctlbyname(sysid, NULL, NULL, &desired, sizeof(desired));
}
static void
adj_cpu_pwrdom(int dom, int inc)
{
if (AdjustCpuFreq && (inc == 0 || AdjustCpuFreqOverride == 0))
acpi_set_cpufreq(dom, inc);
}
static void
adj_cpu_perf(int cpu, int inc)
{
if (DebugOpt) {
if (inc)
printf("cpu%d increase perf\n", cpu);
else
printf("cpu%d decrease perf\n", cpu);
}
if (HasPerfbias)
set_perfbias(cpu, inc);
if (AdjustCstate)
set_cstate(cpu, inc);
}
static void
adj_perf(cpumask_t xcpu_used, cpumask_t xcpu_pwrdom_used)
{
int cpu, inc;
if (AdjustUsched) {
cpumask_t old_usched_used;
old_usched_used = usched_cpu_used;
usched_cpu_used = cpu_used;
if (CPUMASK_TESTZERO(usched_cpu_used))
CPUMASK_ORBIT(usched_cpu_used, 0);
if (CPUMASK_CMPMASKNEQ(usched_cpu_used, old_usched_used))
set_uschedcpus();
}
CPUMASK_XORMASK(xcpu_used, cpu_used);
if (NFreqChanged & NFREQ_ADJPERF)
CPUMASK_ASSBMASK(xcpu_used, NCpus);
while (CPUMASK_TESTNZERO(xcpu_used)) {
cpu = BSFCPUMASK(xcpu_used);
CPUMASK_NANDBIT(xcpu_used, cpu);
if (CPUMASK_TESTBIT(cpu_used, cpu)) {
inc = 1;
} else {
inc = 0;
}
adj_cpu_perf(cpu, inc);
}
CPUMASK_XORMASK(xcpu_pwrdom_used, cpu_pwrdom_used);
if (NFreqChanged & NFREQ_ADJPERF)
CPUMASK_ASSBMASK(xcpu_pwrdom_used, NCpus);
while (CPUMASK_TESTNZERO(xcpu_pwrdom_used)) {
int dom;
dom = BSFCPUMASK(xcpu_pwrdom_used);
CPUMASK_NANDBIT(xcpu_pwrdom_used, dom);
if (CPUMASK_TESTBIT(cpu_pwrdom_used, dom)) {
inc = 1;
} else {
inc = 0;
}
adj_cpu_pwrdom(dom, inc);
}
NFreqChanged &= ~NFREQ_ADJPERF;
}
static void
restore_perf(void)
{
cpumask_t ocpu_used, ocpu_pwrdom_used;
HighestCpuFreq = 0;
ocpu_used = cpu_used;
ocpu_pwrdom_used = cpu_pwrdom_used;
CPUMASK_ASSBMASK(cpu_used, NCpus);
cpu_pwrdom_used = cpu_pwrdom_mask;
adj_perf(ocpu_used, ocpu_pwrdom_used);
if (AdjustCstate) {
if (DebugOpt)
printf("global set cstate %s\n", orig_global_cx);
sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
orig_global_cx, strlen(orig_global_cx) + 1);
}
}
static int
probe_cstate(void)
{
char cx_supported[1024];
const char *target;
char *ptr;
int idle_hlt, deep = 1;
size_t len;
len = sizeof(idle_hlt);
if (sysctlbyname("machdep.cpu_idle_hlt", &idle_hlt, &len, NULL, 0) < 0)
return 0;
if (idle_hlt != 1)
return 0;
len = sizeof(cx_supported);
if (sysctlbyname("machdep.mwait.CX.supported", cx_supported, &len,
NULL, 0) < 0)
return 0;
len = sizeof(orig_global_cx);
if (sysctlbyname("machdep.mwait.CX.idle", orig_global_cx, &len,
NULL, 0) < 0)
return 0;
strlcpy(cpu_perf_cx, "AUTODEEP", sizeof(cpu_perf_cx));
cpu_perf_cxlen = strlen(cpu_perf_cx) + 1;
if (sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
cpu_perf_cx, cpu_perf_cxlen) < 0) {
deep = 0;
strlcpy(cpu_perf_cx, "AUTO", sizeof(cpu_perf_cx));
cpu_perf_cxlen = strlen(cpu_perf_cx) + 1;
if (sysctlbyname("machdep.mwait.CX.idle", NULL, NULL,
cpu_perf_cx, cpu_perf_cxlen) < 0)
return 0;
}
if (!deep)
target = "C2/0";
else
target = NULL;
for (ptr = strtok(cx_supported, " "); ptr != NULL;
ptr = strtok(NULL, " ")) {
if (target == NULL ||
(target != NULL && strcmp(ptr, target) == 0)) {
strlcpy(cpu_idle_cx, ptr, sizeof(cpu_idle_cx));
cpu_idle_cxlen = strlen(cpu_idle_cx) + 1;
if (target != NULL)
break;
}
}
if (cpu_idle_cxlen == 0)
return 0;
if (DebugOpt) {
printf("cstate orig %s, perf %s, idle %s\n",
orig_global_cx, cpu_perf_cx, cpu_idle_cx);
}
return 1;
}
static void
set_cstate(int cpu, int inc)
{
const char *cst;
char sysid[64];
size_t len;
if (inc) {
cst = cpu_perf_cx;
len = cpu_perf_cxlen;
} else {
cst = cpu_idle_cx;
len = cpu_idle_cxlen;
}
if (DebugOpt)
printf("cpu%d set cstate %s\n", cpu, cst);
snprintf(sysid, sizeof(sysid), "machdep.mwait.CX.idle%d", cpu);
sysctlbyname(sysid, NULL, NULL, cst, len);
}
static void
restore_backlight(void)
{
if (BackLightDown) {
BackLightDown = 0;
sysctlbyname("hw.backlight_level", NULL, NULL,
&OldBackLightLevel, sizeof(OldBackLightLevel));
}
}
static int
get_cputemp(void)
{
char sysid[64];
struct sensor sensor;
size_t sensor_size;
int t;
int mt = -1;
int n;
for (n = 0; ; ++n) {
t = 0;
snprintf(sysid, sizeof(sysid),
"hw.sensors.cpu_node%d.temp0", n);
sensor_size = sizeof(sensor);
if (sysctlbyname(sysid, &sensor, &sensor_size, NULL, 0) < 0)
break;
t = -1;
if ((sensor.flags & (SENSOR_FINVALID | SENSOR_FUNKNOWN)) == 0) {
t = (int)((sensor.value - 273150000) / 1000000);
if (mt < t)
mt = t;
}
}
if (n)
return mt;
for (n = 0; ; ++n) {
t = 0;
snprintf(sysid, sizeof(sysid),
"hw.sensors.cpu%d.temp0", n);
sensor_size = sizeof(sensor);
if (sysctlbyname(sysid, &sensor, &sensor_size, NULL, 0) < 0)
break;
t = -1;
if ((sensor.flags & (SENSOR_FINVALID | SENSOR_FUNKNOWN)) == 0) {
t = (int)((sensor.value - 273150000) / 1000000);
if (mt < t)
mt = t;
}
}
return mt;
}
static void
set_global_freq(int freq)
{
if (freq > 0)
sysctlbyname("hw.acpi.cpu.px_global",
NULL, NULL, &freq, sizeof(freq));
}
static int
get_global_freq(void)
{
int freq;
size_t freq_size;
freq = -1;
freq_size = sizeof(freq);
sysctlbyname("hw.acpi.cpu.px_global", &freq, &freq_size, NULL, 0);
return freq;
}
static void
mon_cputemp(void)
{
static int last_temp = -1;
static int last_idx = -1;
int temp = get_cputemp();
int idx;
int lowest;
int highest;
static int CurPXGlobal __unused;
acpi_get_cpufreq(0, &lowest, &highest);
if (last_temp < 0 || (NFreqChanged & NFREQ_CPUTEMP)) {
NFreqChanged &= ~NFREQ_CPUTEMP;
last_temp = temp << 8;
} else if (temp < last_temp) {
last_temp = (last_temp * 15 + (temp << 8)) / 16;
if (DebugOpt) {
printf("Falling temp %d (use %d)\n",
temp, (last_temp >> 8));
}
} else {
last_temp = (last_temp * 15 + (temp << 8)) / 16;
if (DebugOpt) {
printf("Rising temp %d (use %d)\n",
temp, (last_temp >> 8));
}
}
temp = last_temp >> 8;
if (DebugOpt)
printf("Temp %d {%d-%d} NFreq=%d)\n",
temp, MinTemp, MaxTemp, NFreq);
if (temp <= 0)
return;
if (NFreq == 0)
return;
if (temp <= MinTemp) {
if (AdjustCpuFreqOverride) {
AdjustCpuFreqOverride = 0;
CurPXGlobal = 0;
NFreqChanged = NFREQ_ALL;
last_idx = -1;
syslog(LOG_ALERT,
"Temp below %d, returning to normal operation",
MinTemp);
if (SavedPXGlobal)
set_global_freq(SavedPXGlobal);
}
return;
}
if (AdjustCpuFreqOverride == 0 &&
temp <= MinTemp + (MaxTemp - MinTemp) / 10 + 1) {
return;
}
if (AdjustCpuFreqOverride == 0) {
AdjustCpuFreqOverride = 1;
SavedPXGlobal = get_global_freq();
CurPXGlobal = 0;
NFreqChanged = NFREQ_ALL;
last_idx = -1;
syslog(LOG_ALERT,
"Temp %d {%d-%d}, entering temperature control mode",
temp, MinTemp, MaxTemp);
}
if (temp > MaxTemp + (MaxTemp - MinTemp) / 10 + 1) {
syslog(LOG_ALERT,
"Temp %d {%d-%d}, TOO HOT!!!",
temp, MinTemp, MaxTemp);
}
idx = (temp - MinTemp) * NFreq / (MaxTemp - MinTemp);
if (idx < 0 || idx >= NFreq)
idx = NFreq - 1;
if (DebugOpt)
printf("Temp index %d (use %d)\n", idx, last_idx);
if (last_idx >= 0 && idx < last_idx)
idx = last_idx - 1;
else if (last_idx >= 0 && idx > last_idx)
idx = last_idx + 1;
last_idx = idx;
while (HighestCpuFreq > 0 && idx < NFreq &&
FreqAry[idx] > HighestCpuFreq) {
++idx;
}
#if 0
while (LowestCpuFreq > 0 && idx > 0 &&
FreqAry[idx] < LowestCpuFreq) {
--idx;
}
#endif
if (FreqAry[idx] != CurPXGlobal) {
CurPXGlobal = FreqAry[idx];
#if 0
syslog(LOG_ALERT,
"Temp %d {%d-%d}, set frequency %d",
temp, MinTemp, MaxTemp, CurPXGlobal);
#endif
}
set_global_freq(CurPXGlobal);
}