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 int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu) 320 { 321 int low, high; 322 323 if (!cpus) 324 return -1; 325 326 low = 0; 327 high = __perf_cpu_map__nr(cpus); 328 while (low < high) { 329 int idx = (low + high) / 2; 330 struct perf_cpu cpu_at_idx = __perf_cpu_map__cpu(cpus, idx); 331 332 if (cpu_at_idx.cpu == cpu.cpu) 333 return idx; 334 335 if (cpu_at_idx.cpu > cpu.cpu) 336 high = idx; 337 else 338 low = idx + 1; 339 } 340 341 return -1; 342 } 343 344 bool perf_cpu_map__has(const struct perf_cpu_map *cpus, struct perf_cpu cpu) 345 { 346 return perf_cpu_map__idx(cpus, cpu) != -1; 347 } 348 349 bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_map *rhs) 350 { 351 int nr; 352 353 if (lhs == rhs) 354 return true; 355 356 if (!lhs || !rhs) 357 return false; 358 359 nr = __perf_cpu_map__nr(lhs); 360 if (nr != __perf_cpu_map__nr(rhs)) 361 return false; 362 363 for (int idx = 0; idx < nr; idx++) { 364 if (__perf_cpu_map__cpu(lhs, idx).cpu != __perf_cpu_map__cpu(rhs, idx).cpu) 365 return false; 366 } 367 return true; 368 } 369 370 bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map *map) 371 { 372 return map && __perf_cpu_map__cpu(map, 0).cpu == -1; 373 } 374 375 struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map) 376 { 377 struct perf_cpu result = { 378 .cpu = -1 379 }; 380 381 // cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well. 382 return __perf_cpu_map__nr(map) > 0 383 ? __perf_cpu_map__cpu(map, __perf_cpu_map__nr(map) - 1) 384 : result; 385 } 386 387 /** Is 'b' a subset of 'a'. */ 388 bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b) 389 { 390 if (a == b || !b) 391 return true; 392 if (!a || __perf_cpu_map__nr(b) > __perf_cpu_map__nr(a)) 393 return false; 394 395 for (int i = 0, j = 0; i < __perf_cpu_map__nr(a); i++) { 396 if (__perf_cpu_map__cpu(a, i).cpu > __perf_cpu_map__cpu(b, j).cpu) 397 return false; 398 if (__perf_cpu_map__cpu(a, i).cpu == __perf_cpu_map__cpu(b, j).cpu) { 399 j++; 400 if (j == __perf_cpu_map__nr(b)) 401 return true; 402 } 403 } 404 return false; 405 } 406 407 /* 408 * Merge two cpumaps 409 * 410 * orig either gets freed and replaced with a new map, or reused 411 * with no reference count change (similar to "realloc") 412 * other has its reference count increased. 413 */ 414 415 struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, 416 struct perf_cpu_map *other) 417 { 418 struct perf_cpu *tmp_cpus; 419 int tmp_len; 420 int i, j, k; 421 struct perf_cpu_map *merged; 422 423 if (perf_cpu_map__is_subset(orig, other)) 424 return orig; 425 if (perf_cpu_map__is_subset(other, orig)) { 426 perf_cpu_map__put(orig); 427 return perf_cpu_map__get(other); 428 } 429 430 tmp_len = __perf_cpu_map__nr(orig) + __perf_cpu_map__nr(other); 431 tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu)); 432 if (!tmp_cpus) 433 return NULL; 434 435 /* Standard merge algorithm from wikipedia */ 436 i = j = k = 0; 437 while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) { 438 if (__perf_cpu_map__cpu(orig, i).cpu <= __perf_cpu_map__cpu(other, j).cpu) { 439 if (__perf_cpu_map__cpu(orig, i).cpu == __perf_cpu_map__cpu(other, j).cpu) 440 j++; 441 tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++); 442 } else 443 tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++); 444 } 445 446 while (i < __perf_cpu_map__nr(orig)) 447 tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++); 448 449 while (j < __perf_cpu_map__nr(other)) 450 tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++); 451 assert(k <= tmp_len); 452 453 merged = cpu_map__trim_new(k, tmp_cpus); 454 free(tmp_cpus); 455 perf_cpu_map__put(orig); 456 return merged; 457 } 458 459 struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig, 460 struct perf_cpu_map *other) 461 { 462 struct perf_cpu *tmp_cpus; 463 int tmp_len; 464 int i, j, k; 465 struct perf_cpu_map *merged = NULL; 466 467 if (perf_cpu_map__is_subset(other, orig)) 468 return perf_cpu_map__get(orig); 469 if (perf_cpu_map__is_subset(orig, other)) 470 return perf_cpu_map__get(other); 471 472 tmp_len = max(__perf_cpu_map__nr(orig), __perf_cpu_map__nr(other)); 473 tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu)); 474 if (!tmp_cpus) 475 return NULL; 476 477 i = j = k = 0; 478 while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) { 479 if (__perf_cpu_map__cpu(orig, i).cpu < __perf_cpu_map__cpu(other, j).cpu) 480 i++; 481 else if (__perf_cpu_map__cpu(orig, i).cpu > __perf_cpu_map__cpu(other, j).cpu) 482 j++; 483 else { 484 j++; 485 tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++); 486 } 487 } 488 if (k) 489 merged = cpu_map__trim_new(k, tmp_cpus); 490 free(tmp_cpus); 491 return merged; 492 } 493