xref: /netbsd-src/external/gpl3/gcc.old/dist/libgomp/config/linux/affinity.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /* Copyright (C) 2006-2015 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 
39 #ifdef HAVE_PTHREAD_AFFINITY_NP
40 
41 #ifndef CPU_ALLOC_SIZE
42 #define CPU_ISSET_S(idx, size, set) CPU_ISSET(idx, set)
43 #define CPU_ZERO_S(size, set) CPU_ZERO(set)
44 #define CPU_SET_S(idx, size, set) CPU_SET(idx, set)
45 #define CPU_CLR_S(idx, size, set) CPU_CLR(idx, set)
46 #endif
47 
48 void
49 gomp_init_affinity (void)
50 {
51   if (gomp_places_list == NULL)
52     {
53       if (!gomp_affinity_init_level (1, ULONG_MAX, true))
54 	return;
55     }
56 
57   struct gomp_thread *thr = gomp_thread ();
58   pthread_setaffinity_np (pthread_self (), gomp_cpuset_size,
59 			  (cpu_set_t *) gomp_places_list[0]);
60   thr->place = 1;
61   thr->ts.place_partition_off = 0;
62   thr->ts.place_partition_len = gomp_places_list_len;
63 }
64 
65 void
66 gomp_init_thread_affinity (pthread_attr_t *attr, unsigned int place)
67 {
68   pthread_attr_setaffinity_np (attr, gomp_cpuset_size,
69 			       (cpu_set_t *) gomp_places_list[place]);
70 }
71 
72 void **
73 gomp_affinity_alloc (unsigned long count, bool quiet)
74 {
75   unsigned long i;
76   void **ret;
77   char *p;
78 
79   if (gomp_cpusetp == NULL)
80     {
81       if (!quiet)
82 	gomp_error ("Could not get CPU affinity set");
83       return NULL;
84     }
85 
86   ret = malloc (count * sizeof (void *) + count * gomp_cpuset_size);
87   if (ret == NULL)
88     {
89       if (!quiet)
90 	gomp_error ("Out of memory trying to allocate places list");
91       return NULL;
92     }
93 
94   p = (char *) (ret + count);
95   for (i = 0; i < count; i++, p += gomp_cpuset_size)
96     ret[i] = p;
97   return ret;
98 }
99 
100 void
101 gomp_affinity_init_place (void *p)
102 {
103   cpu_set_t *cpusetp = (cpu_set_t *) p;
104   CPU_ZERO_S (gomp_cpuset_size, cpusetp);
105 }
106 
107 bool
108 gomp_affinity_add_cpus (void *p, unsigned long num,
109 			unsigned long len, long stride, bool quiet)
110 {
111   cpu_set_t *cpusetp = (cpu_set_t *) p;
112   unsigned long max = 8 * gomp_cpuset_size;
113   for (;;)
114     {
115       if (num >= max)
116 	{
117 	  if (!quiet)
118 	    gomp_error ("Logical CPU number %lu out of range", num);
119 	  return false;
120 	}
121       CPU_SET_S (num, gomp_cpuset_size, cpusetp);
122       if (--len == 0)
123 	return true;
124       if ((stride < 0 && num + stride > num)
125 	  || (stride > 0 && num + stride < num))
126 	{
127 	  if (!quiet)
128 	    gomp_error ("Logical CPU number %lu+%ld out of range",
129 			num, stride);
130 	  return false;
131 	}
132       num += stride;
133     }
134 }
135 
136 bool
137 gomp_affinity_remove_cpu (void *p, unsigned long num)
138 {
139   cpu_set_t *cpusetp = (cpu_set_t *) p;
140   if (num >= 8 * gomp_cpuset_size)
141     {
142       gomp_error ("Logical CPU number %lu out of range", num);
143       return false;
144     }
145   if (!CPU_ISSET_S (num, gomp_cpuset_size, cpusetp))
146     {
147       gomp_error ("Logical CPU %lu to be removed is not in the set", num);
148       return false;
149     }
150   CPU_CLR_S (num, gomp_cpuset_size, cpusetp);
151   return true;
152 }
153 
154 bool
155 gomp_affinity_copy_place (void *p, void *q, long stride)
156 {
157   unsigned long i, max = 8 * gomp_cpuset_size;
158   cpu_set_t *destp = (cpu_set_t *) p;
159   cpu_set_t *srcp = (cpu_set_t *) q;
160 
161   CPU_ZERO_S (gomp_cpuset_size, destp);
162   for (i = 0; i < max; i++)
163     if (CPU_ISSET_S (i, gomp_cpuset_size, srcp))
164       {
165 	if ((stride < 0 && i + stride > i)
166 	    || (stride > 0 && (i + stride < i || i + stride >= max)))
167 	  {
168 	    gomp_error ("Logical CPU number %lu+%ld out of range", i, stride);
169 	    return false;
170 	  }
171 	CPU_SET_S (i + stride, gomp_cpuset_size, destp);
172       }
173   return true;
174 }
175 
176 bool
177 gomp_affinity_same_place (void *p, void *q)
178 {
179 #ifdef CPU_EQUAL_S
180   return CPU_EQUAL_S (gomp_cpuset_size, (cpu_set_t *) p, (cpu_set_t *) q);
181 #else
182   return memcmp (p, q, gomp_cpuset_size) == 0;
183 #endif
184 }
185 
186 bool
187 gomp_affinity_finalize_place_list (bool quiet)
188 {
189   unsigned long i, j;
190 
191   for (i = 0, j = 0; i < gomp_places_list_len; i++)
192     {
193       cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[i];
194       bool nonempty = false;
195 #ifdef CPU_AND_S
196       CPU_AND_S (gomp_cpuset_size, cpusetp, cpusetp, gomp_cpusetp);
197       nonempty = gomp_cpuset_popcount (gomp_cpuset_size, cpusetp) != 0;
198 #else
199       unsigned long k, max = gomp_cpuset_size / sizeof (cpusetp->__bits[0]);
200       for (k = 0; k < max; k++)
201 	if ((cpusetp->__bits[k] &= gomp_cpusetp->__bits[k]) != 0)
202 	  nonempty = true;
203 #endif
204       if (nonempty)
205 	gomp_places_list[j++] = gomp_places_list[i];
206     }
207 
208   if (j == 0)
209     {
210       if (!quiet)
211 	gomp_error ("None of the places contain usable logical CPUs");
212       return false;
213     }
214   else if (j < gomp_places_list_len)
215     {
216       if (!quiet)
217 	gomp_error ("Number of places reduced from %ld to %ld because some "
218 		    "places didn't contain any usable logical CPUs",
219 		    gomp_places_list_len, j);
220       gomp_places_list_len = j;
221     }
222   return true;
223 }
224 
225 bool
226 gomp_affinity_init_level (int level, unsigned long count, bool quiet)
227 {
228   unsigned long i, max = 8 * gomp_cpuset_size;
229 
230   if (gomp_cpusetp)
231     {
232       unsigned long maxcount
233 	= gomp_cpuset_popcount (gomp_cpuset_size, gomp_cpusetp);
234       if (count > maxcount)
235 	count = maxcount;
236     }
237   gomp_places_list = gomp_affinity_alloc (count, quiet);
238   gomp_places_list_len = 0;
239   if (gomp_places_list == NULL)
240     return false;
241   /* SMT (threads).  */
242   if (level == 1)
243     {
244       for (i = 0; i < max && gomp_places_list_len < count; i++)
245 	if (CPU_ISSET_S (i, gomp_cpuset_size, gomp_cpusetp))
246 	  {
247 	    gomp_affinity_init_place (gomp_places_list[gomp_places_list_len]);
248 	    gomp_affinity_add_cpus (gomp_places_list[gomp_places_list_len],
249 				    i, 1, 0, true);
250 	    ++gomp_places_list_len;
251 	  }
252       return true;
253     }
254   else
255     {
256       char name[sizeof ("/sys/devices/system/cpu/cpu/topology/"
257 			"thread_siblings_list") + 3 * sizeof (unsigned long)];
258       size_t prefix_len = sizeof ("/sys/devices/system/cpu/cpu") - 1;
259       cpu_set_t *copy = gomp_alloca (gomp_cpuset_size);
260       FILE *f;
261       char *line = NULL;
262       size_t linelen = 0;
263 
264       memcpy (name, "/sys/devices/system/cpu/cpu", prefix_len);
265       memcpy (copy, gomp_cpusetp, gomp_cpuset_size);
266       for (i = 0; i < max && gomp_places_list_len < count; i++)
267 	if (CPU_ISSET_S (i, gomp_cpuset_size, copy))
268 	  {
269 	    sprintf (name + prefix_len, "%lu/topology/%s_siblings_list",
270 		     i, level == 2 ? "thread" : "core");
271 	    f = fopen (name, "r");
272 	    if (f != NULL)
273 	      {
274 		if (getline (&line, &linelen, f) > 0)
275 		  {
276 		    char *p = line;
277 		    bool seen_i = false;
278 		    void *pl = gomp_places_list[gomp_places_list_len];
279 		    gomp_affinity_init_place (pl);
280 		    while (*p && *p != '\n')
281 		      {
282 			unsigned long first, last;
283 			errno = 0;
284 			first = strtoul (p, &p, 10);
285 			if (errno)
286 			  break;
287 			last = first;
288 			if (*p == '-')
289 			  {
290 			    errno = 0;
291 			    last = strtoul (p + 1, &p, 10);
292 			    if (errno || last < first)
293 			      break;
294 			  }
295 			for (; first <= last; first++)
296 			  if (CPU_ISSET_S (first, gomp_cpuset_size, copy)
297 			      && gomp_affinity_add_cpus (pl, first, 1, 0,
298 							 true))
299 			    {
300 			      CPU_CLR_S (first, gomp_cpuset_size, copy);
301 			      if (first == i)
302 				seen_i = true;
303 			    }
304 			if (*p == ',')
305 			  ++p;
306 		      }
307 		    if (seen_i)
308 		      gomp_places_list_len++;
309 		  }
310 		fclose (f);
311 	      }
312 	  }
313       if (gomp_places_list_len == 0)
314 	{
315 	  if (!quiet)
316 	    gomp_error ("Error reading %s topology",
317 			level == 2 ? "core" : "socket");
318 	  free (gomp_places_list);
319 	  gomp_places_list = NULL;
320 	  return false;
321 	}
322       return true;
323     }
324   return false;
325 }
326 
327 void
328 gomp_affinity_print_place (void *p)
329 {
330   unsigned long i, max = 8 * gomp_cpuset_size, len;
331   cpu_set_t *cpusetp = (cpu_set_t *) p;
332   bool notfirst = false;
333 
334   for (i = 0, len = 0; i < max; i++)
335     if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
336       {
337 	if (len == 0)
338 	  {
339 	    if (notfirst)
340 	      fputc (',', stderr);
341 	    notfirst = true;
342 	    fprintf (stderr, "%lu", i);
343 	  }
344 	++len;
345       }
346     else
347       {
348 	if (len > 1)
349 	  fprintf (stderr, ":%lu", len);
350 	len = 0;
351       }
352   if (len > 1)
353     fprintf (stderr, ":%lu", len);
354 }
355 
356 #else
357 
358 #include "../posix/affinity.c"
359 
360 #endif
361