[PATCH 1/2] kernel32: implement GetLogicalProcessorInformation

Claudio Fontana claudio.fontana at gmail.com
Thu Nov 24 13:34:29 CST 2011


First implementation of GetLogicalProcessorInformation.
Limitations: all logical processors are added to the same NUMA set,
and all cores are added to the same package.
Only the linux-specific helper function is implemented, so for now
everybody else gets the fallback hard-coded results only.

The linux-specific function is based on sysfs.

The new OS-specific functions can be easily added as additional
ifdefs by following the linux example.
---
 dlls/kernel32/process.c         |  350 ++++++++++++++++++++++++++++++++++++++-
 dlls/kernel32/tests/Makefile.in |    1 +
 dlls/kernel32/tests/glpi.c      |   82 +++++++++
 3 files changed, 430 insertions(+), 3 deletions(-)
 create mode 100644 dlls/kernel32/tests/glpi.c

diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c
index b6c0b9d..de39244 100644
--- a/dlls/kernel32/process.c
+++ b/dlls/kernel32/process.c
@@ -51,6 +51,12 @@
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winnt.h"
+
 #include "winternl.h"
 #include "kernel_private.h"
 #include "psapi.h"
@@ -3705,14 +3711,352 @@ HANDLE WINAPI GetCurrentProcess(void)
     return (HANDLE)~(ULONG_PTR)0;
 }
 
