Skip to content

Commit 7a71ac4

Browse files
authored
Fix runtime initialization on linux with cpu hotplug enabled (#128069)
On unix, during initialization, the runtime obtains the total number of CPUs via `sysconf(_SC_NPROCESSORS_CONF)`. This should return the current number of cpus that are currently present on the system. It turns out linux has cpu hotplug support, so this number can increase. When hotplug is enabled, the kernel reserves storage for the max possible number of CPUs. This max number is exported in `/sys/devices/system/cpu/possible`. The problem is that, when allocating the `cpu_set_t*` for use with `sched_getaffinity`, this api failed because the OS expected for the cpu set to have reserved space for the maximum amount of cpu's, not just for the ones that are currently present.
1 parent 06c1222 commit 7a71ac4

7 files changed

Lines changed: 88 additions & 7 deletions

File tree

src/coreclr/gc/unix/gcenv.unix.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <minipal/memorybarrierprocesswide.h>
2727
#include <minipal/thread.h>
2828
#include <minipal/time.h>
29+
#include <minipal/cpucount.h>
2930

3031
#if HAVE_SWAPCTL
3132
#include <sys/swap.h>
@@ -146,7 +147,7 @@ bool GCToOSInterface::Initialize()
146147
return false;
147148
}
148149

149-
int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF);
150+
int configuredCpuCount = minipal_get_cpu_max_possible_count();
150151
if (configuredCpuCount == -1)
151152
{
152153
return false;

src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "RhConfig.h"
2929

3030
#include <unistd.h>
31+
#include <minipal/cpucount.h>
3132
#include <sched.h>
3233
#include <sys/mman.h>
3334
#include <sys/types.h>
@@ -456,10 +457,10 @@ void InitializeCurrentProcessCpuCount()
456457
{
457458
#if HAVE_SCHED_GETAFFINITY
458459

459-
int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF);
460+
int configuredCpuCount = minipal_get_cpu_max_possible_count();
460461
if (configuredCpuCount == -1)
461462
{
462-
// In the unlikely event that sysconf(_SC_NPROCESSORS_CONF) fails, just assume a reasonable default maximum number of CPUs to avoid failing.
463+
// In the unlikely event that minipal_get_cpu_max_possible_count() fails, just assume a reasonable default maximum number of CPUs to avoid failing.
463464
configuredCpuCount = CPU_SETSIZE;
464465
}
465466

src/coreclr/pal/src/misc/sysinfo.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Revision History:
2626
#include <unistd.h>
2727
#include <minipal/utils.h>
2828
#include <minipal/ospagesize.h>
29+
#include <minipal/cpucount.h>
2930
#define __STDC_FORMAT_MACROS
3031
#include <inttypes.h>
3132
#include <sys/types.h>
@@ -157,10 +158,10 @@ PAL_GetLogicalCpuCountFromOS()
157158
{
158159
#if HAVE_SCHED_GETAFFINITY
159160

160-
int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF);
161+
int configuredCpuCount = minipal_get_cpu_max_possible_count();
161162
if (configuredCpuCount == -1)
162163
{
163-
// In the unlikely event that sysconf(_SC_NPROCESSORS_CONF) fails, just assume a reasonable default maximum number of CPUs to avoid failing.
164+
// In the unlikely event that minipal_get_cpu_max_possible_count() fails, just assume a reasonable default maximum number of CPUs to avoid failing.
164165
configuredCpuCount = CPU_SETSIZE;
165166
}
166167

src/coreclr/pal/src/thread/thread.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do
2828
#include "pal/virtual.h"
2929

3030
#include <minipal/thread.h>
31+
#include <minipal/cpucount.h>
3132

3233
#if defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID
3334
#include <sys/cdefs.h>
@@ -1392,10 +1393,10 @@ CPalThread::ThreadEntry(
13921393
// - https://forum.snapcraft.io/t/requesting-autoconnect-for-interfaces-in-pigmeat-process-control-home/17987/13
13931394

13941395
{
1395-
int configuredCpuCount = sysconf(_SC_NPROCESSORS_CONF);
1396+
int configuredCpuCount = minipal_get_cpu_max_possible_count();
13961397
if (configuredCpuCount == -1)
13971398
{
1398-
// In the unlikely event that sysconf(_SC_NPROCESSORS_CONF) fails, just assume a reasonable default maximum number of CPUs to avoid failing thread creation.
1399+
// In the unlikely event that minipal_get_cpu_max_possible_count() fails, just assume a reasonable default maximum number of CPUs to avoid failing thread creation.
13991400
configuredCpuCount = CPU_SETSIZE;
14001401
}
14011402

src/native/minipal/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ if(NOT WIN32 AND NOT CLR_CMAKE_TARGET_ARCH_WASM AND NOT HOST_WASM)
2424
list(APPEND SOURCES ospagesize.c)
2525
endif()
2626

27+
if(CLR_CMAKE_HOST_UNIX)
28+
list(APPEND SOURCES cpucount.c)
29+
endif()
30+
2731
# Provide an object library for scenarios where we ship static libraries
2832
include_directories(${CLR_SRC_NATIVE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
2933

src/native/minipal/cpucount.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#include "cpucount.h"
5+
6+
#include <stdio.h>
7+
#include <unistd.h>
8+
9+
int minipal_get_cpu_max_possible_count(void)
10+
{
11+
#if defined(__linux__)
12+
FILE* f = fopen("/sys/devices/system/cpu/possible", "r");
13+
if (f != NULL)
14+
{
15+
int maxCpu = -1;
16+
for (;;)
17+
{
18+
int lo, hi;
19+
int matched = fscanf(f, "%d-%d", &lo, &hi);
20+
if (matched == 1)
21+
{
22+
hi = lo;
23+
}
24+
else if (matched != 2)
25+
{
26+
break;
27+
}
28+
29+
if (maxCpu < hi)
30+
{
31+
maxCpu = hi;
32+
}
33+
34+
int ch = fgetc(f);
35+
if (ch == EOF || ch != ',')
36+
{
37+
break;
38+
}
39+
}
40+
fclose(f);
41+
if (maxCpu != -1)
42+
{
43+
return maxCpu + 1;
44+
}
45+
}
46+
#endif
47+
48+
return (int)sysconf(_SC_NPROCESSORS_CONF);
49+
}

src/native/minipal/cpucount.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#ifndef HAVE_MINIPAL_CPUCOUNT_H
5+
#define HAVE_MINIPAL_CPUCOUNT_H
6+
7+
#ifdef __cplusplus
8+
extern "C"
9+
{
10+
#endif // __cplusplus
11+
12+
// Returns the maximum number of CPUs that could ever be available on this system,
13+
// suitable for sizing cpu_set_t allocations via CPU_ALLOC.
14+
//
15+
// On Linux, this reads /sys/devices/system/cpu/possible to account for CPU hotplug.
16+
// This may be larger than the number of online or present CPUs.
17+
// Falls back to sysconf(_SC_NPROCESSORS_CONF) if the sysfs file is unavailable.
18+
int minipal_get_cpu_max_possible_count(void);
19+
20+
#ifdef __cplusplus
21+
}
22+
#endif // __cplusplus
23+
24+
#endif // HAVE_MINIPAL_CPUCOUNT_H

0 commit comments

Comments
 (0)