Skip to content

Commit

Permalink
Add statically linked libnuma support for AOT (#80671)
Browse files Browse the repository at this point in the history
* Add statically linked libnuma support for AOT

* Fix windows build

* Update src/coreclr/nativeaot/docs/compiling.md

* Align formatting in doc

* Address CR feedback

* Add ability to skip static PIE

* Cleanup pal.cpp

* Highlight LGPL with a note in doc

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
  • Loading branch information
am11 and jkotas committed Jan 17, 2023
1 parent 0090de4 commit e9b9489
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 203 deletions.
1 change: 1 addition & 0 deletions src/coreclr/gc/unix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ include(configure.cmake)

set(GC_PAL_SOURCES
gcenv.unix.cpp
numasupport.dynamic.cpp
events.cpp
cgroup.cpp)

Expand Down
147 changes: 3 additions & 144 deletions src/coreclr/gc/unix/gcenv.unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "gcenv.os.h"
#include "gcenv.unix.inl"
#include "volatile.h"
#include "numasupport.h"

#if HAVE_SWAPCTL
#include <sys/swap.h>
Expand Down Expand Up @@ -135,36 +136,6 @@ typedef cpuset_t cpu_set_t;
#endif
#endif // __APPLE__

#if HAVE_NUMA_H

#include <numa.h>
#include <numaif.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.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

#if defined(HOST_ARM) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64)
#define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF
#else
Expand Down Expand Up @@ -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:
Expand Down
124 changes: 124 additions & 0 deletions src/coreclr/gc/unix/numasupport.dynamic.cpp
Original file line number Diff line number Diff line change
@@ -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 <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#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
}
42 changes: 42 additions & 0 deletions src/coreclr/gc/unix/numasupport.h
Original file line number Diff line number Diff line change
@@ -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 <numa.h>
#include <numaif.h>

#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__
32 changes: 32 additions & 0 deletions src/coreclr/gc/unix/numasupport.static.cpp
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit e9b9489

Please sign in to comment.