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(r: &cpus->refcnt, n: 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(nr_cpus: 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(r: 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(r: 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(map: cpus, 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: 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(cpus: lhs); |
360 | if (nr != __perf_cpu_map__nr(cpus: 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(cpus: map) > 0 |
383 | ? __perf_cpu_map__cpu(map, __perf_cpu_map__nr(cpus: 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(cpus: b) > __perf_cpu_map__nr(cpus: a)) |
393 | return false; |
394 | |
395 | for (int i = 0, j = 0; i < __perf_cpu_map__nr(cpus: 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(cpus: 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(a: orig, b: other)) |
424 | return orig; |
425 | if (perf_cpu_map__is_subset(a: other, b: orig)) { |
426 | perf_cpu_map__put(map: orig); |
427 | return perf_cpu_map__get(map: other); |
428 | } |
429 | |
430 | tmp_len = __perf_cpu_map__nr(cpus: orig) + __perf_cpu_map__nr(cpus: 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(cpus: orig) && j < __perf_cpu_map__nr(cpus: 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(nr_cpus: k, tmp_cpus); |
454 | free(tmp_cpus); |
455 | perf_cpu_map__put(map: 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(a: other, b: orig)) |
468 | return perf_cpu_map__get(map: orig); |
469 | if (perf_cpu_map__is_subset(a: orig, b: other)) |
470 | return perf_cpu_map__get(map: other); |
471 | |
472 | tmp_len = max(__perf_cpu_map__nr(cpus: orig), __perf_cpu_map__nr(cpus: 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(cpus: orig) && j < __perf_cpu_map__nr(cpus: 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(nr_cpus: k, tmp_cpus); |
490 | free(tmp_cpus); |
491 | return merged; |
492 | } |
493 | |