(root)/
gcc-13.2.0/
libgomp/
config/
linux/
affinity.c
       1  /* Copyright (C) 2006-2023 Free Software Foundation, Inc.
       2     Contributed by Jakub Jelinek <jakub@redhat.com>.
       3  
       4     This file is part of the GNU Offloading and Multi Processing Library
       5     (libgomp).
       6  
       7     Libgomp is free software; you can redistribute it and/or modify it
       8     under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 3, or (at your option)
      10     any later version.
      11  
      12     Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
      13     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      14     FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      15     more details.
      16  
      17     Under Section 7 of GPL version 3, you are granted additional
      18     permissions described in the GCC Runtime Library Exception, version
      19     3.1, as published by the Free Software Foundation.
      20  
      21     You should have received a copy of the GNU General Public License and
      22     a copy of the GCC Runtime Library Exception along with this program;
      23     see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24     <http://www.gnu.org/licenses/>.  */
      25  
      26  /* This is a Linux specific implementation of a CPU affinity setting.  */
      27  
      28  #ifndef _GNU_SOURCE
      29  #define _GNU_SOURCE 1
      30  #endif
      31  #include "libgomp.h"
      32  #include "proc.h"
      33  #include <errno.h>
      34  #include <stdlib.h>
      35  #include <stdio.h>
      36  #include <string.h>
      37  #include <unistd.h>
      38  #include <limits.h>
      39  
      40  #ifdef HAVE_PTHREAD_AFFINITY_NP
      41  
      42  #ifndef CPU_ALLOC_SIZE
      43  #define CPU_ISSET_S(idx, size, set) CPU_ISSET(idx, set)
      44  #define CPU_ZERO_S(size, set) CPU_ZERO(set)
      45  #define CPU_SET_S(idx, size, set) CPU_SET(idx, set)
      46  #define CPU_CLR_S(idx, size, set) CPU_CLR(idx, set)
      47  #endif
      48  
      49  void
      50  gomp_init_affinity (void)
      51  {
      52    if (gomp_places_list == NULL)
      53      {
      54        if (!gomp_affinity_init_level (1, ULONG_MAX, true))
      55  	return;
      56      }
      57  
      58    struct gomp_thread *thr = gomp_thread ();
      59    pthread_setaffinity_np (pthread_self (), gomp_cpuset_size,
      60  			  (cpu_set_t *) gomp_places_list[0]);
      61    thr->place = 1;
      62    thr->ts.place_partition_off = 0;
      63    thr->ts.place_partition_len = gomp_places_list_len;
      64  }
      65  
      66  void
      67  gomp_init_thread_affinity (pthread_attr_t *attr, unsigned int place)
      68  {
      69    pthread_attr_setaffinity_np (attr, gomp_cpuset_size,
      70  			       (cpu_set_t *) gomp_places_list[place]);
      71  }
      72  
      73  void **
      74  gomp_affinity_alloc (unsigned long count, bool quiet)
      75  {
      76    unsigned long i;
      77    void **ret;
      78    char *p;
      79  
      80    if (gomp_cpusetp == NULL)
      81      {
      82        if (!quiet)
      83  	gomp_error ("Could not get CPU affinity set");
      84        return NULL;
      85      }
      86  
      87    ret = malloc (count * sizeof (void *) + count * gomp_cpuset_size);
      88    if (ret == NULL)
      89      {
      90        if (!quiet)
      91  	gomp_error ("Out of memory trying to allocate places list");
      92        return NULL;
      93      }
      94  
      95    p = (char *) (ret + count);
      96    for (i = 0; i < count; i++, p += gomp_cpuset_size)
      97      ret[i] = p;
      98    return ret;
      99  }
     100  
     101  void
     102  gomp_affinity_init_place (void *p)
     103  {
     104    cpu_set_t *cpusetp = (cpu_set_t *) p;
     105    CPU_ZERO_S (gomp_cpuset_size, cpusetp);
     106  }
     107  
     108  bool
     109  gomp_affinity_add_cpus (void *p, unsigned long num,
     110  			unsigned long len, long stride, bool quiet)
     111  {
     112    cpu_set_t *cpusetp = (cpu_set_t *) p;
     113    unsigned long max = 8 * gomp_cpuset_size;
     114    for (;;)
     115      {
     116        if (num >= max)
     117  	{
     118  	  if (!quiet)
     119  	    gomp_error ("Logical CPU number %lu out of range", num);
     120  	  return false;
     121  	}
     122        CPU_SET_S (num, gomp_cpuset_size, cpusetp);
     123        if (--len == 0)
     124  	return true;
     125        if ((stride < 0 && num + stride > num)
     126  	  || (stride > 0 && num + stride < num))
     127  	{
     128  	  if (!quiet)
     129  	    gomp_error ("Logical CPU number %lu+%ld out of range",
     130  			num, stride);
     131  	  return false;
     132  	}
     133        num += stride;
     134      }
     135  }
     136  
     137  bool
     138  gomp_affinity_remove_cpu (void *p, unsigned long num)
     139  {
     140    cpu_set_t *cpusetp = (cpu_set_t *) p;
     141    if (num >= 8 * gomp_cpuset_size)
     142      {
     143        gomp_error ("Logical CPU number %lu out of range", num);
     144        return false;
     145      }
     146    if (!CPU_ISSET_S (num, gomp_cpuset_size, cpusetp))
     147      {
     148        gomp_error ("Logical CPU %lu to be removed is not in the set", num);
     149        return false;
     150      }
     151    CPU_CLR_S (num, gomp_cpuset_size, cpusetp);
     152    return true;
     153  }
     154  
     155  bool
     156  gomp_affinity_copy_place (void *p, void *q, long stride)
     157  {
     158    unsigned long i, max = 8 * gomp_cpuset_size;
     159    cpu_set_t *destp = (cpu_set_t *) p;
     160    cpu_set_t *srcp = (cpu_set_t *) q;
     161  
     162    CPU_ZERO_S (gomp_cpuset_size, destp);
     163    for (i = 0; i < max; i++)
     164      if (CPU_ISSET_S (i, gomp_cpuset_size, srcp))
     165        {
     166  	if ((stride < 0 && i + stride > i)
     167  	    || (stride > 0 && (i + stride < i || i + stride >= max)))
     168  	  {
     169  	    gomp_error ("Logical CPU number %lu+%ld out of range", i, stride);
     170  	    return false;
     171  	  }
     172  	CPU_SET_S (i + stride, gomp_cpuset_size, destp);
     173        }
     174    return true;
     175  }
     176  
     177  bool
     178  gomp_affinity_same_place (void *p, void *q)
     179  {
     180  #ifdef CPU_EQUAL_S
     181    return CPU_EQUAL_S (gomp_cpuset_size, (cpu_set_t *) p, (cpu_set_t *) q);
     182  #else
     183    return memcmp (p, q, gomp_cpuset_size) == 0;
     184  #endif
     185  }
     186  
     187  bool
     188  gomp_affinity_finalize_place_list (bool quiet)
     189  {
     190    unsigned long i, j;
     191  
     192    for (i = 0, j = 0; i < gomp_places_list_len; i++)
     193      {
     194        cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[i];
     195        bool nonempty = false;
     196  #ifdef CPU_AND_S
     197        CPU_AND_S (gomp_cpuset_size, cpusetp, cpusetp, gomp_cpusetp);
     198        nonempty = gomp_cpuset_popcount (gomp_cpuset_size, cpusetp) != 0;
     199  #else
     200        unsigned long k, max = gomp_cpuset_size / sizeof (cpusetp->__bits[0]);
     201        for (k = 0; k < max; k++)
     202  	if ((cpusetp->__bits[k] &= gomp_cpusetp->__bits[k]) != 0)
     203  	  nonempty = true;
     204  #endif
     205        if (nonempty)
     206  	gomp_places_list[j++] = gomp_places_list[i];
     207      }
     208  
     209    if (j == 0)
     210      {
     211        if (!quiet)
     212  	gomp_error ("None of the places contain usable logical CPUs");
     213        return false;
     214      }
     215    else if (j < gomp_places_list_len)
     216      {
     217        if (!quiet)
     218  	gomp_error ("Number of places reduced from %ld to %ld because some "
     219  		    "places didn't contain any usable logical CPUs",
     220  		    gomp_places_list_len, j);
     221        gomp_places_list_len = j;
     222      }
     223    return true;
     224  }
     225  
     226  /* Find the index of the last level cache.  We assume the index
     227     of the last level cache is the same for all logical CPUs.
     228     Also, if there are multiple caches with the same highest level,
     229     assume they have the same shared_cpu_list and pick the last one
     230     from them (highest index number).  */
     231  
     232  static int
     233  gomp_affinity_find_last_cache_level (char *name, size_t prefix_len,
     234  				     unsigned long cpu)
     235  {
     236    int ret = -1;
     237    unsigned long maxval = 0;
     238    char *line = NULL;
     239    size_t linelen = 0;
     240    FILE *f;
     241  
     242    for (int l = 0; l < 128; l++)
     243      {
     244        sprintf (name + prefix_len, "%lu/cache/index%u/level", cpu, l);
     245        f = fopen (name, "r");
     246        if (f == NULL)
     247  	break;
     248        if (getline (&line, &linelen, f) > 0)
     249  	{
     250  	  unsigned long val;
     251  	  char *p;
     252  	  errno = 0;
     253  	  val = strtoul (line, &p, 10);
     254  	  if (!errno && p > line && val >= maxval)
     255  	    {
     256  	      ret = l;
     257  	      maxval = val;
     258  	    }
     259  	}
     260        fclose (f);
     261      }
     262    free (line);
     263    return ret;
     264  }
     265  
     266  static void
     267  gomp_affinity_init_level_1 (int level, int this_level, unsigned long count,
     268  			    cpu_set_t *copy, char *name, bool quiet)
     269  {
     270    size_t prefix_len = sizeof ("/sys/devices/system/cpu/cpu") - 1;
     271    FILE *f;
     272    char *line = NULL;
     273    size_t linelen = 0;
     274    unsigned long i, max = 8 * gomp_cpuset_size;
     275    int init = -1;
     276  
     277    for (i = 0; i < max && gomp_places_list_len < count; i++)
     278      if (CPU_ISSET_S (i, gomp_cpuset_size, copy))
     279        {
     280  	if (level == 4)
     281  	  {
     282  	    if (init == -1)
     283  	      {
     284  		init = gomp_affinity_find_last_cache_level (name, prefix_len,
     285  							    i);
     286  		if (init == -1)
     287  		  {
     288  		    CPU_CLR_S (i, gomp_cpuset_size, copy);
     289  		    continue;
     290  		  }
     291  		sprintf (name + prefix_len,
     292  			 "%lu/cache/index%u/shared_cpu_list", i, init);
     293  	      }
     294  	  }
     295  	else
     296  	  sprintf (name + prefix_len, "%lu/topology/%s_siblings_list",
     297  		   i, this_level == 3 ? "core" : "thread");
     298  	f = fopen (name, "r");
     299  	if (f == NULL)
     300  	  {
     301  	    CPU_CLR_S (i, gomp_cpuset_size, copy);
     302  	    continue;
     303  	  }
     304  	if (getline (&line, &linelen, f) > 0)
     305  	  {
     306  	    char *p = line, *end;
     307  	    void *pl = gomp_places_list[gomp_places_list_len];
     308  	    if (level == this_level)
     309  	      gomp_affinity_init_place (pl);
     310  	    while (*p && *p != '\n')
     311  	      {
     312  		unsigned long first, last;
     313  		errno = 0;
     314  		first = strtoul (p, &end, 10);
     315  		if (errno || end == p)
     316  		  break;
     317  		p = end;
     318  		last = first;
     319  		if (*p == '-')
     320  		  {
     321  		    errno = 0;
     322  		    last = strtoul (p + 1, &end, 10);
     323  		    if (errno || end == p + 1 || last < first)
     324  		      break;
     325  		    p = end;
     326  		  }
     327  		for (; first <= last; first++)
     328  		  if (!CPU_ISSET_S (first, gomp_cpuset_size, copy))
     329  		    continue;
     330  		  else if (this_level == 3 && level < this_level)
     331  		    gomp_affinity_init_level_1 (level, 2, count, copy,
     332  						name, quiet);
     333  		  else
     334  		    {
     335  		      if (level == 1)
     336  			{
     337  			  pl = gomp_places_list[gomp_places_list_len];
     338  			  gomp_affinity_init_place (pl);
     339  			}
     340  		      if (gomp_affinity_add_cpus (pl, first, 1, 0, true))
     341  			{
     342  			  CPU_CLR_S (first, gomp_cpuset_size, copy);
     343  			  if (level == 1
     344  			      && ++gomp_places_list_len >= count)
     345  			    {
     346  			      fclose (f);
     347  			      free (line);
     348  			      return;
     349  			    }
     350  			}
     351  		    }
     352  		if (*p == ',')
     353  		  ++p;
     354  	      }
     355  	    if (level == this_level
     356  		&& !CPU_ISSET_S (i, gomp_cpuset_size, copy))
     357  	      gomp_places_list_len++;
     358  	    CPU_CLR_S (i, gomp_cpuset_size, copy);
     359  	  }
     360  	fclose (f);
     361        }
     362    free (line);
     363  }
     364  
     365  static void
     366  gomp_affinity_init_numa_domains (unsigned long count, cpu_set_t *copy,
     367  				 char *name)
     368  {
     369    FILE *f;
     370    char *nline = NULL, *line = NULL;
     371    size_t nlinelen = 0, linelen = 0;
     372    char *q;
     373    size_t prefix_len = sizeof ("/sys/devices/system/node/") - 1;
     374  
     375    strcpy (name, "/sys/devices/system/node/online");
     376    f = fopen (name, "r");
     377    if (f == NULL || getline (&nline, &nlinelen, f) <= 0)
     378      {
     379        if (f)
     380  	fclose (f);
     381        return;
     382      }
     383    fclose (f);
     384    q = nline;
     385    while (*q && *q != '\n' && gomp_places_list_len < count)
     386      {
     387        unsigned long nfirst, nlast;
     388        char *end;
     389  
     390        errno = 0;
     391        nfirst = strtoul (q, &end, 10);
     392        if (errno || end == q)
     393  	break;
     394        q = end;
     395        nlast = nfirst;
     396        if (*q == '-')
     397  	{
     398  	  errno = 0;
     399  	  nlast = strtoul (q + 1, &end, 10);
     400  	  if (errno || end == q + 1 || nlast < nfirst)
     401  	    break;
     402  	  q = end;
     403  	}
     404        for (; nfirst <= nlast && gomp_places_list_len < count; nfirst++)
     405  	{
     406  	  sprintf (name + prefix_len, "node%lu/cpulist", nfirst);
     407  	  f = fopen (name, "r");
     408  	  if (f == NULL)
     409  	    continue;
     410  	  if (getline (&line, &linelen, f) > 0)
     411  	    {
     412  	      char *p = line;
     413  	      void *pl = NULL;
     414  	      bool seen = false;
     415  
     416  	      while (*p && *p != '\n')
     417  		{
     418  		  unsigned long first, last;
     419  
     420  		  errno = 0;
     421  		  first = strtoul (p, &end, 10);
     422  		  if (errno || end == p)
     423  		    break;
     424  		  p = end;
     425  		  last = first;
     426  		  if (*p == '-')
     427  		    {
     428  		      errno = 0;
     429  		      last = strtoul (p + 1, &end, 10);
     430  		      if (errno || end == p + 1 || last < first)
     431  			break;
     432  		      p = end;
     433  		    }
     434  		  for (; first <= last; first++)
     435  		    {
     436  		      if (!CPU_ISSET_S (first, gomp_cpuset_size, copy))
     437  			continue;
     438  		      if (pl == NULL)
     439  			{
     440  			  pl = gomp_places_list[gomp_places_list_len];
     441  			  gomp_affinity_init_place (pl);
     442  			}
     443  		      if (gomp_affinity_add_cpus (pl, first, 1, 0, true))
     444  			{
     445  			  CPU_CLR_S (first, gomp_cpuset_size, copy);
     446  			  if (!seen)
     447  			    {
     448  			      gomp_places_list_len++;
     449  			      seen = true;
     450  			    }
     451  			}
     452  		    }
     453  		  if (*p == ',')
     454  		    ++p;
     455  		}
     456  	    }
     457  	  fclose (f);
     458  	}
     459        if (*q == ',')
     460  	++q;
     461      }
     462    free (line);
     463    free (nline);
     464  }
     465  
     466  bool
     467  gomp_affinity_init_level (int level, unsigned long count, bool quiet)
     468  {
     469    char name[sizeof ("/sys/devices/system/cpu/cpu/topology/"
     470  		    "thread_siblings_list") + 6 * sizeof (unsigned long)];
     471    cpu_set_t *copy;
     472  
     473    if (gomp_cpusetp)
     474      {
     475        unsigned long maxcount
     476  	= gomp_cpuset_popcount (gomp_cpuset_size, gomp_cpusetp);
     477        if (count > maxcount)
     478  	count = maxcount;
     479      }
     480    gomp_places_list = gomp_affinity_alloc (count, quiet);
     481    gomp_places_list_len = 0;
     482    if (gomp_places_list == NULL)
     483      return false;
     484  
     485    copy = gomp_alloca (gomp_cpuset_size);
     486    strcpy (name, "/sys/devices/system/cpu/cpu");
     487    memcpy (copy, gomp_cpusetp, gomp_cpuset_size);
     488    if (level == 5)
     489      gomp_affinity_init_numa_domains (count, copy, name);
     490    else
     491      gomp_affinity_init_level_1 (level, level > 3 ? level : 3, count, copy,
     492  				name, quiet);
     493    if (gomp_places_list_len == 0)
     494      {
     495        if (!quiet)
     496  	gomp_error ("Error reading core/socket topology");
     497        free (gomp_places_list);
     498        gomp_places_list = NULL;
     499        return false;
     500      }
     501    return true;
     502  }
     503  
     504  void
     505  gomp_affinity_print_place (void *p)
     506  {
     507    unsigned long i, max = 8 * gomp_cpuset_size, len;
     508    cpu_set_t *cpusetp = (cpu_set_t *) p;
     509    bool notfirst = false;
     510  
     511    for (i = 0, len = 0; i < max; i++)
     512      if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
     513        {
     514  	if (len == 0)
     515  	  {
     516  	    if (notfirst)
     517  	      fputc (',', stderr);
     518  	    notfirst = true;
     519  	    fprintf (stderr, "%lu", i);
     520  	  }
     521  	++len;
     522        }
     523      else
     524        {
     525  	if (len > 1)
     526  	  fprintf (stderr, ":%lu", len);
     527  	len = 0;
     528        }
     529    if (len > 1)
     530      fprintf (stderr, ":%lu", len);
     531  }
     532  
     533  int
     534  omp_get_place_num_procs (int place_num)
     535  {
     536    if (place_num < 0 || place_num >= gomp_places_list_len)
     537      return 0;
     538  
     539    cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
     540    return gomp_cpuset_popcount (gomp_cpuset_size, cpusetp);
     541  }
     542  
     543  void
     544  omp_get_place_proc_ids (int place_num, int *ids)
     545  {
     546    if (place_num < 0 || place_num >= gomp_places_list_len)
     547      return;
     548  
     549    cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
     550    unsigned long i, max = 8 * gomp_cpuset_size;
     551    for (i = 0; i < max; i++)
     552      if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
     553        *ids++ = i;
     554  }
     555  
     556  void
     557  gomp_get_place_proc_ids_8 (int place_num, int64_t *ids)
     558  {
     559    if (place_num < 0 || place_num >= gomp_places_list_len)
     560      return;
     561  
     562    cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
     563    unsigned long i, max = 8 * gomp_cpuset_size;
     564    for (i = 0; i < max; i++)
     565      if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
     566        *ids++ = i;
     567  }
     568  
     569  void
     570  gomp_display_affinity_place (char *buffer, size_t size, size_t *ret,
     571  			     int place)
     572  {
     573    cpu_set_t *cpusetp;
     574    char buf[sizeof (long) * 3 + 4];
     575    if (place >= 0 && place < gomp_places_list_len)
     576      cpusetp = (cpu_set_t *) gomp_places_list[place];
     577    else if (gomp_cpusetp)
     578      cpusetp = gomp_cpusetp;
     579    else
     580      {
     581        if (gomp_available_cpus > 1)
     582  	sprintf (buf, "0-%lu", gomp_available_cpus - 1);
     583        else
     584  	strcpy (buf, "0");
     585        gomp_display_string (buffer, size, ret, buf, strlen (buf));
     586        return;
     587      }
     588  
     589    unsigned long i, max = 8 * gomp_cpuset_size, start;
     590    bool prev_set = false;
     591    start = max;
     592    for (i = 0; i <= max; i++)
     593      {
     594        bool this_set;
     595        if (i == max)
     596  	this_set = false;
     597        else
     598  	this_set = CPU_ISSET_S (i, gomp_cpuset_size, cpusetp);
     599        if (this_set != prev_set)
     600  	{
     601  	  prev_set = this_set;
     602  	  if (this_set)
     603  	    {
     604  	      char *p = buf;
     605  	      if (start != max)
     606  		*p++ = ',';
     607  	      sprintf (p, "%lu", i);
     608  	      start = i;
     609  	    }
     610  	  else if (i == start + 1)
     611  	    continue;
     612  	  else
     613  	    sprintf (buf, "-%lu", i - 1);
     614  	  gomp_display_string (buffer, size, ret, buf, strlen (buf));
     615  	}
     616      }
     617  }
     618  
     619  ialias(omp_get_place_num_procs)
     620  ialias(omp_get_place_proc_ids)
     621  
     622  #else
     623  
     624  #include "../../affinity.c"
     625  
     626  #endif