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
48void
49gomp_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
65void
66gomp_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
72void **
73gomp_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
100void
101gomp_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
107bool
108gomp_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
136bool
137gomp_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
154bool
155gomp_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
176bool
177gomp_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
186bool
187gomp_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
225bool
226gomp_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
327void
328gomp_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