Last time I proposed a CPU frequency scheduling change to make it
more performance oriented, however this would hurt badly the battery
life for nomad users.

As we don't like tweaks and complicated settings, I create a new
CPU frequency policy that is oriented for nomad users, it reduces
performance a bit but from my (short) experiments it would give a
way better battery life while keeping the system responsive which
is not allowing apm -L

The current auto mode is kept as it is now and I added a new
powersaving mode that will be triggered automatically by apmd when
the system is on battery AND automatic mode is activated.

So, on A/C you have the usual automatic policy and on battery it
would switch on the new powersaving policy.

I hope this could be useful for nomad people and later we could
tune the automatic mode (to be used on A/C) to be giving more
performance, without affecting people on battery.

The code change is relatively simple, I'm pretty sure this can be
improved but I wanted to share it as this for now so people can
comment and/or try it out.

I created a new policy which is basically a copy/paste of the current
one with a different name and differents thresholds, declared it
in apmd. There is another change in apmd to check on event if we
are using the policy auto and if on battery or not, this takes care
of changing the policy automatically. I'm not sure it's at the right
place. As for the code in the kernel, I'm quite sure it could be
merged into one and use different thresholds depending on the policy
name currently in use.

Kernel + usr.sbin/apmd + usr.sbin/apm recompilation required

Index: sys/kern//sched_bsd.c
===================================================================
RCS file: /home/reposync/src/sys/kern/sched_bsd.c,v
retrieving revision 1.69
diff -u -p -r1.69 sched_bsd.c
--- sys/kern//sched_bsd.c       9 Sep 2021 18:41:39 -0000       1.69
+++ sys/kern//sched_bsd.c       25 Sep 2021 18:09:40 -0000
@@ -531,6 +531,7 @@ void (*cpu_setperf)(int);
 #define PERFPOL_MANUAL 0
 #define PERFPOL_AUTO 1
 #define PERFPOL_HIGH 2
+#define PERFPOL_POWERSAVING 4
 int perflevel = 100;
 int perfpolicy = PERFPOL_MANUAL;
 
@@ -541,7 +542,9 @@ int perfpolicy = PERFPOL_MANUAL;
 #include <sys/sysctl.h>
 
 void setperf_auto(void *);
+void setperf_powersaving(void *);
 struct timeout setperf_to = TIMEOUT_INITIALIZER(setperf_auto, NULL);
+struct timeout setperf_to_powersaving = 
TIMEOUT_INITIALIZER(setperf_powersaving, NULL);
 
 void
 setperf_auto(void *v)
@@ -606,6 +609,73 @@ setperf_auto(void *v)
        timeout_add_msec(&setperf_to, 100);
 }
 
