As the title suggests, make omp_get_device_distances more
robust - fixing issues mentioned by Thomas.

In particular, it now handles a concurrent calls to the function
before the distance array has been filled. And it honors that some
systems prefer node 0 and 8 to node 0 and 1 (aka noncontiguous
node numbers).

And I found a new reason why numbering does not work:
  'No NUMA configuration found' (reported by the kernel, e.g. via dmesg)
(I now get this on my laptop; I am pretty sure it worked three
weeks ago.) In that case, -1 is reported.

I tried it on an PowerPC system (that has '0,8' nodes) albeit with some
debug output as the PCI system does not contain NUMA data (that's
consistent with 'nvidia-smi topo -m' that also lacks that data). And I tried it on a '0-1' node system with AMD GPUs. I also checked that busy wait works, again adding some debug printf output. If virtualized - or numa is not available for other reasons, '-1' is the reported distance by the kernel - which GCC turns into '10' (default value of inner-node distance) for the reported distance as documented at https://gcc.gnu.org/onlinedocs/libgomp/omp_005fget_005fdevice_005fdistances.html Comments before I commit this patch later today? Tobias PS: I believe this patch addresses all follow-up remarks of the previous commit, cf. also PR125877.
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.

 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 a3d84870e6e..85214e56dc4 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;
 }

Reply via email to