diff --git a/src/coreclr/gc/unix/CMakeLists.txt b/src/coreclr/gc/unix/CMakeLists.txt index e8915fb1b7ddf..36d9cfe406e18 100644 --- a/src/coreclr/gc/unix/CMakeLists.txt +++ b/src/coreclr/gc/unix/CMakeLists.txt @@ -5,6 +5,7 @@ include(configure.cmake) set(GC_PAL_SOURCES gcenv.unix.cpp + numasupport.dynamic.cpp events.cpp cgroup.cpp) diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 7ad9a38a75e51..b25194a6173bc 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -20,6 +20,7 @@ #include "gcenv.os.h" #include "gcenv.unix.inl" #include "volatile.h" +#include "numasupport.h" #if HAVE_SWAPCTL #include @@ -135,36 +136,6 @@ typedef cpuset_t cpu_set_t; #endif #endif // __APPLE__ -#if HAVE_NUMA_H - -#include -#include -#include -#include -#include -#include - -// List of all functions from the numa library that are used -#define FOR_ALL_NUMA_FUNCTIONS \ - PER_FUNCTION_BLOCK(mbind) \ - PER_FUNCTION_BLOCK(numa_available) \ - PER_FUNCTION_BLOCK(numa_max_node) \ - PER_FUNCTION_BLOCK(numa_node_of_cpu) - -// Declare pointers to all the used numa functions -#define PER_FUNCTION_BLOCK(fn) extern decltype(fn)* fn##_ptr; -FOR_ALL_NUMA_FUNCTIONS -#undef PER_FUNCTION_BLOCK - -// Redefine all calls to numa functions as calls through pointers that are set -// to the functions of libnuma in the initialization. -#define mbind(...) mbind_ptr(__VA_ARGS__) -#define numa_available() numa_available_ptr() -#define numa_max_node() numa_max_node_ptr() -#define numa_node_of_cpu(...) numa_node_of_cpu_ptr(__VA_ARGS__) - -#endif // HAVE_NUMA_H - #if defined(HOST_ARM) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) #define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF #else @@ -233,120 +204,8 @@ uint32_t g_pageSizeUnixInl = 0; AffinitySet g_processAffinitySet; -// The highest NUMA node available -int g_highestNumaNode = 0; -// Is numa available -bool g_numaAvailable = false; - -void* g_numaHandle = nullptr; - -#if HAVE_NUMA_H -#define PER_FUNCTION_BLOCK(fn) decltype(fn)* fn##_ptr; -FOR_ALL_NUMA_FUNCTIONS -#undef PER_FUNCTION_BLOCK - -#if defined(__linux__) -static bool ShouldOpenLibNuma() -{ - // This is a simple heuristic to determine if libnuma.so should be opened. There's - // no point in linking and resolving everything in this library if we're running on - // a system that's not NUMA-capable. - int fd = open("/sys/devices/system/node/possible", O_RDONLY | O_CLOEXEC); - - if (fd == -1) - { - // sysfs might not be mounted, not available, or the interface might have - // changed. Return `true' here so NUMASupportInitialize() can try initializing - // NUMA support with libnuma. - return true; - } - - while (true) - { - char buffer[32]; - ssize_t bytesRead = read(fd, buffer, 32); - - if (bytesRead == -1 && errno == EINTR) - { - continue; - } - - close(fd); - - // If an unknown error happened (bytesRead < 0), or the file was empty - // (bytesRead = 0), let libnuma handle this. Otherwise, if there's just - // one NUMA node, don't bother linking in libnuma. - return (bytesRead <= 0) ? true : strncmp(buffer, "0\n", bytesRead) != 0; - } -} -#else -static bool ShouldOpenLibNuma() -{ - return true; -} -#endif // __linux__ - -#endif // HAVE_NUMA_H - -// Initialize data structures for getting and setting thread affinities to processors and -// querying NUMA related processor information. -// On systems with no NUMA support, it behaves as if there was a single NUMA node with -// a single group of processors. -void NUMASupportInitialize() -{ -#if HAVE_NUMA_H - if (!ShouldOpenLibNuma()) - { - g_numaAvailable = false; - g_highestNumaNode = 0; - return; - } - - g_numaHandle = dlopen("libnuma.so.1", RTLD_LAZY); - if (g_numaHandle == 0) - { - g_numaHandle = dlopen("libnuma.so.1.0.0", RTLD_LAZY); - if (g_numaHandle == 0) - { - g_numaHandle = dlopen("libnuma.so", RTLD_LAZY); - } - } - if (g_numaHandle != 0) - { -#define PER_FUNCTION_BLOCK(fn) \ - fn##_ptr = (decltype(fn)*)dlsym(g_numaHandle, #fn); \ - if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from libnuma\n"); abort(); } -FOR_ALL_NUMA_FUNCTIONS -#undef PER_FUNCTION_BLOCK - - if (numa_available() == -1) - { - dlclose(g_numaHandle); - } - else - { - g_numaAvailable = true; - g_highestNumaNode = numa_max_node(); - } - } -#endif // HAVE_NUMA_H - if (!g_numaAvailable) - { - // No NUMA - g_highestNumaNode = 0; - } -} - -// Cleanup of the NUMA support data structures -void NUMASupportCleanup() -{ -#if HAVE_NUMA_H - if (g_numaAvailable) - { - dlclose(g_numaHandle); - } -#endif // HAVE_NUMA_H -} +extern "C" int g_highestNumaNode; +extern "C" bool g_numaAvailable; // Initialize the interface implementation // Return: diff --git a/src/coreclr/gc/unix/numasupport.dynamic.cpp b/src/coreclr/gc/unix/numasupport.dynamic.cpp new file mode 100644 index 0000000000000..45cf278464b43 --- /dev/null +++ b/src/coreclr/gc/unix/numasupport.dynamic.cpp @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "numasupport.h" + +// The highest NUMA node available +int g_highestNumaNode = 0; +// Is numa available +bool g_numaAvailable = false; + +#if HAVE_NUMA_H +#include +#include +#include +#include +#include +#include +#include + +#define PER_FUNCTION_BLOCK(fn) decltype(fn)* fn##_ptr; +FOR_ALL_NUMA_FUNCTIONS +#undef PER_FUNCTION_BLOCK + +void* g_numaHandle = nullptr; + +static bool ShouldOpenLibNuma() +{ +#ifdef TARGET_LINUX + // This is a simple heuristic to determine if libnuma.so should be opened. There's + // no point in linking and resolving everything in this library if we're running on + // a system that's not NUMA-capable. + int fd = open("/sys/devices/system/node/possible", O_RDONLY | O_CLOEXEC); + + if (fd == -1) + { + // sysfs might not be mounted, not available, or the interface might have + // changed. Return `true' here so NUMASupportInitialize() can try initializing + // NUMA support with libnuma. + return true; + } + + while (true) + { + char buffer[32]; + ssize_t bytesRead = read(fd, buffer, 32); + + if (bytesRead == -1 && errno == EINTR) + { + continue; + } + + close(fd); + + // If an unknown error happened (bytesRead < 0), or the file was empty + // (bytesRead = 0), let libnuma handle this. Otherwise, if there's just + // one NUMA node, don't bother linking in libnuma. + return (bytesRead <= 0) ? true : strncmp(buffer, "0\n", bytesRead) != 0; + } +#else + return true; +#endif // TARGET_LINUX +} + +#endif // HAVE_NUMA_H + +// Initialize data structures for getting and setting thread affinities to processors and +// querying NUMA related processor information. +// On systems with no NUMA support, it behaves as if there was a single NUMA node with +// a single group of processors. +void NUMASupportInitialize() +{ +#if HAVE_NUMA_H + if (!ShouldOpenLibNuma()) + { + g_numaAvailable = false; + g_highestNumaNode = 0; + return; + } + + g_numaHandle = dlopen("libnuma.so.1", RTLD_LAZY); + if (g_numaHandle == 0) + { + g_numaHandle = dlopen("libnuma.so.1.0.0", RTLD_LAZY); + if (g_numaHandle == 0) + { + g_numaHandle = dlopen("libnuma.so", RTLD_LAZY); + } + } + if (g_numaHandle != 0) + { +#define PER_FUNCTION_BLOCK(fn) \ + fn##_ptr = (decltype(fn)*)dlsym(g_numaHandle, #fn); \ + if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from libnuma\n"); abort(); } +FOR_ALL_NUMA_FUNCTIONS +#undef PER_FUNCTION_BLOCK + + if (numa_available() == -1) + { + dlclose(g_numaHandle); + } + else + { + g_numaAvailable = true; + g_highestNumaNode = numa_max_node(); + } + } +#endif // HAVE_NUMA_H + if (!g_numaAvailable) + { + // No NUMA + g_highestNumaNode = 0; + } +} + +// Cleanup of the NUMA support data structures +void NUMASupportCleanup() +{ +#if HAVE_NUMA_H + if (g_numaAvailable) + { + dlclose(g_numaHandle); + } +#endif // HAVE_NUMA_H +} diff --git a/src/coreclr/gc/unix/numasupport.h b/src/coreclr/gc/unix/numasupport.h new file mode 100644 index 0000000000000..6cbe644e261f3 --- /dev/null +++ b/src/coreclr/gc/unix/numasupport.h @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __NUMASUPPORT_H__ +#define __NUMASUPPORT_H__ + +#include "config.gc.h" + +#if HAVE_NUMA_H + +#include +#include + +#endif // HAVE_NUMA_H + +void NUMASupportInitialize(); +void NUMASupportCleanup(); + +#if HAVE_NUMA_H + +// List of all functions from the numa library that are used +#define FOR_ALL_NUMA_FUNCTIONS \ + PER_FUNCTION_BLOCK(mbind) \ + PER_FUNCTION_BLOCK(numa_available) \ + PER_FUNCTION_BLOCK(numa_max_node) \ + PER_FUNCTION_BLOCK(numa_node_of_cpu) + +// Declare pointers to all the used numa functions +#define PER_FUNCTION_BLOCK(fn) extern decltype(fn)* fn##_ptr; +FOR_ALL_NUMA_FUNCTIONS +#undef PER_FUNCTION_BLOCK + +// Redefine all calls to numa functions as calls through pointers that are set +// to the functions of libnuma in the initialization. +#define mbind(...) mbind_ptr(__VA_ARGS__) +#define numa_available() numa_available_ptr() +#define numa_max_node() numa_max_node_ptr() +#define numa_node_of_cpu(...) numa_node_of_cpu_ptr(__VA_ARGS__) + +#endif // HAVE_NUMA_H + +#endif // __NUMASUPPORT_H__ diff --git a/src/coreclr/gc/unix/numasupport.static.cpp b/src/coreclr/gc/unix/numasupport.static.cpp new file mode 100644 index 0000000000000..630ae52b162aa --- /dev/null +++ b/src/coreclr/gc/unix/numasupport.static.cpp @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "numasupport.h" + +#if HAVE_NUMA_H +#define PER_FUNCTION_BLOCK(fn) decltype(fn)* fn##_ptr = fn; +FOR_ALL_NUMA_FUNCTIONS +#undef PER_FUNCTION_BLOCK + +#endif // HAVE_NUMA_H + +// The highest NUMA node available +int g_highestNumaNode = 0; +// Is numa available +bool g_numaAvailable = false; + +void NUMASupportInitialize() +{ +#if HAVE_NUMA_H + if (numa_available() != -1) + { + g_numaAvailable = true; + g_highestNumaNode = numa_max_node(); + } +#endif // HAVE_NUMA_H +} + +void NUMASupportCleanup() +{ + // nop +} diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 8ed556c0fae8f..166e393818434 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -50,6 +50,8 @@ The .NET Foundation licenses this file to you under the MIT license. + + @@ -69,6 +71,12 @@ The .NET Foundation licenses this file to you under the MIT license. + + + + + + @@ -121,12 +129,14 @@ The .NET Foundation licenses this file to you under the MIT license. + - + + diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index 290deac1531e2..de943043c9ae6 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -263,3 +263,11 @@ if(NOT CLR_CMAKE_TARGET_ARCH_WASM) else() add_subdirectory(Portable) endif() + +if (CLR_CMAKE_TARGET_UNIX) + add_library(numasupportdynamic STATIC ${GC_DIR}/unix/numasupport.dynamic.cpp) + install_static_library(numasupportdynamic aotsdk nativeaot) + + add_library(numasupportstatic STATIC ${GC_DIR}/unix/numasupport.static.cpp) + install_static_library(numasupportstatic aotsdk nativeaot) +endif(CLR_CMAKE_TARGET_UNIX) diff --git a/src/coreclr/nativeaot/docs/compiling.md b/src/coreclr/nativeaot/docs/compiling.md index b8915706c1297..de7524384ffb3 100644 --- a/src/coreclr/nativeaot/docs/compiling.md +++ b/src/coreclr/nativeaot/docs/compiling.md @@ -26,9 +26,9 @@ Once you have added the package sources, add a reference to the ILCompiler packa or by adding the following element to the project file: ```xml - - - + + + ``` ## Cross-architecture compilation @@ -36,9 +36,9 @@ or by adding the following element to the project file: Native AOT toolchain allows targeting ARM64 on an x64 host and vice versa for both Windows and Linux and is now supported in the SDK. Cross-OS compilation, such as targeting Linux on a Windows host, is not supported. For SDK support, add the following to your project file, ```xml - - true - + + true + ``` Targeting win-arm64 on a Windows x64 host machine, @@ -77,22 +77,24 @@ NativeAOT binaries built with this feature can run even when libicu libraries ar You can use this feature by adding the `StaticICULinking` property to your project file as follows: ```xml - - true - + + true + ``` This feature is only supported on Linux. This feature is not supported when crosscompiling. +License (Unicode): https://github.com/unicode-org/icu/blob/main/icu4c/LICENSE + ### Prerequisites -Ubuntu (20.04+) -``` -sudo apt-get install libicu-dev cmake +Ubuntu +```sh +apt install libicu-dev cmake ``` Alpine -``` +```sh apk add cmake icu-static icu-dev ``` @@ -104,16 +106,50 @@ NativeAOT binaries built with this feature can run even when OpenSSL libraries a You can use this feature by adding the `StaticOpenSslLinking` property to your project file as follows: ```xml - - true - + + true + ``` This feature is only supported on Linux. This feature is not supported when crosscompiling. +License for OpenSSL v3+ (Apache v2.0): https://github.com/openssl/openssl/blob/master/LICENSE.txt +License for OpenSSL releases prior to v3 (dual OpenSSL and SSLeay license): https://www.openssl.org/source/license-openssl-ssleay.txt + ### Prerequisites -Alpine +Ubuntu +```sh +apt install libssl-dev cmake ``` + +Alpine +```sh apk add cmake openssl-dev openssl-libs-static ``` + +## Using statically linked NUMA +This feature can statically link NUMA library (libnuma.a) into your applications at build time. +NativeAOT binaries built with this feature can run even when NUMA libraries are not installed. + +You can use this feature by adding the `StaticNumaLinking` property to your project file as follows: + +```xml + + true + +``` + +License (LGPL v2.1): https://github.com/numactl/numactl/blob/master/LICENSE.LGPL2.1. Note that this license imposes specific requirements on distribution of statically linked binaries. + +### Prerequisites + +Ubuntu +```sh +apt install libnuma-dev +``` + +Alpine +```sh +apk add numactl-dev +``` diff --git a/src/coreclr/pal/src/include/pal/numa.h b/src/coreclr/pal/src/include/pal/numa.h deleted file mode 100644 index 82ce5a37f2aa3..0000000000000 --- a/src/coreclr/pal/src/include/pal/numa.h +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - - - -Module Name: - - include/pal/numa.h - -Abstract: - - Header file for the NUMA functions. - - - ---*/ - -#ifndef _PAL_NUMA_H_ -#define _PAL_NUMA_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif // __cplusplus - -BOOL -NUMASupportInitialize(); - -VOID -NUMASupportCleanup(); - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif /* _PAL_CRITSECT_H_ */ diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp index bddda8ceb023b..c88febfc26162 100644 --- a/src/coreclr/pal/src/init/pal.cpp +++ b/src/coreclr/pal/src/init/pal.cpp @@ -36,7 +36,6 @@ SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do th #include "pal/utils.h" #include "pal/debug.h" #include "pal/init.h" -#include "pal/numa.h" #include "pal/stackstring.hpp" #include "pal/cgroup.h" #include @@ -102,7 +101,7 @@ using namespace CorUnix; // necessary prototype here // -extern "C" BOOL CRTInitStdStreams( void ); +extern "C" BOOL CRTInitStdStreams(void); extern bool g_running_in_exe; @@ -692,7 +691,6 @@ Initialize( } goto done; - NUMASupportCleanup(); /* No cleanup required for CRTInitStdStreams */ CLEANUP15: FILECleanupStdHandles();