VirtualBox

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

Last change on this file since 103795 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette