[PATCH 5/5] ntdll: Add more complete implementation of NtPowerInformation (try 2)

James Eder jimportal at gmail.com
Tue Sep 11 12:06:26 CDT 2012


This patch:
  * Moves CPU Hz detection out of fill_cpu_info() and implements it
    locally.  The cpuHz global was only used by this function and we
    need to detect this on the fly for CurrentMhz anyway.
  * For Linux systems with cpufreq more complete and dynamic information
    is gathered.  For systems without cpufreq (systems running under
    under QEMU, for example) the information is gathered from
    /proc/cpuinfo.
  * For Apple we a get better value for MaxMhz but we're still reporting
    the same information for all processors rather than providing it on
    a per-CPU basis.
  * For the BSDs we're still the same as before, reporting the same
    number for all fields across all processors.

*** For this patch, some testing is needed. ***  I have no Apples or BSDs
so that bit of code is based on code removed from fill_cpu_info and some
internet searching.  I could try a BSD flavor or two in Qemu, but I have
no intention of buying a Mac.  So, since I have to ask for testing anyway,
I might as well ask for it from BSDers too.  Please test and report back
if you happen to use one of those operating systems.

For testing, you can do something like:
  $ WINEDEBUG=+ntdll wine notepad 2>&1 | grep NtPowerInformation
which will show the contents of the array.  For example it could look
like:
  trace:ntdll:NtPowerInformation cpu_power[0] = 0 3200 800 3200 0 0
  trace:ntdll:NtPowerInformation cpu_power[1] = 1 3200 800 3200 0 0
  trace:ntdll:NtPowerInformation cpu_power[2] = 2 3200 3200 3200 0 0
  trace:ntdll:NtPowerInformation cpu_power[3] = 3 3200 800 3200 0 0

The numbers after the '=' sign are the members of a
PROCESSOR_POWER_INFORMATION structure (in order: Number, MaxMhz,
CurrentMhz, MhzLimit, MaxIdleState, CurrentIdleState)
---
 dlls/ntdll/nt.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 156 insertions(+), 29 deletions(-)

diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c
index d38fedb..031c38c 100644
--- a/dlls/ntdll/nt.c
+++ b/dlls/ntdll/nt.c
@@ -810,7 +810,6 @@ NTSTATUS WINAPI NtSetIntervalProfile(
 }
 
 static  SYSTEM_CPU_INFORMATION cached_sci;
-static  ULONGLONG cpuHz = 1000000000; /* default to a 1GHz */
 
 #define AUTH	0x68747541	/* "Auth" */
 #define ENTI	0x69746e65	/* "enti" */
@@ -898,10 +897,10 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
 }
 
 /******************************************************************
- *		fill_cpu_info
+ * fill_cpu_info
  *
- * inits a couple of places with CPU related information:
- * - cached_sci & cpuHZ in this file
+ * Initializes a couple of places with CPU related information:
+ * - cached_sci in this file
  * - Peb->NumberOfProcessors
  * - SharedUserData->ProcessFeatures[] array
  */
@@ -1019,16 +1018,6 @@ void fill_cpu_info(void)
                     cached_sci.Revision = cached_sci.Revision | x;
                 continue;
             }
-            if (!strcasecmp(line, "cpu MHz"))
-            {
-                double cmz;
-                if (sscanf( value, "%lf", &cmz ) == 1)
-                {
-                    /* SYSTEMINFO doesn't have a slot for cpu speed, so store in a global */
-                    cpuHz = cmz * 1000 * 1000;
-                }
-                continue;
-            }
             if (!strcasecmp(line, "fdiv_bug"))
             {
                 if (!strncasecmp(value, "yes",3))
@@ -1190,10 +1179,6 @@ void fill_cpu_info(void)
         ret = sysctlbyname("hw.ncpu", &num, &len, NULL, 0);
         if (!ret)
             NtCurrentTeb()->Peb->NumberOfProcessors = num;
-
-        len = sizeof(num);
-        if (!sysctlbyname("hw.clockrate", &num, &len, NULL, 0))
-            cpuHz = num * 1000 * 1000;
     }
 #elif defined(__sun)
     {
@@ -1300,9 +1285,6 @@ void fill_cpu_info(void)
             default: break;
             } /* switch (cputype) */
         }
