[PATCH 4/4] ntdll: Add more complete implementation of NtPowerInformation

James Eder jimportal at gmail.com
Wed Sep 12 14:41:59 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 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 | 184 +++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 154 insertions(+), 30 deletions(-)

diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c
index ca05203..0805355 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" */
@@ -901,7 +900,7 @@ static inline void get_cpuinfo(SYSTEM_CPU_INFORMATION* info)
  *		fill_cpu_info
  *
  * inits a couple of places with CPU related information:
- * - cached_sci & cpuHZ in this file
+ * - 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)
     {
@@ -1219,7 +1204,6 @@ void fill_cpu_info(void)
 #elif defined (__APPLE__)
     {
         size_t valSize;
-        unsigned long long longVal;
         int value;
         int cputype;
         char buffer[1024];
@@ -1300,9 +1284,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 +2242,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 +2319,155 @@ 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;
-
-			WARN("semi-stub: ProcessorInformation\n");
+			int i, out_cpus;
 
 			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;
-
-			for(i = 0; i < NtCurrentTeb()->Peb->NumberOfProcessors; i++) {
+#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;
+
+				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].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