• Welcome to OS2World OLD-STATIC-BACKUP Forum.
 

News:

This is an old OS2World backup forum for reference only. IT IS READ ONLY!!!

If you need help with OS/2 - eComStation visit http://www.os2world.com/forum

Main Menu

acpi cpu daemon

Started by abwillis, 2009.01.31, 20:45:46

Previous topic - Next topic

abwillis

eCS's ACPI does not currently have any way to automatically throttle the CPU when not needed.  If running ACPI you either run at full or you can manually use setgetthrtl.exe but this is mostly for testing as it isn't convenient for actual use as you would then have to set the throttle back up manually as well. Here is some code to watch the cpu usage and change the throttle respectively.  This code is not real dynamic right now but appears to more or less do the job.  I am hoping others will also pick this up and maybe come up with something until a better approach is found.  I am running this program on a hyperthreading SMP system (one CPU but is being seen and used as 2) running eCS 2.0 rc6a.  RC6a puts setgetthrtl.exe into the boot:\ecs\bin  if ACPI was installed from the Betazone on another system then it is in the utils directory and setgetthrtl.exe either has to be in the path or in the same directory as the executable.
The bulk of the code I got from edm2, which in turn came from the SMP addendum.inf from the OS2 toolkit.  I have left in currently the ability to set the time for the checks between 1-9 seconds, I changed from failing if no number is given to defaulting to 5 seconds. 
I am open to better code suggestions :)
I am very interested in opinions on the best timing for how often to do the cpu checks.
I have been thinking it may be a good idea to boost the priority, should it run at normal priority but higher delta? or foreground server? time critical?
I have currently just selected throttle settings to idle settings to what seemed like they would work OK, better ideas for this?
From what I can tell, you cannot set the throttle settings independently on various processors so I only set CPU 0, however, I only have a hyperthreading machine to check on and not a true SMP system... if setgetthrtl.exe is run with no switches then it will show current throttle state (2nd CPU here show throttle disabled). The parameters for setgetthrtl.exe are cpu number (starting at 0) and throttle (mine shows min 13 and max 100).
And now for the code :)
#define INCL_BASE

  #include <os2.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

#define DEBUG 0

  APIRET APIENTRY DosPerfSysCall(ULONG ulCommand, ULONG ulParm1,
                                 ULONG ulParm2, ULONG ulParm3);

  #define ORD_DOS32PERFSYSCALL       976
  #define CMD_KI_RDCNT            (0x63)

  typedef struct _CPUUTIL {
    ULONG ulTimeLow;     /* Low 32 bits of time stamp      */
    ULONG ulTimeHigh;    /* High 32 bits of time stamp     */
    ULONG ulIdleLow;     /* Low 32 bits of idle time       */
    ULONG ulIdleHigh;    /* High 32 bits of idle time      */
    ULONG ulBusyLow;     /* Low 32 bits of busy time       */
    ULONG ulBusyHigh;    /* High 32 bits of busy time      */
    ULONG ulIntrLow;     /* Low 32 bits of interrupt time  */
    ULONG ulIntrHigh;    /* High 32 bits of interrupt time */
   } CPUUTIL;

  typedef CPUUTIL *PCPUUTIL;

  /* Convert 8-byte (low, high) time value to double */
  #define LL2F(high, low) (4294967296.0*(high)+(low))

  /* This is a 1 processor example */
  int main (int argc, char *argv[])
  {
     APIRET      rc;
     int         i, iter, sleep_sec, cputhrtl50;
     double      ts_val, ts_val_prev;
     double      idle_val, idle_val_prev;
     double      busy_val, busy_val_prev;
     double      intr_val, intr_val_prev;
     CPUUTIL     CPUUtil;

     if ((argc < 2) || (*argv[1] < '1') || (*argv[1] > '9')) {
     sleep_sec = 5;
     } else
     sleep_sec = *argv[1] - '0';


     cputhrtl50 = 100;
     iter = 0;
     do {
         rc = DosPerfSysCall(CMD_KI_RDCNT,(ULONG) &CPUUtil,0,0);
         if (rc) {
             fprintf(stderr, "CMD_KI_RDCNT failed rc = %d\n",rc);
             exit(1);
         }
         ts_val = LL2F(CPUUtil.ulTimeHigh, CPUUtil.ulTimeLow);
         idle_val = LL2F(CPUUtil.ulIdleHigh, CPUUtil.ulIdleLow);
         busy_val = LL2F(CPUUtil.ulBusyHigh, CPUUtil.ulBusyLow);
         intr_val = LL2F(CPUUtil.ulIntrHigh, CPUUtil.ulIntrLow);

         if (iter > 0) {
             double  ts_delta = ts_val - ts_val_prev;
#if DEBUG
             printf("idle: %4.2f%%  busy: %4.2f%%  intr: %4.2f%%\n",
                    (idle_val - idle_val_prev)/ts_delta*100.0,
                    (busy_val - busy_val_prev)/ts_delta*100.0,
                    (intr_val - intr_val_prev)/ts_delta*100.0);
#endif

         if (((idle_val - idle_val_prev)/ts_delta*100.0) > 75){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) > 85){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) > 90){
             if (cputhrtl50 != 15){
             system( "setgetthrtl 0 15");
             cputhrtl50 = 15;}} else
             if (cputhrtl50 != 25){
             system( "setgetthrtl 0 25");
             cputhrtl50 = 25;}} else
             if (cputhrtl50 != 50){
             system( "setgetthrtl 0 50");
             cputhrtl50 = 50;}}


         if (((idle_val - idle_val_prev)/ts_delta*100.0) < 35){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) < 25){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) < 10){
             if (cputhrtl50 != 100){
             system( "setgetthrtl 0 100");
             cputhrtl50 = 100;}} else
             if (cputhrtl50 != 75){
             system( "setgetthrtl 0 75");
             cputhrtl50 = 75;}} else
             if (cputhrtl50 != 50){
             system( "setgetthrtl 0 50");
             cputhrtl50 = 50;}}

         }

         ts_val_prev = ts_val;
         idle_val_prev = idle_val;
         busy_val_prev = busy_val;
         intr_val_prev = intr_val;

         iter++;
         DosSleep(1000*sleep_sec);

     } while (1);
  }

