From e4ff3eafac59ea60ca5975d0def393d27e2697ea 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 | 69 ++++++++++--------- .../concurrentutil/numa/OSNuma.java | 22 ++++-- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java b/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java index 0169c09..d87ad4a 100644 --- a/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java +++ b/src/main/java/ca/spottedleaf/concurrentutil/numa/LinuxNuma.java @@ -25,52 +25,52 @@ 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; - } else { + if (cpuMask != null) { final int totalCpus = LibNuma.numa_num_possible_cpus(); - - int[] coreToNuma = new int[0]; - 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 - 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; + 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) { + // failed to gather topology + ok = false; + break; } - // it is set, so mark it in the core mapping - if (coreToNuma.length <= cpu) { - coreToNuma = Arrays.copyOf(coreToNuma, cpu + 1); + + for (int cpu = 0; cpu < totalCpus; ++cpu) { + final int bit = LibNuma.numa_bitmask_isbitset(cpuMask, cpu); + if (bit == 0) { + // not set + continue; + } + // it is set, so mark it in the core mapping coreToNuma[cpu] = node; } } - } - 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 - costArray[i][j] = dist <= 0 ? 255 : 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); + // distance is 0 when it cannot be determined + costArray[i][j] = dist <= 0 ? 255 : dist; + } + } + + instance = new LinuxNuma(coreToNuma, costArray); } } - - INSTANCE = coreToNuma == null ? null : new LinuxNuma(coreToNuma, costArray); } } finally { if (cpuMask != null) { @@ -78,6 +78,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 1bcc425..e7bdff1 100644 --- a/src/main/java/ca/spottedleaf/concurrentutil/numa/OSNuma.java +++ b/src/main/java/ca/spottedleaf/concurrentutil/numa/OSNuma.java @@ -129,7 +129,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())); @@ -151,7 +155,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][]; @@ -195,7 +204,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 @@ -214,7 +228,7 @@ public int[] getCores(final int numaId) { @Override public int getCurrentNumaNode() { - return this.coreToNuma[this.getCurrentCore()]; + return this.getNumaNode(this.getCurrentCore()); } }