diff options
author | Nikolay Sturm <sturm@cvs.openbsd.org> | 2005-11-23 08:02:59 +0000 |
---|---|---|
committer | Nikolay Sturm <sturm@cvs.openbsd.org> | 2005-11-23 08:02:59 +0000 |
commit | 319a8520491f8cffb4982572d824150e1857c383 (patch) | |
tree | a5d8ba3811a1c63e2cb158d0e9aa1cf69dec3a71 /usr.sbin | |
parent | 2609c36d428c78eda3c28cef167ca0374334ebe7 (diff) |
permit apmd to run on systems without apm support
let apmd control hw.setperf manually or dynamically
tested by Chris Kuethe and beck@, 'get it in' deraadt@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/apm/apm.8 | 23 | ||||
-rw-r--r-- | usr.sbin/apm/apm.c | 37 | ||||
-rw-r--r-- | usr.sbin/apmd/apm-proto.h | 17 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.8 | 40 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.c | 172 | ||||
-rw-r--r-- | usr.sbin/apmd/apmsubr.c | 17 |
6 files changed, 277 insertions, 29 deletions
diff --git a/usr.sbin/apm/apm.8 b/usr.sbin/apm/apm.8 index af0188211cb..3ecd14f5a81 100644 --- a/usr.sbin/apm/apm.8 +++ b/usr.sbin/apm/apm.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: apm.8,v 1.21 2005/11/15 01:27:58 jmc Exp $ +.\" $OpenBSD: apm.8,v 1.22 2005/11/23 08:02:58 sturm Exp $ .\" .\" Copyright (c) 1996 John T. Kohl .\" All rights reserved. @@ -38,7 +38,7 @@ .Op Fl f Ar sockname .Br .Nm apm -.Op Fl ablmSsvz +.Op Fl AabHLlmPSsvz .Op Fl f Ar sockname .Sh DESCRIPTION .Nm @@ -52,6 +52,10 @@ displays the current power management state in verbose form. .Pp The options are as follows: .Bl -tag -width Ds +.It Fl A +Set +.Xr apmd 8 +to automatic performance adjustment mode. .It Fl a Display the external charger (A/C status). 0 means disconnected, 1 @@ -65,10 +69,25 @@ Set the name of the socket via which to contact .Xr apmd 8 to .Pa sockname . +.It Fl H +Set +.Xr apmd 8 +to manual performance adjustment mode and +.Va hw.setperf +to 100. +.It Fl L +Set +.Xr apmd 8 +to manual performance adjustment mode and +.Va hw.setperf +to 0. .It Fl l Display the estimated battery lifetime (in percent). .It Fl m Display the estimated battery lifetime (in minutes). +.It Fl P +Display the performance adjustment mode. +0 means uninitialized, 1 means manual, and 2 means automatic mode. .It Fl S Put the system into stand-by (light sleep) mode. .It Fl s diff --git a/usr.sbin/apm/apm.c b/usr.sbin/apm/apm.c index f6a7918a5bf..334384b6d2f 100644 --- a/usr.sbin/apm/apm.c +++ b/usr.sbin/apm/apm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: apm.c,v 1.12 2005/11/15 01:25:48 jmc Exp $ */ +/* $OpenBSD: apm.c,v 1.13 2005/11/23 08:02:58 sturm Exp $ */ /* * Copyright (c) 1996 John T. Kohl @@ -58,7 +58,7 @@ int send_command(int fd, struct apm_command *cmd, struct apm_reply *reply); void usage(void) { - fprintf(stderr,"usage: %s [-ablmSsvz] [-f sockname]\n", + fprintf(stderr,"usage: %s [-ablmPSsvz] [-f sockname] [-A | -L | -H]\n", __progname); exit(1); } @@ -142,13 +142,14 @@ main(int argc, char *argv[]) int dopct = FALSE; int dobstate = FALSE; int domin = FALSE; + int doperf = FALSE; int verbose = FALSE; int ch, fd, rval; enum apm_action action = NONE; struct apm_command command; struct apm_reply reply; - while ((ch = getopt(argc, argv, "lmbvasSzf:")) != -1) { + while ((ch = getopt(argc, argv, "AHLlmbvaPsSzf:")) != -1) { switch (ch) { case 'v': verbose = TRUE; @@ -166,6 +167,21 @@ main(int argc, char *argv[]) usage(); action = STANDBY; break; + case 'A': + if (action != NONE) + usage(); + action = SETPERF_AUTO; + break; + case 'H': + if (action != NONE) + usage(); + action = SETPERF_HIGH; + break; + case 'L': + if (action != NONE) + usage(); + action = SETPERF_LOW; + break; case 's': if (action != NONE && action != GETSTATUS) usage(); @@ -196,6 +212,12 @@ main(int argc, char *argv[]) doac = TRUE; action = GETSTATUS; break; + case 'P': + if (action != NONE && action != GETSTATUS) + usage(); + doperf = TRUE; + action = GETSTATUS; + break; default: usage(); } @@ -210,6 +232,7 @@ main(int argc, char *argv[]) case NONE: action = GETSTATUS; verbose = doac = dopct = dobstate = dostatus = domin = TRUE; + doperf = TRUE; /* fallthrough */ case GETSTATUS: if (fd == -1) { @@ -222,6 +245,9 @@ main(int argc, char *argv[]) /* fallthrough */ case SUSPEND: case STANDBY: + case SETPERF_LOW: + case SETPERF_HIGH: + case SETPERF_AUTO: command.action = action; break; default: @@ -254,6 +280,8 @@ main(int argc, char *argv[]) reply.batterystate.ac_state); if (dostatus) printf("1\n"); + if (doperf) + printf("%d\n", reply.perfstate); break; } if (dobstate) @@ -288,6 +316,9 @@ main(int argc, char *argv[]) if (doac) printf("A/C adapter state: %s\n", ac_state(reply.batterystate.ac_state)); + if (doperf) + printf("Performance state: %s\n", + perf_state(reply.perfstate)); if (dostatus) printf("Power management enabled\n"); break; diff --git a/usr.sbin/apmd/apm-proto.h b/usr.sbin/apmd/apm-proto.h index 0eeb403f04e..ea07c7c2170 100644 --- a/usr.sbin/apmd/apm-proto.h +++ b/usr.sbin/apmd/apm-proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: apm-proto.h,v 1.4 2002/07/04 07:31:16 deraadt Exp $ */ +/* $OpenBSD: apm-proto.h,v 1.5 2005/11/23 08:02:58 sturm Exp $ */ /* * Copyright (c) 1996 John T. Kohl @@ -33,7 +33,10 @@ enum apm_action { NONE, SUSPEND, STANDBY, - GETSTATUS + GETSTATUS, + SETPERF_LOW, + SETPERF_HIGH, + SETPERF_AUTO }; enum apm_state { @@ -42,6 +45,12 @@ enum apm_state { STANDING_BY }; +enum apm_perfstate { + PERF_NONE, + PERF_MANUAL, + PERF_AUTO +}; + struct apm_command { int vno; enum apm_action action; @@ -50,10 +59,12 @@ struct apm_command { struct apm_reply { int vno; enum apm_state newstate; + enum apm_perfstate perfstate; struct apm_power_info batterystate; }; -#define APMD_VNO 1 +#define APMD_VNO 2 extern const char *battstate(int state); extern const char *ac_state(int state); +extern const char *perf_state(int state); diff --git a/usr.sbin/apmd/apmd.8 b/usr.sbin/apmd/apmd.8 index 3f7ff6892a9..a64bb0951fc 100644 --- a/usr.sbin/apmd/apmd.8 +++ b/usr.sbin/apmd/apmd.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: apmd.8,v 1.29 2005/11/15 01:19:56 jmc Exp $ +.\" $OpenBSD: apmd.8,v 1.30 2005/11/23 08:02:58 sturm Exp $ .\" .\" Copyright (c) 1995 John T. Kohl .\" All rights reserved. @@ -34,7 +34,7 @@ .Nd Advanced Power Management monitor daemon .Sh SYNOPSIS .Nm apmd -.Op Fl ademps +.Op Fl AadeHLmps .Op Fl f Ar devname .Op Fl S Ar sockname .Op Fl t Ar seconds @@ -61,6 +61,10 @@ with logging facility .Pp The options are as follows: .Bl -tag -width Ds +.It Fl A +Start +.Nm +in automatic performance adjustment mode. .It Fl a BIOS-initiated suspend or standby requests are ignored if the system is connected to line current and not running from @@ -78,6 +82,18 @@ Use of this flag unconditionally enables power status messages. .It Fl f Ar devname Specify an alternate device file name, .Ar devname . +.It Fl H +Start +.Nm +in manual performance adjustment mode, initialising +.Va hw.setperf +to 100. +.It Fl L +Start +.Nm +in manual performance adjustment mode, initialising +.Va hw.setperf +to 0. .It Fl m Do not disable power status messages issued by the APM driver. In normal operation these status messages are disabled as they are @@ -123,6 +139,26 @@ once per 10 minutes, but may be specified using the command-line flag. .El .Pp +In automatic performance adjustment mode, +.Nm +monitors the processor's idle time and adjusts +.Va hw.setperf +dynamically. +This keeps the processor at low speed whenever possible, reducing noise and +power consumption. +When idle time falls below 10%, the daemon raises +.Va hw.setperf +as much as necessary. +When idle time is above 30%, +.Nm +reduces +.Va hw.setperf +as much as possible. +By default +.Nm +does not change +.Va hw.setperf . +.Pp When a client requests a suspend or stand-by mode, .Nm does not wait for positive confirmation that the requested diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c index 48e38260b33..d368d17acc7 100644 --- a/usr.sbin/apmd/apmd.c +++ b/usr.sbin/apmd/apmd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: apmd.c,v 1.36 2005/11/22 09:31:02 mickey Exp $ */ +/* $OpenBSD: apmd.c,v 1.37 2005/11/23 08:02:58 sturm Exp $ */ /* * Copyright (c) 1995, 1996 John T. Kohl @@ -37,6 +37,8 @@ #include <sys/wait.h> #include <sys/event.h> #include <sys/time.h> +#include <sys/dkstat.h> +#include <sys/sysctl.h> #include <stdio.h> #include <syslog.h> #include <fcntl.h> @@ -58,14 +60,24 @@ const char sockfile[] = _PATH_APM_SOCKET; int debug = 0; +int doperf = PERF_NONE; +#define PERFINC 50 +#define PERFDEC 20 +#define PERFMIN 0 +#define PERFMAX 100 +#define PERFINCTHRES 10 +#define PERFDECTHRES 30 + extern char *__progname; void usage(void); int power_status(int fd, int force, struct apm_power_info *pinfo); int bind_socket(const char *sn); enum apm_state handle_client(int sock_fd, int ctl_fd); +void perf_status(void); void suspend(int ctl_fd); void stand_by(int ctl_fd); +void setperf(int new_perf); void sigexit(int signo); void do_etc_file(const char *file); void sockunlink(void); @@ -82,8 +94,8 @@ void usage(void) { fprintf(stderr, - "usage: %s [-ademps] [-f devname] [-S sockname] [-t seconds]\n", - __progname); + "usage: %s [-ademps] [-f devname] [-S sockname] [-t seconds]" + " [-A | -L | -H]\n", __progname); exit(1); } @@ -120,6 +132,18 @@ power_status(int fd, int force, struct apm_power_info *pinfo) static struct apm_power_info last; int acon = 0; + if (fd == -1) { + if (pinfo) { + bstate.battery_state = 255; + bstate.ac_state = 255; + bstate.battery_life = 0; + bstate.minutes_left = -1; + *pinfo = bstate; + } + + return 0; + } + if (ioctl(fd, APM_IOC_GETPOWER, &bstate) == 0) { /* various conditions under which we report status: something changed * enough since last report, or asked to force a print */ @@ -167,6 +191,56 @@ power_status(int fd, int force, struct apm_power_info *pinfo) return acon; } +void +perf_status(void) +{ + static long cp_time_old[CPUSTATES]; + static int avg_idle; + long change, cp_time[CPUSTATES]; + int cp_time_mib[] = {CTL_KERN, KERN_CPTIME}; + int hw_perf_mib[] = {CTL_HW, HW_SETPERF}; + int i, idle, perf; + int sum = 0; + size_t cp_time_sz = sizeof(cp_time); + size_t perf_sz = sizeof(perf); + + if (sysctl(cp_time_mib, 2, &cp_time, &cp_time_sz, NULL, 0) < 0) + error("cannot read kern.cp_time", NULL); + + for (i = 0; i < CPUSTATES; i++) { + if ((change = cp_time[i] - cp_time_old[i]) < 0) { + /* counter wrapped */ + change = ((unsigned long)cp_time[i] - + (unsigned long)cp_time_old[i]); + } + sum += change; + if (i == CP_IDLE) + idle = change; + } + if (sum == 0) + sum = 1; + + /* smooth data */ + avg_idle = (avg_idle + (100 * idle) / sum) / 2; + + if (sysctl(hw_perf_mib, 2, &perf, &perf_sz, NULL, 0) < 0) + error("cannot read hw.setperf", NULL); + + if (avg_idle < PERFINCTHRES && perf < PERFMAX) { + perf += PERFINC; + if (perf > PERFMAX) + perf = PERFMAX; + setperf(perf); + } else if (avg_idle > PERFDECTHRES && perf > PERFMIN) { + perf -= PERFDEC; + if (perf < PERFMIN) + perf = PERFMIN; + setperf(perf); + } + + memcpy(cp_time_old, cp_time, sizeof(cp_time_old)); +} + char socketname[MAXPATHLEN]; void @@ -245,11 +319,29 @@ handle_client(int sock_fd, int ctl_fd) case STANDBY: reply.newstate = STANDING_BY; break; + case SETPERF_LOW: + doperf = PERF_MANUAL; + reply.newstate = NORMAL; + syslog(LOG_NOTICE, "setting hw.setperf to %d", PERFMIN); + setperf(PERFMIN); + break; + case SETPERF_HIGH: + doperf = PERF_MANUAL; + reply.newstate = NORMAL; + syslog(LOG_NOTICE, "setting hw.setperf to %d", PERFMAX); + setperf(PERFMAX); + break; + case SETPERF_AUTO: + doperf = PERF_AUTO; + reply.newstate = NORMAL; + syslog(LOG_NOTICE, "setting hw.setperf automatically"); + break; default: reply.newstate = NORMAL; break; } + reply.perfstate = doperf; reply.vno = APMD_VNO; if (send(cli_fd, &reply, sizeof(reply), 0) != sizeof(reply)) syslog(LOG_INFO, "client reply botch"); @@ -294,11 +386,12 @@ main(int argc, char *argv[]) int messages = 0; int noacsleep = 0; struct timespec ts = {TIMO, 0}, sts = {0, 0}; + time_t apmtimeout = 0; const char *sockname = sockfile; - int kq; + int kq, nchanges; struct kevent ev[2]; - while ((ch = getopt(argc, argv, "adsepmf:t:S:")) != -1) + while ((ch = getopt(argc, argv, "aAdHLsepmf:t:S:")) != -1) switch(ch) { case 'a': noacsleep = 1; @@ -326,6 +419,23 @@ main(int argc, char *argv[]) case 'p': pctonly = 1; break; + case 'A': + if (doperf != PERF_NONE) + usage(); + doperf = PERF_AUTO; + break; + case 'L': + if (doperf != PERF_NONE) + usage(); + doperf = PERF_MANUAL; + setperf(PERFMIN); + break; + case 'H': + if (doperf != PERF_NONE) + usage(); + doperf = PERF_MANUAL; + setperf(PERFMAX); + break; case 'm': messages = 1; break; @@ -349,10 +459,10 @@ main(int argc, char *argv[]) (void) signal(SIGHUP, sigexit); (void) signal(SIGINT, sigexit); - if ((ctl_fd = open(fname, O_RDWR)) == -1) - error("cannot open device file `%s'", fname); - - if (fcntl(ctl_fd, F_SETFD, 1) == -1) + if ((ctl_fd = open(fname, O_RDWR)) == -1) { + if (errno != ENXIO) + error("cannot open device file `%s'", fname); + } else if (fcntl(ctl_fd, F_SETFD, 1) == -1) error("cannot set close-on-exec for `%s'", fname); sock_fd = bind_socket(sockname); @@ -382,11 +492,16 @@ main(int argc, char *argv[]) if (kq <= 0) error("kqueue", NULL); - EV_SET(&ev[0], ctl_fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, - 0, 0, NULL); - EV_SET(&ev[1], sock_fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, + EV_SET(&ev[0], sock_fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, NULL); - if (kevent(kq, ev, 2, NULL, 0, &sts) < 0) + if (ctl_fd == -1) + nchanges = 1; + else { + EV_SET(&ev[1], ctl_fd, EVFILT_READ, EV_ADD | EV_ENABLE | + EV_CLEAR, 0, 0, NULL); + nchanges = 2; + } + if (kevent(kq, ev, nchanges, NULL, 0, &sts) < 0) error("kevent", NULL); for (;;) { @@ -394,14 +509,24 @@ main(int argc, char *argv[]) sts = ts; + if (doperf == PERF_AUTO) { + sts.tv_sec = 1; + perf_status(); + } + + apmtimeout += sts.tv_sec; if ((rv = kevent(kq, NULL, 0, ev, 1, &sts)) < 0) break; - /* wakeup for timeout: take status */ - powerbak = power_status(ctl_fd, 0, 0); - if (powerstatus != powerbak) { - powerstatus = powerbak; - powerchange = 1; + if (apmtimeout >= ts.tv_sec) { + apmtimeout = 0; + + /* wakeup for timeout: take status */ + powerbak = power_status(ctl_fd, 0, 0); + if (powerstatus != powerbak) { + powerstatus = powerbak; + powerchange = 1; + } } if (!rv) @@ -489,6 +614,17 @@ main(int argc, char *argv[]) } void +setperf(int new_perf) +{ + int hw_perf_mib[] = {CTL_HW, HW_SETPERF}; + int perf; + size_t perf_sz = sizeof(perf); + + if (sysctl(hw_perf_mib, 2, &perf, &perf_sz, &new_perf, perf_sz) < 0) + error("cannot set hw.setperf", NULL); +} + +void do_etc_file(const char *file) { pid_t pid; diff --git a/usr.sbin/apmd/apmsubr.c b/usr.sbin/apmd/apmsubr.c index 95943e5c1db..7cc8305c8cc 100644 --- a/usr.sbin/apmd/apmsubr.c +++ b/usr.sbin/apmd/apmsubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: apmsubr.c,v 1.3 2001/07/07 01:10:42 mickey Exp $ */ +/* $OpenBSD: apmsubr.c,v 1.4 2005/11/23 08:02:58 sturm Exp $ */ /* * Copyright (c) 1995,1996 John T. Kohl @@ -70,3 +70,18 @@ ac_state(int state) return "invalid AC status"; } } + +const char * +perf_state(int state) +{ + switch (state) { + case PERF_NONE: + return "uninitialized"; + case PERF_MANUAL: + return "manual"; + case PERF_AUTO: + return "auto"; + default: + return "invalid performance status"; + } +} |