abwillis

#1
I had compiled this with gcc 335 and 432 but decided to try OpenWatcom to have the code checked by another compiler. 

  #define CMD_KI_RDCNT              (0x63)

  typedef struct _CPUUTIL {
    ULONG ulTimeLow;     /* Low 32 bits of time stamp      */
    ULONG ulTimeHigh;    /* High 32 bits of time stamp     */
    ULONG ulIdleLow;     /* Low 32 bits of idle time       */
    ULONG ulIdleHigh;    /* High 32 bits of idle time      */
    ULONG ulBusyLow;     /* Low 32 bits of busy time       */
    ULONG ulBusyHigh;    /* High 32 bits of busy time      */
    ULONG ulIntrLow;     /* Low 32 bits of interrupt time  */
    ULONG ulIntrHigh;    /* High 32 bits of interrupt time */
   } CPUUTIL;

Watcom complained of the above being redefined so I used #if to remove it.  On the single core cpu I am building it on it behaves as expected... However, on the SMP system it does not appear to properly read the idle times, unless I #define DEBUG which right now only turns on a single printf.
OW 1.7a.
Hmm, bizarre, I just found that GCC does not work correctly with the #if on the abovein place either.  It, however, it does not work if DEBUG is defined as OW does.  The code compiles but it never executes the setgetthrtl code. 
If, for instance, I use #if _GCC_ and don't define _GCC_ then GCC compile fails but OW succeeds.  However, if _GCC_ is compiled it is just the opposite and all makes sense to me at up to this point.  However, when I execute the code nothing seems to occur with either compiler in terms of executing the setgetthrtl.exe.  If I define DEBUG then the code does execute for OW but not GCC.
As another data point... I just removed the entire section of code and built with OW and it still behaves the same as with the #if in place (thought it might do differently just because GCC did).  None of this part is making any sense to me.

abwillis

I still don't know why some of the problems but this code works with GCC or OW.

#define INCL_BASE

  #include <os2.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

/* #define DEBUG 1 */

  APIRET APIENTRY DosPerfSysCall(ULONG ulCommand, ULONG ulParm1,
                                 ULONG ulParm2, ULONG ulParm3);

  #define ORD_DOS32PERFSYSCALL       976
#ifndef CMD_KI_RDCNT
  #define CMD_KI_RDCNT            (0x63)

  typedef struct _CPUUTIL {
    ULONG ulTimeLow;     /* Low 32 bits of time stamp      */
    ULONG ulTimeHigh;    /* High 32 bits of time stamp     */
    ULONG ulIdleLow;     /* Low 32 bits of idle time       */
    ULONG ulIdleHigh;    /* High 32 bits of idle time      */
    ULONG ulBusyLow;     /* Low 32 bits of busy time       */
    ULONG ulBusyHigh;    /* High 32 bits of busy time      */
    ULONG ulIntrLow;     /* Low 32 bits of interrupt time  */
    ULONG ulIntrHigh;    /* High 32 bits of interrupt time */
   } CPUUTIL;

  typedef CPUUTIL *PCPUUTIL;
