xref: /netbsd-src/external/gpl3/gcc.old/dist/libgomp/config/linux/affinity.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
1 /* Copyright (C) 2006-2020 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 static void
227 gomp_affinity_init_level_1 (int level, int this_level, unsigned long count,
228 			    cpu_set_t *copy, char *name, bool quiet)
229 {
230   size_t prefix_len = sizeof ("/sys/devices/system/cpu/cpu") - 1;
231   FILE *f;
232   char *line = NULL;
233   size_t linelen = 0;
234   unsigned long i, max = 8 * gomp_cpuset_size;
235 
236   for (i = 0; i < max && gomp_places_list_len < count; i++)
237     if (CPU_ISSET_S (i, gomp_cpuset_size, copy))
238       {
239 	sprintf (name + prefix_len, "%lu/topology/%s_siblings_list",
240 		 i, this_level == 3 ? "core" : "thread");
241 	f = fopen (name, "r");
242 	if (f == NULL)
243 	  {
244 	    CPU_CLR_S (i, gomp_cpuset_size, copy);
245 	    continue;
246 	  }
247 	if (getline (&line, &linelen, f) > 0)
248 	  {
249 	    char *p = line;
250 	    void *pl = gomp_places_list[gomp_places_list_len];
251 	    if (level == this_level)
252 	      gomp_affinity_init_place (pl);
253 	    while (*p && *p != '\n')
254 	      {
255 		unsigned long first, last;
256 		errno = 0;
257 		first = strtoul (p, &p, 10);
258 		if (errno)
259 		  break;
260 		last = first;
261 		if (*p == '-')
262 		  {
263 		    errno = 0;
264 		    last = strtoul (p + 1, &p, 10);
265 		    if (errno || last < first)
266 		      break;
267 		  }
268 		for (; first <= last; first++)
269 		  if (!CPU_ISSET_S (first, gomp_cpuset_size, copy))
270 		    continue;
271 		  else if (this_level == 3 && level < this_level)
272 		    gomp_affinity_init_level_1 (level, 2, count, copy,
273 						name, quiet);
274 		  else
275 		    {
276 		      if (level == 1)
277 			{
278 			  pl = gomp_places_list[gomp_places_list_len];
279 			  gomp_affinity_init_place (pl);
280 			}
281 		      if (gomp_affinity_add_cpus (pl, first, 1, 0, true))
282 			{
283 			  CPU_CLR_S (first, gomp_cpuset_size, copy);
284 			  if (level == 1
285 			      && ++gomp_places_list_len >= count)
286 			    {
287 			      fclose (f);
288 			      free (line);
289 			      return;
290 			    }
291 			}
292 		    }
293 		if (*p == ',')
294 		  ++p;
295 	      }
296 	    if (level == this_level
297 		&& !CPU_ISSET_S (i, gomp_cpuset_size, copy))
298 	      gomp_places_list_len++;
299 	    CPU_CLR_S (i, gomp_cpuset_size, copy);
300 	  }
301 	fclose (f);
302       }
303   free (line);
304 }
305 
306 bool
307 gomp_affinity_init_level (int level, unsigned long count, bool quiet)
308 {
309   char name[sizeof ("/sys/devices/system/cpu/cpu/topology/"
310 		    "thread_siblings_list") + 3 * sizeof (unsigned long)];
311   cpu_set_t *copy;
312 
313   if (gomp_cpusetp)
314     {
315       unsigned long maxcount
316 	= gomp_cpuset_popcount (gomp_cpuset_size, gomp_cpusetp);
317       if (count > maxcount)
318 	count = maxcount;
319     }
320   gomp_places_list = gomp_affinity_alloc (count, quiet);
321   gomp_places_list_len = 0;
322   if (gomp_places_list == NULL)
323     return false;
324 
325   copy = gomp_alloca (gomp_cpuset_size);
326   strcpy (name, "/sys/devices/system/cpu/cpu");
327   memcpy (copy, gomp_cpusetp, gomp_cpuset_size);
328   gomp_affinity_init_level_1 (level, 3, count, copy, name, quiet);
329   if (gomp_places_list_len == 0)
330     {
331       if (!quiet)
332 	gomp_error ("Error reading core/socket topology");
333       free (gomp_places_list);
334       gomp_places_list = NULL;
335       return false;
336     }
337   return true;
338 }
339 
340 void
341 gomp_affinity_print_place (void *p)
342 {
343   unsigned long i, max = 8 * gomp_cpuset_size, len;
344   cpu_set_t *cpusetp = (cpu_set_t *) p;
345   bool notfirst = false;
346 
347   for (i = 0, len = 0; i < max; i++)
348     if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
349       {
350 	if (len == 0)
351 	  {
352 	    if (notfirst)
353 	      fputc (',', stderr);
354 	    notfirst = true;
355 	    fprintf (stderr, "%lu", i);
356 	  }
357 	++len;
358       }
359     else
360       {
361 	if (len > 1)
362 	  fprintf (stderr, ":%lu", len);
363 	len = 0;
364       }
365   if (len > 1)
366     fprintf (stderr, ":%lu", len);
367 }
368 
369 int
370 omp_get_place_num_procs (int place_num)
371 {
372   if (place_num < 0 || place_num >= gomp_places_list_len)
373     return 0;
374 
375   cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
376   return gomp_cpuset_popcount (gomp_cpuset_size, cpusetp);
377 }
378 
379 void
380 omp_get_place_proc_ids (int place_num, int *ids)
381 {
382   if (place_num < 0 || place_num >= gomp_places_list_len)
383     return;
384 
385   cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
386   unsigned long i, max = 8 * gomp_cpuset_size;
387   for (i = 0; i < max; i++)
388     if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
389       *ids++ = i;
390 }
391 
392 void
393 gomp_get_place_proc_ids_8 (int place_num, int64_t *ids)
394 {
395   if (place_num < 0 || place_num >= gomp_places_list_len)
396     return;
397 
398   cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
399   unsigned long i, max = 8 * gomp_cpuset_size;
400   for (i = 0; i < max; i++)
401     if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
402       *ids++ = i;
403 }
404 
405 void
406 gomp_display_affinity_place (char *buffer, size_t size, size_t *ret,
407 			     int place)
408 {
409   cpu_set_t *cpusetp;
410   char buf[sizeof (long) * 3 + 4];
411   if (place >= 0 && place < gomp_places_list_len)
412     cpusetp = (cpu_set_t *) gomp_places_list[place];
413   else if (gomp_cpusetp)
414     cpusetp = gomp_cpusetp;
415   else
416     {
417       if (gomp_available_cpus > 1)
418 	sprintf (buf, "0-%lu", gomp_available_cpus - 1);
419       else
420 	strcpy (buf, "0");
421       gomp_display_string (buffer, size, ret, buf, strlen (buf));
422       return;
423     }
424 
425   unsigned long i, max = 8 * gomp_cpuset_size, start;
426   bool prev_set = false;
427   start = max;
428   for (i = 0; i <= max; i++)
429     {
430       bool this_set;
431       if (i == max)
432 	this_set = false;
433       else
434 	this_set = CPU_ISSET_S (i, gomp_cpuset_size, cpusetp);
435       if (this_set != prev_set)
436 	{
437 	  prev_set = this_set;
438 	  if (this_set)
439 	    {
440 	      char *p = buf;
441 	      if (start != max)
442 		*p++ = ',';
443 	      sprintf (p, "%lu", i);
444 	      start = i;
445 	    }
446 	  else if (i == start + 1)
447 	    continue;
448 	  else
449 	    sprintf (buf, "-%lu", i - 1);
450 	  gomp_display_string (buffer, size, ret, buf, strlen (buf));
451 	}
452     }
453 }
454 
455 ialias(omp_get_place_num_procs)
456 ialias(omp_get_place_proc_ids)
457 
458 #else
459 
460 #include "../../affinity.c"
461 
462 #endif
463