https://gcc.gnu.org/g:929b1c42f3d23dcffacdedb005ab83d3607b47ce
commit r17-2397-g929b1c42f3d23dcffacdedb005ab83d3607b47ce Author: Tobias Burnus <[email protected]> Date: Tue Jul 14 16:09:54 2026 +0200 libgomp: Robustify omp_get_device_distances [PR125877] libgomp/ChangeLog: PR libgomp/125877 * config/linux/numa.c (gomp_get_numa_distance): Avoid data race issue during initalization and handle non-contiguous node numbering. Diff: --- libgomp/config/linux/numa.c | 186 ++++++++++++++++++++++++++++++++------------ 1 file changed, 136 insertions(+), 50 deletions(-) diff --git a/libgomp/config/linux/numa.c b/libgomp/config/linux/numa.c index a3d84870e6e8..85214e56dc41 100644 --- a/libgomp/config/linux/numa.c +++ b/libgomp/config/linux/numa.c @@ -37,9 +37,6 @@ #include <sys/syscall.h> -static int num_numa_nodes = 0; -static int *numa_distances = NULL; - int gomp_get_current_numa_node () { @@ -51,59 +48,148 @@ gomp_get_current_numa_node () int gomp_get_numa_distance (int node1, int node2) { - if (node1 < 0 || node2 < 0 || num_numa_nodes < 0) - return -1; + /* num_numa_nodes: 0 = uninit, -1 = being initialized, -2 = error, + > 0 = the number of numa nodes. + numa_distances: a 2D array of #nodes * #nodes with the distances. + remap: map the numa number to the index in numa_distances; + as numa numbers might not be contiguous. + num_remap: size of the remap array. */ + static int num_numa_nodes = 0; + static int *numa_distances = NULL; + static unsigned num_remap = 0; + static unsigned *remap = NULL; - if (numa_distances == NULL) +retry: + int num = __atomic_load_n (&num_numa_nodes, MEMMODEL_RELAXED); + if (node1 < 0 || node2 < 0 || num <= -2) + return -1; + if (num > 0) { - num_numa_nodes = -1; - DIR *dir = opendir ("/sys/devices/system/node"); - if (!dir) + if (node1 > num_remap || node2 > num_remap) return -1; - struct dirent *dp; - int cnt = 0; - errno = 0; - while ((dp = readdir(dir)) != NULL) - if (strncmp ("node", dp->d_name, 4 /* strlen ("node") */) == 0) - cnt++; - else if (errno) - { - closedir (dir); - return -1; - } - closedir (dir); - numa_distances = (int *) gomp_malloc (sizeof (int) * cnt * cnt); + node1 = remap[node1]; + node2 = remap[node2]; + if (node1 >= num || node2 >= num) + return -1; + return numa_distances[node1 * num_numa_nodes + node2]; + } + + /* Obtain distance data - or busy wait until another thread did so. */ + int val = 0; + if (!__atomic_compare_exchange_n (&num_numa_nodes, &val, -1, false, + MEMMODEL_ACQUIRE, MEMMODEL_ACQUIRE)) + { + while (-1 == __atomic_load_n (&num_numa_nodes, MEMMODEL_ACQUIRE)); + __asm volatile ("" : : : "memory"); + goto retry; + } + + /* Obtain numa nodes. */ - constexpr int len = sizeof ("/sys/devices/system/node/node12345/" - "distance"); - char filename[len]; + num = 0; + FILE *f = fopen ("/sys/devices/system/node/online", "r"); + if (f == NULL) + goto fail; + char *lineptr = NULL; + size_t nline; + if (getline (&lineptr, &nline, f) <= 0) + { + free (lineptr); + fclose (f); + goto fail; + } + fclose (f); + + /* Create mapping array between the node number and contiguous + n-th online node count. */ + unsigned constexpr UNSET_REMAP = (unsigned) -1; + num_remap = 8; + remap = malloc (sizeof (*remap) * num_remap); + if (!remap) + goto fail; + for (int i = 0; i < num_remap; i++) + remap[i] = UNSET_REMAP; + char *q = lineptr; + while (*q && *q != '\n') + { + unsigned nfirst, nlast; + char *end; - for (int i = 0; i < cnt; i++) + errno = 0; + nfirst = strtoul (q, &end, 10); + if (errno || end == q) + goto fail; + q = end; + nlast = nfirst; + if (*q == '-') + { + errno = 0; + nlast = strtoul (q + 1, &end, 10); + if (errno || end == q + 1 || nlast < nfirst) + goto fail; + q = end; + } + if (num_remap <= nlast) { - if (len < snprintf (filename, sizeof (filename), - "/sys/devices/system/node/node%d/distance", i)) - return -1; - int distance = -1; - FILE *in = fopen (filename, "r"); - if (!in) - { - free (numa_distances); - return -1; - } - for (int j = 0; j < cnt; j++) - { - fscanf (in, "%d", &distance); - if (distance == -1) - { - fclose (in); - free (numa_distances); - return -1; - } - numa_distances[i * cnt + j] = distance; - } - fclose (in); + int n = nlast > 2 * num_remap ? nlast + 1 : num_remap * 2; + remap = realloc (remap, sizeof (*remap) * n); + if (!remap || n <= nlast) + goto fail; + for (int i = num_remap; i < n; i++) + remap[i] = UNSET_REMAP; + num_remap = n; } - num_numa_nodes = cnt; + for (; nfirst <= nlast; nfirst++) + remap[nfirst] = num++; + if (*q == ',') + ++q; } - return numa_distances[node1 * num_numa_nodes + node2]; + free (lineptr); + lineptr = NULL; + + /* Fill distance data. */ + numa_distances = (int *) gomp_malloc (sizeof (*numa_distances) * num * num); + + for (int i = 0; i < num_remap; i++) + if (remap[i] == UNSET_REMAP) + continue; + else + { + constexpr int len = sizeof ("/sys/devices/system/node/node12345/" + "distance"); + char filename[len]; + int node = remap[i]; + if (node < 0) + goto fail; + if (len < snprintf (filename, sizeof (filename), + "/sys/devices/system/node/node%d/distance", i)) + goto fail; + FILE *in = fopen (filename, "r"); + if (!in) + goto fail; + for (int j = 0; j < num; j++) + { + /* Distance -1 might happen on virtualized systems or when the + kernel reports "No NUMA configuration found". The code assumes + that only distance values show up for online nodes. */ + int distance; + if (fscanf (in, "%d", &distance) != 1) + { + fclose (in); + goto fail; + } + numa_distances[node * num + j] = distance; + } + fclose (in); + } + __asm volatile ("" : : : "memory"); + __atomic_store_n (&num_numa_nodes, num, MEMMODEL_RELAXED); + goto retry; + +fail: + free (lineptr); + free (remap); + free (numa_distances); + __atomic_store_n (&num_numa_nodes, -2, MEMMODEL_RELAXED); + return -1; }