#endif

  /* Convert 8-byte (low, high) time value to double */
  #define LL2F(high, low) (4294967296.0*(high)+(low))

  /* This is a 1 processor example */
  int main (int argc, char *argv[])
  {
     APIRET      rc;
     int         i, iter, sleep_sec, cputhrtl50;
     double      ts_val, ts_val_prev;
     double      idle_val, idle_val_prev;
     double      busy_val, busy_val_prev;
     double      intr_val, intr_val_prev;
     CPUUTIL     CPUUtil;

     if ((argc < 2) || (*argv[1] < '1') || (*argv[1] > '9')) {
     sleep_sec = 5;
     } else
     sleep_sec = *argv[1] - '0';


     cputhrtl50 = 100;
     iter = 0;
     do {
         rc = DosPerfSysCall(CMD_KI_RDCNT,(ULONG) &CPUUtil,0,0);
         if (rc) {
             fprintf(stderr, "CMD_KI_RDCNT failed rc = %d\n",rc);
             exit(1);
         }
         ts_val = LL2F(CPUUtil.ulTimeHigh, CPUUtil.ulTimeLow);
         idle_val = LL2F(CPUUtil.ulIdleHigh, CPUUtil.ulIdleLow);
         busy_val = LL2F(CPUUtil.ulBusyHigh, CPUUtil.ulBusyLow);
         intr_val = LL2F(CPUUtil.ulIntrHigh, CPUUtil.ulIntrLow);

         if (iter > 0) {
             double  ts_delta = ts_val - ts_val_prev;
#ifdef DEBUG
             printf("idle: %4.2f%%  busy: %4.2f%%  intr: %4.2f%%\n",
                    (idle_val - idle_val_prev)/ts_delta*100.0,
                    (busy_val - busy_val_prev)/ts_delta*100.0,
                    (intr_val - intr_val_prev)/ts_delta*100.0);
#endif

         if (((idle_val - idle_val_prev)/ts_delta*100.0) > 75){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) > 85){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) > 90){
             if (cputhrtl50 != 15){
             system( "setgetthrtl 0 15");
             cputhrtl50 = 15;}} else
             if (cputhrtl50 != 25){
             system( "setgetthrtl 0 25");
             cputhrtl50 = 25;}} else
             if (cputhrtl50 != 50){
             system( "setgetthrtl 0 50");
             cputhrtl50 = 50;}}


         if (((idle_val - idle_val_prev)/ts_delta*100.0) < 35){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) < 25){
         if (((idle_val - idle_val_prev)/ts_delta*100.0) < 10){
             if (cputhrtl50 != 100){
             system( "setgetthrtl 0 100");
             cputhrtl50 = 100;}} else
             if (cputhrtl50 != 75){
             system( "setgetthrtl 0 75");
             cputhrtl50 = 75;}} else
             if (cputhrtl50 != 50){
             system( "setgetthrtl 0 50");
             cputhrtl50 = 50;}}

         }

         ts_val_prev = ts_val;
         idle_val_prev = idle_val;
         busy_val_prev = busy_val;
         intr_val_prev = intr_val;

         iter = 1;
         DosSleep(1000*sleep_sec);

     } while (1);
return 0;
  }

abwillis

Also, setgetstate can be added as well as setgetthrtl.  I didn't do it here for 2 main reasons:
1)  setgetstate doesn't do anything on the machine I am using it on
2)  it has numerous possible states... my T42 which I don't use ACPI on has 5 states I think but the desktop machine I was working on this for shows 2 states, I don't know how to pull the number of states dynamically.

Joachim

Hello Andy,

sounds like a much needed tool. Would you mind posting a compiled version for us mortals to test? :)

Thanks,

Joachim

abwillis

I've uploaded ACPIThrottle to hobbes.  While doing so I saw that there is a CPUspeed that I had forgotten about.  Unfortunately there is no source included with it.  I haven't tried it on the system I was wanting this for but didn't have much success with it in my testing on another system some time ago when I tried it.  I would recommend though, that people give it a shot as I think it is probably a better approach than mine.

Joachim

Thanks,

I'll certainly give it a try :)

Joachim

DavidG

Quote from: abwillis on 2009.02.02, 00:41:26
I've uploaded ACPIThrottle to hobbes.  While doing so I saw that there is a CPUspeed that I had forgotten about.  Unfortunately there is no source included with it.  I haven't tried it on the system I was wanting this for but didn't have much success with it in my testing on another system some time ago when I tried it.  I would recommend though, that people give it a shot as I think it is probably a better approach than mine.

Is this similar to the following program found on Hobbes?

http://hobbes.nmsu.edu/download/pub/os2/util/system/cpuspeed1.4.zip

abwillis

Quote from: DavidG on 2009.02.02, 11:00:23
Quote from: abwillis on 2009.02.02, 00:41:26
I've uploaded ACPIThrottle to hobbes.  While doing so I saw that there is a CPUspeed that I had forgotten about.  Unfortunately there is no source included with it.  I haven't tried it on the system I was wanting this for but didn't have much success with it in my testing on another system some time ago when I tried it.  I would recommend though, that people give it a shot as I think it is probably a better approach than mine.

Is this similar to the following program found on Hobbes?

http://hobbes.nmsu.edu/download/pub/os2/util/system/cpuspeed1.4.zip
Yes, that is the one I am referencing above.  It ties directly into acpi32.dll and started giving errors on my T42 during testing at some ACPI level.  I had forgotten about it when I decided I wanted to something like this on my Desktop system and haven't tried it there yet.
Andy