summaryrefslogtreecommitdiff
path: root/src/gc/unix
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2019-04-05 02:53:32 +0200
committerJan Vorlicek <janvorli@microsoft.com>2019-04-09 01:26:55 +0200
commit060990945a0117cb824b9d1c1e6c3a336533a3ee (patch)
treee762ccb63b9843ccf75d6f87b6ab14cfe352d40c /src/gc/unix
parent187410154a323024938a3281fb5a49f5e59ec350 (diff)
downloadcoreclr-060990945a0117cb824b9d1c1e6c3a336533a3ee.tar.gz
coreclr-060990945a0117cb824b9d1c1e6c3a336533a3ee.tar.bz2
coreclr-060990945a0117cb824b9d1c1e6c3a336533a3ee.zip
Remove Unix CPU groups emulation
This change removes CPU groups emulation from Unix PAL and modifies the GC and thread pool code accordingly.
Diffstat (limited to 'src/gc/unix')
-rw-r--r--src/gc/unix/gcenv.unix.cpp137
1 files changed, 124 insertions, 13 deletions
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index c71d211e01..a6d56f2433 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -55,6 +55,33 @@
#include "globals.h"
#include "cgroup.h"
+#if HAVE_NUMA_H
+
+#include <numa.h>
+#include <numaif.h>
+#include <dlfcn.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(_ARM_) || defined(_ARM64_)
#define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF
#else
@@ -109,6 +136,74 @@ uint32_t g_pageSizeUnixInl = 0;
AffinitySet g_processAffinitySet;
+#if HAVE_CPUSET_T
+typedef cpuset_t cpu_set_t;
+#endif
+
+// 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
+#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
+ g_numaHandle = dlopen("libnuma.so", RTLD_LAZY);
+ if (g_numaHandle == 0)
+ {
+ g_numaHandle = dlopen("libnuma.so.1", RTLD_LAZY);
+ }
+ if (g_numaHandle != 0)
+ {
+ dlsym(g_numaHandle, "numa_allocate_cpumask");
+#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
+}
+
// Initialize the interface implementation
// Return:
// true if it has succeeded, false if it has failed
@@ -221,6 +316,8 @@ bool GCToOSInterface::Initialize()
#endif // HAVE_SCHED_GETAFFINITY
+ NUMASupportInitialize();
+
return true;
}
@@ -235,6 +332,7 @@ void GCToOSInterface::Shutdown()
munmap(g_helperPage, OS_PAGE_SIZE);
CleanupCGroup();
+ NUMASupportCleanup();
}
// Get numeric id of the current thread if possible on the
@@ -468,8 +566,29 @@ void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
// true if it has succeeded, false if it has failed
bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint16_t node)
{
- assert(node == NUMA_NODE_UNDEFINED && "Numa allocation is not ported to local GC on unix yet");
- return mprotect(address, size, PROT_WRITE | PROT_READ) == 0;
+ bool success = mprotect(address, size, PROT_WRITE | PROT_READ) == 0;
+
+#if HAVE_NUMA_H
+ if (success && g_numaAvailable && (node != NUMA_NODE_UNDEFINED))
+ {
+ if ((int)node <= g_highestNumaNode)
+ {
+ int nodeMaskLength = (g_highestNumaNode + 1 + sizeof(unsigned long) - 1) / sizeof(unsigned long);
+ unsigned long *nodeMask = (unsigned long*)alloca(nodeMaskLength * sizeof(unsigned long));
+ memset(nodeMask, 0, nodeMaskLength);
+
+ int index = node / sizeof(unsigned long);
+ int mask = ((unsigned long)1) << (node & (sizeof(unsigned long) - 1));
+ nodeMask[index] = mask;
+
+ int st = mbind(address, size, MPOL_PREFERRED, nodeMask, g_highestNumaNode, 0);
+ assert(st == 0);
+ // If the mbind fails, we still return the allocated memory since the node is just a hint
+ }
+ }
+#endif // HAVE_NUMA_H
+
+ return success;
}
// Decomit virtual memory range.
@@ -775,13 +894,7 @@ uint32_t GCToOSInterface::GetTotalProcessorCount()
bool GCToOSInterface::CanEnableGCNumaAware()
{
- return false;
-}
-
-bool GCToOSInterface::GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no)
-{
- assert(!"Numa has not been ported to local GC for unix");
- return false;
+ return g_numaAvailable;
}
// Get processor number and optionally its NUMA node number for the specified heap number
@@ -806,10 +919,8 @@ bool GCToOSInterface::GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_n
if (GCToOSInterface::CanEnableGCNumaAware())
{
- if (!GCToOSInterface::GetNumaProcessorNode(procNumber, node_no))
- {
- *node_no = NUMA_NODE_UNDEFINED;
- }
+ int result = numa_node_of_cpu(procNumber);
+ *node_no = (result >= 0) ? (uint16_t)result : NUMA_NODE_UNDEFINED;
}
else
{