Signed-off-by: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/ntdll/tests/info.c | 87 ++++++++++++++++++++++++++-
dlls/ntdll/unix/system.c | 123 +++++++++++++++++++++++++++++++++++++++
include/winnt.h | 41 +++++++++++++
include/winternl.h | 1 +
4 files changed, 251 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c
index 85c523881ef..80ad333031c 100644
--- a/dlls/ntdll/tests/info.c
+++ b/dlls/ntdll/tests/info.c
@@ -21,6 +21,7 @@
#include "ntdll_test.h"
#include <winnls.h>
#include <stdio.h>
+#include "wine/heap.h"
static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
static NTSTATUS (WINAPI * pNtSetSystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG);
@@ -46,8 +47,8 @@ static NTSTATUS (WINAPI * pNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, void
static NTSTATUS (WINAPI * pNtCreateDebugObject)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, ULONG );
static NTSTATUS (WINAPI * pNtSetInformationDebugObject)(HANDLE,DEBUGOBJECTINFOCLASS,PVOID,ULONG,ULONG*);
static NTSTATUS (WINAPI * pDbgUiConvertStateChangeStructure)(DBGUI_WAIT_STATE_CHANGE*,DEBUG_EVENT*);
-
static BOOL is_wow64;
+static const BOOL is_win64 = sizeof(void *) > sizeof(int);
/* one_before_last_pid is used to be able to compare values of a still running process
with the output of the test_query_process_times and test_query_process_handlecount tests.
@@ -1099,6 +1100,89 @@ static void test_query_logicalprocex(void)
HeapFree(GetProcessHeap(), 0, infoex_group);
}
+static void test_query_cpusetinfo(void)
+{
+ SYSTEM_CPU_SET_INFORMATION *info;
+ unsigned int i, cpu_count;
+ ULONG len, expected_len;
+ NTSTATUS status;
+ SYSTEM_INFO si;
+ HANDLE process;
+
+ if (!pNtQuerySystemInformationEx)
+ return;
+
+ GetSystemInfo(&si);
+ cpu_count = si.dwNumberOfProcessors;
+ expected_len = cpu_count * sizeof(*info);
+
+ process = GetCurrentProcess();
+
+ status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL, 0, &len);
+ if (status == STATUS_INVALID_INFO_CLASS)
+ {
+ win_skip("SystemCpuSetInformation is not supported\n");
+ return;
+ }
+
+ ok(status == STATUS_BUFFER_TOO_SMALL, "Got unexpected status %#x.\n", status);
+ ok(len == expected_len, "Got unexpected length %u.\n", len);
+
+ len = 0xdeadbeef;
+ status = pNtQuerySystemInformation(SystemCpuSetInformation, NULL, 0, &len);
+ ok(status == (is_win64 ? STATUS_INVALID_PARAMETER : STATUS_INVALID_INFO_CLASS),
+ "Got unexpected status %#x.\n", status);
+ ok(len == 0xdeadbeef, "Got unexpected len %u.\n", len);
+
+ len = 0xdeadbeef;
+ process = (HANDLE)0xdeadbeef;
+ status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL, 0, &len);
+ ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#x.\n", status);
+ ok(len == 0xdeadbeef, "Got unexpected length %u.\n", len);
+
+ len = 0xdeadbeef;
+ process = NULL;
+ status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, 4 * sizeof(process), NULL, 0, &len);
+ ok(status == (is_win64 ? STATUS_INVALID_PARAMETER : STATUS_BUFFER_TOO_SMALL),
+ "Got unexpected status %#x.\n", status);
+ ok(len == (is_win64 ? 0xdeadbeef : expected_len), "Got unexpected length %u.\n", len);
+
+ len = 0xdeadbeef;
+ status = pNtQuerySystemInformationEx(SystemCpuSetInformation, NULL, sizeof(process), NULL, 0, &len);
+ ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#x.\n", status);
+ ok(len == 0xdeadbeef, "Got unexpected length %u.\n", len);
+
+ status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL, 0, &len);
+ ok(status == STATUS_BUFFER_TOO_SMALL, "Got unexpected status %#x.\n", status);
+ ok(len == expected_len, "Got unexpected length %u.\n", len);
+
+ len = 0xdeadbeef;
+ status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL,
+ expected_len, &len);
+ ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#x.\n", status);
+ ok(len == 0xdeadbeef, "Got unexpected length %u.\n", len);
+
+ info = heap_alloc(expected_len);
+ len = 0;
+ status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), info, expected_len, &len);
+ ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status);
+ ok(len == expected_len, "Got unexpected length %u.\n", len);
+
+ for (i = 0; i < cpu_count; ++i)
+ {
+ SYSTEM_CPU_SET_INFORMATION *d = &info[i];
+
+ ok(d->Size == sizeof(*d), "Got unexpected size %u, i %u.\n", d->Size, i);
+ ok(d->Type == CpuSetInformation, "Got unexpected type %u, i %u.\n", d->Type, i);
+ ok(d->CpuSet.Id == 0x100 + i, "Got unexpected Id %#x, i %u.\n", d->CpuSet.Id, i);
+ ok(!d->CpuSet.Group, "Got unexpected Group %u, i %u.\n", d->CpuSet.Group, i);
+ ok(d->CpuSet.LogicalProcessorIndex == i, "Got unexpected LogicalProcessorIndex %u, i %u.\n",
+ d->CpuSet.LogicalProcessorIndex, i);
+ ok(!d->CpuSet.AllFlags, "Got unexpected AllFlags %#x, i %u.\n", d->CpuSet.AllFlags, i);
+ }
+ heap_free(info);
+}
+
static void test_query_firmware(void)
{
static const ULONG min_sfti_len = FIELD_OFFSET(SYSTEM_FIRMWARE_TABLE_INFORMATION, TableBuffer);
@@ -3059,6 +3143,7 @@ START_TEST(info)
test_query_regquota();
test_query_logicalproc();
test_query_logicalprocex();
+ test_query_cpusetinfo();
test_query_firmware();
test_query_data_alignment();
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c
index 6fa71ddfb91..7bd7e26b63b 100644
--- a/dlls/ntdll/unix/system.c
+++ b/dlls/ntdll/unix/system.c
@@ -170,6 +170,8 @@ struct smbios_boot_info
SYSTEM_CPU_INFORMATION cpu_info = { 0 };
+static const BOOL is_win64 = (sizeof(void *) > sizeof(int));
+
/*******************************************************************************
* Architecture specific feature detection for CPUs
*
@@ -1166,6 +1168,98 @@ static NTSTATUS create_logical_proc_info( SYSTEM_LOGICAL_PROCESSOR_INFORMATION *
}
#endif
+static NTSTATUS create_cpuset_info(SYSTEM_CPU_SET_INFORMATION *info)
+{
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *proc_info;
+ BYTE core_index, cache_index, max_cache_level;
+ unsigned int i, j, count;
+ BYTE *proc_info_buffer;
+ DWORD cpu_info_size;
+ ULONG64 cpu_mask;
+ NTSTATUS status;
+
+ count = NtCurrentTeb()->Peb->NumberOfProcessors;
+
+ cpu_info_size = 3 * sizeof(*proc_info);
+ if (!(proc_info_buffer = malloc(cpu_info_size)))
+ return STATUS_NO_MEMORY;
+
+ if ((status = create_logical_proc_info(NULL, (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX **)&proc_info_buffer,
+ &cpu_info_size, RelationAll)))
+ {
+ free(proc_info_buffer);
+ return status;
+ }
+
+ max_cache_level = 0;
+ proc_info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)proc_info_buffer;
+ for (i = 0; (BYTE *)proc_info != proc_info_buffer + cpu_info_size; ++i)
+ {
+ if (proc_info->Relationship == RelationCache)
+ {
+ if (max_cache_level < proc_info->u.Cache.Level)
+ max_cache_level = proc_info->u.Cache.Level;
+ }
+ proc_info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)((BYTE *)proc_info + proc_info->Size);
+ }
+
+ memset(info, 0, count * sizeof(*info));
+
+ core_index = 0;
+ cache_index = 0;
+ proc_info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)proc_info_buffer;
+ for (i = 0; i < count; ++i)
+ {
+ info[i].Size = sizeof(*info);
+ info[i].Type = CpuSetInformation;
+ info[i].u.CpuSet.Id = 0x100 + i;
+ info[i].u.CpuSet.LogicalProcessorIndex = i;
+ }
+
+ for (i = 0; (BYTE *)proc_info != (BYTE *)proc_info_buffer + cpu_info_size; ++i)
+ {
+ if (proc_info->Relationship == RelationProcessorCore)
+ {
+ if (proc_info->u.Processor.GroupCount != 1)
+ {
+ FIXME("Unsupported group count %u.\n", proc_info->u.Processor.GroupCount);
+ continue;
+ }
+ cpu_mask = proc_info->u.Processor.GroupMask[0].Mask;
+ for (j = 0; j < count; ++j)
+ if (((ULONG64)1 << j) & cpu_mask)
+ {
+ info[j].u.CpuSet.CoreIndex = core_index;
+ info[j].u.CpuSet.EfficiencyClass = proc_info->u.Processor.EfficiencyClass;
+ }
+ ++core_index;
+ }
+ else if (proc_info->Relationship == RelationCache)
+ {
+ if (proc_info->u.Cache.Level == max_cache_level)
+ {
+ cpu_mask = proc_info->u.Cache.GroupMask.Mask;
+ for (j = 0; j < count; ++j)
+ if (((ULONG64)1 << j) & cpu_mask)
+ info[j].u.CpuSet.LastLevelCacheIndex = cache_index;
+ }
+ ++cache_index;
+ }
+ else if (proc_info->Relationship == RelationNumaNode)
+ {
+ cpu_mask = proc_info->u.NumaNode.GroupMask.Mask;
+ for (j = 0; j < count; ++j)
+ if (((ULONG64)1 << j) & cpu_mask)
+ info[j].u.CpuSet.NumaNodeIndex = proc_info->u.NumaNode.NodeNumber;
+ }
+ proc_info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)((BYTE *)proc_info + proc_info->Size);
+ }
+
+ free(proc_info_buffer);
+
+ return STATUS_SUCCESS;
+}
+
#ifdef linux
static void copy_smbios_string( char **buffer, char *s, size_t len )
@@ -2651,6 +2745,10 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class,
break;
}
+ case SystemCpuSetInformation:
+ return is_win64 ? NtQuerySystemInformationEx(class, NULL, 0, info, size, ret_size)
+ : STATUS_INVALID_INFO_CLASS;
+
case SystemRecommendedSharedDataAlignment:
{
len = sizeof(DWORD);
@@ -2773,6 +2871,31 @@ NTSTATUS WINAPI NtQuerySystemInformationEx( SYSTEM_INFORMATION_CLASS class,
break;
}
+ case SystemCpuSetInformation:
+ {
+ unsigned int cpu_count = NtCurrentTeb()->Peb->NumberOfProcessors;
+ PROCESS_BASIC_INFORMATION pbi;
+ HANDLE process;
+
+ if (!query || query_len < sizeof(HANDLE) || (is_win64 && query_len != sizeof(HANDLE)))
+ return STATUS_INVALID_PARAMETER;
+
+ process = *(HANDLE *)query;
+ if (process && (ret = NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL)))
+ return ret;
+
+ if (size < (len = cpu_count * sizeof(SYSTEM_CPU_SET_INFORMATION)))
+ {
+ ret = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+ if (!info)
+ return STATUS_ACCESS_VIOLATION;
+
+ if ((ret = create_cpuset_info(info)))
+ return ret;
+ break;
+ }
default:
FIXME( "(0x%08x,%p,%u,%p,%u,%p) stub\n", class, query, query_len, info, size, ret_size );
break;
diff --git a/include/winnt.h b/include/winnt.h
index 0a6027118fe..840966f657b 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -6726,6 +6726,47 @@ typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX
} DUMMYUNIONNAME;
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
+typedef enum _CPU_SET_INFORMATION_TYPE
+{
+ CpuSetInformation,
+} CPU_SET_INFORMATION_TYPE, *PCPU_SET_INFORMATION_TYPE;
+
+typedef struct _SYSTEM_CPU_SET_INFORMATION
+{
+ DWORD Size;
+ CPU_SET_INFORMATION_TYPE Type;
+ union
+ {
+ struct
+ {
+ DWORD Id;
+ WORD Group;
+ BYTE LogicalProcessorIndex;
+ BYTE CoreIndex;
+ BYTE LastLevelCacheIndex;
+ BYTE NumaNodeIndex;
+ BYTE EfficiencyClass;
+ union
+ {
+ BYTE AllFlags;
+ struct
+ {
+ BYTE Parked : 1;
+ BYTE Allocated : 1;
+ BYTE AllocatedToTargetProcess : 1;
+ BYTE RealTime : 1;
+ BYTE ReservedFlags : 4;
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME2;
+ union {
+ DWORD Reserved;
+ BYTE SchedulingClass;
+ };
+ DWORD64 AllocationTag;
+ } CpuSet;
+ } DUMMYUNIONNAME;
+} SYSTEM_CPU_SET_INFORMATION, *PSYSTEM_CPU_SET_INFORMATION;
+
/* Threadpool things */
typedef DWORD TP_VERSION,*PTP_VERSION;
diff --git a/include/winternl.h b/include/winternl.h
index fcdedaec8aa..c8fb7031d91 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1551,6 +1551,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS {
SystemFileCacheInformationEx = 81,
SystemDynamicTimeZoneInformation = 102,
SystemLogicalProcessorInformationEx = 107,
+ SystemCpuSetInformation = 175,
SystemHypervisorSharedPageInformation = 197,
SystemInformationClassMax
} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;
--
2.30.2