- Timestamp:
- Oct 12, 2016 12:38:06 PM (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/win/mp-win.cpp
r62592 r64220 39 39 #include <iprt/ldr.h> 40 40 #include <iprt/mem.h> 41 42 43 AssertCompile(MAXIMUM_PROCESSORS <= RTCPUSET_MAX_CPUS); 44 45 46 /** @todo RTmpCpuId(). */ 41 #include <iprt/once.h> 42 #if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) 43 # include <iprt/asm-amd64-x86.h> 44 #endif 45 46 #include "internal-r3-win.h" 47 48 49 50 /********************************************************************************************************************************* 51 * Defined Constants And Macros * 52 *********************************************************************************************************************************/ 53 /** Initialize once. */ 54 static RTONCE g_MpInitOnce = RTONCE_INITIALIZER; 55 //static decltype(GetMaximumProcessorCount) *g_pfnGetMaximumProcessorCount; 56 static decltype(GetCurrentProcessorNumber) *g_pfnGetCurrentProcessorNumber; 57 static decltype(GetCurrentProcessorNumberEx) *g_pfnGetCurrentProcessorNumberEx; 58 static decltype(GetLogicalProcessorInformation) *g_pfnGetLogicalProcessorInformation; 59 static decltype(GetLogicalProcessorInformationEx) *g_pfnGetLogicalProcessorInformationEx; 60 61 62 /********************************************************************************************************************************* 63 * Global Variables * 64 *********************************************************************************************************************************/ 65 /** The required buffer size for getting group relations. */ 66 static uint32_t g_cbRtMpWinGrpRelBuf; 67 /** The max number of CPUs (RTMpGetCount). */ 68 static uint32_t g_cRtMpWinMaxCpus; 69 /** The max number of CPU cores (RTMpGetCoreCount). */ 70 static uint32_t g_cRtMpWinMaxCpuCores; 71 /** The max number of groups. */ 72 static uint32_t g_cRtMpWinMaxCpuGroups; 73 /** Static per group info. */ 74 static struct 75 { 76 /** The CPU ID (and CPU set index) of the first CPU in the group. */ 77 uint16_t idFirstCpu; 78 /** The max CPUs in the group. */ 79 uint16_t cMaxCpus; 80 } g_aRtMpWinCpuGroups[RTCPUSET_MAX_CPUS]; 81 82 83 /** 84 * @callback_method_impl{FNRTONCE, Resolves dynamic imports.} 85 */ 86 static DECLCALLBACK(int32_t) rtMpWinInitOnce(void *pvUser) 87 { 88 RT_NOREF(pvUser); 89 90 Assert(g_WinOsInfoEx.dwOSVersionInfoSize != 0); 91 Assert(g_hModKernel32 != NULL); 92 93 /* 94 * Resolve dynamic APIs. 95 */ 96 #define RESOLVE_API(a_szMod, a_FnName) \ 97 do { \ 98 RT_CONCAT(g_pfn,a_FnName) = (decltype(a_FnName) *)GetProcAddress(g_hModKernel32, #a_FnName); \ 99 } while (0) 100 //RESOLVE_API("kernel32.dll", GetMaximumProcessorCount); /* Calls GetLogicalProcessorInformationEx/RelationGroup in W10. */ 101 RESOLVE_API("kernel32.dll", GetCurrentProcessorNumber); 102 RESOLVE_API("kernel32.dll", GetCurrentProcessorNumberEx); 103 RESOLVE_API("kernel32.dll", GetLogicalProcessorInformation); 104 RESOLVE_API("kernel32.dll", GetLogicalProcessorInformationEx); 105 106 /* 107 * Query group information, partitioning CPU IDs and CPU set 108 * indexes (they are the same). 109 * 110 * We ASSUME the the GroupInfo index is the same as the group number. 111 * We ASSUME there are no inactive groups, because otherwise it will 112 * be difficult to tell how many possible CPUs we can have and do a 113 * reasonable CPU ID/index partitioning. 114 * 115 * Note! We will die if there are too many processors! 116 */ 117 union 118 { 119 SYSTEM_INFO SysInfo; 120 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Info; 121 uint8_t abPaddingG[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) 122 + sizeof(PROCESSOR_GROUP_INFO) * RTCPUSET_MAX_CPUS]; 123 uint8_t abPaddingC[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) 124 + (sizeof(PROCESSOR_RELATIONSHIP) + sizeof(GROUP_AFFINITY)) 125 * RTCPUSET_MAX_CPUS]; 126 } uBuf; 127 if (g_pfnGetLogicalProcessorInformationEx) 128 { 129 /* Query the information. */ 130 DWORD cbData = sizeof(uBuf); 131 AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, &uBuf.Info, &cbData) != FALSE, 132 ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf))); 133 AssertFatalMsg(uBuf.Info.Relationship == RelationGroup, 134 ("Relationship = %u, expected %u!\n", uBuf.Info.Relationship, RelationGroup)); 135 AssertFatalMsg(uBuf.Info.Group.MaximumGroupCount <= RT_ELEMENTS(g_aRtMpWinCpuGroups), 136 ("MaximumGroupCount is %u, we only support up to %u!\n", 137 uBuf.Info.Group.MaximumGroupCount, RT_ELEMENTS(g_aRtMpWinCpuGroups))); 138 139 AssertMsg(uBuf.Info.Group.MaximumGroupCount == uBuf.Info.Group.ActiveGroupCount, /* 2nd assumption mentioned above. */ 140 ("%u vs %u\n", uBuf.Info.Group.MaximumGroupCount, uBuf.Info.Group.ActiveGroupCount)); 141 AssertFatal(uBuf.Info.Group.MaximumGroupCount >= uBuf.Info.Group.ActiveGroupCount); 142 143 144 /* Process the active groups. */ 145 g_cRtMpWinMaxCpuGroups = uBuf.Info.Group.MaximumGroupCount; 146 uint16_t idxCpu = 0; 147 uint32_t idxGroup = 0; 148 for (; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++) 149 { 150 g_aRtMpWinCpuGroups[idxGroup].idFirstCpu = idxCpu; 151 g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = uBuf.Info.Group.GroupInfo[idxGroup].MaximumProcessorCount; 152 idxCpu += uBuf.Info.Group.GroupInfo[idxGroup].MaximumProcessorCount; 153 } 154 155 /* Just in case the 2nd assumption doesn't hold true and there are inactive groups. */ 156 for (; idxGroup < uBuf.Info.Group.MaximumGroupCount; idxGroup++) 157 { 158 g_aRtMpWinCpuGroups[idxGroup].idFirstCpu = idxCpu; 159 g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = RT_MAX(MAXIMUM_PROC_PER_GROUP, 64); 160 idxCpu += RT_MAX(MAXIMUM_PROC_PER_GROUP, 64); 161 } 162 163 g_cRtMpWinMaxCpus = idxCpu; 164 } 165 else 166 { 167 /* Legacy: */ 168 GetSystemInfo(&uBuf.SysInfo); 169 g_cRtMpWinMaxCpus = uBuf.SysInfo.dwNumberOfProcessors; 170 g_cRtMpWinMaxCpuGroups = 1; 171 g_aRtMpWinCpuGroups[0].idFirstCpu = 0; 172 g_aRtMpWinCpuGroups[0].cMaxCpus = uBuf.SysInfo.dwNumberOfProcessors; 173 } 174 175 AssertFatalMsg(g_cRtMpWinMaxCpus <= RTCPUSET_MAX_CPUS, 176 ("g_cRtMpWinMaxCpus=%u (%#x); RTCPUSET_MAX_CPUS=%u\n", g_cRtMpWinMaxCpus, g_cRtMpWinMaxCpus, RTCPUSET_MAX_CPUS)); 177 178 g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) 179 + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO); 180 181 /* 182 * Get information about cores. 183 * 184 * Note! This will only give us info about active processors according to 185 * MSDN, we'll just have to hope that CPUs aren't hotplugged after we 186 * initialize here (or that the API consumers doesn't care too much). 187 */ 188 /** @todo A hot CPU plug event would be nice. */ 189 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus; 190 if (g_pfnGetLogicalProcessorInformationEx) 191 { 192 /* Query the information. */ 193 DWORD cbData = sizeof(uBuf); 194 AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationProcessorCore, &uBuf.Info, &cbData) != FALSE, 195 ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf))); 196 g_cRtMpWinMaxCpuCores = 0; 197 for (uint32_t off = 0; off < cbData; ) 198 { 199 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pCur = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)&uBuf.abPaddingG[off]; 200 AssertFatalMsg(pCur->Relationship == RelationProcessorCore, 201 ("off = %#x, Relationship = %u, expected %u!\n", off, pCur->Relationship, RelationProcessorCore)); 202 g_cRtMpWinMaxCpuCores++; 203 off += pCur->Size; 204 } 205 206 #if ARCH_BITS == 32 207 if (g_cRtMpWinMaxCpuCores > g_cRtMpWinMaxCpus) 208 { 209 /** @todo WOW64 trouble where the emulation environment has folded the high 210 * processor masks (63..32) into the low (31..0), hiding some 211 * processors from us. Currently we don't deal with that. */ 212 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus; 213 } 214 else 215 AssertStmt(g_cRtMpWinMaxCpuCores > 0, g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus); 216 #else 217 AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus, 218 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus); 219 #endif 220 } 221 else 222 { 223 /* 224 * Sadly, on XP and Server 2003, even if the API is present, it does not tell us 225 * how many physical cores there are (any package will look like a single core). 226 * That is worse than not using the API at all, so just skip it unless it's Vista+. 227 */ 228 if ( g_pfnGetLogicalProcessorInformation 229 && g_WinOsInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT 230 && g_WinOsInfoEx.dwMajorVersion >= 6) 231 { 232 /* Query the info. */ 233 DWORD cbSysProcInfo = _4K; 234 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION paSysInfo = NULL; 235 BOOL fRc = FALSE; 236 do 237 { 238 cbSysProcInfo = RT_ALIGN_32(cbSysProcInfo, 256); 239 void *pv = RTMemRealloc(paSysInfo, cbSysProcInfo); 240 if (!pv) 241 break; 242 paSysInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)pv; 243 fRc = g_pfnGetLogicalProcessorInformation(paSysInfo, &cbSysProcInfo); 244 } while (!fRc && GetLastError() == ERROR_INSUFFICIENT_BUFFER); 245 if (fRc) 246 { 247 /* Count the cores in the result. */ 248 g_cRtMpWinMaxCpuCores = 0; 249 uint32_t i = cbSysProcInfo / sizeof(paSysInfo[0]); 250 while (i-- > 0) 251 if (paSysInfo[i].Relationship == RelationProcessorCore) 252 g_cRtMpWinMaxCpuCores++; 253 254 AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus, 255 g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus); 256 } 257 RTMemFree(paSysInfo); 258 } 259 } 260 261 return VINF_SUCCESS; 262 } 263 264 265 RTDECL(RTCPUID) RTMpCpuId(void) 266 { 267 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 268 269 if (g_pfnGetCurrentProcessorNumberEx) 270 { 271 PROCESSOR_NUMBER ProcNum; 272 g_pfnGetCurrentProcessorNumberEx(&ProcNum); 273 Assert(ProcNum.Group < g_cRtMpWinMaxCpuGroups); 274 Assert(ProcNum.Number < g_aRtMpWinCpuGroups[ProcNum.Group].cMaxCpus); 275 return g_aRtMpWinCpuGroups[ProcNum.Group].idFirstCpu + ProcNum.Number; 276 } 277 278 if (g_pfnGetCurrentProcessorNumber) 279 { 280 /* Should be safe wrt processor numbering, I hope... Only affects W2k3 and Vista. */ 281 Assert(g_cRtMpWinMaxCpuGroups == 1); 282 return g_pfnGetCurrentProcessorNumber(); 283 } 284 285 /* The API was introduced with W2K3 according to MSDN. */ 286 #if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) 287 return ASMGetApicId(); 288 #else 289 # error "Not ported to this architecture." 290 return NIL_RTAPICID; 291 #endif 292 } 293 47 294 48 295 RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu) 49 296 { 50 return idCpu < MAXIMUM_PROCESSORS ? idCpu : -1; 297 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 298 299 /* 1:1 mapping, just do range checking. */ 300 return idCpu < g_cRtMpWinMaxCpus ? idCpu : -1; 51 301 } 52 302 … … 54 304 RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu) 55 305 { 56 return (unsigned)iCpu < MAXIMUM_PROCESSORS ? iCpu : NIL_RTCPUID; 306 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 307 308 /* 1:1 mapping, just do range checking. */ 309 return (unsigned)iCpu < g_cRtMpWinMaxCpus ? iCpu : NIL_RTCPUID; 57 310 } 58 311 … … 60 313 RTDECL(RTCPUID) RTMpGetMaxCpuId(void) 61 314 { 62 return MAXIMUM_PROCESSORS - 1; 315 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 316 return g_cRtMpWinMaxCpus - 1; 63 317 } 64 318 … … 66 320 RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu) 67 321 { 322 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 68 323 RTCPUSET Set; 69 324 return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu); … … 73 328 RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu) 74 329 { 75 RTCPUSET Set; 76 return RTCpuSetIsMember(RTMpGetSet(&Set), idCpu); 330 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 331 /* Any CPU between 0 and g_cRtMpWinMaxCpus are possible. */ 332 return idCpu < g_cRtMpWinMaxCpus; 77 333 } 78 334 … … 90 346 RTDECL(RTCPUID) RTMpGetCount(void) 91 347 { 348 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 349 return g_cRtMpWinMaxCpus; 350 } 351 352 353 RTDECL(RTCPUID) RTMpGetCoreCount(void) 354 { 355 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 356 return g_cRtMpWinMaxCpuCores; 357 } 358 359 360 RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet) 361 { 362 RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); 363 364 if (g_pfnGetLogicalProcessorInformationEx) 365 { 366 /* 367 * Get the group relation info. 368 * 369 * In addition to the ASSUMPTIONS that are documented in rtMpWinInitOnce, 370 * we ASSUME that PROCESSOR_GROUP_INFO::MaximumProcessorCount gives the 371 * active processor mask width. 372 */ 373 DWORD cbInfo = g_cbRtMpWinGrpRelBuf; 374 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)alloca(cbInfo); 375 AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, pInfo, &cbInfo) != FALSE, 376 ("last error = %u, cbInfo = %u (in %u)\n", GetLastError(), cbInfo, g_cbRtMpWinGrpRelBuf)); 377 AssertFatalMsg(pInfo->Relationship == RelationGroup, 378 ("Relationship = %u, expected %u!\n", pInfo->Relationship, RelationGroup)); 379 AssertFatalMsg(pInfo->Group.MaximumGroupCount == g_cRtMpWinMaxCpuGroups, 380 ("MaximumGroupCount is %u, expected %u!\n", pInfo->Group.MaximumGroupCount, g_cRtMpWinMaxCpuGroups)); 381 382 RTCpuSetEmpty(pSet); 383 for (uint32_t idxGroup = 0; idxGroup < pInfo->Group.MaximumGroupCount; idxGroup++) 384 { 385 Assert(pInfo->Group.GroupInfo[idxGroup].MaximumProcessorCount == g_aRtMpWinCpuGroups[idxGroup].cMaxCpus); 386 Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount <= g_aRtMpWinCpuGroups[idxGroup].cMaxCpus); 387 388 KAFFINITY fActive = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorMask; 389 if (fActive != 0) 390 { 391 #ifdef RT_STRICT 392 uint32_t cMembersLeft = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount; 393 #endif 394 int const idxFirst = g_aRtMpWinCpuGroups[idxGroup].idFirstCpu; 395 int const cMembers = g_aRtMpWinCpuGroups[idxGroup].cMaxCpus; 396 for (int idxMember = 0; idxMember < cMembers; idxMember++) 397 { 398 if (fActive & 1) 399 { 400 #ifdef RT_STRICT 401 cMembersLeft--; 402 #endif 403 RTCpuSetAddByIndex(pSet, idxFirst + idxMember); 404 fActive >>= 1; 405 if (!fActive) 406 break; 407 } 408 else 409 { 410 fActive >>= 1; 411 } 412 } 413 Assert(cMembersLeft == 0); 414 } 415 else 416 Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount == 0); 417 } 418 419 return pSet; 420 } 421 422 /* 423 * Legacy fallback code. 424 */ 92 425 SYSTEM_INFO SysInfo; 93 426 GetSystemInfo(&SysInfo); 94 Assert((RTCPUID)SysInfo.dwNumberOfProcessors == SysInfo.dwNumberOfProcessors);95 return SysInfo.dwNumberOfProcessors;96 }97 98 99 RTDECL(RTCPUID) RTMpGetCoreCount(void)100 {101 /*102 * Resolve the API dynamically (one try) as it requires XP w/ sp3 or later.103 */104 typedef BOOL (WINAPI *PFNGETLOGICALPROCINFO)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);105 static PFNGETLOGICALPROCINFO s_pfnGetLogicalProcInfo = (PFNGETLOGICALPROCINFO)~(uintptr_t)0;106 if (s_pfnGetLogicalProcInfo == (PFNGETLOGICALPROCINFO)~(uintptr_t)0)107 s_pfnGetLogicalProcInfo = (PFNGETLOGICALPROCINFO)RTLdrGetSystemSymbol("kernel32.dll", "GetLogicalProcessorInformation");108 109 /*110 * Sadly, on XP and Server 2003, even if the API is present, it does not tell us111 * how many physical cores there are (any package will look like a single core).112 * That is worse than not using the API at all, so just skip it unless it's Vista+.113 */114 bool fIsVistaOrLater = false;115 OSVERSIONINFOEX OSInfoEx = { 0 };116 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);117 if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)118 && (OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT)119 && (OSInfoEx.dwMajorVersion >= 6))120 fIsVistaOrLater = true;121 122 if (s_pfnGetLogicalProcInfo && fIsVistaOrLater)123 {124 /*125 * Query the information. This unfortunately requires a buffer, so we126 * start with a guess and let windows advice us if it's too small.127 */128 DWORD cbSysProcInfo = _4K;129 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION paSysInfo = NULL;130 BOOL fRc = FALSE;131 do132 {133 cbSysProcInfo = RT_ALIGN_32(cbSysProcInfo, 256);134 void *pv = RTMemRealloc(paSysInfo, cbSysProcInfo);135 if (!pv)136 break;137 paSysInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)pv;138 fRc = s_pfnGetLogicalProcInfo(paSysInfo, &cbSysProcInfo);139 } while (!fRc && GetLastError() == ERROR_INSUFFICIENT_BUFFER);140 if (fRc)141 {142 /*143 * Parse the result.144 */145 uint32_t cCores = 0;146 uint32_t i = cbSysProcInfo / sizeof(paSysInfo[0]);147 while (i-- > 0)148 if (paSysInfo[i].Relationship == RelationProcessorCore)149 cCores++;150 151 RTMemFree(paSysInfo);152 Assert(cCores > 0);153 return cCores;154 }155 156 RTMemFree(paSysInfo);157 }158 159 /* If we don't have the necessary API or if it failed, return the same160 value as the generic implementation. */161 return RTMpGetCount();162 }163 164 165 RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)166 {167 SYSTEM_INFO SysInfo;168 GetSystemInfo(&SysInfo);169 /** @todo port to W2K8 / W7 w/ > 64 CPUs & grouping. */170 427 return RTCpuSetFromU64(pSet, SysInfo.dwActiveProcessorMask); 171 428 } … … 182 439 RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void) 183 440 { 184 /** @todo this isn't entirely correct . */441 /** @todo this isn't entirely correct, but whatever. */ 185 442 return RTMpGetCoreCount(); 186 443 }
Note:
See TracChangeset
for help on using the changeset viewer.