VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/mp-win.cpp@ 95766

Last change on this file since 95766 was 95766, checked in by vboxsync, 22 months ago

IPRT/mp-win.cpp: Must include iprt/alloca.h to use it. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 30.5 KB
Line 
1/* $Id: mp-win.cpp 95766 2022-07-20 19:37:51Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_SYSTEM
32#include <iprt/win/windows.h>
33
34#include <iprt/mp.h>
35#include "internal/iprt.h"
36
37#include <iprt/alloca.h>
38#include <iprt/assert.h>
39#include <iprt/cpuset.h>
40#include <iprt/ldr.h>
41#include <iprt/mem.h>
42#include <iprt/once.h>
43#include <iprt/param.h>
44#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
45# include <iprt/asm-amd64-x86.h>
46#endif
47#if defined(VBOX) && !defined(IN_GUEST) && !defined(IN_RT_STATIC)
48# include <VBox/sup.h>
49# define IPRT_WITH_GIP_MP_INFO
50#else
51# undef IPRT_WITH_GIP_MP_INFO
52#endif
53
54#include "internal-r3-win.h"
55
56
57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
60/** @def RTMPWIN_UPDATE_GIP_GLOBALS
61 * Does lazy (re-)initialization using information provieded by GIP. */
62#ifdef IPRT_WITH_GIP_MP_INFO
63# define RTMPWIN_UPDATE_GIP_GLOBALS() \
64 do { RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP(); } while (0)
65#else
66# define RTMPWIN_UPDATE_GIP_GLOBALS() do { } while (0)
67#endif
68
69/** @def RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP
70 * Does lazy (re-)initialization using information provieded by GIP and
71 * declare and initalize a pGip local variable. */
72#ifdef IPRT_WITH_GIP_MP_INFO
73#define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() \
74 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage; \
75 if (pGip) \
76 { \
77 if ( pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC \
78 && RTOnce(&g_MpInitOnceGip, rtMpWinInitOnceGip, NULL) == VINF_SUCCESS) \
79 { \
80 if (g_cRtMpWinActiveCpus >= pGip->cOnlineCpus) \
81 { /* likely */ } \
82 else \
83 rtMpWinRefreshGip(); \
84 } \
85 else \
86 pGip = NULL; \
87 } else do { } while (0)
88#else
89# define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() do { } while (0)
90#endif
91
92
93/*********************************************************************************************************************************
94* Global Variables *
95*********************************************************************************************************************************/
96/** Initialize once. */
97static RTONCE g_MpInitOnce = RTONCE_INITIALIZER;
98#ifdef IPRT_WITH_GIP_MP_INFO
99/** Initialize once using GIP. */
100static RTONCE g_MpInitOnceGip = RTONCE_INITIALIZER;
101#endif
102
103static decltype(GetMaximumProcessorCount) *g_pfnGetMaximumProcessorCount;
104//static decltype(GetActiveProcessorCount) *g_pfnGetActiveProcessorCount;
105static decltype(GetCurrentProcessorNumber) *g_pfnGetCurrentProcessorNumber;
106static decltype(GetCurrentProcessorNumberEx) *g_pfnGetCurrentProcessorNumberEx;
107static decltype(GetLogicalProcessorInformation) *g_pfnGetLogicalProcessorInformation;
108static decltype(GetLogicalProcessorInformationEx) *g_pfnGetLogicalProcessorInformationEx;
109
110
111/** The required buffer size for getting group relations. */
112static uint32_t g_cbRtMpWinGrpRelBuf;
113/** The max number of CPUs (RTMpGetCount). */
114static uint32_t g_cRtMpWinMaxCpus;
115/** The max number of CPU cores (RTMpGetCoreCount). */
116static uint32_t g_cRtMpWinMaxCpuCores;
117/** The max number of groups. */
118static uint32_t g_cRtMpWinMaxCpuGroups;
119/** The number of active CPUs the last time we checked. */
120static uint32_t volatile g_cRtMpWinActiveCpus;
121/** Static per group info.
122 * @remarks With 256 entries this takes up 33KB.
123 * @sa g_aRtMpNtCpuGroups */
124static struct
125{
126 /** The max CPUs in the group. */
127 uint16_t cMaxCpus;
128 /** The number of active CPUs at the time of initialization. */
129 uint16_t cActiveCpus;
130 /** CPU set indexes for each CPU in the group. */
131 int16_t aidxCpuSetMembers[64];
132} g_aRtMpWinCpuGroups[256];
133/** Maps CPU set indexes to RTCPUID.
134 * @sa g_aidRtMpNtByCpuSetIdx */
135RTCPUID g_aidRtMpWinByCpuSetIdx[RTCPUSET_MAX_CPUS];
136
137
138/**
139 * @callback_method_impl{FNRTONCE,
140 * Resolves dynamic imports and initializes globals.}
141 */
142static DECLCALLBACK(int32_t) rtMpWinInitOnce(void *pvUser)
143{
144 RT_NOREF(pvUser);
145
146 Assert(g_WinOsInfoEx.dwOSVersionInfoSize != 0);
147 Assert(g_hModKernel32 != NULL);
148
149 /*
150 * Resolve dynamic APIs.
151 */
152#define RESOLVE_API(a_szMod, a_FnName) \
153 do { \
154 RT_CONCAT(g_pfn,a_FnName) = (decltype(a_FnName) *)GetProcAddress(g_hModKernel32, #a_FnName); \
155 } while (0)
156 RESOLVE_API("kernel32.dll", GetMaximumProcessorCount);
157 //RESOLVE_API("kernel32.dll", GetActiveProcessorCount); - slow :/
158 RESOLVE_API("kernel32.dll", GetCurrentProcessorNumber);
159 RESOLVE_API("kernel32.dll", GetCurrentProcessorNumberEx);
160 RESOLVE_API("kernel32.dll", GetLogicalProcessorInformation);
161 RESOLVE_API("kernel32.dll", GetLogicalProcessorInformationEx);
162
163 /*
164 * Reset globals.
165 */
166 for (unsigned i = 0; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++)
167 g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID;
168 for (unsigned idxGroup = 0; idxGroup < RT_ELEMENTS(g_aRtMpWinCpuGroups); idxGroup++)
169 {
170 g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = 0;
171 g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0;
172 for (unsigned idxMember = 0; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers); idxMember++)
173 g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1;
174 }
175
176 /*
177 * Query group information, partitioning CPU IDs and CPU set indexes.
178 *
179 * We ASSUME the GroupInfo index is the same as the group number.
180 *
181 * We CANNOT ASSUME that the kernel CPU indexes are assigned in any given
182 * way, though they usually are in group order by active processor. So,
183 * we do that to avoid trouble. We must use information provided thru GIP
184 * if we want the kernel CPU set indexes. Even there, the inactive CPUs
185 * wont have sensible indexes. Sigh.
186 *
187 * We try to assign IDs to inactive CPUs in the same manner as mp-r0drv-nt.cpp
188 *
189 * Note! We will die (AssertFatal) if there are too many processors!
190 */
191 union
192 {
193 SYSTEM_INFO SysInfo;
194 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Info;
195 uint8_t abPaddingG[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
196 + sizeof(PROCESSOR_GROUP_INFO) * 256];
197 uint8_t abPaddingC[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
198 + (sizeof(PROCESSOR_RELATIONSHIP) + sizeof(GROUP_AFFINITY))
199 * RTCPUSET_MAX_CPUS];
200 } uBuf;
201 if (g_pfnGetLogicalProcessorInformationEx)
202 {
203 /* Query the information. */
204 DWORD cbData = sizeof(uBuf);
205 AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, &uBuf.Info, &cbData) != FALSE,
206 ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf)));
207 AssertFatalMsg(uBuf.Info.Relationship == RelationGroup,
208 ("Relationship = %u, expected %u!\n", uBuf.Info.Relationship, RelationGroup));
209 AssertFatalMsg(uBuf.Info.Group.MaximumGroupCount <= RT_ELEMENTS(g_aRtMpWinCpuGroups),
210 ("MaximumGroupCount is %u, we only support up to %u!\n",
211 uBuf.Info.Group.MaximumGroupCount, RT_ELEMENTS(g_aRtMpWinCpuGroups)));
212
213 AssertMsg(uBuf.Info.Group.MaximumGroupCount == uBuf.Info.Group.ActiveGroupCount, /* 2nd assumption mentioned above. */
214 ("%u vs %u\n", uBuf.Info.Group.MaximumGroupCount, uBuf.Info.Group.ActiveGroupCount));
215 AssertFatal(uBuf.Info.Group.MaximumGroupCount >= uBuf.Info.Group.ActiveGroupCount);
216
217 g_cRtMpWinMaxCpuGroups = uBuf.Info.Group.MaximumGroupCount;
218
219 /* Count max cpus (see mp-r0drv0-nt.cpp) why we don't use GetMaximumProcessorCount(ALL). */
220 uint32_t idxGroup;
221 g_cRtMpWinMaxCpus = 0;
222 for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++)
223 g_cRtMpWinMaxCpus += uBuf.Info.Group.GroupInfo[idxGroup].MaximumProcessorCount;
224
225 /* Process the active groups. */
226 uint32_t cActive = 0;
227 uint32_t cInactive = 0;
228 uint32_t idxCpu = 0;
229 uint32_t idxCpuSetNextInactive = g_cRtMpWinMaxCpus - 1;
230 for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++)
231 {
232 PROCESSOR_GROUP_INFO const *pGroupInfo = &uBuf.Info.Group.GroupInfo[idxGroup];
233 g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = pGroupInfo->MaximumProcessorCount;
234 g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = pGroupInfo->ActiveProcessorCount;
235 for (uint32_t idxMember = 0; idxMember < pGroupInfo->MaximumProcessorCount; idxMember++)
236 {
237 if (pGroupInfo->ActiveProcessorMask & RT_BIT_64(idxMember))
238 {
239 g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpu;
240 g_aidRtMpWinByCpuSetIdx[idxCpu] = idxCpu;
241 idxCpu++;
242 cActive++;
243 }
244 else
245 {
246 if (idxCpuSetNextInactive >= idxCpu)
247 {
248 g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
249 g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive;
250 idxCpuSetNextInactive--;
251 }
252 cInactive++;
253 }
254 }
255 }
256 g_cRtMpWinActiveCpus = cActive;
257 Assert(cActive + cInactive <= g_cRtMpWinMaxCpus);
258 Assert(idxCpu <= idxCpuSetNextInactive + 1);
259 Assert(idxCpu <= g_cRtMpWinMaxCpus);
260
261 /* Just in case the 2nd assumption doesn't hold true and there are inactive groups. */
262 for (; idxGroup < uBuf.Info.Group.MaximumGroupCount; idxGroup++)
263 {
264 DWORD cMaxMembers = g_pfnGetMaximumProcessorCount(idxGroup);
265 g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers;
266 g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0;
267 for (uint32_t idxMember = 0; idxMember < cMaxMembers; idxMember++)
268 {
269 if (idxCpuSetNextInactive >= idxCpu)
270 {
271 g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
272 g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive;
273 idxCpuSetNextInactive--;
274 }
275 cInactive++;
276 }
277 }
278 Assert(cActive + cInactive <= g_cRtMpWinMaxCpus);
279 Assert(idxCpu <= idxCpuSetNextInactive + 1);
280 }
281 else
282 {
283 /* Legacy: */
284 GetSystemInfo(&uBuf.SysInfo);
285 g_cRtMpWinMaxCpuGroups = 1;
286 g_cRtMpWinMaxCpus = uBuf.SysInfo.dwNumberOfProcessors;
287 g_aRtMpWinCpuGroups[0].cMaxCpus = uBuf.SysInfo.dwNumberOfProcessors;
288 g_aRtMpWinCpuGroups[0].cActiveCpus = uBuf.SysInfo.dwNumberOfProcessors;
289
290 for (uint32_t idxMember = 0; idxMember < uBuf.SysInfo.dwNumberOfProcessors; idxMember++)
291 {
292 g_aRtMpWinCpuGroups[0].aidxCpuSetMembers[idxMember] = idxMember;
293 g_aidRtMpWinByCpuSetIdx[idxMember] = idxMember;
294 }
295 }
296
297 AssertFatalMsg(g_cRtMpWinMaxCpus <= RTCPUSET_MAX_CPUS,
298 ("g_cRtMpWinMaxCpus=%u (%#x); RTCPUSET_MAX_CPUS=%u\n", g_cRtMpWinMaxCpus, g_cRtMpWinMaxCpus, RTCPUSET_MAX_CPUS));
299
300 g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
301 + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO);
302
303 /*
304 * Get information about cores.
305 *
306 * Note! This will only give us info about active processors according to
307 * MSDN, we'll just have to hope that CPUs aren't hotplugged after we
308 * initialize here (or that the API consumers doesn't care too much).
309 */
310 /** @todo A hot CPU plug event would be nice. */
311 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus;
312 if (g_pfnGetLogicalProcessorInformationEx)
313 {
314 /* Query the information. */
315 DWORD cbData = sizeof(uBuf);
316 AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationProcessorCore, &uBuf.Info, &cbData) != FALSE,
317 ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf)));
318 g_cRtMpWinMaxCpuCores = 0;
319 for (uint32_t off = 0; off < cbData; )
320 {
321 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pCur = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)&uBuf.abPaddingG[off];
322 AssertFatalMsg(pCur->Relationship == RelationProcessorCore,
323 ("off = %#x, Relationship = %u, expected %u!\n", off, pCur->Relationship, RelationProcessorCore));
324 g_cRtMpWinMaxCpuCores++;
325 off += pCur->Size;
326 }
327
328#if ARCH_BITS == 32
329 if (g_cRtMpWinMaxCpuCores > g_cRtMpWinMaxCpus)
330 {
331 /** @todo WOW64 trouble where the emulation environment has folded the high
332 * processor masks (63..32) into the low (31..0), hiding some
333 * processors from us. Currently we don't deal with that. */
334 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus;
335 }
336 else
337 AssertStmt(g_cRtMpWinMaxCpuCores > 0, g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
338#else
339 AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus,
340 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
341#endif
342 }
343 else
344 {
345 /*
346 * Sadly, on XP and Server 2003, even if the API is present, it does not tell us
347 * how many physical cores there are (any package will look like a single core).
348 * That is worse than not using the API at all, so just skip it unless it's Vista+.
349 */
350 if ( g_pfnGetLogicalProcessorInformation
351 && g_WinOsInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
352 && g_WinOsInfoEx.dwMajorVersion >= 6)
353 {
354 /* Query the info. */
355 DWORD cbSysProcInfo = _4K;
356 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION paSysInfo = NULL;
357 BOOL fRc = FALSE;
358 do
359 {
360 cbSysProcInfo = RT_ALIGN_32(cbSysProcInfo, 256);
361 void *pv = RTMemRealloc(paSysInfo, cbSysProcInfo);
362 if (!pv)
363 break;
364 paSysInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)pv;
365 fRc = g_pfnGetLogicalProcessorInformation(paSysInfo, &cbSysProcInfo);
366 } while (!fRc && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
367 if (fRc)
368 {
369 /* Count the cores in the result. */
370 g_cRtMpWinMaxCpuCores = 0;
371 uint32_t i = cbSysProcInfo / sizeof(paSysInfo[0]);
372 while (i-- > 0)
373 if (paSysInfo[i].Relationship == RelationProcessorCore)
374 g_cRtMpWinMaxCpuCores++;
375
376 AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus,
377 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
378 }
379 RTMemFree(paSysInfo);
380 }
381 }
382
383 return VINF_SUCCESS;
384}
385
386
387#ifdef IPRT_WITH_GIP_MP_INFO
388/**
389 * @callback_method_impl{FNRTONCE, Updates globals with information from GIP.}
390 */
391static DECLCALLBACK(int32_t) rtMpWinInitOnceGip(void *pvUser)
392{
393 RT_NOREF(pvUser);
394 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
395
396 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
397 if ( pGip
398 && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
399 {
400 /*
401 * Update globals.
402 */
403 if (g_cRtMpWinMaxCpus != pGip->cPossibleCpus)
404 g_cRtMpWinMaxCpus = pGip->cPossibleCpus;
405 if (g_cRtMpWinActiveCpus != pGip->cOnlineCpus)
406 g_cRtMpWinActiveCpus = pGip->cOnlineCpus;
407 Assert(g_cRtMpWinMaxCpuGroups == pGip->cPossibleCpuGroups);
408 if (g_cRtMpWinMaxCpuGroups != pGip->cPossibleCpuGroups)
409 {
410 g_cRtMpWinMaxCpuGroups = pGip->cPossibleCpuGroups;
411 g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
412 + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO);
413 }
414
415 /*
416 * Update CPU set IDs.
417 */
418 for (unsigned i = g_cRtMpWinMaxCpus; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++)
419 g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID;
420
421 unsigned const cbGip = pGip->cPages * PAGE_SIZE;
422 for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++)
423 {
424 uint32_t idxMember;
425 uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup];
426 if (offCpuGroup < cbGip)
427 {
428 PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup);
429 uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers;
430 AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers),
431 cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers));
432 g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers;
433 g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
434
435 for (idxMember = 0; idxMember < cMaxMembers; idxMember++)
436 {
437 int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember];
438 g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet;
439 if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
440# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
441 g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember);
442# else
443 g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet;
444# endif
445 }
446 }
447 else
448 idxMember = 0;
449 for (; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers); idxMember++)
450 g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1;
451 }
452 }
453
454 return VINF_SUCCESS;
455}
456
457
458/**
459 * Refreshes globals from GIP after one or more CPUs were added.
460 *
461 * There are potential races here. We might race other threads and we may race
462 * more CPUs being added.
463 */
464static void rtMpWinRefreshGip(void)
465{
466 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
467 if ( pGip
468 && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
469 {
470 /*
471 * Since CPUs cannot be removed, we only have to update the IDs and
472 * indexes of CPUs that we think are inactive and the group member counts.
473 */
474 for (;;)
475 {
476 unsigned const cbGip = pGip->cPages * PAGE_SIZE;
477 uint32_t const cGipActiveCpus = pGip->cOnlineCpus;
478 uint32_t const cMyActiveCpus = ASMAtomicReadU32(&g_cRtMpWinActiveCpus);
479 ASMCompilerBarrier();
480
481 for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++)
482 {
483 uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup];
484 if (offCpuGroup < cbGip)
485 {
486 PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup);
487 uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers;
488 AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers),
489 cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers));
490 for (uint32_t idxMember = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus; idxMember < cMaxMembers; idxMember++)
491 {
492 int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember];
493 g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet;
494 if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
495# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
496 g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember);
497# else
498 g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet;
499# endif
500 }
501 g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
502 g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
503 }
504 else
505 Assert(g_aRtMpWinCpuGroups[idxGroup].cActiveCpus == 0);
506 }
507
508 ASMCompilerBarrier();
509 if (cGipActiveCpus == pGip->cOnlineCpus)
510 if (ASMAtomicCmpXchgU32(&g_cRtMpWinActiveCpus, cGipActiveCpus, cMyActiveCpus))
511 break;
512 }
513 }
514}
515
516#endif /* IPRT_WITH_GIP_MP_INFO */
517
518
519/*
520 * Conversion between CPU ID and set index.
521 */
522
523RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
524{
525 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
526 RTMPWIN_UPDATE_GIP_GLOBALS();
527
528#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
529 if (idCpu != NIL_RTCPUID)
530 return RTMpSetIndexFromCpuGroupMember(rtMpCpuIdGetGroup(idCpu), rtMpCpuIdGetGroupMember(idCpu));
531 return -1;
532
533#else
534 /* 1:1 mapping, just do range checking. */
535 return idCpu < g_cRtMpWinMaxCpus ? idCpu : -1;
536#endif
537}
538
539
540RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
541{
542 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
543 RTMPWIN_UPDATE_GIP_GLOBALS();
544
545 if ((unsigned)iCpu < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
546 {
547 RTCPUID idCpu = g_aidRtMpWinByCpuSetIdx[iCpu];
548
549#if defined(IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER) && defined(RT_STRICT)
550 /* Check the correctness of the mapping table. */
551 RTCPUID idCpuGip = NIL_RTCPUID;
552 if ( pGip
553 && (unsigned)iCpu < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))
554 {
555 unsigned idxSupCpu = pGip->aiCpuFromCpuSetIdx[idxGuess];
556 if (idxSupCpu < pGip->cCpus)
557 if (pGip->aCPUs[idxSupCpu].enmState != SUPGIPCPUSTATE_INVALID)
558 idCpuGip = pGip->aCPUs[idxSupCpu].idCpu;
559 }
560 AssertMsg(idCpu == idCpuGip, ("table:%#x gip:%#x\n", idCpu, idCpuGip));
561#endif
562
563 return idCpu;
564 }
565 return NIL_RTCPUID;
566}
567
568
569RTDECL(int) RTMpSetIndexFromCpuGroupMember(uint32_t idxGroup, uint32_t idxMember)
570{
571 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
572 RTMPWIN_UPDATE_GIP_GLOBALS();
573
574 if (idxGroup < g_cRtMpWinMaxCpuGroups)
575 if (idxMember < g_aRtMpWinCpuGroups[idxGroup].cMaxCpus)
576 return g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember];
577 return -1;
578}
579
580
581RTDECL(uint32_t) RTMpGetCpuGroupCounts(uint32_t idxGroup, uint32_t *pcActive)
582{
583 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
584 RTMPWIN_UPDATE_GIP_GLOBALS();
585
586 if (idxGroup < g_cRtMpWinMaxCpuGroups)
587 {
588 if (pcActive)
589 *pcActive = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus;
590 return g_aRtMpWinCpuGroups[idxGroup].cMaxCpus;
591 }
592 if (pcActive)
593 *pcActive = 0;
594 return 0;
595}
596
597
598RTDECL(uint32_t) RTMpGetMaxCpuGroupCount(void)
599{
600 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
601 RTMPWIN_UPDATE_GIP_GLOBALS();
602
603 return g_cRtMpWinMaxCpuGroups;
604}
605
606
607
608/*
609 * Get current CPU.
610 */
611
612RTDECL(RTCPUID) RTMpCpuId(void)
613{
614 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
615 RTMPWIN_UPDATE_GIP_GLOBALS();
616
617 PROCESSOR_NUMBER ProcNum;
618 ProcNum.Group = 0;
619 ProcNum.Number = 0xff;
620 if (g_pfnGetCurrentProcessorNumberEx)
621 g_pfnGetCurrentProcessorNumberEx(&ProcNum);
622 else if (g_pfnGetCurrentProcessorNumber)
623 {
624 DWORD iCpu = g_pfnGetCurrentProcessorNumber();
625 Assert(iCpu < g_cRtMpWinMaxCpus);
626 ProcNum.Number = iCpu;
627 }
628 else
629 {
630#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
631 ProcNum.Number = ASMGetApicId();
632#else
633# error "Not ported to this architecture."
634 return NIL_RTCPUID;
635#endif
636 }
637
638#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
639 return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number);
640#else
641 return RTMpSetIndexFromCpuGroupMember(ProcNum.Group, ProcNum.Number);
642#endif
643}
644
645
646/*
647 * Possible CPUs and cores.
648 */
649
650RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
651{
652 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
653 RTMPWIN_UPDATE_GIP_GLOBALS();
654
655#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
656 return RTMPCPUID_FROM_GROUP_AND_NUMBER(g_cRtMpWinMaxCpuGroups - 1,
657 g_aRtMpWinCpuGroups[g_cRtMpWinMaxCpuGroups - 1].cMaxCpus - 1);
658#else
659 return g_cRtMpWinMaxCpus - 1;
660#endif
661}
662
663
664RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
665{
666 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
667 RTMPWIN_UPDATE_GIP_GLOBALS();
668
669 /* Any CPU between 0 and g_cRtMpWinMaxCpus are possible. */
670 return idCpu < g_cRtMpWinMaxCpus;
671}
672
673
674RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
675{
676 RTCPUID iCpu = RTMpGetCount();
677 RTCpuSetEmpty(pSet);
678 while (iCpu-- > 0)
679 RTCpuSetAddByIndex(pSet, iCpu);
680 return pSet;
681}
682
683
684RTDECL(RTCPUID) RTMpGetCount(void)
685{
686 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
687 RTMPWIN_UPDATE_GIP_GLOBALS();
688
689 return g_cRtMpWinMaxCpus;
690}
691
692
693RTDECL(RTCPUID) RTMpGetCoreCount(void)
694{
695 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
696 RTMPWIN_UPDATE_GIP_GLOBALS();
697
698 return g_cRtMpWinMaxCpuCores;
699}
700
701
702/*
703 * Online CPUs and cores.
704 */
705
706RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
707{
708 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
709
710#ifdef IPRT_WITH_GIP_MP_INFO
711 RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP();
712 if (pGip)
713 {
714 *pSet = pGip->OnlineCpuSet;
715 return pSet;
716 }
717#endif
718
719 if (g_pfnGetLogicalProcessorInformationEx)
720 {
721 /*
722 * Get the group relation info.
723 *
724 * In addition to the ASSUMPTIONS that are documented in rtMpWinInitOnce,
725 * we ASSUME that PROCESSOR_GROUP_INFO::MaximumProcessorCount gives the
726 * active processor mask width.
727 */
728 /** @todo this is not correct for WOW64 */
729 DWORD cbInfo = g_cbRtMpWinGrpRelBuf;
730 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)alloca(cbInfo);
731 AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, pInfo, &cbInfo) != FALSE,
732 ("last error = %u, cbInfo = %u (in %u)\n", GetLastError(), cbInfo, g_cbRtMpWinGrpRelBuf));
733 AssertFatalMsg(pInfo->Relationship == RelationGroup,
734 ("Relationship = %u, expected %u!\n", pInfo->Relationship, RelationGroup));
735 AssertFatalMsg(pInfo->Group.MaximumGroupCount == g_cRtMpWinMaxCpuGroups,
736 ("MaximumGroupCount is %u, expected %u!\n", pInfo->Group.MaximumGroupCount, g_cRtMpWinMaxCpuGroups));
737
738 RTCpuSetEmpty(pSet);
739 for (uint32_t idxGroup = 0; idxGroup < pInfo->Group.MaximumGroupCount; idxGroup++)
740 {
741 Assert(pInfo->Group.GroupInfo[idxGroup].MaximumProcessorCount == g_aRtMpWinCpuGroups[idxGroup].cMaxCpus);
742 Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount <= g_aRtMpWinCpuGroups[idxGroup].cMaxCpus);
743
744 KAFFINITY fActive = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorMask;
745 if (fActive != 0)
746 {
747#ifdef RT_STRICT
748 uint32_t cMembersLeft = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount;
749#endif
750 int const cMembers = g_aRtMpWinCpuGroups[idxGroup].cMaxCpus;
751 for (int idxMember = 0; idxMember < cMembers; idxMember++)
752 {
753 if (fActive & 1)
754 {
755#ifdef RT_STRICT
756 cMembersLeft--;
757#endif
758 RTCpuSetAddByIndex(pSet, g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember]);
759 fActive >>= 1;
760 if (!fActive)
761 break;
762 }
763 else
764 {
765 fActive >>= 1;
766 }
767 }
768 Assert(cMembersLeft == 0);
769 }
770 else
771 Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount == 0);
772 }
773
774 return pSet;
775 }
776
777 /*
778 * Legacy fallback code.
779 */
780 SYSTEM_INFO SysInfo;
781 GetSystemInfo(&SysInfo);
782 return RTCpuSetFromU64(pSet, SysInfo.dwActiveProcessorMask);
783}
784
785
786RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
787{
788 RTCPUSET Set;
789 return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu);
790}
791
792
793RTDECL(RTCPUID) RTMpGetOnlineCount(void)
794{
795#ifdef IPRT_WITH_GIP_MP_INFO
796 RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP();
797 if (pGip)
798 return pGip->cOnlineCpus;
799#endif
800
801 RTCPUSET Set;
802 RTMpGetOnlineSet(&Set);
803 return RTCpuSetCount(&Set);
804}
805
806
807RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
808{
809 /** @todo this isn't entirely correct, but whatever. */
810 return RTMpGetCoreCount();
811}
812
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use