--- usr.sbin/powerd/powerd.c +++ usr.sbin/powerd/powerd.c @@ -24,15 +24,15 @@ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include -__FBSDID("$FreeBSD: releng/12.1/usr.sbin/powerd/powerd.c 342981 2019-01-13 02:19:01Z avos $"); +__FBSDID("$FreeBSD: head/usr.sbin/powerd/powerd.c 358478 2020-02-29 22:31:23Z cperciva $"); #include #include #include #include #include #include @@ -82,28 +82,29 @@ #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 -static int read_usage_times(int *load); +static int read_usage_times(int *load, int nonice, int noidle); static int read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq); static int set_freq(int freq); static void acline_init(void); static void acline_read(void); static int devd_init(void); static void devd_close(void); static void handle_sigs(int sig); static void parse_mode(char *arg, int *mode, int ch); static void usage(void); /* Sysctl data structures. */ static int cp_times_mib[2]; +static int cpx_times_mib[2]; static int freq_mib[4]; static int levels_mib[4]; static int acline_mib[4]; static size_t acline_mib_len; /* Configuration */ static int cpu_running_mark; @@ -131,23 +132,28 @@ #define DEVD_RETRY_INTERVAL 60 /* seconds */ static struct timeval tried_devd; /* * This function returns summary load of all CPUs. It was made so * intentionally to not reduce performance in scenarios when several * threads are processing requests as a pipeline -- running one at - * a time on different CPUs and waiting for each other. + * a time on different CPUs and waiting for each other. If nonice + * is 100, only user+sys+intr time will be counted as load; any + * nice time will be treated as if idle, likewise noidle. I.e. these + * nice and/or idle loads are weighted by nonice and noidle, resp. + * NOTE nonice and noidle are the "reverse" from the the command line. */ static int -read_usage_times(int *load) +read_usage_times(int *load, int nonice, int noidle) { static long *cp_times = NULL, *cp_times_old = NULL; + static long *cpx_times = NULL, *cpx_times_old = NULL; static int ncpus = 0; - size_t cp_times_len; - int error, cpu, i, total; + size_t cp_times_len, cpx_times_len; + int error, cpu, i, total, excl; 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) @@ -155,36 +161,71 @@ 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_times_len = sizeof(long) * CPUSTATES * ncpus; error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0); if (error) return (error); + if (noidle) { + if (cpx_times == NULL) { + cpx_times_len = 0; + error = sysctl(cpx_times_mib, 2, NULL, &cpx_times_len, NULL, 0); + if (error) + return (error); + if ((cpx_times = malloc(cpx_times_len)) == NULL) + return (errno); + if ((cpx_times_old = malloc(cpx_times_len)) == NULL) { + free(cpx_times); + cpx_times = NULL; + return (errno); + } + } + /* FIXME This could be as assert (...); */ + /* ncpus = cpx_times_len / (sizeof(long) * CPUSTATES_EXT); */ + cpx_times_len = sizeof(long) * CPUSTATES_EXT * ncpus; + error = sysctl(cpx_times_mib, 2, cpx_times, &cpx_times_len, NULL, 0); + if (error) + return (error); + } + 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; + excl = cp_times[cpu * CPUSTATES + CP_IDLE] - + cp_times_old[cpu * CPUSTATES + CP_IDLE]; + if (nonice) + excl += nonice*(cp_times[cpu * CPUSTATES + CP_NICE] - + cp_times_old[cpu * CPUSTATES + CP_NICE])/100; + /* These two can overlap: e.g. a command started "nice idprio 5 ..." */ + else if (noidle) + excl += noidle*(cpx_times[cpu * CPUSTATES_EXT + CPX_NICE_IDLE] - + cpx_times_old[cpu * CPUSTATES_EXT + CPX_NICE_IDLE])/100; + if (noidle) + excl += noidle*(cpx_times[cpu * CPUSTATES_EXT + CPX_USER_IDLE] - + cpx_times_old[cpu * CPUSTATES_EXT + CPX_USER_IDLE])/100; + + *load += 100 - excl * 100 / total; } } memcpy(cp_times_old, cp_times, cp_times_len); + if (noidle) + memcpy(cpx_times_old, cpx_times, cpx_times_len); return (0); } static int read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq) { @@ -192,16 +233,18 @@ int i, j; size_t len = 0; if (sysctl(levels_mib, 4, NULL, &len, NULL, 0)) return (-1); if ((freqstr = malloc(len)) == NULL) return (-1); - if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) + if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) { + free(freqstr); return (-1); + } *numfreqs = 1; for (p = freqstr; *p != '\0'; p++) if (*p == ' ') (*numfreqs)++; if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) { @@ -467,15 +510,15 @@ } static void usage(void) { fprintf(stderr, -"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n"); +"usage: powerd [-v] [-a mode] [-b mode] [-n mode] [-i %%] [-m freq] [-M freq] [-N %%] [-I %%] [-p ival] [-r %%] [-s source] [-P pidfile]\n"); exit(1); } int main(int argc, char * argv[]) { struct timeval timeout; @@ -484,29 +527,31 @@ struct pidfh *pfh = NULL; const char *pidfile = NULL; int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load; int minfreq = -1, maxfreq = -1; int ch, mode, mode_ac, mode_battery, mode_none, idle, to; uint64_t mjoules_used; size_t len; + int nonice, noidle; /* Default mode for all AC states is 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; mjoules_used = 0; vflag = 0; + nonice = noidle = 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:n:p:P:r:s:v")) != -1) + while ((ch = getopt(argc, argv, "a:b:i:m:M:N:I:n:p:P:r:s:v")) != -1) switch (ch) { case 'a': parse_mode(optarg, &mode_ac, ch); break; case 'b': parse_mode(optarg, &mode_battery, ch); break; @@ -533,14 +578,30 @@ maxfreq = atoi(optarg); if (maxfreq < 0) { warnx("%d is not a valid CPU frequency", maxfreq); usage(); } break; + case 'N': + nonice = atoi(optarg); + if (nonice < 0 || nonice > 100) { + warnx("%d is not a valid percent", nonice); + usage(); + } else + nonice = 100 - nonice; + break; + case 'I': + noidle = atoi(optarg); + if (noidle < 0 || noidle > 100) { + warnx("%d is not a valid percent", noidle); + usage(); + } else + noidle = 100 - noidle; + break; case 'n': parse_mode(optarg, &mode_none, ch); break; case 'p': poll_ival = atoi(optarg); if (poll_ival < 5) { warnx("poll interval is in units of ms"); @@ -570,23 +631,28 @@ /* Poll interval is in units of ms. */ poll_ival *= 1000; /* Look up various sysctl MIBs. */ len = 2; if (sysctlnametomib("kern.cp_times", cp_times_mib, &len)) err(1, "lookup kern.cp_times"); + if (noidle) { + len = 2; + if (sysctlnametomib("kern.cp_times_ext", cpx_times_mib, &len)) + err(1, "lookup kern.cp_times_ext"); + } 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"); /* Check if we can read the load and supported freqs. */ - if (read_usage_times(NULL)) + if (read_usage_times(NULL, nonice, noidle)) 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. */ @@ -760,15 +826,15 @@ continue; } } continue; } /* Adaptive mode; get the current CPU usage times. */ - if (read_usage_times(&load)) { + if (read_usage_times(&load, nonice, noidle)) { if (vflag) warn("read_usage_times() failed"); continue; } if (mode == MODE_ADAPTIVE) { if (load > cpu_running_mark) { --- usr.sbin/powerd/powerd.8 +++ usr.sbin/powerd/powerd.8 @@ -18,128 +18,147 @@ .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: releng/12.1/usr.sbin/powerd/powerd.8 342981 2019-01-13 02:19:01Z avos $ +.\" $FreeBSD: head/usr.sbin/powerd/powerd.8 358574 2020-03-03 13:25:08Z 0mp $ .\" -.Dd January 13, 2019 +.Dd Jan 27, 2021 .Dt POWERD 8 .Os .Sh NAME .Nm powerd .Nd "system power control utility" .Sh SYNOPSIS .Nm .Op Fl a Ar mode .Op Fl b Ar mode -.Op Fl i Ar percent -.Op Fl m Ar freq -.Op Fl M Ar freq .Op Fl n Ar mode -.Op Fl p Ar ival -.Op Fl P Ar pidfile +.Op Fl M Ar freq +.Op Fl m Ar freq +.Op Fl N Ar percent +.Op Fl I Ar percent +.Op Fl i Ar percent .Op Fl r Ar percent +.Op Fl p Ar ival .Op Fl s Ar source +.Op Fl P Ar pidfile .Op Fl v .Sh DESCRIPTION The .Nm utility monitors the system state and sets various power control options accordingly. It offers power-saving modes that can be individually selected for operation on AC power or batteries. -.Bl -tag -width ".Ar hiadaptive" -.It Ar maximum +.Bl -tag -width "hiadaptive" +.It Cm maximum Choose the highest performance values. May be abbreviated as -.Ar max . -.It Ar minimum +.Cm max . +.It Cm minimum Choose the lowest performance values to get the most power savings. May be abbreviated as -.Ar min . -.It Ar adaptive +.Cm min . +.It Cm adaptive Attempt to strike a balance by degrading performance when the system appears idle and increasing it when the system is busy. It offers a good balance between a small performance loss for greatly increased power savings. May be abbreviated as -.Ar adp . -.It Ar hiadaptive +.Cm adp . +.It Cm hiadaptive Like -.Ar adaptive +.Cm adaptive mode, but tuned for systems where performance and interactivity are more important than power consumption. It increases frequency faster, reduces frequency less aggressively, and will maintain full frequency for longer. May be abbreviated as -.Ar hadp . +.Cm hadp . .El .Pp The default mode is -.Ar adaptive +.Cm adaptive for battery power and -.Ar hiadaptive +.Cm hiadaptive for the rest. .Pp .Nm recognizes these runtime options: -.Bl -tag -width ".Fl r Ar percent" +.Bl -tag -width "-r percent" .It Fl a Ar mode Selects the .Ar mode to use while on AC power. .It Fl b Ar mode Selects the .Ar mode to use while on battery power. -.It Fl i Ar percent -Specifies the CPU load percent level when adaptive -mode should begin to degrade performance to save power. -The default is 50% or lower. -.It Fl m Ar freq -Specifies the minimum frequency to throttle down to. -.It Fl M Ar freq -Specifies the maximum frequency to throttle up to. .It Fl n Ar mode Selects the .Ar mode to use normally when the AC line state is unknown. -.It Fl p Ar ival -Specifies a different polling interval (in milliseconds) for AC line state -and system idle levels. -The default is 250 ms. -.It Fl P Ar pidfile -Specifies an alternative file in which the process ID should be stored. -The default is -.Pa /var/run/powerd.pid . +.It Fl M Ar freq +Specifies the maximum frequency to throttle up to. +.It Fl m Ar freq +Specifies the minimum frequency to throttle down to. +.It Fl N Ar percent +Weight "nice" CPU time by the given scale. +.It Fl I Ar percent +Weight "user idle" CPU time by the given scale. +.Pp +In both cases, 0 counts like "system idle" for the purpose of +.Nm +\'s internal load calculation; i.e., do not increase the CPU frequency +if the CPU is only busy with "nice" or "user idle" processes. +However, the system and interrupt load caused by these processes +can not be scaled out. +The implied default is 100, to fully count "nice" and "user idle" load. +.It Fl i Ar percent +Specifies the CPU load percent level when adaptive +mode should begin to degrade performance to save power. +The default is 50% or lower. .It Fl r Ar percent Specifies the CPU load percent level where adaptive mode should consider the CPU running and increase performance. The default is 75% or higher. +.It Fl p Ar ival +Specifies a different polling interval (in milliseconds) for AC line state +and system idle levels. +The default is 250 ms. .It Fl s Ar source Enforces method for AC line state refresh; by default, it is chosen automatically. The set of valid methods is .Cm sysctl , devd and .Cm apm (i386 only). +.It Fl P Ar pidfile +Specifies an alternative file in which the process ID should be stored. .It Fl v Verbose mode. Messages about power changes will be printed to stdout and .Nm will operate in the foreground. .El +.Sh FILES +.Bl -tag -width "/var/run/powerd.pid" +.It Pa /var/run/powerd.pid +The default PID file. +.El .Sh SEE ALSO .Xr acpi 4 , .Xr apm 4 , -.Xr cpufreq 4 +.Xr cpufreq 4 , +.Xr nice 1 , +.Xr idprio 1 . .Sh HISTORY The .Nm utility first appeared in .Fx 6.0 . .Sh AUTHORS .An -nosplit