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