1 // SPDX-License-Identifier: GPL-2.0-only
3 #include <perf/cpumap.h>
5 #include <linux/refcount.h>
6 #include <internal/cpumap.h>
14 #include <api/fs/fs.h>
16 #define MAX_NR_CPUS 4096
18 void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus)
20 RC_CHK_ACCESS(map)->nr = nr_cpus;
23 struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus)
25 RC_STRUCT(perf_cpu_map) *cpus;
26 struct perf_cpu_map *result;
31 cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus);
32 if (ADD_RC_CHK(result, cpus)) {
34 refcount_set(&cpus->refcnt, 1);
39 struct perf_cpu_map *perf_cpu_map__new_any_cpu(void)
41 struct perf_cpu_map *cpus = perf_cpu_map__alloc(1);
44 RC_CHK_ACCESS(cpus)->map[0].cpu = -1;
49 static void cpu_map__delete(struct perf_cpu_map *map)
52 WARN_ONCE(refcount_read(perf_cpu_map__refcnt(map)) != 0,
53 "cpu_map refcnt unbalanced\n");
58 struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map)
60 struct perf_cpu_map *result;
62 if (RC_CHK_GET(result, map))
63 refcount_inc(perf_cpu_map__refcnt(map));
68 void perf_cpu_map__put(struct perf_cpu_map *map)
71 if (refcount_dec_and_test(perf_cpu_map__refcnt(map)))
78 static struct perf_cpu_map *cpu_map__new_sysconf(void)
80 struct perf_cpu_map *cpus;
81 int nr_cpus, nr_cpus_conf;
83 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
87 nr_cpus_conf = sysconf(_SC_NPROCESSORS_CONF);
88 if (nr_cpus != nr_cpus_conf) {
89 pr_warning("Number of online CPUs (%d) differs from the number configured (%d) the CPU map will only cover the first %d CPUs.",
90 nr_cpus, nr_cpus_conf, nr_cpus);
93 cpus = perf_cpu_map__alloc(nr_cpus);
97 for (i = 0; i < nr_cpus; ++i)
98 RC_CHK_ACCESS(cpus)->map[i].cpu = i;
104 static struct perf_cpu_map *cpu_map__new_sysfs_online(void)
106 struct perf_cpu_map *cpus = NULL;
110 if (sysfs__read_str("devices/system/cpu/online", &buf, &buf_len) >= 0) {
111 cpus = perf_cpu_map__new(buf);
117 struct perf_cpu_map *perf_cpu_map__new_online_cpus(void)
119 struct perf_cpu_map *cpus = cpu_map__new_sysfs_online();
124 return cpu_map__new_sysconf();
128 static int cmp_cpu(const void *a, const void *b)
130 const struct perf_cpu *cpu_a = a, *cpu_b = b;
132 return cpu_a->cpu - cpu_b->cpu;
135 static struct perf_cpu __perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
137 return RC_CHK_ACCESS(cpus)->map[idx];
140 static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, const struct perf_cpu *tmp_cpus)
142 size_t payload_size = nr_cpus * sizeof(struct perf_cpu);
143 struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr_cpus);
147 memcpy(RC_CHK_ACCESS(cpus)->map, tmp_cpus, payload_size);
148 qsort(RC_CHK_ACCESS(cpus)->map, nr_cpus, sizeof(struct perf_cpu), cmp_cpu);
151 for (i = 0; i < nr_cpus; i++) {
153 __perf_cpu_map__cpu(cpus, i).cpu !=
154 __perf_cpu_map__cpu(cpus, i - 1).cpu) {
155 RC_CHK_ACCESS(cpus)->map[j++].cpu =
156 __perf_cpu_map__cpu(cpus, i).cpu;
159 perf_cpu_map__set_nr(cpus, j);
160 assert(j <= nr_cpus);
165 struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list)
167 struct perf_cpu_map *cpus = NULL;
168 unsigned long start_cpu, end_cpu = 0;
171 struct perf_cpu *tmp_cpus = NULL, *tmp;
175 return perf_cpu_map__new_online_cpus();
178 * must handle the case of empty cpumap to cover
179 * TOPOLOGY header for NUMA nodes with no CPU
180 * ( e.g., because of CPU hotplug)
182 if (!isdigit(*cpu_list) && *cpu_list != '\0')
185 while (isdigit(*cpu_list)) {
187 start_cpu = strtoul(cpu_list, &p, 0);
188 if (start_cpu >= INT_MAX
189 || (*p != '\0' && *p != ',' && *p != '-' && *p != '\n'))
195 end_cpu = strtoul(cpu_list, &p, 0);
197 if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',' && *p != '\n'))
200 if (end_cpu < start_cpu)
206 WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. "
207 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
209 for (; start_cpu <= end_cpu; start_cpu++) {
210 /* check for duplicates */
211 for (i = 0; i < nr_cpus; i++)
212 if (tmp_cpus[i].cpu == (int)start_cpu)
215 if (nr_cpus == max_entries) {
216 max_entries += max(end_cpu - start_cpu + 1, 16UL);
217 tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu));
222 tmp_cpus[nr_cpus++].cpu = (int)start_cpu;
231 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
232 } else if (*cpu_list != '\0') {
233 pr_warning("Unexpected characters at end of cpu list ('%s'), using online CPUs.",
235 cpus = perf_cpu_map__new_online_cpus();
237 cpus = perf_cpu_map__new_any_cpu();
245 static int __perf_cpu_map__nr(const struct perf_cpu_map *cpus)
247 return RC_CHK_ACCESS(cpus)->nr;
250 struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
252 struct perf_cpu result = {
256 if (cpus && idx < __perf_cpu_map__nr(cpus))
257 return __perf_cpu_map__cpu(cpus, idx);
262 int perf_cpu_map__nr(const struct perf_cpu_map *cpus)
264 return cpus ? __perf_cpu_map__nr(cpus) : 1;
267 bool perf_cpu_map__has_any_cpu_or_is_empty(const struct perf_cpu_map *map)
269 return map ? __perf_cpu_map__cpu(map, 0).cpu == -1 : true;
272 bool perf_cpu_map__is_any_cpu_or_is_empty(const struct perf_cpu_map *map)
277 return __perf_cpu_map__nr(map) == 1 && __perf_cpu_map__cpu(map, 0).cpu == -1;
280 bool perf_cpu_map__is_empty(const struct perf_cpu_map *map)
285 int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu)
293 high = __perf_cpu_map__nr(cpus);
295 int idx = (low + high) / 2;
296 struct perf_cpu cpu_at_idx = __perf_cpu_map__cpu(cpus, idx);
298 if (cpu_at_idx.cpu == cpu.cpu)
301 if (cpu_at_idx.cpu > cpu.cpu)
310 bool perf_cpu_map__has(const struct perf_cpu_map *cpus, struct perf_cpu cpu)
312 return perf_cpu_map__idx(cpus, cpu) != -1;
315 bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_map *rhs)
325 nr = __perf_cpu_map__nr(lhs);
326 if (nr != __perf_cpu_map__nr(rhs))
329 for (int idx = 0; idx < nr; idx++) {
330 if (__perf_cpu_map__cpu(lhs, idx).cpu != __perf_cpu_map__cpu(rhs, idx).cpu)
336 bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map *map)
338 return map && __perf_cpu_map__cpu(map, 0).cpu == -1;
341 struct perf_cpu perf_cpu_map__min(const struct perf_cpu_map *map)
343 struct perf_cpu cpu, result = {
348 perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) {
355 struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map)
357 struct perf_cpu result = {
361 // cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well.
362 return __perf_cpu_map__nr(map) > 0
363 ? __perf_cpu_map__cpu(map, __perf_cpu_map__nr(map) - 1)
367 /** Is 'b' a subset of 'a'. */
368 bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b)
372 if (!a || __perf_cpu_map__nr(b) > __perf_cpu_map__nr(a))
375 for (int i = 0, j = 0; i < __perf_cpu_map__nr(a); i++) {
376 if (__perf_cpu_map__cpu(a, i).cpu > __perf_cpu_map__cpu(b, j).cpu)
378 if (__perf_cpu_map__cpu(a, i).cpu == __perf_cpu_map__cpu(b, j).cpu) {
380 if (j == __perf_cpu_map__nr(b))
390 * If 'other' is subset of '*orig', '*orig' keeps itself with no reference count
391 * change (similar to "realloc").
393 * If '*orig' is subset of 'other', '*orig' reuses 'other' with its reference
396 * Otherwise, '*orig' gets freed and replaced with a new map.
398 int perf_cpu_map__merge(struct perf_cpu_map **orig, struct perf_cpu_map *other)
400 struct perf_cpu *tmp_cpus;
403 struct perf_cpu_map *merged;
405 if (perf_cpu_map__is_subset(*orig, other))
407 if (perf_cpu_map__is_subset(other, *orig)) {
408 perf_cpu_map__put(*orig);
409 *orig = perf_cpu_map__get(other);
413 tmp_len = __perf_cpu_map__nr(*orig) + __perf_cpu_map__nr(other);
414 tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu));
418 /* Standard merge algorithm from wikipedia */
420 while (i < __perf_cpu_map__nr(*orig) && j < __perf_cpu_map__nr(other)) {
421 if (__perf_cpu_map__cpu(*orig, i).cpu <= __perf_cpu_map__cpu(other, j).cpu) {
422 if (__perf_cpu_map__cpu(*orig, i).cpu == __perf_cpu_map__cpu(other, j).cpu)
424 tmp_cpus[k++] = __perf_cpu_map__cpu(*orig, i++);
426 tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++);
429 while (i < __perf_cpu_map__nr(*orig))
430 tmp_cpus[k++] = __perf_cpu_map__cpu(*orig, i++);
432 while (j < __perf_cpu_map__nr(other))
433 tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++);
434 assert(k <= tmp_len);
436 merged = cpu_map__trim_new(k, tmp_cpus);
438 perf_cpu_map__put(*orig);
443 struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig,
444 struct perf_cpu_map *other)
446 struct perf_cpu *tmp_cpus;
449 struct perf_cpu_map *merged = NULL;
451 if (perf_cpu_map__is_subset(other, orig))
452 return perf_cpu_map__get(orig);
453 if (perf_cpu_map__is_subset(orig, other))
454 return perf_cpu_map__get(other);
456 tmp_len = max(__perf_cpu_map__nr(orig), __perf_cpu_map__nr(other));
457 tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu));
462 while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) {
463 if (__perf_cpu_map__cpu(orig, i).cpu < __perf_cpu_map__cpu(other, j).cpu)
465 else if (__perf_cpu_map__cpu(orig, i).cpu > __perf_cpu_map__cpu(other, j).cpu)
469 tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++);
473 merged = cpu_map__trim_new(k, tmp_cpus);