1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <perf/cpumap.h> 3 #include <stdlib.h> 4 #include <linux/refcount.h> 5 #include <internal/cpumap.h> 6 #include <asm/bug.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <ctype.h> 11 #include <limits.h> 12 #include "internal.h" 13 14 void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus) 15 { 16 RC_CHK_ACCESS(map)->nr = nr_cpus; 17 } 18 19 struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus) 20 { 21 RC_STRUCT(perf_cpu_map) *cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus); 22 struct perf_cpu_map *result; 23 24 if (ADD_RC_CHK(result, cpus)) { 25 cpus->nr = nr_cpus; 26 refcount_set(&cpus->refcnt, 1); 27 } 28 return result; 29 } 30 31 struct perf_cpu_map *perf_cpu_map__new_any_cpu(void) 32 { 33 struct perf_cpu_map *cpus = perf_cpu_map__alloc(1); 34 35 if (cpus) 36 RC_CHK_ACCESS(cpus)->map[0].cpu = -1; 37 38 return cpus; 39 } 40 41 static void cpu_map__delete(struct perf_cpu_map *map) 42 { 43 if (map) { 44 WARN_ONCE(refcount_read(perf_cpu_map__refcnt(map)) != 0, 45 "cpu_map refcnt unbalanced\n"); 46 RC_CHK_FREE(map); 47 } 48 } 49 50 struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map) 51 { 52 struct perf_cpu_map *result; 53 54 if (RC_CHK_GET(result, map)) 55 refcount_inc(perf_cpu_map__refcnt(map)); 56 57 return result; 58 } 59 60 void perf_cpu_map__put(struct perf_cpu_map *map) 61 { 62 if (map) { 63 if (refcount_dec_and_test(perf_cpu_map__refcnt(map))) 64 cpu_map__delete(map); 65 else 66 RC_CHK_PUT(map); 67 } 68 } 69 70 static struct perf_cpu_map *cpu_map__new_sysconf(void) 71 { 72 struct perf_cpu_map *cpus; 73 int nr_cpus, nr_cpus_conf; 74 75 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); 76 if (nr_cpus < 0) 77 return NULL; 78 79 nr_cpus_conf = sysconf(_SC_NPROCESSORS_CONF); 80 if (nr_cpus != nr_cpus_conf) { 81 pr_warning("Number of online CPUs (%d) differs from the number configured (%d) the CPU map will only cover the first %d CPUs.", 82 nr_cpus, nr_cpus_conf, nr_cpus); 83 } 84 85 cpus = perf_cpu_map__alloc(nr_cpus); 86 if (cpus != NULL) { 87 int i; 88 89 for (i = 0; i < nr_cpus; ++i) 90 RC_CHK_ACCESS(cpus)->map[i].cpu = i; 91 } 92 93 return cpus; 94 } 95 96 static struct perf_cpu_map *cpu_map__new_sysfs_online(void) 97 { 98 struct perf_cpu_map *cpus = NULL; 99 FILE *onlnf; 100 101 onlnf = fopen("/sys/devices/system/cpu/online", "r"); 102 if (onlnf) { 103 cpus = perf_cpu_map__read(onlnf); 104 fclose(onlnf); 105 } 106 return cpus; 107 } 108 109 struct perf_cpu_map *perf_cpu_map__new_online_cpus(void) 110 { 111 struct perf_cpu_map *cpus = cpu_map__new_sysfs_online(); 112 113 if (cpus) 114 return cpus; 115 116 return cpu_map__new_sysconf(); 117 } 118 119 120 static int cmp_cpu(const void *a, const void *b) 121 { 122 const struct perf_cpu *cpu_a = a, *cpu_b = b; 123 124 return cpu_a->cpu - cpu_b->cpu; 125 } 126 127 static struct perf_cpu __perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) 128 { 129 return RC_CHK_ACCESS(cpus)->map[idx]; 130 } 131 132 static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, const struct perf_cpu *tmp_cpus) 133 { 134 size_t payload_size = nr_cpus * sizeof(struct perf_cpu); 135 struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr_cpus); 136 int i, j; 137 138 if (cpus != NULL) { 139 memcpy(RC_CHK_ACCESS(cpus)->map, tmp_cpus, payload_size); 140 qsort(RC_CHK_ACCESS(cpus)->map, nr_cpus, sizeof(struct perf_cpu), cmp_cpu); 141 /* Remove dups */ 142 j = 0; 143 for (i = 0; i < nr_cpus; i++) { 144 if (i == 0 || 145 __perf_cpu_map__cpu(cpus, i).cpu != 146 __perf_cpu_map__cpu(cpus, i - 1).cpu) { 147 RC_CHK_ACCESS(cpus)->map[j++].cpu = 148 __perf_cpu_map__cpu(cpus, i).cpu; 149 } 150 } 151 perf_cpu_map__set_nr(cpus, j); 152 assert(j <= nr_cpus); 153 } 154 return cpus; 155 } 156 157 struct perf_cpu_map *perf_cpu_map__read(FILE *file) 158 { 159 struct perf_cpu_map *cpus = NULL; 160 int nr_cpus = 0; 161 struct perf_cpu *tmp_cpus = NULL, *tmp; 162 int max_entries = 0; 163 int n, cpu, prev; 164 char sep; 165 166 sep = 0; 167 prev = -1; 168 for (;;) { 169 n = fscanf(file, "%u%c", &cpu, &sep); 170 if (n <= 0) 171 break; 172 if (prev >= 0) { 173 int new_max = nr_cpus + cpu - prev - 1; 174 175 WARN_ONCE(new_max >= MAX_NR_CPUS, "Perf can support %d CPUs. " 176 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS); 177 178 if (new_max >= max_entries) { 179 max_entries = new_max + MAX_NR_CPUS / 2; 180 tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu)); 181 if (tmp == NULL) 182 goto out_free_tmp; 183 tmp_cpus = tmp; 184 } 185 186 while (++prev < cpu) 187 tmp_cpus[nr_cpus++].cpu = prev; 188 } 189 if (nr_cpus == max_entries) { 190 max_entries += MAX_NR_CPUS; 191 tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu)); 192 if (tmp == NULL) 193 goto out_free_tmp; 194 tmp_cpus = tmp; 195 } 196 197 tmp_cpus[nr_cpus++].cpu = cpu; 198 if (n == 2 && sep == '-') 199 prev = cpu; 200 else 201 prev = -1; 202 if (n == 1 || sep == '\n') 203 break; 204 } 205 206 if (nr_cpus > 0) 207 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); 208 out_free_tmp: 209 free(tmp_cpus); 210 return cpus; 211 } 212 213 struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list) 214 { 215 struct perf_cpu_map *cpus = NULL; 216 unsigned long start_cpu, end_cpu = 0; 217 char *p = NULL; 218 int i, nr_cpus = 0; 219 struct perf_cpu *tmp_cpus = NULL, *tmp; 220 int max_entries = 0; 221 222 if (!cpu_list) 223 return perf_cpu_map__new_online_cpus(); 224 225 /* 226 * must handle the case of empty cpumap to cover 227 * TOPOLOGY header for NUMA nodes with no CPU 228 * ( e.g., because of CPU hotplug) 229 */ 230 if (!isdigit(*cpu_list) && *cpu_list != '\0') 231 goto out; 232 233 while (isdigit(*cpu_list)) { 234 p = NULL; 235 start_cpu = strtoul(cpu_list, &p, 0); 236 if (start_cpu >= INT_MAX 237 || (*p != '\0' && *p != ',' && *p != '-')) 238 goto invalid; 239 240 if (*p == '-') { 241 cpu_list = ++p; 242 p = NULL; 243 end_cpu = strtoul(cpu_list, &p, 0); 244 245 if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) 246 goto invalid; 247 248 if (end_cpu < start_cpu) 249 goto invalid; 250 } else { 251 end_cpu = start_cpu; 252 } 253 254 WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. " 255 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS); 256 257 for (; start_cpu <= end_cpu; start_cpu++) { 258 /* check for duplicates */ 259 for (i = 0; i < nr_cpus; i++) 260 if (tmp_cpus[i].cpu == (int)start_cpu) 261 goto invalid; 262 263 if (nr_cpus == max_entries) { 264 max_entries += MAX_NR_CPUS; 265 tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu)); 266 if (tmp == NULL) 267 goto invalid; 268 tmp_cpus = tmp; 269 } 270 tmp_cpus[nr_cpus++].cpu = (int)start_cpu; 271 } 272 if (*p) 273 ++p; 274 275 cpu_list = p; 276 } 277 278 if (nr_cpus > 0) 279 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); 280 else if (*cpu_list != '\0') { 281 pr_warning("Unexpected characters at end of cpu list ('%s'), using online CPUs.", 282 cpu_list); 283 cpus = perf_cpu_map__new_online_cpus(); 284 } else 285 cpus = perf_cpu_map__new_any_cpu(); 286 invalid: 287 free(tmp_cpus); 288 out: 289 return cpus; 290 } 291 292 static int __perf_cpu_map__nr(const struct perf_cpu_map *cpus) 293 { 294 return RC_CHK_ACCESS(cpus)->nr; 295 } 296 297 struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) 298 { 299 struct perf_cpu result = { 300 .cpu = -1 301 }; 302 303 if (cpus && idx < __perf_cpu_map__nr(cpus)) 304 return __perf_cpu_map__cpu(cpus, idx); 305 306 return result; 307 } 308 309 int perf_cpu_map__nr(const struct perf_cpu_map *cpus) 310 { 311 return cpus ? __perf_cpu_map__nr(cpus) : 1; 312 } 313 314 bool perf_cpu_map__has_any_cpu_or_is_empty(const struct perf_cpu_map *map) 315 { 316 return map ? __perf_cpu_map__cpu(map, 0).cpu == -1 : true; 317 } 318 319 bool perf_cpu_map__is_any_cpu_or_is_empty(const struct perf_cpu_map *map) 320 { 321 if (!map) 322 return true; 323 324 return __perf_cpu_map__nr(map) == 1 && __perf_cpu_map__cpu(map, 0).cpu == -1; 325 } 326 327 bool perf_cpu_map__is_empty(const struct perf_cpu_map *map) 328 { 329 return map == NULL; 330 } 331 332 int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu) 333 { 334 int low, high; 335 336 if (!cpus) 337 return -1; 338 339 low = 0; 340 high = __perf_cpu_map__nr(cpus); 341 while (low < high) { 342 int idx = (low + high) / 2; 343 struct perf_cpu cpu_at_idx = __perf_cpu_map__cpu(cpus, idx); 344 345 if (cpu_at_idx.cpu == cpu.cpu) 346 return idx; 347 348 if (cpu_at_idx.cpu > cpu.cpu) 349 high = idx; 350 else 351 low = idx + 1; 352 } 353 354 return -1; 355 } 356 357 bool perf_cpu_map__has(const struct perf_cpu_map *cpus, struct perf_cpu cpu) 358 { 359 return perf_cpu_map__idx(cpus, cpu) != -1; 360 } 361 362 bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_map *rhs) 363 { 364 int nr; 365 366 if (lhs == rhs) 367 return true; 368 369 if (!lhs || !rhs) 370 return false; 371 372 nr = __perf_cpu_map__nr(lhs); 373 if (nr != __perf_cpu_map__nr(rhs)) 374 return false; 375 376 for (int idx = 0; idx < nr; idx++) { 377 if (__perf_cpu_map__cpu(lhs, idx).cpu != __perf_cpu_map__cpu(rhs, idx).cpu) 378 return false; 379 } 380 return true; 381 } 382 383 bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map *map) 384 { 385 return map && __perf_cpu_map__cpu(map, 0).cpu == -1; 386 } 387 388 struct perf_cpu perf_cpu_map__min(const struct perf_cpu_map *map) 389 { 390 struct perf_cpu cpu, result = { 391 .cpu = -1 392 }; 393 int idx; 394 395 perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) { 396 result = cpu; 397 break; 398 } 399 return result; 400 } 401 402 struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map) 403 { 404 struct perf_cpu result = { 405 .cpu = -1 406 }; 407 408 // cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well. 409 return __perf_cpu_map__nr(map) > 0 410 ? __perf_cpu_map__cpu(map, __perf_cpu_map__nr(map) - 1) 411 : result; 412 } 413 414 /** Is 'b' a subset of 'a'. */ 415 bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b) 416 { 417 if (a == b || !b) 418 return true; 419 if (!a || __perf_cpu_map__nr(b) > __perf_cpu_map__nr(a)) 420 return false; 421 422 for (int i = 0, j = 0; i < __perf_cpu_map__nr(a); i++) { 423 if (__perf_cpu_map__cpu(a, i).cpu > __perf_cpu_map__cpu(b, j).cpu) 424 return false; 425 if (__perf_cpu_map__cpu(a, i).cpu == __perf_cpu_map__cpu(b, j).cpu) { 426 j++; 427 if (j == __perf_cpu_map__nr(b)) 428 return true; 429 } 430 } 431 return false; 432 } 433 434 /* 435 * Merge two cpumaps 436 * 437 * orig either gets freed and replaced with a new map, or reused 438 * with no reference count change (similar to "realloc") 439 * other has its reference count increased. 440 */ 441 442 struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, 443 struct perf_cpu_map *other) 444 { 445 struct perf_cpu *tmp_cpus; 446 int tmp_len; 447 int i, j, k; 448 struct perf_cpu_map *merged; 449 450 if (perf_cpu_map__is_subset(orig, other)) 451 return orig; 452 if (perf_cpu_map__is_subset(other, orig)) { 453 perf_cpu_map__put(orig); 454 return perf_cpu_map__get(other); 455 } 456 457 tmp_len = __perf_cpu_map__nr(orig) + __perf_cpu_map__nr(other); 458 tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu)); 459 if (!tmp_cpus) 460 return NULL; 461 462 /* Standard merge algorithm from wikipedia */ 463 i = j = k = 0; 464 while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) { 465 if (__perf_cpu_map__cpu(orig, i).cpu <= __perf_cpu_map__cpu(other, j).cpu) { 466 if (__perf_cpu_map__cpu(orig, i).cpu == __perf_cpu_map__cpu(other, j).cpu) 467 j++; 468 tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++); 469 } else 470 tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++); 471 } 472 473 while (i < __perf_cpu_map__nr(orig)) 474 tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++); 475 476 while (j < __perf_cpu_map__nr(other)) 477 tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++); 478 assert(k <= tmp_len); 479 480 merged = cpu_map__trim_new(k, tmp_cpus); 481 free(tmp_cpus); 482 perf_cpu_map__put(orig); 483 return merged; 484 } 485 486 struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig, 487 struct perf_cpu_map *other) 488 { 489 struct perf_cpu *tmp_cpus; 490 int tmp_len; 491 int i, j, k; 492 struct perf_cpu_map *merged = NULL; 493 494 if (perf_cpu_map__is_subset(other, orig)) 495 return perf_cpu_map__get(orig); 496 if (perf_cpu_map__is_subset(orig, other)) 497 return perf_cpu_map__get(other); 498 499 tmp_len = max(__perf_cpu_map__nr(orig), __perf_cpu_map__nr(other)); 500 tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu)); 501 if (!tmp_cpus) 502 return NULL; 503 504 i = j = k = 0; 505 while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) { 506 if (__perf_cpu_map__cpu(orig, i).cpu < __perf_cpu_map__cpu(other, j).cpu) 507 i++; 508 else if (__perf_cpu_map__cpu(orig, i).cpu > __perf_cpu_map__cpu(other, j).cpu) 509 j++; 510 else { 511 j++; 512 tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++); 513 } 514 } 515 if (k) 516 merged = cpu_map__trim_new(k, tmp_cpus); 517 free(tmp_cpus); 518 return merged; 519 } 520