18 months ago I posted a patch to make apmd's -C and -A modes work half-sensibly on multi-processor machines:
http://marc.info/?l=openbsd-tech&m=123315164930014&w=2 The patch went into the tree but was backed out because on some very slow, very old Sparc machines it apparently couldn't react quickly enough to changes in processor utilisation. Since I had no access to such a machine, I couldn't debug it, and the patch died. That means that apmd -C has been close to useless on MP machines, since it needs all CPUs to be working hard before it increases hw.setperf. One day hopefully this functionality will move into the kernel (I know Ted and others are working towards that), but in the meantime someone asked me if I could resurrect the backed-out patch which I attach to the end of this e-mail. This patch cranks hw.setperf to 100 as soon as increased activity is noticed and then gradually backs it off it can (in contrast to the old patch which ramped up gradually; the new behaviour should increase battery life). It also reflects recent changes to apmd.c. Apart from that it's largely the same as before. It's been working for me over the last few days, but hasn't been extensively tested so YMMV. However, if you work off battery on an MP machine, you might find it helpful. Laurie -- http://tratt.net/laurie/ -- Personal http://fetegeo.org/ -- Free text geocoding http://convergepl.org/ -- The Converge programming language Index: apmd.c =================================================================== RCS file: /cvs/src/usr.sbin/apmd/apmd.c,v retrieving revision 1.56 diff -u -r1.56 apmd.c --- apmd.c 2 Apr 2010 04:12:46 -0000 1.56 +++ apmd.c 27 Jun 2010 12:05:11 -0000 @@ -1,4 +1,4 @@ -/* $OpenBSD: apmd.c,v 1.56 2010/04/02 04:12:46 deraadt Exp $ */ +/* $OpenBSD: apmd.c,v 1.51 2009/02/04 05:22:58 tedu Exp $ */ /* * Copyright (c) 1995, 1996 John T. Kohl @@ -61,12 +61,14 @@ int debug = 0; int doperf = PERF_NONE; -#define PERFINC 50 -#define PERFDEC 20 +#define PERFDEC 15 #define PERFMIN 0 #define PERFMAX 100 #define PERFINCTHRES 10 #define PERFDECTHRES 30 +#define PERFTIMEOUTERR 999999999 +#define PERFTIMEOUTFAST 10000000 +#define PERFTIMEOUTSLOW 25000000 extern char *__progname; @@ -74,9 +76,8 @@ 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); -int get_avg_idle_mp(int ncpu); -int get_avg_idle_up(void); -void perf_status(struct apm_power_info *pinfo, int ncpu); +int get_min_idle_mp(int ncpu); +useconds_t perf_status(struct apm_power_info *pinfo, int ncpu); void suspend(int ctl_fd); void stand_by(int ctl_fd); void setperf(int new_perf); @@ -195,13 +196,12 @@ /* multi- and uni-processor case */ int -get_avg_idle_mp(int ncpu) +get_min_idle_mp(int ncpu) { static int64_t **cp_time_old; static int64_t **cp_time; - static int *avg_idle; int64_t change, sum, idle; - int i, cpu, min_avg_idle; + int i, cpu, min_idle; size_t cp_time_sz = CPUSTATES * sizeof(int64_t); if (!cp_time_old) @@ -212,11 +212,7 @@ if ((cp_time = calloc(sizeof(int64_t *), ncpu)) == NULL) return -1; - if (!avg_idle) - if ((avg_idle = calloc(sizeof(int), ncpu)) == NULL) - return -1; - - min_avg_idle = 0; + min_idle = 100; for (cpu = 0; cpu < ncpu; cpu++) { int cp_time_mib[] = {CTL_KERN, KERN_CPTIME2, cpu}; @@ -230,14 +226,14 @@ calloc(sizeof(int64_t), CPUSTATES)) == NULL) return -1; - if (sysctl(cp_time_mib, 3, cp_time[cpu], &cp_time_sz, NULL, 0) - < 0) + if (sysctl(cp_time_mib, 3, cp_time[cpu], &cp_time_sz, NULL, 0)) syslog(LOG_INFO, "cannot read kern.cp_time2"); sum = 0; + idle = 100; for (i = 0; i < CPUSTATES; i++) { - if ((change = cp_time[cpu][i] - cp_time_old[cpu][i]) - < 0) { + change = cp_time[cpu][i] - cp_time_old[cpu][i]; + if (change < 0) { /* counter wrapped */ change = ((uint64_t)cp_time[cpu][i] - (uint64_t)cp_time_old[cpu][i]); @@ -249,72 +245,28 @@ if (sum == 0) sum = 1; - /* smooth data */ - avg_idle[cpu] = (avg_idle[cpu] + (100 * idle) / sum) / 2; - - if (cpu == 0) - min_avg_idle = avg_idle[cpu]; - - if (avg_idle[cpu] < min_avg_idle) - min_avg_idle = avg_idle[cpu]; + if ((100 * idle) / sum < min_idle) + min_idle = (100 * idle) / sum; memcpy(cp_time_old[cpu], cp_time[cpu], cp_time_sz); } - return min_avg_idle; -} - -int -get_avg_idle_up(void) -{ - static long cp_time_old[CPUSTATES]; - static int avg_idle; - long change, cp_time[CPUSTATES]; - int cp_time_mib[] = {CTL_KERN, KERN_CPTIME}; - size_t cp_time_sz = sizeof(cp_time); - int i, idle, sum = 0; - - if (sysctl(cp_time_mib, 2, &cp_time, &cp_time_sz, NULL, 0) < 0) - syslog(LOG_INFO, "cannot read kern.cp_time"); - - 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; - - memcpy(cp_time_old, cp_time, sizeof(cp_time_old)); - - return avg_idle; + return min_idle; } -void +useconds_t perf_status(struct apm_power_info *pinfo, int ncpu) { - int avg_idle; + int avg_idle, min_idle; int hw_perf_mib[] = {CTL_HW, HW_SETPERF}; int perf; - int forcehi = 0; size_t perf_sz = sizeof(perf); - if (ncpu > 1) { - avg_idle = get_avg_idle_mp(ncpu); - } else { - avg_idle = get_avg_idle_up(); - } - if (avg_idle == -1) - return; + return PERFTIMEOUTERR; + + if (sysctl(hw_perf_mib, 2, &perf, &perf_sz, NULL, 0) < 0) + syslog(LOG_INFO, "cannot read hw.setperf"); switch (doperf) { case PERF_AUTO: @@ -324,28 +276,27 @@ * the battery is absent */ if (pinfo->ac_state == APM_AC_ON && pinfo->battery_life > 15 || - pinfo->battery_state == APM_BATTERY_ABSENT) - forcehi = 1; - break; + pinfo->battery_state == APM_BATTERY_ABSENT) { + setperf(PERFMAX); + return PERFTIMEOUTSLOW; + } + /* fallthrough */ case PERF_COOL: - forcehi = 0; - break; + min_idle = get_min_idle_mp(ncpu); + if (min_idle < PERFINCTHRES && perf < PERFMAX) { + setperf(PERFMAX); + return PERFTIMEOUTFAST; + } else if (min_idle > PERFDECTHRES && perf > PERFMIN) { + perf -= PERFDEC; + if (perf < PERFMIN) + perf = PERFMIN; + setperf(perf); + return PERFTIMEOUTSLOW; + } else + return PERFTIMEOUTSLOW; } - if (sysctl(hw_perf_mib, 2, &perf, &perf_sz, NULL, 0) < 0) - syslog(LOG_INFO, "cannot read hw.setperf"); - - if (forcehi || (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); - } + return PERFTIMEOUTSLOW; } char socketname[MAXPATHLEN]; @@ -624,8 +575,8 @@ sts = ts; if (doperf == PERF_AUTO || doperf == PERF_COOL) { - sts.tv_sec = 1; - perf_status(&pinfo, ncpu); + sts.tv_sec = 0; + sts.tv_nsec = perf_status(&pinfo, ncpu); } apmtimeout += sts.tv_sec;