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