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