summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolay Sturm <sturm@cvs.openbsd.org>2005-11-23 08:02:59 +0000
committerNikolay Sturm <sturm@cvs.openbsd.org>2005-11-23 08:02:59 +0000
commit319a8520491f8cffb4982572d824150e1857c383 (patch)
treea5d8ba3811a1c63e2cb158d0e9aa1cf69dec3a71
parent2609c36d428c78eda3c28cef167ca0374334ebe7 (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@
-rw-r--r--usr.sbin/apm/apm.823
-rw-r--r--usr.sbin/apm/apm.c37
-rw-r--r--usr.sbin/apmd/apm-proto.h17
-rw-r--r--usr.sbin/apmd/apmd.840
-rw-r--r--usr.sbin/apmd/apmd.c172
-rw-r--r--usr.sbin/apmd/apmsubr.c17
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";
+ }
+}