[PATCH] kernel32: implement GetLogicalProcessorInformation (try 2)

Claudio Fontana claudio.fontana at gmail.com
Fri Nov 25 08:58:00 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 fallback is also triggered when the OS-specific APIs fail.

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         |  343 ++++++++++++++++++++++++++++++++++++++-
 dlls/kernel32/tests/Makefile.in |    1 +
 dlls/kernel32/tests/glpi.c      |   92 +++++++++++
 3 files changed, 433 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..4806c75 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,345 @@ 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.
+ */
+
+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 = 0x01;
+    buffer[1].ProcessorMask = 0x01;
+    buffer[2].ProcessorMask = 0x01;
+    buffer[3].ProcessorMask = 0x01;
+    buffer[4].ProcessorMask = 0x01;
+
+    buffer[0].Relationship = RelationProcessorCore;
+    buffer[1].Relationship = RelationNumaNode;
+    buffer[2].Relationship = RelationCache;
+    buffer[3].Relationship = RelationCache;
+    buffer[4].Relationship = RelationProcessorPackage;
+
+    U(buffer[0]).ProcessorCore.Flags = 0x00;
+    U(buffer[1]).NumaNode.NodeNumber = 0x00;
+
+    U(buffer[2]).Cache.Level = 0x01;
+    U(buffer[2]).Cache.Associativity = 0x08;
+    U(buffer[2]).Cache.LineSize = 64;
+    U(buffer[2]).Cache.Size = 16 << 10; /* 16 KB */
+    U(buffer[2]).Cache.Type = CacheData;
+
+    U(buffer[3]).Cache.Level = 0x02;
+    U(buffer[3]).Cache.Associativity = 0x08;
+    U(buffer[3]).Cache.LineSize = 64;
+    U(buffer[3]).Cache.Size = 1024 << 10; /* 1024 KB */
+    U(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 63
+#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);
+                U(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 */
+            U(cores[n_cores]).Reserved[0] = core_id;
+
+            /* look for this core_id in previous elements */
+            for (i = 0; i < n_cores; i++)
+                if (U(cores[i]).Reserved[0] == U(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 = 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)
+                        U(caches[n_caches]).Cache.Level = 0x01;
+                    else
+                        U(caches[n_caches]).Cache.Level = 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)
+                        U(caches[n_caches]).Cache.Associativity = 0x08;
+                    else
+                        U(caches[n_caches]).Cache.Associativity = 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)
+                        U(caches[n_caches]).Cache.LineSize = 64;
+                    else
+                        U(caches[n_caches]).Cache.LineSize = 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)
+                        U(caches[n_caches]).Cache.Size = 16 << 10;
+                    else
+                        U(caches[n_caches]).Cache.Size = 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))
+                        U(caches[n_caches]).Cache.Type = CacheData;
+                    else switch (string[0])
+                    {
+                    case 'D':
+                        U(caches[n_caches]).Cache.Type = CacheData; break;
+                    case 'I':
+                        U(caches[n_caches]).Cache.Type = CacheInstruction; break;
+                    case 'U': default:
+                        U(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;
+            U(numa[0]).NumaNode.NodeNumber = 0x00;
+            package[0].Relationship = RelationProcessorPackage;
+
+            for (i = 0; i < n_cores; i++)
+            {
+                U(cores[i]).Reserved[0] = 0;
+                U(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_INVALID_PARAMETER);
+        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..96eeffc
--- /dev/null
+++ b/dlls/kernel32/tests/glpi.c
@@ -0,0 +1,92 @@
+#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 BOOL (WINAPI *pGetLogicalProcessorInformation)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION b, PDWORD plen);
+
+static void test_glpi(void)
+{
+    SYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer[200];
+    DWORD buflen = sizeof(buffer);
+    BOOL ret; int i, n;
+
+    if (pGetLogicalProcessorInformation == NULL)
+    {
+        /* only exists on XP SP3 and later */
+        win_skip("GetLogicalProcessorInformation not found.\n");
+        return;
+    }
+
+    ret = pGetLogicalProcessorInformation(NULL, &buflen);
+    ok(!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER,
+       "Passing NULL as buffer should not be ok.\n");
+
+    ret = pGetLogicalProcessorInformation(buffer, NULL);
+    ok(!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER,
+       "Passing NULL as pbuflen should not be ok.\n");
+
+    ret = pGetLogicalProcessorInformation(buffer, &buflen);
+    ok(ret, "Normal glpi call (%d)\n", GetLastError());
+
+    ret = pGetLogicalProcessorInformation(buffer, &buflen);
+    ok(ret, "glpi call with the resulting buflen (%d)\n", buflen);
+
+    buflen--;
+
+    ret = pGetLogicalProcessorInformation(buffer, &buflen);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "glpi call with insufficient buflen %d lasterr %d\n", 
+       buflen, GetLastError());
+
+    ret = pGetLogicalProcessorInformation(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);
+
+    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, U(buffer[i]).ProcessorCore.Flags);
+            break;
+        case RelationNumaNode:
+            printf("NumaNode      idx=%03d mask=%lxh noden=%u\n",
+                    i, buffer[i].ProcessorMask, U(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,
+                   U(buffer[i]).Cache.Level, U(buffer[i]).Cache.Associativity,
+                   U(buffer[i]).Cache.LineSize, U(buffer[i]).Cache.Size,
+                   U(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);
+        }
+    }
+}
+
+START_TEST(glpi)
+{
+    HINSTANCE hdll;
+    hdll = GetModuleHandleA("kernel32.dll");
+
+    pGetLogicalProcessorInformation = (void *)GetProcAddress(hdll, "GetLogicalProcessorInformation");
+
+    test_glpi();
+}
-- 
1.6.4




More information about the wine-patches mailing list