+/***************
+ * glpi_impl_fb:
+ *   a fallback implementation of GetLogicalProcessorInformation
+ *   for the systems we do not support yet,
+ *   or for when the OS-specific API fails.
+ *   Same arguments and return value as glpi.
+ */
+
+#undef SLPI
+#ifdef NONAMELESSUNION
+#define SLPI(buffer, idx) buffer[idx].DUMMYUNIONNAME
+#else
+#define SLPI(buffer, idx) buffer[idx]
+#endif /* NONAMELESSUNION */
+
+static BOOL
+glpi_impl_fb(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer, PDWORD pbuflen)
+{
+    int glpi_n = 5;
+    FIXME("(%p,%p): reporting hard coded information\n", buffer, pbuflen);
+
+    if (*pbuflen < sizeof(*buffer) * glpi_n)
+    {
+	SetLastError(ERROR_INSUFFICIENT_BUFFER);
+	*pbuflen = sizeof(*buffer) * glpi_n;
+	return FALSE;
+    }
+
+    buffer[0].ProcessorMask = (ULONG_PTR)0x01;
+    buffer[1].ProcessorMask = (ULONG_PTR)0x01;
+    buffer[2].ProcessorMask = (ULONG_PTR)0x01;
+    buffer[3].ProcessorMask = (ULONG_PTR)0x01;
+    buffer[4].ProcessorMask = (ULONG_PTR)0x01;
+
+    buffer[0].Relationship = RelationProcessorCore;
+    buffer[1].Relationship = RelationNumaNode;
+    buffer[2].Relationship = RelationCache;
+    buffer[3].Relationship = RelationCache;
+    buffer[4].Relationship = RelationProcessorPackage;
+
+    SLPI(buffer, 0).ProcessorCore.Flags = (BYTE)0x00;
+    SLPI(buffer, 1).NumaNode.NodeNumber = (DWORD)0x00;
+
+    SLPI(buffer, 2).Cache.Level = (BYTE)0x01;
+    SLPI(buffer, 2).Cache.Associativity = (BYTE)0x08;
+    SLPI(buffer, 2).Cache.LineSize = (WORD)64;
+    SLPI(buffer, 2).Cache.Size = (DWORD)16 << 10; /* 16 KB */
+    SLPI(buffer, 2).Cache.Type = CacheData;
+
+    SLPI(buffer, 3).Cache.Level = (BYTE)0x02;
+    SLPI(buffer, 3).Cache.Associativity = (BYTE)0x08;
+    SLPI(buffer, 3).Cache.LineSize = (WORD)64;
+    SLPI(buffer, 3).Cache.Size = (DWORD)1024 << 10; /* 1024 KB */
+    SLPI(buffer, 3).Cache.Type = CacheUnified;
+    /* no additional info for processor package at buffer[4] needed. */
+
+    *pbuflen = sizeof(*buffer) * glpi_n;
+    return TRUE;
+}
+
+#ifdef linux
+/******************
+ * glpi_impl_linux:
+ *   an implementation of GetLogicalProcessorInformation
+ *   for linux sysfs.
+ * Returns: -1 on API failure (trigger fallback),
+ *          otherwise see glpi.
+ */
+
+#define SYS_CPU "/sys/devices/system/cpu/cpu"
+#define SYS_CPU_MAX 63
+#define SYS_CPU_COREID "/topology/core_id"
+#define SYS_CPU_PACKAGEID "/topology/physical_package_id"
+#define SYS_CPU_CACHE "/cache/index"
+#define SYS_CPU_CACHE_MAX 12
+#define SYS_CPU_CACHE_LEVEL "/level"
+#define SYS_CPU_CACHE_SIZE "/size"
+#define SYS_CPU_CACHE_TYPE "/type"
+#define SYS_CPU_CACHE_LINE "/coherency_line_size"
+#define SYS_CPU_CACHE_ASSOC "/ways_of_associativity"
+
+static int glpi_impl_linux(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer,
+			   PDWORD pbuflen)
+{
+    /* remember the glpi results, so that subsequent calls are faster */
+    static int n_cores = 0;
+    static int n_caches = 0;
+    static PSYSTEM_LOGICAL_PROCESSOR_INFORMATION cores = NULL;
+    static PSYSTEM_LOGICAL_PROCESSOR_INFORMATION caches = NULL;
+    static SYSTEM_LOGICAL_PROCESSOR_INFORMATION numa[1];
+    static SYSTEM_LOGICAL_PROCESSOR_INFORMATION package[1];
+
+    if (!n_cores)
+    {
+	int n_cpus = 0; int cpu_i;
+	char path[PATH_MAX];
+
+	TRACE("building new cached result.\n");
+
+	for (n_cpus = 0; n_cpus < SYS_CPU_MAX; n_cpus++)
+	{
+	    snprintf(path, sizeof(path), SYS_CPU "%d", n_cpus);
+	    if (access(path, R_OK | X_OK) != 0)
+		break;
+	}
+
+	TRACE("detected %d logical cpus.\n", n_cpus);
+	if (n_cpus < 1)
+	{
+	    TRACE("requesting fallback implementation.\n");
+	    return -1;
+	}
+
+	cores = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cores) * n_cpus);
+	caches = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*caches) * 1);
+
+	for (cpu_i = 0; cpu_i < n_cpus; cpu_i++)
+	{
+	    FILE *f; int core_id; int i;
+	    snprintf(path, sizeof(path), SYS_CPU "%d" SYS_CPU_COREID, cpu_i);
+	    cores[n_cores].ProcessorMask = (ULONG_PTR)0x01 << cpu_i;
+	    cores[n_cores].Relationship = RelationProcessorCore;
+
+	    if (!(f = fopen(path, "r")) || fscanf(f, "%d", &core_id) != 1)
+	    {
+		/* topology info not available, provide default cpuid=cpu_i */
+		ERR("could not read cpu%d topology.\n", cpu_i);
+		SLPI(cores, n_cores).Reserved[0] = cpu_i;
+		n_cores++;
+		if (f)
+		    fclose(f);
+		continue; /* next cpu */
+	    }
+
+	    /* normal sane case */
+	    fclose(f);
+	    /* store the core_id to catch cpus with same core_id */
+	    SLPI(cores, n_cores).Reserved[0] = core_id;
+
+	    /* look for this core_id in previous elements */
+	    for (i = 0; i < n_cores; i++)
+		if (SLPI(cores, i).Reserved[0] == SLPI(cores, n_cores).Reserved[0])
+		    break;
+
+	    if (i < n_cores)
+	    {
+		TRACE("found existing coreid %d for cpu%d.\n", core_id, cpu_i);
+		/* add this logical processor to the existing mask */
+		cores[i].ProcessorMask |= cores[n_cores].ProcessorMask;
+		/* do not advance n_cores: this element can be overwritten. */
+	    }
+	    else
+	    {
+		TRACE("found new coreid %d for cpu%d.\n", core_id, cpu_i);
+
+		/* look for caches */
+		for (i = 0; i < SYS_CPU_CACHE_MAX; i++)
+		{
+		    unsigned long value;
+		    char string[80];
+
+		    snprintf(path, sizeof(path), SYS_CPU "%d" SYS_CPU_CACHE "%d",
+			     cpu_i, i);
+		    if (access(path, R_OK | X_OK) != 0)
+			break; /* no more caches found. */
+
+		    caches = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+					 caches, sizeof(*caches) * (n_caches + 1));
+
+		    /* we keep the index into the cores array to set
+		       the right value at the end. */
+		    caches[n_caches].ProcessorMask = (ULONG_PTR)n_cores;
+		    caches[n_caches].Relationship = RelationCache;
+
+		    snprintf(path, sizeof(path), SYS_CPU "%d" SYS_CPU_CACHE "%d"
+			     SYS_CPU_CACHE_LEVEL, cpu_i, i);
+		    if (!(f = fopen(path, "r")) || fscanf(f, "%lu", &value) != 1)
+			SLPI(caches, n_caches).Cache.Level = (BYTE)0x01;
+		    else
+			SLPI(caches, n_caches).Cache.Level = (BYTE)value;
+		    if (f)
+			fclose(f);
+
+		    snprintf(path, sizeof(path), SYS_CPU "%d" SYS_CPU_CACHE "%d"
+			     SYS_CPU_CACHE_ASSOC, cpu_i, i);
+		    if (!(f = fopen(path, "r")) || fscanf(f, "%lu", &value) != 1)
+			SLPI(caches, n_caches).Cache.Associativity = 0x08;
+		    else
+			SLPI(caches, n_caches).Cache.Associativity = (BYTE)value;
+		    if (f)
+			fclose(f);
+
+		    snprintf(path, sizeof(path), SYS_CPU "%d" SYS_CPU_CACHE "%d"
+			     SYS_CPU_CACHE_LINE, cpu_i, i);
+		    if (!(f = fopen(path, "r")) || fscanf(f, "%lu", &value) != 1)
+			SLPI(caches, n_caches).Cache.LineSize = (WORD)64;
+		    else
+			SLPI(caches, n_caches).Cache.LineSize = (WORD)value;
+		    if (f)
+			fclose(f);
+
+		    snprintf(path, sizeof(path), SYS_CPU "%d" SYS_CPU_CACHE "%d"
+			     SYS_CPU_CACHE_SIZE, cpu_i, i);
+		    if (!(f = fopen(path, "r")) || fscanf(f, "%lu", &value) != 1)
+			SLPI(caches, n_caches).Cache.Size = (DWORD)16 << 10;
+		    else
+			SLPI(caches, n_caches).Cache.Size = (DWORD)value << 10;
+		    if (f)
+			fclose(f);
+
+		    snprintf(path, sizeof(path), SYS_CPU "%d" SYS_CPU_CACHE "%d"
+			     SYS_CPU_CACHE_TYPE, cpu_i, i);
+		    if (!(f = fopen(path, "r")) || !fgets(string, sizeof(string), f))
+			SLPI(caches, n_caches).Cache.Type = CacheData;
+		    else switch (string[0])
+                    {
+		    case 'D':
+			SLPI(caches, n_caches).Cache.Type = CacheData; break;
+		    case 'I':
+			SLPI(caches, n_caches).Cache.Type = CacheInstruction; break;
+		    case 'U': default:
+			SLPI(caches, n_caches).Cache.Type =
+			    string[2] == 'i' ? CacheUnified : CacheTrace; break;
+		    }
+
+		    if (f)
+			fclose(f);
+
+		    n_caches++;
+		}
+		n_cores++;
+		TRACE("detected %d caches for coreid %d\n", i, core_id);
+	    }
+	}
+
+	if (n_caches < 1)
+	{
+	    HeapFree(GetProcessHeap(), 0, caches);
+	    caches = NULL;
+	}
+
+	/* trim leftovers */
+	cores = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+			    cores, sizeof(*cores) * n_cores);
+
+	if (1) /* final cleanup work */
+	{
+	    int i;
+	    /* set the real processor mask in the caches. */
+	    for (i = 0; i < n_caches; i++)
+	    {
+		int cores_i;
+		cores_i = caches[i].ProcessorMask;
+		caches[i].ProcessorMask = cores[cores_i].ProcessorMask;
+	    }
+
+	    /* remove the reserved values from the cores, set flags,
+	       build numa and package. */
+
+	    numa[0].ProcessorMask = 0x00;
+	    numa[0].Relationship = RelationNumaNode;
+	    SLPI(numa, 0).NumaNode.NodeNumber = 0x00;
+	    package[0].Relationship = RelationProcessorPackage;
+
+	    for (i = 0; i < n_cores; i++)
+	    {
+		SLPI(cores, i).Reserved[0] = 0;
+		SLPI(cores, i).ProcessorCore.Flags = 0x00; /* ?unclear */
+		numa[0].ProcessorMask |= cores[i].ProcessorMask;
+	    }
+
+	    package[0].ProcessorMask = numa[0].ProcessorMask;
+	}
+    }
+
+    TRACE("getting cached result.\n");
+
+    if (1) /* final check for required size, final result */
+    {
+	DWORD required; required = sizeof(*cores) * (n_cores + n_caches + 2);
+	if (*pbuflen < required)
+	{
+	    TRACE("return with failure (ERROR_INSUFFICIENT_BUFFER).\n");
+	    SetLastError(ERROR_INSUFFICIENT_BUFFER);
+	    *pbuflen = required;
+	    return 0;
+	}
+
+	*pbuflen = required;
+
+	/* beware: buffer is SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer; */
+	memcpy(buffer, cores, sizeof(*cores) * n_cores);
+	buffer += n_cores;
+
+	if (n_caches > 0)
+	{
+	    memcpy(buffer, caches, sizeof(*caches) * n_caches);
+	    buffer += n_caches;
+	}
+
+	*buffer++ = package[0];
+	*buffer++ = numa[0];
+    }
+
+    TRACE("return success!\n");
+    return 1;
+}
+
+#undef SYS_CPU
+#undef SYS_CPU_MAX
+#undef SYS_CPU_COREID
+#undef SYS_CPU_PACKAGEID
+#undef SYS_CPU_CACHE
+#undef SYS_CPU_CACHE_MAX
+#undef SYS_CPU_CACHE_LEVEL
+#undef SYS_CPU_CACHE_SIZE
+#undef SYS_CPU_CACHE_TYPE
+#undef SYS_CPU_CACHE_LINE
+#undef SYS_CPU_CACHE_ASSOC
+
+#endif /* linux */
+
 /***********************************************************************
  *           GetLogicalProcessorInformation     (KERNEL32.@)
  */
 BOOL WINAPI GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer, PDWORD pBufLen)
 {
-    FIXME("(%p,%p): stub\n", buffer, pBufLen);
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    int rv;
+
+    if (pBufLen == NULL || buffer == NULL)
+    {
+	SetLastError(ERROR_BAD_ARGUMENTS);
+	return FALSE;
+    }
+
+#if defined linux
+    rv = glpi_impl_linux(buffer, pBufLen);
+#else /* no supported OS found */
+    rv = -1;
+#endif
+
+    /* fallback hard-coded result */
+    if (rv < 0)
+	rv = glpi_impl_fb(buffer, pBufLen);
+
+    return rv;
 }
 
 /***********************************************************************
diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in
index dce27db..1505944 100644
--- a/dlls/kernel32/tests/Makefile.in
+++ b/dlls/kernel32/tests/Makefile.in
@@ -17,6 +17,7 @@ C_SRCS = \
 	file.c \
 	format_msg.c \
 	generated.c \
+	glpi.c \
 	heap.c \
 	loader.c \
 	locale.c \
diff --git a/dlls/kernel32/tests/glpi.c b/dlls/kernel32/tests/glpi.c
new file mode 100644
index 0000000..a81d1d5
--- /dev/null
+++ b/dlls/kernel32/tests/glpi.c
@@ -0,0 +1,82 @@
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+static void test_glpi(void)
+{
+    SYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer[200];
+    DWORD buflen = sizeof(buffer);
+    BOOL ret; int i, n;
+
+    ret = GetLogicalProcessorInformation(NULL, &buflen);
+    ok(!ret, "Passing NULL as buffer should not be ok.\n");
+
+    ret = GetLogicalProcessorInformation(buffer, NULL);
+    ok(!ret, "Passing NULL as pbuflen should not be ok.\n");
+
+    ret = GetLogicalProcessorInformation(buffer, &buflen);
+    ok(ret, "Normal glpi call (%d)\n", GetLastError());
+
+    ret = GetLogicalProcessorInformation(buffer, &buflen);
+    ok(ret, "glpi call with the resulting buflen (%d)\n", buflen);
+
+    buflen--;
+
+    ret = GetLogicalProcessorInformation(buffer, &buflen);
+    ok(!ret, "glpi call with insufficient buflen (%d)\n", buflen);
+
+    ret = GetLogicalProcessorInformation(buffer, &buflen);
+    ok(ret, "glpi call with resulting buflen (%d)\n", buflen);
+
+    n = buflen / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
+
+    printf("glpi result: %d array elements.\n", n);
+
+#ifdef NONAMELESSUNION
+#define BUFFER_I buffer[i].u
+#else
+#define BUFFER_I buffer[i]
+#endif /* NONAMELESSUNION */
+
+    for (i = 0; i < n; i++)
+    {
+	switch (buffer[i].Relationship)
+	{
+	case RelationProcessorCore:
+	    printf("ProcessorCore idx=%03d mask=%lxh flags=%u\n",
+		   i, buffer[i].ProcessorMask, BUFFER_I.ProcessorCore.Flags);
+	    break;
+	case RelationNumaNode:
+	    printf("NumaNode      idx=%03d mask=%lxh noden=%u\n",
+		    i, buffer[i].ProcessorMask, BUFFER_I.NumaNode.NodeNumber);
+	    break;
+	case RelationCache:
+	    printf("Cache         idx=%03d mask=%lxh "
+		    "l=%u, a=%u, ls=%u, sz=%u, ty=%u\n",
+		   i, buffer[i].ProcessorMask,
+		   BUFFER_I.Cache.Level, BUFFER_I.Cache.Associativity,
+		   BUFFER_I.Cache.LineSize, BUFFER_I.Cache.Size,
+		   BUFFER_I.Cache.Type);
+	    break;
+	case RelationProcessorPackage:
+	    printf("ProcessorPack idx=%03d mask=%lxh\n",
+		   i, buffer[i].ProcessorMask);
+	    break;
+	default:
+	    ok(0, "invalid relationship %d", buffer[i].Relationship);
+	}
+    }
+#undef BUFFER_I
+}
+
+START_TEST(glpi)
+{
+    test_glpi();
+}
+
-- 
1.6.4




More information about the wine-patches mailing list