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 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 static void 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 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 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 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 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 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