[1] | 1 | /* $Id: mp-linux.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[10419] | 3 | * IPRT - Multiprocessor, Linux.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[76553] | 7 | * Copyright (C) 2006-2019 Oracle Corporation
|
---|
[1] | 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
|
---|
[5999] | 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.
|
---|
[1] | 25 | */
|
---|
| 26 |
|
---|
| 27 |
|
---|
[57358] | 28 | /*********************************************************************************************************************************
|
---|
| 29 | * Header Files *
|
---|
| 30 | *********************************************************************************************************************************/
|
---|
[1] | 31 | #define LOG_GROUP RTLOGGROUP_SYSTEM
|
---|
[10419] | 32 | #include <stdio.h>
|
---|
| 33 | #include <errno.h>
|
---|
| 34 |
|
---|
[7362] | 35 | #include <iprt/mp.h>
|
---|
[46639] | 36 | #include "internal/iprt.h"
|
---|
| 37 |
|
---|
| 38 | #include <iprt/alloca.h>
|
---|
[7362] | 39 | #include <iprt/cpuset.h>
|
---|
[1] | 40 | #include <iprt/assert.h>
|
---|
[10419] | 41 | #include <iprt/string.h>
|
---|
[15399] | 42 | #include <iprt/linux/sysfs.h>
|
---|
[1] | 43 |
|
---|
| 44 |
|
---|
[10419] | 45 | /**
|
---|
[33540] | 46 | * Internal worker that determines the max possible CPU count.
|
---|
[10419] | 47 | *
|
---|
| 48 | * @returns Max cpus.
|
---|
| 49 | */
|
---|
[10421] | 50 | static RTCPUID rtMpLinuxMaxCpus(void)
|
---|
[10419] | 51 | {
|
---|
[10421] | 52 | #if 0 /* this doesn't do the right thing :-/ */
|
---|
[10419] | 53 | int cMax = sysconf(_SC_NPROCESSORS_CONF);
|
---|
| 54 | Assert(cMax >= 1);
|
---|
| 55 | return cMax;
|
---|
[10421] | 56 | #else
|
---|
| 57 | static uint32_t s_cMax = 0;
|
---|
| 58 | if (!s_cMax)
|
---|
| 59 | {
|
---|
| 60 | int cMax = 1;
|
---|
| 61 | for (unsigned iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
|
---|
[15399] | 62 | if (RTLinuxSysFsExists("devices/system/cpu/cpu%d", iCpu))
|
---|
[10421] | 63 | cMax = iCpu + 1;
|
---|
| 64 | ASMAtomicUoWriteU32((uint32_t volatile *)&s_cMax, cMax);
|
---|
| 65 | return cMax;
|
---|
| 66 | }
|
---|
| 67 | return s_cMax;
|
---|
| 68 | #endif
|
---|
[10419] | 69 | }
|
---|
| 70 |
|
---|
[10442] | 71 | /**
|
---|
| 72 | * Internal worker that picks the processor speed in MHz from /proc/cpuinfo.
|
---|
| 73 | *
|
---|
| 74 | * @returns CPU frequency.
|
---|
| 75 | */
|
---|
| 76 | static uint32_t rtMpLinuxGetFrequency(RTCPUID idCpu)
|
---|
| 77 | {
|
---|
[10481] | 78 | FILE *pFile = fopen("/proc/cpuinfo", "r");
|
---|
| 79 | if (!pFile)
|
---|
[10442] | 80 | return 0;
|
---|
[10419] | 81 |
|
---|
[10442] | 82 | char sz[256];
|
---|
| 83 | RTCPUID idCpuFound = NIL_RTCPUID;
|
---|
[10481] | 84 | uint32_t Frequency = 0;
|
---|
| 85 | while (fgets(sz, sizeof(sz), pFile))
|
---|
[10442] | 86 | {
|
---|
[10481] | 87 | char *psz;
|
---|
[46326] | 88 | if ( !strncmp(sz, RT_STR_TUPLE("processor"))
|
---|
[10442] | 89 | && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
|
---|
| 90 | && (psz = strchr(sz, ':')))
|
---|
| 91 | {
|
---|
| 92 | psz += 2;
|
---|
| 93 | int64_t iCpu;
|
---|
| 94 | int rc = RTStrToInt64Ex(psz, NULL, 0, &iCpu);
|
---|
| 95 | if (RT_SUCCESS(rc))
|
---|
| 96 | idCpuFound = iCpu;
|
---|
| 97 | }
|
---|
| 98 | else if ( idCpu == idCpuFound
|
---|
[46326] | 99 | && !strncmp(sz, RT_STR_TUPLE("cpu MHz"))
|
---|
[10442] | 100 | && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
|
---|
| 101 | && (psz = strchr(sz, ':')))
|
---|
| 102 | {
|
---|
| 103 | psz += 2;
|
---|
| 104 | int64_t v;
|
---|
| 105 | int rc = RTStrToInt64Ex(psz, &psz, 0, &v);
|
---|
| 106 | if (RT_SUCCESS(rc))
|
---|
[10445] | 107 | {
|
---|
[10481] | 108 | Frequency = v;
|
---|
[10445] | 109 | break;
|
---|
| 110 | }
|
---|
[10442] | 111 | }
|
---|
| 112 | }
|
---|
[10481] | 113 | fclose(pFile);
|
---|
| 114 | return Frequency;
|
---|
[10442] | 115 | }
|
---|
| 116 |
|
---|
| 117 |
|
---|
[7362] | 118 | /** @todo RTmpCpuId(). */
|
---|
[1] | 119 |
|
---|
[7362] | 120 | RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
|
---|
[1] | 121 | {
|
---|
[26254] | 122 | return idCpu < rtMpLinuxMaxCpus() ? (int)idCpu : -1;
|
---|
[7362] | 123 | }
|
---|
[1] | 124 |
|
---|
| 125 |
|
---|
[7362] | 126 | RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
|
---|
| 127 | {
|
---|
[10419] | 128 | return (unsigned)iCpu < rtMpLinuxMaxCpus() ? iCpu : NIL_RTCPUID;
|
---|
[1] | 129 | }
|
---|
| 130 |
|
---|
| 131 |
|
---|
[7362] | 132 | RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
|
---|
[1] | 133 | {
|
---|
[10419] | 134 | return rtMpLinuxMaxCpus() - 1;
|
---|
[7362] | 135 | }
|
---|
| 136 |
|
---|
| 137 |
|
---|
| 138 | RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
|
---|
| 139 | {
|
---|
[10419] | 140 | /** @todo check if there is a simpler interface than this... */
|
---|
[60373] | 141 | int64_t i = 0;
|
---|
| 142 | int rc = RTLinuxSysFsReadIntFile(0, &i, "devices/system/cpu/cpu%d/online", (int)idCpu);
|
---|
| 143 | if ( RT_FAILURE(rc)
|
---|
[15399] | 144 | && RTLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu))
|
---|
[10419] | 145 | {
|
---|
[28863] | 146 | /** @todo Assert(!RTLinuxSysFsExists("devices/system/cpu/cpu%d/online",
|
---|
| 147 | * (int)idCpu));
|
---|
| 148 | * Unfortunately, the online file wasn't always world readable (centos
|
---|
| 149 | * 2.6.18-164). */
|
---|
[10419] | 150 | i = 1;
|
---|
[60373] | 151 | rc = VINF_SUCCESS;
|
---|
[10419] | 152 | }
|
---|
| 153 |
|
---|
[28858] | 154 | AssertMsg(i == 0 || i == -1 || i == 1, ("i=%d\n", i));
|
---|
[60373] | 155 | return RT_SUCCESS(rc) && i != 0;
|
---|
[7362] | 156 | }
|
---|
| 157 |
|
---|
| 158 |
|
---|
[9429] | 159 | RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
|
---|
[7362] | 160 | {
|
---|
[10419] | 161 | /** @todo check this up with hotplugging! */
|
---|
[15399] | 162 | return RTLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu);
|
---|
[7362] | 163 | }
|
---|
| 164 |
|
---|
| 165 |
|
---|
| 166 | RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
|
---|
| 167 | {
|
---|
| 168 | RTCpuSetEmpty(pSet);
|
---|
[10419] | 169 | RTCPUID cMax = rtMpLinuxMaxCpus();
|
---|
| 170 | for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
|
---|
| 171 | if (RTMpIsCpuPossible(idCpu))
|
---|
| 172 | RTCpuSetAdd(pSet, idCpu);
|
---|
[7362] | 173 | return pSet;
|
---|
| 174 | }
|
---|
| 175 |
|
---|
| 176 |
|
---|
| 177 | RTDECL(RTCPUID) RTMpGetCount(void)
|
---|
| 178 | {
|
---|
[10419] | 179 | RTCPUSET Set;
|
---|
| 180 | RTMpGetSet(&Set);
|
---|
| 181 | return RTCpuSetCount(&Set);
|
---|
[7362] | 182 | }
|
---|
[1] | 183 |
|
---|
[7362] | 184 |
|
---|
[46144] | 185 | RTDECL(RTCPUID) RTMpGetCoreCount(void)
|
---|
| 186 | {
|
---|
[46639] | 187 | RTCPUID cMax = rtMpLinuxMaxCpus();
|
---|
[46640] | 188 | uint32_t *paidCores = (uint32_t *)alloca(sizeof(paidCores[0]) * (cMax + 1));
|
---|
[50059] | 189 | uint32_t *paidPckgs = (uint32_t *)alloca(sizeof(paidPckgs[0]) * (cMax + 1));
|
---|
[46639] | 190 | uint32_t cCores = 0;
|
---|
[46144] | 191 | for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
|
---|
| 192 | {
|
---|
| 193 | if (RTMpIsCpuPossible(idCpu))
|
---|
| 194 | {
|
---|
[60373] | 195 | int64_t idCore = 0;
|
---|
| 196 | int64_t idPckg = 0;
|
---|
| 197 |
|
---|
| 198 | int rc = RTLinuxSysFsReadIntFile(0, &idCore, "devices/system/cpu/cpu%d/topology/core_id", (int)idCpu);
|
---|
| 199 | if (RT_SUCCESS(rc))
|
---|
| 200 | rc = RTLinuxSysFsReadIntFile(0, &idPckg, "devices/system/cpu/cpu%d/topology/physical_package_id", (int)idCpu);
|
---|
| 201 |
|
---|
| 202 | if (RT_SUCCESS(rc))
|
---|
[50059] | 203 | {
|
---|
[60373] | 204 | uint32_t i;
|
---|
| 205 |
|
---|
| 206 | for (i = 0; i < cCores; i++)
|
---|
| 207 | if ( paidCores[i] == (uint32_t)idCore
|
---|
| 208 | && paidPckgs[i] == (uint32_t)idPckg)
|
---|
| 209 | break;
|
---|
| 210 | if (i >= cCores)
|
---|
| 211 | {
|
---|
| 212 | paidCores[cCores] = (uint32_t)idCore;
|
---|
| 213 | paidPckgs[cCores] = (uint32_t)idPckg;
|
---|
| 214 | cCores++;
|
---|
| 215 | }
|
---|
[50059] | 216 | }
|
---|
[46144] | 217 | }
|
---|
| 218 | }
|
---|
[46639] | 219 | Assert(cCores > 0);
|
---|
[46144] | 220 | return cCores;
|
---|
| 221 | }
|
---|
| 222 |
|
---|
| 223 |
|
---|
[7362] | 224 | RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
|
---|
| 225 | {
|
---|
[10419] | 226 | RTCpuSetEmpty(pSet);
|
---|
| 227 | RTCPUID cMax = rtMpLinuxMaxCpus();
|
---|
| 228 | for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
|
---|
| 229 | if (RTMpIsCpuOnline(idCpu))
|
---|
| 230 | RTCpuSetAdd(pSet, idCpu);
|
---|
| 231 | return pSet;
|
---|
[7362] | 232 | }
|
---|
[1] | 233 |
|
---|
[7362] | 234 |
|
---|
| 235 | RTDECL(RTCPUID) RTMpGetOnlineCount(void)
|
---|
| 236 | {
|
---|
| 237 | RTCPUSET Set;
|
---|
| 238 | RTMpGetOnlineSet(&Set);
|
---|
| 239 | return RTCpuSetCount(&Set);
|
---|
[1] | 240 | }
|
---|
| 241 |
|
---|
[10442] | 242 |
|
---|
[46639] | 243 | RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
|
---|
| 244 | {
|
---|
| 245 | RTCPUID cMax = rtMpLinuxMaxCpus();
|
---|
[46640] | 246 | uint32_t *paidCores = (uint32_t *)alloca(sizeof(paidCores[0]) * (cMax + 1));
|
---|
[50059] | 247 | uint32_t *paidPckgs = (uint32_t *)alloca(sizeof(paidPckgs[0]) * (cMax + 1));
|
---|
[46639] | 248 | uint32_t cCores = 0;
|
---|
| 249 | for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
|
---|
| 250 | {
|
---|
| 251 | if (RTMpIsCpuOnline(idCpu))
|
---|
| 252 | {
|
---|
[60373] | 253 | int64_t idCore = 0;
|
---|
| 254 | int64_t idPckg = 0;
|
---|
| 255 |
|
---|
| 256 | int rc = RTLinuxSysFsReadIntFile(0, &idCore, "devices/system/cpu/cpu%d/topology/core_id", (int)idCpu);
|
---|
| 257 | if (RT_SUCCESS(rc))
|
---|
| 258 | rc = RTLinuxSysFsReadIntFile(0, &idPckg, "devices/system/cpu/cpu%d/topology/physical_package_id", (int)idCpu);
|
---|
| 259 |
|
---|
| 260 | if (RT_SUCCESS(rc))
|
---|
[50059] | 261 | {
|
---|
[60373] | 262 | uint32_t i;
|
---|
| 263 |
|
---|
| 264 | for (i = 0; i < cCores; i++)
|
---|
| 265 | if ( paidCores[i] == idCore
|
---|
| 266 | && paidPckgs[i] == idPckg)
|
---|
| 267 | break;
|
---|
| 268 | if (i >= cCores)
|
---|
| 269 | {
|
---|
| 270 | paidCores[cCores] = idCore;
|
---|
| 271 | paidPckgs[cCores] = idPckg;
|
---|
| 272 | cCores++;
|
---|
| 273 | }
|
---|
[50059] | 274 | }
|
---|
[46639] | 275 | }
|
---|
| 276 | }
|
---|
| 277 | Assert(cCores > 0);
|
---|
| 278 | return cCores;
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 |
|
---|
| 282 |
|
---|
[10442] | 283 | RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
|
---|
| 284 | {
|
---|
[60373] | 285 | int64_t kHz = 0;
|
---|
| 286 | int rc = RTLinuxSysFsReadIntFile(0, &kHz, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", (int)idCpu);
|
---|
| 287 | if (RT_FAILURE(rc))
|
---|
[10442] | 288 | {
|
---|
[10481] | 289 | /*
|
---|
| 290 | * The file may be just unreadable - in that case use plan B, i.e.
|
---|
[10446] | 291 | * /proc/cpuinfo to get the data we want. The assumption is that if
|
---|
| 292 | * cpuinfo_cur_freq doesn't exist then the speed won't change, and
|
---|
| 293 | * thus cur == max. If it does exist then cpuinfo contains the
|
---|
[10481] | 294 | * current frequency.
|
---|
| 295 | */
|
---|
[10446] | 296 | kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
|
---|
[10442] | 297 | }
|
---|
| 298 | return (kHz + 999) / 1000;
|
---|
| 299 | }
|
---|
| 300 |
|
---|
| 301 |
|
---|
| 302 | RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
|
---|
| 303 | {
|
---|
[60373] | 304 | int64_t kHz = 0;
|
---|
| 305 | int rc = RTLinuxSysFsReadIntFile(0, &kHz, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu);
|
---|
| 306 | if (RT_FAILURE(rc))
|
---|
[10446] | 307 | {
|
---|
[10481] | 308 | /*
|
---|
| 309 | * Check if the file isn't there - if it is there, then /proc/cpuinfo
|
---|
| 310 | * would provide current frequency information, which is wrong.
|
---|
| 311 | */
|
---|
[15399] | 312 | if (!RTLinuxSysFsExists("devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu))
|
---|
[10446] | 313 | kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
|
---|
| 314 | else
|
---|
| 315 | kHz = 0;
|
---|
| 316 | }
|
---|
[10442] | 317 | return (kHz + 999) / 1000;
|
---|
| 318 | }
|
---|