Page MenuHomeFreeBSD

D60406.id188862.diff
No OneTemporary

D60406.id188862.diff

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

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)

Event Timeline