1 // SPDX-License-Identifier: GPL-2.0 2 #include "util.h" 3 #include <api/fs/fs.h> 4 #include "../perf.h" 5 #include "cpumap.h" 6 #include <assert.h> 7 #include <dirent.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <linux/bitmap.h> 11 #include "asm/bug.h" 12 13 #include "sane_ctype.h" 14 15 static int max_cpu_num; 16 static int max_present_cpu_num; 17 static int max_node_num; 18 static int *cpunode_map; 19 20 static struct cpu_map *cpu_map__default_new(void) 21 { 22 struct cpu_map *cpus; 23 int nr_cpus; 24 25 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); 26 if (nr_cpus < 0) 27 return NULL; 28 29 cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int)); 30 if (cpus != NULL) { 31 int i; 32 for (i = 0; i < nr_cpus; ++i) 33 cpus->map[i] = i; 34 35 cpus->nr = nr_cpus; 36 refcount_set(&cpus->refcnt, 1); 37 } 38 39 return cpus; 40 } 41 42 static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus) 43 { 44 size_t payload_size = nr_cpus * sizeof(int); 45 struct cpu_map *cpus = malloc(sizeof(*cpus) + payload_size); 46 47 if (cpus != NULL) { 48 cpus->nr = nr_cpus; 49 memcpy(cpus->map, tmp_cpus, payload_size); 50 refcount_set(&cpus->refcnt, 1); 51 } 52 53 return cpus; 54 } 55 56 struct cpu_map *cpu_map__read(FILE *file) 57 { 58 struct cpu_map *cpus = NULL; 59 int nr_cpus = 0; 60 int *tmp_cpus = NULL, *tmp; 61 int max_entries = 0; 62 int n, cpu, prev; 63 char sep; 64 65 sep = 0; 66 prev = -1; 67 for (;;) { 68 n = fscanf(file, "%u%c", &cpu, &sep); 69 if (n <= 0) 70 break; 71 if (prev >= 0) { 72 int new_max = nr_cpus + cpu - prev - 1; 73 74 if (new_max >= max_entries) { 75 max_entries = new_max + MAX_NR_CPUS / 2; 76 tmp = realloc(tmp_cpus, max_entries * sizeof(int)); 77 if (tmp == NULL) 78 goto out_free_tmp; 79 tmp_cpus = tmp; 80 } 81 82 while (++prev < cpu) 83 tmp_cpus[nr_cpus++] = prev; 84 } 85 if (nr_cpus == max_entries) { 86 max_entries += MAX_NR_CPUS; 87 tmp = realloc(tmp_cpus, max_entries * sizeof(int)); 88 if (tmp == NULL) 89 goto out_free_tmp; 90 tmp_cpus = tmp; 91 } 92 93 tmp_cpus[nr_cpus++] = cpu; 94 if (n == 2 && sep == '-') 95 prev = cpu; 96 else 97 prev = -1; 98 if (n == 1 || sep == '\n') 99 break; 100 } 101 102 if (nr_cpus > 0) 103 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); 104 else 105 cpus = cpu_map__default_new(); 106 out_free_tmp: 107 free(tmp_cpus); 108 return cpus; 109 } 110 111 static struct cpu_map *cpu_map__read_all_cpu_map(void) 112 { 113 struct cpu_map *cpus = NULL; 114 FILE *onlnf; 115 116 onlnf = fopen("/sys/devices/system/cpu/online", "r"); 117 if (!onlnf) 118 return cpu_map__default_new(); 119 120 cpus = cpu_map__read(onlnf); 121 fclose(onlnf); 122 return cpus; 123 } 124 125 struct cpu_map *cpu_map__new(const char *cpu_list) 126 { 127 struct cpu_map *cpus = NULL; 128 unsigned long start_cpu, end_cpu = 0; 129 char *p = NULL; 130 int i, nr_cpus = 0; 131 int *tmp_cpus = NULL, *tmp; 132 int max_entries = 0; 133 134 if (!cpu_list) 135 return cpu_map__read_all_cpu_map(); 136 137 if (!isdigit(*cpu_list)) 138 goto out; 139 140 while (isdigit(*cpu_list)) { 141 p = NULL; 142 start_cpu = strtoul(cpu_list, &p, 0); 143 if (start_cpu >= INT_MAX 144 || (*p != '\0' && *p != ',' && *p != '-')) 145 goto invalid; 146 147 if (*p == '-') { 148 cpu_list = ++p; 149 p = NULL; 150 end_cpu = strtoul(cpu_list, &p, 0); 151 152 if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) 153 goto invalid; 154 155 if (end_cpu < start_cpu) 156 goto invalid; 157 } else { 158 end_cpu = start_cpu; 159 } 160 161 for (; start_cpu <= end_cpu; start_cpu++) { 162 /* check for duplicates */ 163 for (i = 0; i < nr_cpus; i++) 164 if (tmp_cpus[i] == (int)start_cpu) 165 goto invalid; 166 167 if (nr_cpus == max_entries) { 168 max_entries += MAX_NR_CPUS; 169 tmp = realloc(tmp_cpus, max_entries * sizeof(int)); 170 if (tmp == NULL) 171 goto invalid; 172 tmp_cpus = tmp; 173 } 174 tmp_cpus[nr_cpus++] = (int)start_cpu; 175 } 176 if (*p) 177 ++p; 178 179 cpu_list = p; 180 } 181 182 if (nr_cpus > 0) 183 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); 184 else 185 cpus = cpu_map__default_new(); 186 invalid: 187 free(tmp_cpus); 188 out: 189 return cpus; 190 } 191 192 static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus) 193 { 194 struct cpu_map *map; 195 196 map = cpu_map__empty_new(cpus->nr); 197 if (map) { 198 unsigned i; 199 200 for (i = 0; i < cpus->nr; i++) { 201 /* 202 * Special treatment for -1, which is not real cpu number, 203 * and we need to use (int) -1 to initialize map[i], 204 * otherwise it would become 65535. 205 */ 206 if (cpus->cpu[i] == (u16) -1) 207 map->map[i] = -1; 208 else 209 map->map[i] = (int) cpus->cpu[i]; 210 } 211 } 212 213 return map; 214 } 215 216 static struct cpu_map *cpu_map__from_mask(struct cpu_map_mask *mask) 217 { 218 struct cpu_map *map; 219 int nr, nbits = mask->nr * mask->long_size * BITS_PER_BYTE; 220 221 nr = bitmap_weight(mask->mask, nbits); 222 223 map = cpu_map__empty_new(nr); 224 if (map) { 225 int cpu, i = 0; 226 227 for_each_set_bit(cpu, mask->mask, nbits) 228 map->map[i++] = cpu; 229 } 230 return map; 231 232 } 233 234 struct cpu_map *cpu_map__new_data(struct cpu_map_data *data) 235 { 236 if (data->type == PERF_CPU_MAP__CPUS) 237 return cpu_map__from_entries((struct cpu_map_entries *)data->data); 238 else 239 return cpu_map__from_mask((struct cpu_map_mask *)data->data); 240 } 241 242 size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) 243 { 244 #define BUFSIZE 1024 245 char buf[BUFSIZE]; 246 247 cpu_map__snprint(map, buf, sizeof(buf)); 248 return fprintf(fp, "%s\n", buf); 249 #undef BUFSIZE 250 } 251 252 struct cpu_map *cpu_map__dummy_new(void) 253 { 254 struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); 255 256 if (cpus != NULL) { 257 cpus->nr = 1; 258 cpus->map[0] = -1; 259 refcount_set(&cpus->refcnt, 1); 260 } 261 262 return cpus; 263 } 264 265 struct cpu_map *cpu_map__empty_new(int nr) 266 { 267 struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int) * nr); 268 269 if (cpus != NULL) { 270 int i; 271 272 cpus->nr = nr; 273 for (i = 0; i < nr; i++) 274 cpus->map[i] = -1; 275 276 refcount_set(&cpus->refcnt, 1); 277 } 278 279 return cpus; 280 } 281 282 static void cpu_map__delete(struct cpu_map *map) 283 { 284 if (map) { 285 WARN_ONCE(refcount_read(&map->refcnt) != 0, 286 "cpu_map refcnt unbalanced\n"); 287 free(map); 288 } 289 } 290 291 struct cpu_map *cpu_map__get(struct cpu_map *map) 292 { 293 if (map) 294 refcount_inc(&map->refcnt); 295 return map; 296 } 297 298 void cpu_map__put(struct cpu_map *map) 299 { 300 if (map && refcount_dec_and_test(&map->refcnt)) 301 cpu_map__delete(map); 302 } 303 304 static int cpu__get_topology_int(int cpu, const char *name, int *value) 305 { 306 char path[PATH_MAX]; 307 308 snprintf(path, PATH_MAX, 309 "devices/system/cpu/cpu%d/topology/%s", cpu, name); 310 311 return sysfs__read_int(path, value); 312 } 313 314 int cpu_map__get_socket_id(int cpu) 315 { 316 int value, ret = cpu__get_topology_int(cpu, "physical_package_id", &value); 317 return ret ?: value; 318 } 319 320 int cpu_map__get_socket(struct cpu_map *map, int idx, void *data __maybe_unused) 321 { 322 int cpu; 323 324 if (idx > map->nr) 325 return -1; 326 327 cpu = map->map[idx]; 328 329 return cpu_map__get_socket_id(cpu); 330 } 331 332 static int cmp_ids(const void *a, const void *b) 333 { 334 return *(int *)a - *(int *)b; 335 } 336 337 int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, 338 int (*f)(struct cpu_map *map, int cpu, void *data), 339 void *data) 340 { 341 struct cpu_map *c; 342 int nr = cpus->nr; 343 int cpu, s1, s2; 344 345 /* allocate as much as possible */ 346 c = calloc(1, sizeof(*c) + nr * sizeof(int)); 347 if (!c) 348 return -1; 349 350 for (cpu = 0; cpu < nr; cpu++) { 351 s1 = f(cpus, cpu, data); 352 for (s2 = 0; s2 < c->nr; s2++) { 353 if (s1 == c->map[s2]) 354 break; 355 } 356 if (s2 == c->nr) { 357 c->map[c->nr] = s1; 358 c->nr++; 359 } 360 } 361 /* ensure we process id in increasing order */ 362 qsort(c->map, c->nr, sizeof(int), cmp_ids); 363 364 refcount_set(&c->refcnt, 1); 365 *res = c; 366 return 0; 367 } 368 369 int cpu_map__get_core_id(int cpu) 370 { 371 int value, ret = cpu__get_topology_int(cpu, "core_id", &value); 372 return ret ?: value; 373 } 374 375 int cpu_map__get_core(struct cpu_map *map, int idx, void *data) 376 { 377 int cpu, s; 378 379 if (idx > map->nr) 380 return -1; 381 382 cpu = map->map[idx]; 383 384 cpu = cpu_map__get_core_id(cpu); 385 386 s = cpu_map__get_socket(map, idx, data); 387 if (s == -1) 388 return -1; 389 390 /* 391 * encode socket in upper 16 bits 392 * core_id is relative to socket, and 393 * we need a global id. So we combine 394 * socket+ core id 395 */ 396 return (s << 16) | (cpu & 0xffff); 397 } 398 399 int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) 400 { 401 return cpu_map__build_map(cpus, sockp, cpu_map__get_socket, NULL); 402 } 403 404 int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep) 405 { 406 return cpu_map__build_map(cpus, corep, cpu_map__get_core, NULL); 407 } 408 409 /* setup simple routines to easily access node numbers given a cpu number */ 410 static int get_max_num(char *path, int *max) 411 { 412 size_t num; 413 char *buf; 414 int err = 0; 415 416 if (filename__read_str(path, &buf, &num)) 417 return -1; 418 419 buf[num] = '\0'; 420 421 /* start on the right, to find highest node num */ 422 while (--num) { 423 if ((buf[num] == ',') || (buf[num] == '-')) { 424 num++; 425 break; 426 } 427 } 428 if (sscanf(&buf[num], "%d", max) < 1) { 429 err = -1; 430 goto out; 431 } 432 433 /* convert from 0-based to 1-based */ 434 (*max)++; 435 436 out: 437 free(buf); 438 return err; 439 } 440 441 /* Determine highest possible cpu in the system for sparse allocation */ 442 static void set_max_cpu_num(void) 443 { 444 const char *mnt; 445 char path[PATH_MAX]; 446 int ret = -1; 447 448 /* set up default */ 449 max_cpu_num = 4096; 450 max_present_cpu_num = 4096; 451 452 mnt = sysfs__mountpoint(); 453 if (!mnt) 454 goto out; 455 456 /* get the highest possible cpu number for a sparse allocation */ 457 ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt); 458 if (ret == PATH_MAX) { 459 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 460 goto out; 461 } 462 463 ret = get_max_num(path, &max_cpu_num); 464 if (ret) 465 goto out; 466 467 /* get the highest present cpu number for a sparse allocation */ 468 ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/present", mnt); 469 if (ret == PATH_MAX) { 470 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 471 goto out; 472 } 473 474 ret = get_max_num(path, &max_present_cpu_num); 475 476 out: 477 if (ret) 478 pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num); 479 } 480 481 /* Determine highest possible node in the system for sparse allocation */ 482 static void set_max_node_num(void) 483 { 484 const char *mnt; 485 char path[PATH_MAX]; 486 int ret = -1; 487 488 /* set up default */ 489 max_node_num = 8; 490 491 mnt = sysfs__mountpoint(); 492 if (!mnt) 493 goto out; 494 495 /* get the highest possible cpu number for a sparse allocation */ 496 ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt); 497 if (ret == PATH_MAX) { 498 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 499 goto out; 500 } 501 502 ret = get_max_num(path, &max_node_num); 503 504 out: 505 if (ret) 506 pr_err("Failed to read max nodes, using default of %d\n", max_node_num); 507 } 508 509 int cpu__max_node(void) 510 { 511 if (unlikely(!max_node_num)) 512 set_max_node_num(); 513 514 return max_node_num; 515 } 516 517 int cpu__max_cpu(void) 518 { 519 if (unlikely(!max_cpu_num)) 520 set_max_cpu_num(); 521 522 return max_cpu_num; 523 } 524 525 int cpu__max_present_cpu(void) 526 { 527 if (unlikely(!max_present_cpu_num)) 528 set_max_cpu_num(); 529 530 return max_present_cpu_num; 531 } 532 533 534 int cpu__get_node(int cpu) 535 { 536 if (unlikely(cpunode_map == NULL)) { 537 pr_debug("cpu_map not initialized\n"); 538 return -1; 539 } 540 541 return cpunode_map[cpu]; 542 } 543 544 static int init_cpunode_map(void) 545 { 546 int i; 547 548 set_max_cpu_num(); 549 set_max_node_num(); 550 551 cpunode_map = calloc(max_cpu_num, sizeof(int)); 552 if (!cpunode_map) { 553 pr_err("%s: calloc failed\n", __func__); 554 return -1; 555 } 556 557 for (i = 0; i < max_cpu_num; i++) 558 cpunode_map[i] = -1; 559 560 return 0; 561 } 562 563 int cpu__setup_cpunode_map(void) 564 { 565 struct dirent *dent1, *dent2; 566 DIR *dir1, *dir2; 567 unsigned int cpu, mem; 568 char buf[PATH_MAX]; 569 char path[PATH_MAX]; 570 const char *mnt; 571 int n; 572 573 /* initialize globals */ 574 if (init_cpunode_map()) 575 return -1; 576 577 mnt = sysfs__mountpoint(); 578 if (!mnt) 579 return 0; 580 581 n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt); 582 if (n == PATH_MAX) { 583 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 584 return -1; 585 } 586 587 dir1 = opendir(path); 588 if (!dir1) 589 return 0; 590 591 /* walk tree and setup map */ 592 while ((dent1 = readdir(dir1)) != NULL) { 593 if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1) 594 continue; 595 596 n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name); 597 if (n == PATH_MAX) { 598 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); 599 continue; 600 } 601 602 dir2 = opendir(buf); 603 if (!dir2) 604 continue; 605 while ((dent2 = readdir(dir2)) != NULL) { 606 if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) 607 continue; 608 cpunode_map[cpu] = mem; 609 } 610 closedir(dir2); 611 } 612 closedir(dir1); 613 return 0; 614 } 615 616 bool cpu_map__has(struct cpu_map *cpus, int cpu) 617 { 618 return cpu_map__idx(cpus, cpu) != -1; 619 } 620 621 int cpu_map__idx(struct cpu_map *cpus, int cpu) 622 { 623 int i; 624 625 for (i = 0; i < cpus->nr; ++i) { 626 if (cpus->map[i] == cpu) 627 return i; 628 } 629 630 return -1; 631 } 632 633 int cpu_map__cpu(struct cpu_map *cpus, int idx) 634 { 635 return cpus->map[idx]; 636 } 637 638 size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size) 639 { 640 int i, cpu, start = -1; 641 bool first = true; 642 size_t ret = 0; 643 644 #define COMMA first ? "" : "," 645 646 for (i = 0; i < map->nr + 1; i++) { 647 bool last = i == map->nr; 648 649 cpu = last ? INT_MAX : map->map[i]; 650 651 if (start == -1) { 652 start = i; 653 if (last) { 654 ret += snprintf(buf + ret, size - ret, 655 "%s%d", COMMA, 656 map->map[i]); 657 } 658 } else if (((i - start) != (cpu - map->map[start])) || last) { 659 int end = i - 1; 660 661 if (start == end) { 662 ret += snprintf(buf + ret, size - ret, 663 "%s%d", COMMA, 664 map->map[start]); 665 } else { 666 ret += snprintf(buf + ret, size - ret, 667 "%s%d-%d", COMMA, 668 map->map[start], map->map[end]); 669 } 670 first = false; 671 start = i; 672 } 673 } 674 675 #undef COMMA 676 677 pr_debug("cpumask list: %s\n", buf); 678 return ret; 679 } 680 681 static char hex_char(unsigned char val) 682 { 683 if (val < 10) 684 return val + '0'; 685 if (val < 16) 686 return val - 10 + 'a'; 687 return '?'; 688 } 689 690 size_t cpu_map__snprint_mask(struct cpu_map *map, char *buf, size_t size) 691 { 692 int i, cpu; 693 char *ptr = buf; 694 unsigned char *bitmap; 695 int last_cpu = cpu_map__cpu(map, map->nr - 1); 696 697 bitmap = zalloc((last_cpu + 7) / 8); 698 if (bitmap == NULL) { 699 buf[0] = '\0'; 700 return 0; 701 } 702 703 for (i = 0; i < map->nr; i++) { 704 cpu = cpu_map__cpu(map, i); 705 bitmap[cpu / 8] |= 1 << (cpu % 8); 706 } 707 708 for (cpu = last_cpu / 4 * 4; cpu >= 0; cpu -= 4) { 709 unsigned char bits = bitmap[cpu / 8]; 710 711 if (cpu % 8) 712 bits >>= 4; 713 else 714 bits &= 0xf; 715 716 *ptr++ = hex_char(bits); 717 if ((cpu % 32) == 0 && cpu > 0) 718 *ptr++ = ','; 719 } 720 *ptr = '\0'; 721 free(bitmap); 722 723 buf[size - 1] = '\0'; 724 return ptr - buf; 725 } 726