VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use