1 /* Copyright (C) 2006-2018 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
gomp_init_affinity(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
gomp_init_thread_affinity(pthread_attr_t * attr,unsigned int place)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 **
gomp_affinity_alloc(unsigned long count,bool quiet)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
gomp_affinity_init_place(void * p)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
gomp_affinity_add_cpus(void * p,unsigned long num,unsigned long len,long stride,bool quiet)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
gomp_affinity_remove_cpu(void * p,unsigned long num)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
gomp_affinity_copy_place(void * p,void * q,long stride)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
gomp_affinity_same_place(void * p,void * q)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
gomp_affinity_finalize_place_list(bool quiet)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 static void
gomp_affinity_init_level_1(int level,int this_level,unsigned long count,cpu_set_t * copy,char * name,bool quiet)226 gomp_affinity_init_level_1 (int level, int this_level, unsigned long count,
227 			    cpu_set_t *copy, char *name, bool quiet)
228 {
229   size_t prefix_len = sizeof ("/sys/devices/system/cpu/cpu") - 1;
230   FILE *f;
231   char *line = NULL;
232   size_t linelen = 0;
233   unsigned long i, max = 8 * gomp_cpuset_size;
234 
235   for (i = 0; i < max && gomp_places_list_len < count; i++)
236     if (CPU_ISSET_S (i, gomp_cpuset_size, copy))
237       {
238 	sprintf (name + prefix_len, "%lu/topology/%s_siblings_list",
239 		 i, this_level == 3 ? "core" : "thread");
240 	f = fopen (name, "r");
241 	if (f == NULL)
242 	  {
243 	    CPU_CLR_S (i, gomp_cpuset_size, copy);
244 	    continue;
245 	  }
246 	if (getline (&line, &linelen, f) > 0)
247 	  {
248 	    char *p = line;
249 	    void *pl = gomp_places_list[gomp_places_list_len];
250 	    if (level == this_level)
251 	      gomp_affinity_init_place (pl);
252 	    while (*p && *p != '\n')
253 	      {
254 		unsigned long first, last;
255 		errno = 0;
256 		first = strtoul (p, &p, 10);
257 		if (errno)
258 		  break;
259 		last = first;
260 		if (*p == '-')
261 		  {
262 		    errno = 0;
263 		    last = strtoul (p + 1, &p, 10);
264 		    if (errno || last < first)
265 		      break;
266 		  }
267 		for (; first <= last; first++)
268 		  if (!CPU_ISSET_S (first, gomp_cpuset_size, copy))
269 		    continue;
270 		  else if (this_level == 3 && level < this_level)
271 		    gomp_affinity_init_level_1 (level, 2, count, copy,
272 						name, quiet);
273 		  else
274 		    {
275 		      if (level == 1)
276 			{
277 			  pl = gomp_places_list[gomp_places_list_len];
278 			  gomp_affinity_init_place (pl);
279 			}
280 		      if (gomp_affinity_add_cpus (pl, first, 1, 0, true))
281 			{
282 			  CPU_CLR_S (first, gomp_cpuset_size, copy);
283 			  if (level == 1)
284 			    gomp_places_list_len++;
285 			}
286 		    }
287 		if (*p == ',')
288 		  ++p;
289 	      }
290 	    if (level == this_level
291 		&& !CPU_ISSET_S (i, gomp_cpuset_size, copy))
292 	      gomp_places_list_len++;
293 	    CPU_CLR_S (i, gomp_cpuset_size, copy);
294 	  }
295 	fclose (f);
296       }
297   free (line);
298 }
299 
300 bool
gomp_affinity_init_level(int level,unsigned long count,bool quiet)301 gomp_affinity_init_level (int level, unsigned long count, bool quiet)
302 {
303   char name[sizeof ("/sys/devices/system/cpu/cpu/topology/"
304 		    "thread_siblings_list") + 3 * sizeof (unsigned long)];
305   cpu_set_t *copy;
306 
307   if (gomp_cpusetp)
308     {
309       unsigned long maxcount
310 	= gomp_cpuset_popcount (gomp_cpuset_size, gomp_cpusetp);
311       if (count > maxcount)
312 	count = maxcount;
313     }
314   gomp_places_list = gomp_affinity_alloc (count, quiet);
315   gomp_places_list_len = 0;
316   if (gomp_places_list == NULL)
317     return false;
318 
319   copy = gomp_alloca (gomp_cpuset_size);
320   strcpy (name, "/sys/devices/system/cpu/cpu");
321   memcpy (copy, gomp_cpusetp, gomp_cpuset_size);
322   gomp_affinity_init_level_1 (level, 3, count, copy, name, quiet);
323   if (gomp_places_list_len == 0)
324     {
325       if (!quiet)
326 	gomp_error ("Error reading core/socket topology");
327       free (gomp_places_list);
328       gomp_places_list = NULL;
329       return false;
330     }
331   return true;
332 }
333 
334 void
gomp_affinity_print_place(void * p)335 gomp_affinity_print_place (void *p)
336 {
337   unsigned long i, max = 8 * gomp_cpuset_size, len;
338   cpu_set_t *cpusetp = (cpu_set_t *) p;
339   bool notfirst = false;
340 
341   for (i = 0, len = 0; i < max; i++)
342     if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
343       {
344 	if (len == 0)
345 	  {
346 	    if (notfirst)
347 	      fputc (',', stderr);
348 	    notfirst = true;
349 	    fprintf (stderr, "%lu", i);
350 	  }
351 	++len;
352       }
353     else
354       {
355 	if (len > 1)
356 	  fprintf (stderr, ":%lu", len);
357 	len = 0;
358       }
359   if (len > 1)
360     fprintf (stderr, ":%lu", len);
361 }
362 
363 int
omp_get_place_num_procs(int place_num)364 omp_get_place_num_procs (int place_num)
365 {
366   if (place_num < 0 || place_num >= gomp_places_list_len)
367     return 0;
368 
369   cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
370   return gomp_cpuset_popcount (gomp_cpuset_size, cpusetp);
371 }
372 
373 void
omp_get_place_proc_ids(int place_num,int * ids)374 omp_get_place_proc_ids (int place_num, int *ids)
375 {
376   if (place_num < 0 || place_num >= gomp_places_list_len)
377     return;
378 
379   cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
380   unsigned long i, max = 8 * gomp_cpuset_size;
381   for (i = 0; i < max; i++)
382     if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
383       *ids++ = i;
384 }
385 
386 void
gomp_get_place_proc_ids_8(int place_num,int64_t * ids)387 gomp_get_place_proc_ids_8 (int place_num, int64_t *ids)
388 {
389   if (place_num < 0 || place_num >= gomp_places_list_len)
390     return;
391 
392   cpu_set_t *cpusetp = (cpu_set_t *) gomp_places_list[place_num];
393   unsigned long i, max = 8 * gomp_cpuset_size;
394   for (i = 0; i < max; i++)
395     if (CPU_ISSET_S (i, gomp_cpuset_size, cpusetp))
396       *ids++ = i;
397 }
398 
399 ialias(omp_get_place_num_procs)
400 ialias(omp_get_place_proc_ids)
401 
402 #else
403 
404 #include "../../affinity.c"
405 
406 #endif
407