+void
+setperf_powersaving(void *v)
+{
+       static uint64_t *idleticks, *totalticks;
+       static int downbeats;
+
+       int i, j;
+       int speedup;
+       CPU_INFO_ITERATOR cii;
+       struct cpu_info *ci;
+       uint64_t idle, total, allidle, alltotal;
+
+       if (perfpolicy != PERFPOL_POWERSAVING)
+               return;
+
+       if (!idleticks)
+               if (!(idleticks = mallocarray(ncpusfound, sizeof(*idleticks),
+                   M_DEVBUF, M_NOWAIT | M_ZERO)))
+                       return;
+       if (!totalticks)
+               if (!(totalticks = mallocarray(ncpusfound, sizeof(*totalticks),
+                   M_DEVBUF, M_NOWAIT | M_ZERO))) {
+                       free(idleticks, M_DEVBUF,
+                           sizeof(*idleticks) * ncpusfound);
+                       return;
+               }
+
+       alltotal = allidle = 0;
+       j = 0;
+       speedup = 0;
+       CPU_INFO_FOREACH(cii, ci) {
+               if (!cpu_is_online(ci))
+                       continue;
+               total = 0;
+               for (i = 0; i < CPUSTATES; i++) {
+                       total += ci->ci_schedstate.spc_cp_time[i];
+               }
+               total -= totalticks[j];
+               idle = ci->ci_schedstate.spc_cp_time[CP_IDLE] - idleticks[j];
+               // speedup if at least one core idle time < 33%
+               if (idle < total / 3)
+                       speedup = 1;
+               alltotal += total;
+               allidle += idle;
+               idleticks[j] += idle;
+               totalticks[j] += total;
+               j++;
+       }
+       if (allidle < alltotal / 3)
+               speedup = 1;
+       if (speedup)
+               /* twice as long here because we check every 200ms */
+               downbeats = 1;
+
+       if (speedup && perflevel != 100) {
+               perflevel = 100;
+               cpu_setperf(perflevel);
+       } else if (!speedup && perflevel != 0 && --downbeats <= 0) {
+               perflevel = 0;
+               cpu_setperf(perflevel);
+       }
+
+        /* every 200ms to have a better resolution of the load */
+       timeout_add_msec(&setperf_to_powersaving, 200);
+}
+
+
 int
 sysctl_hwsetperf(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
 {
@@ -644,6 +714,9 @@ sysctl_hwperfpolicy(void *oldp, size_t *
        case PERFPOL_AUTO:
                strlcpy(policy, "auto", sizeof(policy));
                break;
+       case PERFPOL_POWERSAVING:
+               strlcpy(policy, "powersaving", sizeof(policy));
+               break;
        case PERFPOL_HIGH:
                strlcpy(policy, "high", sizeof(policy));
                break;
@@ -662,6 +735,8 @@ sysctl_hwperfpolicy(void *oldp, size_t *
                perfpolicy = PERFPOL_MANUAL;
        else if (strcmp(policy, "auto") == 0)
                perfpolicy = PERFPOL_AUTO;
+       else if (strcmp(policy, "powersaving") == 0)
+               perfpolicy = PERFPOL_POWERSAVING;
        else if (strcmp(policy, "high") == 0)
                perfpolicy = PERFPOL_HIGH;
        else
@@ -669,6 +744,8 @@ sysctl_hwperfpolicy(void *oldp, size_t *
 
        if (perfpolicy == PERFPOL_AUTO) {
                timeout_add_msec(&setperf_to, 200);
+       } else if (perfpolicy == PERFPOL_POWERSAVING) {
+               timeout_add_msec(&setperf_to_powersaving, 200);
        } else if (perfpolicy == PERFPOL_HIGH) {
                perflevel = 100;
                cpu_setperf(perflevel);
Index: usr.sbin/apmd//apm-proto.h
===================================================================
RCS file: /home/reposync/src/usr.sbin/apmd/apm-proto.h,v
retrieving revision 1.12
diff -u -p -r1.12 apm-proto.h
--- usr.sbin/apmd//apm-proto.h  6 Apr 2021 22:09:56 -0000       1.12
+++ usr.sbin/apmd//apm-proto.h  25 Sep 2021 18:04:14 -0000
@@ -38,6 +38,7 @@ enum apm_action {
        SETPERF_LOW,
        SETPERF_HIGH,
        SETPERF_AUTO,
+       SETPERF_POWERSAVING,
 };
 
 enum apm_state {
@@ -51,6 +52,7 @@ enum apm_perfmode {
        PERF_NONE = -1,
        PERF_MANUAL,
        PERF_AUTO,
+       PERF_POWERSAVING,
 };
 
 struct apm_command {
Index: usr.sbin/apmd//apmd.c
===================================================================
RCS file: /home/reposync/src/usr.sbin/apmd/apmd.c,v
retrieving revision 1.106
diff -u -p -r1.106 apmd.c
--- usr.sbin/apmd//apmd.c       12 Jul 2021 15:09:20 -0000      1.106
+++ usr.sbin/apmd//apmd.c       25 Sep 2021 18:04:39 -0000
@@ -140,6 +140,11 @@ power_status(int fd, int force, struct a
        static struct apm_power_info last;
        int acon = 0, priority = LOG_NOTICE;
 
+       /* solene */
+       int perfpol_mib[] = { CTL_HW, HW_PERFPOLICY };
+       char perfpol[32];
+       size_t perfpol_sz = sizeof(perfpol);
+
        if (fd == -1) {
                if (pinfo) {
                        bstate.battery_state = 255;
@@ -152,11 +157,18 @@ power_status(int fd, int force, struct a
                return 0;
        }
 
+       if (sysctl(perfpol_mib, 2, perfpol, &perfpol_sz, NULL, 0) == -1)
+               logmsg(LOG_INFO, "cannot read hw.perfpolicy");
+
        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 */
-               if (bstate.ac_state == APM_AC_ON)
+               if (bstate.ac_state == APM_AC_ON) {
                        acon = 1;
+                       if(strcmp(perfpol, "powersaving") == 0) 
setperfpolicy("auto");
+               } else
+                       if(strcmp(perfpol, "auto") == 0) 
setperfpolicy("powersaving");
+
                if (bstate.battery_state == APM_BATT_CRITICAL &&
                    bstate.battery_state != last.battery_state)
                        priority = LOG_EMERG;
@@ -291,6 +303,11 @@ handle_client(int sock_fd, int ctl_fd)
                logmsg(LOG_NOTICE, "setting hw.perfpolicy to high");
                setperfpolicy("high");
                break;
+       case SETPERF_POWERSAVING:
+               reply.newstate = NORMAL;
+               logmsg(LOG_NOTICE, "setting hw.perfpolicy to powersaving");
+               setperfpolicy("powersaving");
+               break;
        case SETPERF_AUTO:
                reply.newstate = NORMAL;
                logmsg(LOG_NOTICE, "setting hw.perfpolicy to auto");
@@ -308,8 +325,10 @@ handle_client(int sock_fd, int ctl_fd)
                if (strcmp(perfpol, "manual") == 0 ||
                    strcmp(perfpol, "high") == 0) {
                        reply.perfmode = PERF_MANUAL;
-               } else if (strcmp(perfpol, "auto") == 0)
+               } else if (strcmp(perfpol, "auto") == 0) {
                        reply.perfmode = PERF_AUTO;
+               } else if (strcmp(perfpol, "powersaving") == 0)
+                       reply.perfmode = PERF_POWERSAVING;
        }
 
        if (sysctl(cpuspeed_mib, 2, &cpuspeed, &cpuspeed_sz, NULL, 0) == -1) {
@@ -593,6 +612,8 @@ main(int argc, char *argv[])
                                powerstatus = powerbak;
                                powerchange = 1;
                        }
+
+
 
                        if (!powerstatus && autoaction &&
                            autolimit > (int)pinfo.battery_life) {
Index: usr.sbin/apmd//apmsubr.c
===================================================================
RCS file: /home/reposync/src/usr.sbin/apmd/apmsubr.c,v
retrieving revision 1.12
diff -u -p -r1.12 apmsubr.c
--- usr.sbin/apmd//apmsubr.c    8 Jul 2021 18:54:21 -0000       1.12
+++ usr.sbin/apmd//apmsubr.c    25 Sep 2021 18:04:26 -0000
@@ -79,6 +79,8 @@ perf_mode(int mode)
                return "manual";
        case PERF_AUTO:
                return "auto";
+       case PERF_POWERSAVING:
+               return "powersaving";
        default:
                return "invalid";
        }

Reply via email to