The current situation where apmd monitors system load and adjusts the 
hw.setperf sysctl has a number of flaws.  While it's nice to keep the 
policy outside the kernel and allow tuning, the polling interval is too 
slow to be responsive.  More aggressive polling intervals and we end up 
affecting the result, not just measuring it.

Here's a first cut at throwing some code into the kernel.  You'll 
definitely want to kill apmd if you're running it.  It's rather rough, in 
the wrong spot, and so on, but it's a place to start for further hacking.  
I'm not convinced we want to just copy the apmd policy into the kernel, so 
I haven't done so.

Index: init_main.c
===================================================================
RCS file: /cvs/src/sys/kern/init_main.c,v
retrieving revision 1.168
diff -u -r1.168 init_main.c
--- init_main.c 29 Jun 2010 20:25:57 -0000      1.168
+++ init_main.c 30 Jun 2010 03:51:06 -0000
@@ -172,6 +172,55 @@
        EMUL_ENABLED | EMUL_NATIVE,
 };
 
+struct timeout setperf_to;
+
+void auto_setperf(void *v);
+void
+auto_setperf(void *v)
+{
+       int i;
+       long work, workdiff;
+       static long oldwork;
+       long idle, idlediff;
+       static long oldidle;
+       CPU_INFO_ITERATOR cii;
+       struct cpu_info *ci;
+       extern int perflevel;
+
+       if (!cpu_setperf)
+               return;
+
+       work = idle = 0;
+       CPU_INFO_FOREACH(cii, ci) {
+               for (i = 0; i < CPUSTATES; i++) {
+                       if (i == CP_IDLE)
+                               idle += ci->ci_schedstate.spc_cp_time[i];
+                       work += ci->ci_schedstate.spc_cp_time[i];
+               }
+       }
+       workdiff = work - oldwork;
+       idlediff = idle - oldidle;
+       oldwork = work;
+       oldidle = idle;
+
+       if (idlediff < workdiff / 5 ) {
+               perflevel += 10;
+               if (perflevel > 100)
+                       perflevel = 100;
+               else
+                       printf("going up %d\n", perflevel);
+               cpu_setperf(perflevel);
+       } else if (idlediff > workdiff * 7 / 10) {
+               perflevel -= 5;
+               if (perflevel < 0)
+                       perflevel = 0;
+               else
+                       printf("going down %d\n", perflevel);
+               cpu_setperf(perflevel);
+       }
+       
+       timeout_add_msec(&setperf_to, 100);
+}
 
 /*
  * System startup; initialize the world, create process 0, mount root
@@ -545,6 +594,9 @@
         */
        start_init_exec = 1;
        wakeup((void *)&start_init_exec);
+
+       timeout_set(&setperf_to, auto_setperf, NULL);
+       timeout_add_msec(&setperf_to, 100);
 
         /*
          * proc0: nothing to do, back to sleep

Reply via email to