-        valSize = sizeof(longVal);
-        if (!sysctlbyname("hw.cpufrequency", &longVal, &valSize, NULL, 0))
-            cpuHz = longVal;
     }
 #else
     FIXME("not yet supported on this system\n");
@@ -2261,7 +2243,19 @@ NTSTATUS WINAPI NtInitiatePowerAction(
 		SystemAction,MinSystemState,Flags,Asynchronous);
         return STATUS_NOT_IMPLEMENTED;
 }
-	
+
+#ifdef linux
+static int have_cpufreq(void)
+{
+    FILE* f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", "r");
+    if(f)
+    {
+        fclose(f);
+        return 1;
+    }
+    return 0;
+}
+#endif
 
 /******************************************************************************
  *  NtPowerInformation                          [NTDLL.@]
@@ -2326,24 +2320,157 @@ NTSTATUS WINAPI NtPowerInformation(
             return STATUS_SUCCESS;
         }
         case ProcessorInformation: {
+            const int cannedMHz = 1000; /* We fake a 1GHz processor if we can't conjure up real values */
             PROCESSOR_POWER_INFORMATION* cpu_power = lpOutputBuffer;
-            int i;
+            int i, out_cpus;
 
             WARN("semi-stub: ProcessorInformation\n");
 
             if ((lpOutputBuffer == NULL) || (nOutputBufferSize == 0))
                 return STATUS_INVALID_PARAMETER;
-            if ((nOutputBufferSize / sizeof(PROCESSOR_POWER_INFORMATION)) < NtCurrentTeb()->Peb->NumberOfProcessors)
+            out_cpus = NtCurrentTeb()->Peb->NumberOfProcessors;
+            if ((nOutputBufferSize / sizeof(PROCESSOR_POWER_INFORMATION)) < out_cpus)
                 return STATUS_BUFFER_TOO_SMALL;
