From 644b02b437972e29737893843a1b46e6335e197c Mon Sep 17 00:00:00 2001 From: yunuservices Date: Fri, 20 Feb 2026 17:25:10 +0300 Subject: [PATCH] Harden NUMA affinity node validation and sparse CPU mapping --- .../concurrentutil/numa/LinuxNuma.java | 97 +++++++++---------- .../concurrentutil/numa/OSNuma.java | 22 ++++- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java b/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java index ff29b62..8c1428c 100644 --- a/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java +++ b/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java @@ -32,69 +32,67 @@ public final class LinuxNuma extends OSNuma.PreCalculatedNuma { } public static final LinuxNuma INSTANCE; static { - if (!LIBRARIES_AVAILABLE) { - INSTANCE = null; - } else { + LinuxNuma instance = null; + if (LIBRARIES_AVAILABLE) { final int totalNumaNodes = LibNuma.numa_max_node() + 1; final Pointer cpuMask = LibNuma.numa_allocate_cpumask(); try { - if (cpuMask == null) { - INSTANCE = null; - LOGGER.debug("Failed to create Linux NUMA CPU mapping: Failed to allocate cpu mask for libnuma"); - } else { + if (cpuMask != null) { final int totalCpus = LibNuma.numa_num_possible_cpus(); + if (totalCpus > 0) { + final int[] coreToNuma = new int[totalCpus]; + Arrays.fill(coreToNuma, -1); + + boolean ok = true; + for (int node = 0; node < totalNumaNodes; ++node) { + LibNuma.numa_bitmask_clearall(cpuMask); + final int res = LibNuma.numa_node_to_cpus(node, cpuMask); + if (res != 0) { + ok = false; + break; + } - int[] coreToNuma = new int[totalCpus]; - Arrays.fill(coreToNuma, -1); - core_to_numa_setup: - for (int node = 0; node < totalNumaNodes; ++node) { - LibNuma.numa_bitmask_clearall(cpuMask); - final int res = LibNuma.numa_node_to_cpus(node, cpuMask); - if (res != 0) { - // failed - LOGGER.debug("Failed to create Linux NUMA CPU mapping: Failed libnuma numa_node_to_cpus(" + node + ", ...): " + res); - coreToNuma = null; - break; - } - - for (int cpu = 0; cpu < totalCpus; ++cpu) { - final int bit = LibNuma.numa_bitmask_isbitset(cpuMask, cpu); - if (bit == 0) { - // not set - continue; + for (int cpu = 0; cpu < totalCpus; ++cpu) { + final int bit = LibNuma.numa_bitmask_isbitset(cpuMask, cpu); + if (bit == 0) { + continue; + } + if (coreToNuma[cpu] != -1) { + ok = false; + break; + } + if (coreToNuma.length <= cpu) { + coreToNuma = Arrays.copyOf(coreToNuma, cpu + 1); + } + coreToNuma[cpu] = node; } - if (coreToNuma[cpu] != -1) { - LOGGER.debug("Failed to create Linux NUMA CPU mapping: Duplicate node mapping for core " + cpu + ": " + coreToNuma[cpu] + " and " + node); - coreToNuma = null; - break core_to_numa_setup; + if (!ok) { + break; } - // it is set, so mark it in the core mapping - coreToNuma[cpu] = node; } - } - if (coreToNuma != null) { - for (int cpu = 0; cpu < coreToNuma.length; ++cpu) { - final int node = coreToNuma[cpu]; - if (node == -1) { - LOGGER.debug("Failed to create Linux NUMA CPU mapping: No node mapping for core " + cpu); - coreToNuma = null; - break; + + if (ok) { + for (int cpu = 0; cpu < coreToNuma.length; ++cpu) { + if (coreToNuma[cpu] == -1) { + ok = false; + break; + } } } - } - final int[][] costArray = new int[totalNumaNodes][totalNumaNodes]; - for (int i = 0; i < totalNumaNodes; ++i) { - for (int j = 0; j < totalNumaNodes; ++j) { - final int dist = LibNuma.numa_distance(i, j); - // distance is 0 when it cannot be determined - // distances 1-9 are reserved and have no meaning - costArray[i][j] = dist < 10 ? OSNuma.NUMA_DISTANCE_CANNOT_DETERMINE : dist; + if (ok) { + final int[][] costArray = new int[totalNumaNodes][totalNumaNodes]; + for (int i = 0; i < totalNumaNodes; ++i) { + for (int j = 0; j < totalNumaNodes; ++j) { + final int dist = LibNuma.numa_distance(i, j); + costArray[i][j] = dist <= 0 ? 255 : dist; + } + } + + instance = new LinuxNuma(coreToNuma, costArray); } } - - INSTANCE = coreToNuma == null ? null : new LinuxNuma(coreToNuma, costArray); } } finally { if (cpuMask != null) { @@ -102,6 +100,7 @@ public final class LinuxNuma extends OSNuma.PreCalculatedNuma { } } } + INSTANCE = instance; } private LinuxNuma(final int[] coreToNuma, final int[][] costArray) { diff --git a/src/main/java/ca/spottedleaf/concurrentutil/numa/OSNuma.java b/src/main/java/ca/spottedleaf/concurrentutil/numa/OSNuma.java index 9702b6e..327d92f 100644 --- a/src/main/java/ca/spottedleaf/concurrentutil/numa/OSNuma.java +++ b/src/main/java/ca/spottedleaf/concurrentutil/numa/OSNuma.java @@ -131,7 +131,11 @@ public default void setCurrentNumaAffinity(final long[] to) { public default void setCurrentNumaAffinity(final int[] numaNodes) { final IntArrayList cores = new IntArrayList(); for (final int node : numaNodes) { - cores.addAll(IntArrayList.wrap(this.getCores(node))); + final int[] nodeCores = this.getCores(node); + if (nodeCores == null) { + throw new IllegalArgumentException("Unknown NUMA node: " + node); + } + cores.addAll(IntArrayList.wrap(nodeCores)); } this.setCurrentThreadAffinity(FlatBitsetUtil.intsToBitset(cores.toIntArray())); @@ -153,7 +157,12 @@ public PreCalculatedNuma(final int[] coreToNuma, final int[][] costArray) { } for (int core = 0; core < coreToNuma.length; ++core) { - numaToCore[coreToNuma[core]].add(core); + final int node = coreToNuma[core]; + if (node < 0 || node >= numaToCore.length) { + // unknown mapping, ignore + continue; + } + numaToCore[node].add(core); } this.numaToCore = new int[this.costArray.length][]; @@ -197,7 +206,12 @@ public int getNumaNode(final int coreId) { // cannot determine return -1; } - return this.coreToNuma[coreId]; + final int node = this.coreToNuma[coreId]; + if (node < 0 || node >= this.costArray.length) { + // cannot determine + return -1; + } + return node; } @Override @@ -216,7 +230,7 @@ public int[] getCores(final int numaId) { @Override public int getCurrentNumaNode() { - return this.coreToNuma[this.getCurrentCore()]; + return this.getNumaNode(this.getCurrentCore()); } }