summaryrefslogtreecommitdiffstats
path: root/usr.sbin/powerd/powerd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/powerd/powerd.c')
-rw-r--r--usr.sbin/powerd/powerd.c222
1 files changed, 143 insertions, 79 deletions
diff --git a/usr.sbin/powerd/powerd.c b/usr.sbin/powerd/powerd.c
index 8ecff23..23354b9 100644
--- a/usr.sbin/powerd/powerd.c
+++ b/usr.sbin/powerd/powerd.c
@@ -50,13 +50,14 @@ __FBSDID("$FreeBSD$");
#include <machine/apm_bios.h>
#endif
-#define DEFAULT_ACTIVE_PERCENT 65
-#define DEFAULT_IDLE_PERCENT 90
-#define DEFAULT_POLL_INTERVAL 500 /* Poll interval in milliseconds */
+#define DEFAULT_ACTIVE_PERCENT 75
+#define DEFAULT_IDLE_PERCENT 50
+#define DEFAULT_POLL_INTERVAL 250 /* Poll interval in milliseconds */
typedef enum {
MODE_MIN,
MODE_ADAPTIVE,
+ MODE_HIADAPTIVE,
MODE_MAX,
} modes_t;
@@ -77,7 +78,7 @@ const char *modes[] = {
#define DEVDPIPE "/var/run/devd.pipe"
#define DEVCTL_MAXBUF 1024
-static int read_usage_times(long *idle, long *total);
+static int read_usage_times(int *load);
static int read_freqs(int *numfreqs, int **freqs, int **power);
static int set_freq(int freq);
static void acline_init(void);
@@ -89,7 +90,7 @@ static void parse_mode(char *arg, int *mode, int ch);
static void usage(void);
/* Sysctl data structures. */
-static int cp_time_mib[2];
+static int cp_times_mib[2];
static int freq_mib[4];
static int levels_mib[4];
static int acline_mib[3];
@@ -119,27 +120,49 @@ static int devd_pipe = -1;
static struct timeval tried_devd;
static int
-read_usage_times(long *idle, long *total)
+read_usage_times(int *load)
{
- static long idle_old, total_old;
- long cp_time[CPUSTATES], i, total_new;
- size_t cp_time_len;
- int error;
+ static long *cp_times = NULL, *cp_times_old = NULL;
+ static int ncpus = 0;
+ size_t cp_times_len;
+ int error, cpu, i, total;
+
+ if (cp_times == NULL) {
+ cp_times_len = 0;
+ error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0);
+ if (error)
+ return (error);
+ if ((cp_times = malloc(cp_times_len)) == NULL)
+ return (errno);
+ if ((cp_times_old = malloc(cp_times_len)) == NULL) {
+ free(cp_times);
+ cp_times = NULL;
+ return (errno);
+ }
+ ncpus = cp_times_len / (sizeof(long) * CPUSTATES);
+ }
- cp_time_len = sizeof(cp_time);
- error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0);
+ cp_times_len = sizeof(long) * CPUSTATES * ncpus;
+ error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
if (error)
return (error);
- for (total_new = 0, i = 0; i < CPUSTATES; i++)
- total_new += cp_time[i];
-
- if (idle)
- *idle = cp_time[CP_IDLE] - idle_old;
- if (total)
- *total = total_new - total_old;
+
+ if (load) {
+ *load = 0;
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ total = 0;
+ for (i = 0; i < CPUSTATES; i++) {
+ total += cp_times[cpu * CPUSTATES + i] -
+ cp_times_old[cpu * CPUSTATES + i];
+ }
+ if (total == 0)
+ continue;
+ *load += 100 - (cp_times[cpu * CPUSTATES + CP_IDLE] -
+ cp_times_old[cpu * CPUSTATES + CP_IDLE]) * 100 / total;
+ }
+ }
- idle_old = cp_time[CP_IDLE];
- total_old = total_new;
+ memcpy(cp_times_old, cp_times, cp_times_len);
return (0);
}
@@ -190,6 +213,21 @@ read_freqs(int *numfreqs, int **freqs, int **power)
}
static int
+get_freq(void)
+{
+ size_t len;
+ int curfreq;
+
+ len = sizeof(curfreq);
+ if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
+ if (vflag)
+ warn("error reading current CPU frequency");
+ curfreq = 0;
+ }
+ return (curfreq);
+}
+
+static int
set_freq(int freq)
{
@@ -201,6 +239,19 @@ set_freq(int freq)
return (0);
}
+static int
+get_freq_id(int freq, int *freqs, int numfreqs)
+{
+ int i = 1;
+
+ while (i < numfreqs) {
+ if (freqs[i] < freq)
+ break;
+ i++;
+ }
+ return (i - 1);
+}
+
/*
* Try to use ACPI to find the AC line status. If this fails, fall back
* to APM. If nothing succeeds, we'll just run in default mode.
@@ -341,6 +392,8 @@ parse_mode(char *arg, int *mode, int ch)
*mode = MODE_MAX;
else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
*mode = MODE_ADAPTIVE;
+ else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
+ *mode = MODE_HIADAPTIVE;
else
errx(1, "bad option: -%c %s", (char)ch, optarg);
}
@@ -369,14 +422,14 @@ main(int argc, char * argv[])
int nfds;
struct pidfh *pfh = NULL;
const char *pidfile = NULL;
- long idle, total;
- int curfreq, *freqs, i, *mwatts, numfreqs;
+ int freq, curfreq, *freqs, i, j, *mwatts, numfreqs, load;
int ch, mode, mode_ac, mode_battery, mode_none;
uint64_t mjoules_used;
size_t len;
/* Default mode for all AC states is adaptive. */
- mode_ac = mode_battery = mode_none = MODE_ADAPTIVE;
+ mode_ac = mode_none = MODE_HIADAPTIVE;
+ mode_battery = MODE_ADAPTIVE;
cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
cpu_idle_mark = DEFAULT_IDLE_PERCENT;
poll_ival = DEFAULT_POLL_INTERVAL;
@@ -418,7 +471,7 @@ main(int argc, char * argv[])
break;
case 'r':
cpu_running_mark = atoi(optarg);
- if (cpu_running_mark < 0 || cpu_running_mark > 100) {
+ if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
warnx("%d is not a valid percent",
cpu_running_mark);
usage();
@@ -438,8 +491,8 @@ main(int argc, char * argv[])
/* Look up various sysctl MIBs. */
len = 2;
- if (sysctlnametomib("kern.cp_time", cp_time_mib, &len))
- err(1, "lookup kern.cp_time");
+ if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
+ err(1, "lookup kern.cp_times");
len = 4;
if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
err(1, "lookup freq");
@@ -447,8 +500,8 @@ main(int argc, char * argv[])
if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
err(1, "lookup freq_levels");
- /* Check if we can read the idle time and supported freqs. */
- if (read_usage_times(NULL, NULL))
+ /* Check if we can read the load and supported freqs. */
+ if (read_usage_times(NULL))
err(1, "read_usage_times");
if (read_freqs(&numfreqs, &freqs, &mwatts))
err(1, "error reading supported CPU frequencies");
@@ -483,6 +536,9 @@ main(int argc, char * argv[])
signal(SIGINT, handle_sigs);
signal(SIGTERM, handle_sigs);
+ freq = get_freq();
+ if (freq < 1)
+ freq = 1;
/* Main loop. */
for (;;) {
FD_ZERO(&fdset);
@@ -522,37 +578,30 @@ main(int argc, char * argv[])
}
/* Read the current frequency. */
- len = sizeof(curfreq);
- if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
- if (vflag)
- warn("error reading current CPU frequency");
+ if ((curfreq = get_freq()) == 0)
continue;
- }
+ i = get_freq_id(curfreq, freqs, numfreqs);
+
if (vflag) {
- for (i = 0; i < numfreqs; i++) {
- if (freqs[i] == curfreq)
- break;
- }
-
/* Keep a sum of all power actually used. */
- if (i < numfreqs && mwatts[i] != -1)
+ if (mwatts[i] != -1)
mjoules_used +=
(mwatts[i] * (poll_ival / 1000)) / 1000;
}
/* Always switch to the lowest frequency in min mode. */
if (mode == MODE_MIN) {
- if (curfreq != freqs[numfreqs - 1]) {
+ freq = freqs[numfreqs - 1];
+ if (curfreq != freq) {
if (vflag) {
printf("now operating on %s power; "
"changing frequency to %d MHz\n",
- modes[acline_status],
- freqs[numfreqs - 1]);
+ modes[acline_status], freq);
}
- if (set_freq(freqs[numfreqs - 1]) != 0) {
+ if (set_freq(freq) != 0) {
warn("error setting CPU freq %d",
- freqs[numfreqs - 1]);
+ freq);
continue;
}
}
@@ -561,16 +610,16 @@ main(int argc, char * argv[])
/* Always switch to the highest frequency in max mode. */
if (mode == MODE_MAX) {
- if (curfreq != freqs[0]) {
+ freq = freqs[0];
+ if (curfreq != freq) {
if (vflag) {
printf("now operating on %s power; "
"changing frequency to %d MHz\n",
- modes[acline_status],
- freqs[0]);
+ modes[acline_status], freq);
}
- if (set_freq(freqs[0]) != 0) {
+ if (set_freq(freq) != 0) {
warn("error setting CPU freq %d",
- freqs[0]);
+ freq);
continue;
}
}
@@ -578,44 +627,59 @@ main(int argc, char * argv[])
}
/* Adaptive mode; get the current CPU usage times. */
- if (read_usage_times(&idle, &total)) {
+ if (read_usage_times(&load)) {
if (vflag)
warn("read_usage_times() failed");
continue;
}
-
- /*
- * If we're idle less than the active mark, bump up two levels.
- * If we're idle more than the idle mark, drop down one level.
- */
- for (i = 0; i < numfreqs - 1; i++) {
- if (freqs[i] == curfreq)
- break;
- }
- if (idle < (total * cpu_running_mark) / 100 &&
- curfreq < freqs[0]) {
- i -= 2;
- if (i < 0)
- i = 0;
- if (vflag) {
- printf("idle time < %d%%, increasing clock"
- " speed from %d MHz to %d MHz\n",
- cpu_running_mark, curfreq, freqs[i]);
+
+ if (mode == MODE_ADAPTIVE) {
+ if (load > cpu_running_mark) {
+ if (load > 95 || load > cpu_running_mark * 2)
+ freq *= 2;
+ else
+ freq = freq * load / cpu_running_mark;
+ if (freq > freqs[0])
+ freq = freqs[0];
+ } else if (load < cpu_idle_mark &&
+ curfreq * load < freqs[get_freq_id(
+ freq * 7 / 8, freqs, numfreqs)] *
+ cpu_running_mark) {
+ freq = freq * 7 / 8;
+ if (freq < freqs[numfreqs - 1])
+ freq = freqs[numfreqs - 1];
}
- if (set_freq(freqs[i]))
- warn("error setting CPU frequency %d",
- freqs[i]);
- } else if (idle > (total * cpu_idle_mark) / 100 &&
- curfreq > freqs[numfreqs - 1]) {
- i++;
+ } else { /* MODE_HIADAPTIVE */
+ if (load > cpu_running_mark / 2) {
+ if (load > 95 || load > cpu_running_mark)
+ freq *= 4;
+ else
+ freq = freq * load * 2 / cpu_running_mark;
+ if (freq > freqs[0] * 2)
+ freq = freqs[0] * 2;
+ } else if (load < cpu_idle_mark / 2 &&
+ curfreq * load < freqs[get_freq_id(
+ freq * 31 / 32, freqs, numfreqs)] *
+ cpu_running_mark / 2) {
+ freq = freq * 31 / 32;
+ if (freq < freqs[numfreqs - 1])
+ freq = freqs[numfreqs - 1];
+ }
+ }
+ if (vflag) {
+ printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
+ load, curfreq, i, freq);
+ }
+ j = get_freq_id(freq, freqs, numfreqs);
+ if (i != j) {
if (vflag) {
- printf("idle time > %d%%, decreasing clock"
+ printf("changing clock"
" speed from %d MHz to %d MHz\n",
- cpu_idle_mark, curfreq, freqs[i]);
+ freqs[i], freqs[j]);
}
- if (set_freq(freqs[i]) != 0)
+ if (set_freq(freqs[j]))
warn("error setting CPU frequency %d",
- freqs[i]);
+ freqs[j]);
}
}
free(freqs);
OpenPOWER on IntegriCloud