Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F174947762
D60406.id188862.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D60406.id188862.diff
View Options
diff --git a/usr.sbin/powerd/powerd.8 b/usr.sbin/powerd/powerd.8
--- a/usr.sbin/powerd/powerd.8
+++ b/usr.sbin/powerd/powerd.8
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd February 25, 2023
+.Dd October 4, 2026
.Dt POWERD 8
.Os
.Sh NAME
@@ -32,6 +32,7 @@
.Nm
.Op Fl a Ar mode
.Op Fl b Ar mode
+.Op Fl e
.Op Fl i Ar percent
.Op Fl M Ar freq
.Op Fl m Ar freq
@@ -93,6 +94,29 @@
Selects the
.Ar mode
to use while on battery power.
+.It Fl e
+Selects the Energy/Performance Preference management model
+for AMD and Intel CPUs.
+Requires
+.Xr hwpstate_intel 4
+on Intel CPUs, or the hwpstate_amd driver on AMD CPUs.
+.Xr cpufreq 4
+is optional when this option is used; without it,
+.Fl m
+and
+.Fl M
+have no effect.
+.Fl e
+with a working
+.Xr cpufreq 4
+sets both controls.
+The initial EPP values are restored when
+.Nm
+exits with
+.Dv SIGINT
+or
+.Dv SIGTERM
+only.
.It Fl i Ar percent
Specifies the CPU load percent level when adaptive
mode should begin to degrade performance to save power.
@@ -142,6 +166,7 @@
.Xr acpi 4 ,
.Xr apm 4 ,
.Xr cpufreq 4 ,
+.Xr hwpstate_intel 4 ,
.Xr rc.conf 5
.Sh HISTORY
The
@@ -160,6 +185,10 @@
then updated it for
.Xr cpufreq 4 ,
added features, and wrote this manual page.
+.An Sergey A. Osokin
+then updated it for
+.Xr hwpstate_intel 4
+support.
.Sh BUGS
The
.Nm
diff --git a/usr.sbin/powerd/powerd.c b/usr.sbin/powerd/powerd.c
--- a/usr.sbin/powerd/powerd.c
+++ b/usr.sbin/powerd/powerd.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2004 Colin Percival
* Copyright (c) 2005 Nate Lawson
+ * Copyright (c) 2026 Sergey A. Osokin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -67,6 +68,11 @@
#define DEFAULT_IDLE_PERCENT 50
#define DEFAULT_POLL_INTERVAL 250 /* Poll interval in milliseconds */
+#define EPP_MIN 0 /* maximum performance */
+#define EPP_PERF 64 /* half of balanced */
+#define EPP_BALANCED 128 /* balanced */
+#define EPP_MAX 255 /* maximum efficiency */
+
typedef enum {
MODE_MIN,
MODE_ADAPTIVE,
@@ -86,11 +92,17 @@
"unknown"
};
+static const char *arch[2] = {
+ "amd",
+ "intel"
+};
+
#define ACPIAC "hw.acpi.acline"
#define PMUAC "dev.pmu.0.acline"
#define APMDEV "/dev/apm"
#define DEVDPIPE "/var/run/devd.pipe"
#define DEVCTL_MAXBUF 1024
+#define HWPSTATE "dev.hwpstate_%s"
static int read_usage_times(int *load, int nonice);
static int read_freqs(int *numfreqs, int **freqs, int **power,
@@ -116,6 +128,7 @@
static int cpu_running_mark;
static int cpu_idle_mark;
static int poll_ival;
+static int eflag;
static int vflag;
static volatile sig_atomic_t exit_requested;
@@ -259,6 +272,139 @@
return (0);
}
+static int
+get_arch(int *detected_arch)
+{
+ char buf[64];
+ int i;
+ size_t size;
+
+ for (i = 0; i < (int)nitems(arch); i++) {
+ size = 0;
+ snprintf(buf, sizeof(buf), HWPSTATE ".0.%%desc", arch[i]);
+ if (sysctlbyname(buf, NULL, &size, NULL, 0) == 0) {
+ *detected_arch = i;
+ return (0);
+ }
+ if (errno != ENOENT)
+ return (-1);
+ }
+
+ return (-1);
+}
+
+static int
+get_one_epp(int detected_arch, int cpuid)
+{
+ size_t size;
+ char buf[64];
+ int val = 0;
+
+ size = sizeof(val);
+ snprintf(buf, sizeof(buf), HWPSTATE ".%d.epp",
+ arch[detected_arch], cpuid);
+
+ if (sysctlbyname(buf, &val, &size, NULL, 0) < 0) {
+ if (errno == ENOENT)
+ warnx("cpu %d: no EPP control", cpuid);
+ else
+ warn("read EPP %s", buf);
+ return (-1);
+ }
+
+ return (val);
+}
+
+static int
+set_epp(int detected_arch, int maxid, const int *v, int val)
+{
+ char buf[64];
+ int i, error = 0;
+
+ for (i = 0; i <= maxid; i++) {
+ if (v[i] < 0) /* no EPP control */
+ continue;
+ snprintf(buf, sizeof(buf), HWPSTATE ".%d.epp",
+ arch[detected_arch], i);
+ if (sysctlbyname(buf, NULL, NULL, &val,
+ sizeof(val)) < 0) {
+ warn("set EPP %s", buf);
+ error = -1;
+ }
+ }
+
+ return (error);
+}
+
+static void
+restore_epp(int detected_arch, int maxid, const int *v)
+{
+ char buf[64];
+ int i;
+
+ for (i = 0; i <= maxid; i++) {
+ if (v[i] < 0) /* no EPP control */
+ continue;
+ snprintf(buf, sizeof(buf), HWPSTATE ".%d.epp",
+ arch[detected_arch], i);
+ if (sysctlbyname(buf, NULL, NULL, &v[i],
+ sizeof(v[i])) < 0) {
+ warn("restore EPP %s", buf);
+ }
+ }
+}
+
+static int
+calc_epp(int mode, int load)
+{
+ int lo, hi, epp;
+
+ switch (mode) {
+ case MODE_MIN:
+ return (EPP_MAX);
+ case MODE_MAX:
+ return (EPP_MIN);
+ case MODE_ADAPTIVE:
+ lo = EPP_MAX; /* power saving when idle */
+ hi = EPP_PERF; /* performance-biased when busy */
+ break;
+ case MODE_HIADAPTIVE:
+ lo = EPP_BALANCED; /* balanced when idle */
+ hi = EPP_MIN; /* max performance when busy */
+ break;
+ default:
+ return (EPP_BALANCED);
+ }
+
+ if (load <= cpu_idle_mark)
+ return (lo);
+ if (load >= cpu_running_mark)
+ return (hi);
+
+ /* Find a reasonable value between lo and hi. */
+ epp = lo - (lo - hi) * (load - cpu_idle_mark) /
+ (cpu_running_mark - cpu_idle_mark);
+
+ return (epp);
+}
+
+static void
+apply_epp(int detected_arch, int maxid, const int *v, int mode, int load,
+ int *cur_epp)
+{
+ int epp;
+
+ epp = calc_epp(mode, load);
+ if (epp != *cur_epp) {
+ (void)set_epp(detected_arch, maxid, v, epp);
+ *cur_epp = epp; /* do not retry (and warn) on every poll */
+ if (vflag)
+ printf("now operating on %s power; "
+ "setting EPP to %d\n",
+ modes[acline_status], epp);
+ }
+}
+
static int
get_freq(void)
{
@@ -564,7 +710,7 @@
{
fprintf(stderr,
-"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n");
+"usage: powerd [-v] [-a mode] [-b mode] [-e] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n");
exit(1);
}
@@ -582,6 +728,7 @@
uint64_t mjoules_used;
size_t len;
int nonice;
+ int detected_arch, maxid, cur_epp, has_cpufreq, nbad, *v = NULL;
/* Default mode for all AC states is adaptive. */
mode_ac = mode_none = MODE_HIADAPTIVE;
@@ -590,14 +737,19 @@
cpu_idle_mark = DEFAULT_IDLE_PERCENT;
poll_ival = DEFAULT_POLL_INTERVAL;
mjoules_used = 0;
+ eflag = 0;
vflag = 0;
nonice = 0;
+ detected_arch = -1;
+ maxid = 0;
+ cur_epp = -1;
+ nbad = 0;
/* User must be root to control frequencies. */
if (geteuid() != 0)
errx(1, "must be root to run");
- while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1)
+ while ((ch = getopt(argc, argv, "a:b:ei:m:M:Nn:p:P:r:s:v")) != -1)
switch (ch) {
case 'a':
parse_mode(optarg, &mode_ac, ch);
@@ -605,6 +757,9 @@
case 'b':
parse_mode(optarg, &mode_battery, ch);
break;
+ case 'e':
+ eflag = 1;
+ break;
case 's':
parse_acline_mode(optarg, ch);
break;
@@ -663,6 +818,33 @@
usage();
}
+ if (eflag) {
+ if (get_arch(&detected_arch) != 0)
+ errx(1, "no hwpstate_amd(4) or hwpstate_intel(4) "
+ "attached; cannot enable EPP");
+ len = sizeof(maxid);
+ if (sysctlbyname("kern.smp.maxid", &maxid, &len, NULL, 0) < 0)
+ err(1, "sysctlbyname(kern.smp.maxid)");
+ if (vflag)
+ warnx("EPP enabled via hwpstate_%s(4), "
+ "%d CPU(s)", arch[detected_arch], maxid + 1);
+
+ v = calloc(maxid + 1, sizeof(*v));
+ if (v == NULL)
+ err(1, "calloc");
+
+ nbad = 0;
+ for (i = 0; i <= maxid; i++) {
+ if ((v[i] = get_one_epp(detected_arch, i)) < 0)
+ nbad++;
+ }
+ if (nbad == (maxid + 1)) {
+ free(v);
+ errx(EX_UNAVAILABLE, "cannot read EPP of any CPU via "
+ "hwpstate_%s(4)", arch[detected_arch]);
+ }
+ }
+
mode = mode_none;
/* Poll interval is in units of ms. */
@@ -672,20 +854,30 @@
len = 2;
if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
err(1, "lookup kern.cp_times");
+
+ /* cpufreq(4) is optional when EPP is enabled */
+ has_cpufreq = 0;
len = 4;
- if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
- err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting");
- len = 4;
- if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
- err(1, "lookup freq_levels");
+ if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len) == 0) {
+ len = 4;
+ if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib,
+ &len) == 0)
+ has_cpufreq = 1;
+ }
+ if (has_cpufreq) {
+ /* An information-only driver has no levels to read. */
+ if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq)
+ != 0)
+ has_cpufreq = 0;
+ else if (numfreqs == 0)
+ errx(1, "no CPU frequencies in user-specified range");
+ }
+ if (!has_cpufreq && !eflag)
+ errx(EX_UNAVAILABLE, "no usable cpufreq(4) support; try -e");
/* Check if we can read the load and supported freqs. */
if (read_usage_times(NULL, nonice))
err(1, "read_usage_times");
- if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq))
- err(1, "error reading supported CPU frequencies");
- if (numfreqs == 0)
- errx(1, "no CPU frequencies in user-specified range");
/* Run in the background unless in verbose mode. */
if (!vflag) {
@@ -717,10 +909,12 @@
signal(SIGINT, handle_sigs);
signal(SIGTERM, handle_sigs);
- freq = initfreq = curfreq = get_freq();
- i = get_freq_id(curfreq, freqs, numfreqs);
- if (freq < 1)
- freq = 1;
+ if (has_cpufreq) {
+ freq = initfreq = curfreq = get_freq();
+ i = get_freq_id(curfreq, freqs, numfreqs);
+ if (freq < 1)
+ freq = 1;
+ }
/*
* If we are in adaptive mode and the current frequency is outside the
@@ -729,12 +923,13 @@
acline_read(0);
if (acline_status > SRC_UNKNOWN)
errx(1, "invalid AC line status %d", acline_status);
- if ((acline_status == SRC_AC &&
+ if (has_cpufreq &&
+ ((acline_status == SRC_AC &&
(mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) ||
(acline_status == SRC_BATTERY &&
(mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) ||
(acline_status == SRC_UNKNOWN &&
- (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) {
+ (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE)))) {
/* Read the current frequency. */
len = sizeof(curfreq);
if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
@@ -813,21 +1008,26 @@
}
/* Read the current frequency. */
- if (idle % 32 == 0) {
+ if (has_cpufreq && idle % 32 == 0) {
if ((curfreq = get_freq()) == 0)
continue;
i = get_freq_id(curfreq, freqs, numfreqs);
}
idle++;
- if (vflag) {
+ if (vflag && has_cpufreq) {
/* Keep a sum of all power actually used. */
if (mwatts[i] != -1)
mjoules_used +=
(mwatts[i] * (poll_ival / 1000)) / 1000;
}
+ /* Min and max modes do not depend on the load. */
+ if (eflag && (mode == MODE_MIN || mode == MODE_MAX))
+ apply_epp(detected_arch, maxid, v, mode, 0,
+ &cur_epp);
+
/* Always switch to the lowest frequency in min mode. */
- if (mode == MODE_MIN) {
+ if (has_cpufreq && mode == MODE_MIN) {
freq = freqs[numfreqs - 1];
if (curfreq != freq) {
if (vflag) {
@@ -846,7 +1046,7 @@
}
/* Always switch to the highest frequency in max mode. */
- if (mode == MODE_MAX) {
+ if (has_cpufreq && mode == MODE_MAX) {
freq = freqs[0];
if (curfreq != freq) {
if (vflag) {
@@ -864,6 +1064,9 @@
continue;
}
+ if (mode == MODE_MIN || mode == MODE_MAX)
+ continue; /* no cpufreq(4), EPP already set */
+
/* Adaptive mode; get the current CPU usage times. */
if (read_usage_times(&load, nonice)) {
if (vflag)
@@ -871,6 +1074,12 @@
continue;
}
+ if (eflag)
+ apply_epp(detected_arch, maxid, v, mode,
+ load, &cur_epp);
+ if (!has_cpufreq)
+ continue;
+
if (mode == MODE_ADAPTIVE) {
if (load > cpu_running_mark) {
if (load > 95 || load > cpu_running_mark * 2)
@@ -921,10 +1130,16 @@
freqs[j]);
}
}
- if (set_freq(initfreq))
- warn("error setting CPU frequency %d", initfreq);
- free(freqs);
- free(mwatts);
+ if (has_cpufreq) {
+ if (set_freq(initfreq))
+ warn("error setting CPU frequency %d", initfreq);
+ free(freqs);
+ free(mwatts);
+ }
+ if (eflag) {
+ restore_epp(detected_arch, maxid, v);
+ free(v);
+ }
devd_close();
if (!vflag)
pidfile_remove(pfh);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Oct 8, 3:33 AM (9 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40385202
Default Alt Text
D60406.id188862.diff (11 KB)
Attached To
Mode
D60406: powerd: Introduce Energy/Performance Preferences (EPP) support
Attached
Detach File
Event Timeline
Log In to Comment