+#if defined(linux)
+            if(have_cpufreq()) {
+                char filename[128];
+                FILE* f;
+
+                for(i = 0; i < out_cpus; i++) {
+                    sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
+                    f = fopen(filename, "r");
+                    if((f == NULL) || (fscanf(f, "%d", &cpu_power[i].CurrentMhz) != 1))
+                        cpu_power[i].CurrentMhz = cannedMHz;
+                    else
+                        cpu_power[i].CurrentMhz /= 1000;
+                    fclose(f);
+
+                    sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i);
+                    f = fopen(filename, "r");
+                    if((f == NULL) || (fscanf(f, "%d", &cpu_power[i].MaxMhz) != 1))
+                        cpu_power[i].MaxMhz = cpu_power[i].CurrentMhz;
+                    else
+                        cpu_power[i].MaxMhz /= 1000;
+                    fclose(f);
+
+                    sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", i);
+                    f = fopen(filename, "r");
+                    if((f == NULL) || (fscanf(f, "%d", &cpu_power[i].MhzLimit) != 1))
+                        cpu_power[i].MhzLimit = cpu_power[i].MaxMhz;
+                    else
+                        cpu_power[i].MhzLimit /= 1000;
+                    fclose(f);
+
+                    cpu_power[i].Number = i;
+                    cpu_power[i].MaxIdleState = 0;     /* FIXME */
+                    cpu_power[i].CurrentIdleState = 0; /* FIXME */
+                }
+            }
+            else {
+                /* Fall back to using /proc/cpuinfo for systems without cpufreq. For most
+                 * distributions on recent enough hardware, this is only likely to happen
+                 * while running in virtualized environments such as QEMU. */
+                FILE* f = fopen("/proc/cpuinfo", "r");
+                if (f) {
+                    char line[512];
+                    int i = 0;
+                    while (fgets(line, 512, f) != NULL) {
+                        char *s, *value;
+                        if (!(value = strchr(line,':')))
+                            continue;
+                        /* terminate the valuename */
+                        s = value - 1;
+                        while ((s >= line) && isspace(*s)) s--;
+                        *(s + 1) = '\0';
+                        /* and strip leading spaces from value */
+                        value += 1;
+                        while (isspace(*value)) value++;
+                        if ((s = strchr(value,'\n')))
+                            *s = '\0';
+                        if (!strcasecmp(line, "processor")) {
+                            unsigned long p;
+                            if (sscanf(value, "%ld", &p))
+                                cpu_power[i].Number = p;
+                            continue;
+                        }
+                        if (!strcasecmp(line, "cpu MHz")) {
+                            double cmz;
+                            if (sscanf(value, "%lf", &cmz) == 1)
+                                cpu_power[i].CurrentMhz = cmz;
+                            if (cpu_power[i].CurrentMhz > cpu_power[0].MaxMhz)
+                                cpu_power[0].MaxMhz = cpu_power[i].CurrentMhz;
+                            i++;
+                            if (i >= out_cpus)
+                                break;
+                            continue;
+                        }
+                    }
+                    fclose(f);
+                    for(i = 0; i < out_cpus; i++) {
+                        cpu_power[i].MaxMhz = cpu_power[i].MhzLimit = cpu_power[0].MaxMhz;
+                        cpu_power[i].MaxIdleState = 0;     /* FIXME */
+                        cpu_power[i].CurrentIdleState = 0; /* FIXME */
+                    }
+                }
+            }
+#elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__) || defined(__DragonFly__)
+            {
+                int num;
+                size_t valSize = sizeof(num);
+                if (sysctlbyname("hw.clockrate", &num, &valSize, NULL, 0))
+                    num = cannedMHz;
+                for(i = 0; i < out_cpus; i++) {
+                    cpu_power[i].CurrentMhz = num;
+                    cpu_power[i].MaxMhz = num;
+                    cpu_power[i].MhzLimit = num;
+                    cpu_power[i].Number = i;
+                    cpu_power[i].MaxIdleState = 0;     /* FIXME */
+                    cpu_power[i].CurrentIdleState = 0; /* FIXME */
+                }
+            }
+#elif defined (__APPLE__)
+            {
+                size_t valSize;
+                unsigned long long currentMhz;
+                unsigned long long maxMhz;
+
+                valSize = sizeof(currentMhz);
+                if (!sysctlbyname("hw.cpufrequency", &currentMhz, &valSize, NULL, 0))
+                    currentMhz /= 1000000;
+                else
+                    currentMhz = cannedMHz;
 
-            for(i = 0; i < NtCurrentTeb()->Peb->NumberOfProcessors; i++) {
+                valSize = sizeof(maxMhz);
+                if (!sysctlbyname("hw.cpufrequency_max", &maxMhz, &valSize, NULL, 0))
+                    maxMhz /= 1000000;
+                else
+                    maxMhz = currentMhz;
+
+                for(i = 0; i < out_cpus; i++) {
+                    cpu_power[i].CurrentMhz = currentMhz;
+                    cpu_power[i].MaxMhz = maxMhz;
+                    cpu_power[i].MhzLimit = maxMhz;
+                    cpu_power[i].Number = i;
+                    cpu_power[i].MaxIdleState = 0;     /* FIXME */
+                    cpu_power[i].CurrentIdleState = 0; /* FIXME */
+                }
+            }
+#else
+            for(i = 0; i < out_cpus; i++) {
+                cpu_power[i].CurrentMhz = cannedMHz;
+                cpu_power[i].MaxMhz = cannedMHz;
+                cpu_power[i].MhzLimit = cannedMHz;
                 cpu_power[i].Number = i;
-                cpu_power[i].MaxMhz = cpuHz / 1000000;
-                cpu_power[i].CurrentMhz = cpuHz / 1000000;
-                cpu_power[i].MhzLimit = cpuHz / 1000000;
-                cpu_power[i].MaxIdleState = 0; /* FIXME */
+                cpu_power[i].MaxIdleState = 0;     /* FIXME */
                 cpu_power[i].CurrentIdleState = 0; /* FIXME */
             }
+            WARN("Unable to detect CPU MHz for this platform. Reporting %d MHz.\n", cannedMHz);
+#endif
+            for(i = 0; i < out_cpus; i++) {
+                TRACE("cpu_power[%d] = %u %u %u %u %u %u\n", i, cpu_power[i].Number,
+                      cpu_power[i].MaxMhz, cpu_power[i].CurrentMhz, cpu_power[i].MhzLimit,
+                      cpu_power[i].MaxIdleState, cpu_power[i].CurrentIdleState);
+            }
             return STATUS_SUCCESS;
         }
         default:
-- 
1.7.12




More information about the wine-patches mailing list