1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <perf/cpumap.h>
4 #include <linux/refcount.h>
5 #include <internal/cpumap.h>
14 void perf_cpu_map__set_nr(struct perf_cpu_map
*map
, int nr_cpus
)
16 RC_CHK_ACCESS(map
)->nr
= nr_cpus
;
19 struct perf_cpu_map
*perf_cpu_map__alloc(int nr_cpus
)
21 RC_STRUCT(perf_cpu_map
) *cpus
;
22 struct perf_cpu_map
*result
;
27 cpus
= malloc(sizeof(*cpus
) + sizeof(struct perf_cpu
) * nr_cpus
);
28 if (ADD_RC_CHK(result
, cpus
)) {
30 refcount_set(&cpus
->refcnt
, 1);
35 struct perf_cpu_map
*perf_cpu_map__new_any_cpu(void)
37 struct perf_cpu_map
*cpus
= perf_cpu_map__alloc(1);
40 RC_CHK_ACCESS(cpus
)->map
[0].cpu
= -1;
45 static void cpu_map__delete(struct perf_cpu_map
*map
)
48 WARN_ONCE(refcount_read(perf_cpu_map__refcnt(map
)) != 0,
49 "cpu_map refcnt unbalanced\n");
54 struct perf_cpu_map
*perf_cpu_map__get(struct perf_cpu_map
*map
)
56 struct perf_cpu_map
*result
;
58 if (RC_CHK_GET(result
, map
))
59 refcount_inc(perf_cpu_map__refcnt(map
));
64 void perf_cpu_map__put(struct perf_cpu_map
*map
)
67 if (refcount_dec_and_test(perf_cpu_map__refcnt(map
)))
74 static struct perf_cpu_map
*cpu_map__new_sysconf(void)
76 struct perf_cpu_map
*cpus
;
77 int nr_cpus
, nr_cpus_conf
;
79 nr_cpus
= sysconf(_SC_NPROCESSORS_ONLN
);
83 nr_cpus_conf
= sysconf(_SC_NPROCESSORS_CONF
);
84 if (nr_cpus
!= nr_cpus_conf
) {
85 pr_warning("Number of online CPUs (%d) differs from the number configured (%d) the CPU map will only cover the first %d CPUs.",
86 nr_cpus
, nr_cpus_conf
, nr_cpus
);
89 cpus
= perf_cpu_map__alloc(nr_cpus
);
93 for (i
= 0; i
< nr_cpus
; ++i
)
94 RC_CHK_ACCESS(cpus
)->map
[i
].cpu
= i
;
100 static struct perf_cpu_map
*cpu_map__new_sysfs_online(void)
102 struct perf_cpu_map
*cpus
= NULL
;
105 onlnf
= fopen("/sys/devices/system/cpu/online", "r");
107 cpus
= perf_cpu_map__read(onlnf
);
113 struct perf_cpu_map
*perf_cpu_map__new_online_cpus(void)
115 struct perf_cpu_map
*cpus
= cpu_map__new_sysfs_online();
120 return cpu_map__new_sysconf();
124 static int cmp_cpu(const void *a
, const void *b
)
126 const struct perf_cpu
*cpu_a
= a
, *cpu_b
= b
;
128 return cpu_a
->cpu
- cpu_b
->cpu
;
131 static struct perf_cpu
__perf_cpu_map__cpu(const struct perf_cpu_map
*cpus
, int idx
)
133 return RC_CHK_ACCESS(cpus
)->map
[idx
];
136 static struct perf_cpu_map
*cpu_map__trim_new(int nr_cpus
, const struct perf_cpu
*tmp_cpus
)
138 size_t payload_size
= nr_cpus
* sizeof(struct perf_cpu
);
139 struct perf_cpu_map
*cpus
= perf_cpu_map__alloc(nr_cpus
);
143 memcpy(RC_CHK_ACCESS(cpus
)->map
, tmp_cpus
, payload_size
);
144 qsort(RC_CHK_ACCESS(cpus
)->map
, nr_cpus
, sizeof(struct perf_cpu
), cmp_cpu
);
147 for (i
= 0; i
< nr_cpus
; i
++) {
149 __perf_cpu_map__cpu(cpus
, i
).cpu
!=
150 __perf_cpu_map__cpu(cpus
, i
- 1).cpu
) {
151 RC_CHK_ACCESS(cpus
)->map
[j
++].cpu
=
152 __perf_cpu_map__cpu(cpus
, i
).cpu
;
155 perf_cpu_map__set_nr(cpus
, j
);
156 assert(j
<= nr_cpus
);
161 struct perf_cpu_map
*perf_cpu_map__read(FILE *file
)
163 struct perf_cpu_map
*cpus
= NULL
;
165 struct perf_cpu
*tmp_cpus
= NULL
, *tmp
;
173 n
= fscanf(file
, "%u%c", &cpu
, &sep
);
177 int new_max
= nr_cpus
+ cpu
- prev
- 1;
179 WARN_ONCE(new_max
>= MAX_NR_CPUS
, "Perf can support %d CPUs. "
180 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS
);
182 if (new_max
>= max_entries
) {
183 max_entries
= new_max
+ MAX_NR_CPUS
/ 2;
184 tmp
= realloc(tmp_cpus
, max_entries
* sizeof(struct perf_cpu
));
191 tmp_cpus
[nr_cpus
++].cpu
= prev
;
193 if (nr_cpus
== max_entries
) {
194 max_entries
+= MAX_NR_CPUS
;
195 tmp
= realloc(tmp_cpus
, max_entries
* sizeof(struct perf_cpu
));
201 tmp_cpus
[nr_cpus
++].cpu
= cpu
;
202 if (n
== 2 && sep
== '-')
206 if (n
== 1 || sep
== '\n')
211 cpus
= cpu_map__trim_new(nr_cpus
, tmp_cpus
);
217 struct perf_cpu_map
*perf_cpu_map__new(const char *cpu_list
)
219 struct perf_cpu_map
*cpus
= NULL
;
220 unsigned long start_cpu
, end_cpu
= 0;
223 struct perf_cpu
*tmp_cpus
= NULL
, *tmp
;
227 return perf_cpu_map__new_online_cpus();
230 * must handle the case of empty cpumap to cover
231 * TOPOLOGY header for NUMA nodes with no CPU
232 * ( e.g., because of CPU hotplug)
234 if (!isdigit(*cpu_list
) && *cpu_list
!= '\0')
237 while (isdigit(*cpu_list
)) {
239 start_cpu
= strtoul(cpu_list
, &p
, 0);
240 if (start_cpu
>= INT_MAX
241 || (*p
!= '\0' && *p
!= ',' && *p
!= '-'))
247 end_cpu
= strtoul(cpu_list
, &p
, 0);
249 if (end_cpu
>= INT_MAX
|| (*p
!= '\0' && *p
!= ','))
252 if (end_cpu
< start_cpu
)
258 WARN_ONCE(end_cpu
>= MAX_NR_CPUS
, "Perf can support %d CPUs. "
259 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS
);
261 for (; start_cpu
<= end_cpu
; start_cpu
++) {
262 /* check for duplicates */
263 for (i
= 0; i
< nr_cpus
; i
++)
264 if (tmp_cpus
[i
].cpu
== (int)start_cpu
)
267 if (nr_cpus
== max_entries
) {
268 max_entries
+= MAX_NR_CPUS
;
269 tmp
= realloc(tmp_cpus
, max_entries
* sizeof(struct perf_cpu
));
274 tmp_cpus
[nr_cpus
++].cpu
= (int)start_cpu
;
283 cpus
= cpu_map__trim_new(nr_cpus
, tmp_cpus
);
284 else if (*cpu_list
!= '\0') {
285 pr_warning("Unexpected characters at end of cpu list ('%s'), using online CPUs.",
287 cpus
= perf_cpu_map__new_online_cpus();
289 cpus
= perf_cpu_map__new_any_cpu();
296 static int __perf_cpu_map__nr(const struct perf_cpu_map
*cpus
)
298 return RC_CHK_ACCESS(cpus
)->nr
;
301 struct perf_cpu
perf_cpu_map__cpu(const struct perf_cpu_map
*cpus
, int idx
)
303 struct perf_cpu result
= {
307 if (cpus
&& idx
< __perf_cpu_map__nr(cpus
))
308 return __perf_cpu_map__cpu(cpus
, idx
);
313 int perf_cpu_map__nr(const struct perf_cpu_map
*cpus
)
315 return cpus
? __perf_cpu_map__nr(cpus
) : 1;
318 bool perf_cpu_map__has_any_cpu_or_is_empty(const struct perf_cpu_map
*map
)
320 return map
? __perf_cpu_map__cpu(map
, 0).cpu
== -1 : true;
323 bool perf_cpu_map__is_any_cpu_or_is_empty(const struct perf_cpu_map
*map
)
328 return __perf_cpu_map__nr(map
) == 1 && __perf_cpu_map__cpu(map
, 0).cpu
== -1;
331 bool perf_cpu_map__is_empty(const struct perf_cpu_map
*map
)
336 int perf_cpu_map__idx(const struct perf_cpu_map
*cpus
, struct perf_cpu cpu
)
344 high
= __perf_cpu_map__nr(cpus
);
346 int idx
= (low
+ high
) / 2;
347 struct perf_cpu cpu_at_idx
= __perf_cpu_map__cpu(cpus
, idx
);
349 if (cpu_at_idx
.cpu
== cpu
.cpu
)
352 if (cpu_at_idx
.cpu
> cpu
.cpu
)
361 bool perf_cpu_map__has(const struct perf_cpu_map
*cpus
, struct perf_cpu cpu
)
363 return perf_cpu_map__idx(cpus
, cpu
) != -1;
366 bool perf_cpu_map__equal(const struct perf_cpu_map
*lhs
, const struct perf_cpu_map
*rhs
)
376 nr
= __perf_cpu_map__nr(lhs
);
377 if (nr
!= __perf_cpu_map__nr(rhs
))
380 for (int idx
= 0; idx
< nr
; idx
++) {
381 if (__perf_cpu_map__cpu(lhs
, idx
).cpu
!= __perf_cpu_map__cpu(rhs
, idx
).cpu
)
387 bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map
*map
)
389 return map
&& __perf_cpu_map__cpu(map
, 0).cpu
== -1;
392 struct perf_cpu
perf_cpu_map__min(const struct perf_cpu_map
*map
)
394 struct perf_cpu cpu
, result
= {
399 perf_cpu_map__for_each_cpu_skip_any(cpu
, idx
, map
) {
406 struct perf_cpu
perf_cpu_map__max(const struct perf_cpu_map
*map
)
408 struct perf_cpu result
= {
412 // cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well.
413 return __perf_cpu_map__nr(map
) > 0
414 ? __perf_cpu_map__cpu(map
, __perf_cpu_map__nr(map
) - 1)
418 /** Is 'b' a subset of 'a'. */
419 bool perf_cpu_map__is_subset(const struct perf_cpu_map
*a
, const struct perf_cpu_map
*b
)
423 if (!a
|| __perf_cpu_map__nr(b
) > __perf_cpu_map__nr(a
))
426 for (int i
= 0, j
= 0; i
< __perf_cpu_map__nr(a
); i
++) {
427 if (__perf_cpu_map__cpu(a
, i
).cpu
> __perf_cpu_map__cpu(b
, j
).cpu
)
429 if (__perf_cpu_map__cpu(a
, i
).cpu
== __perf_cpu_map__cpu(b
, j
).cpu
) {
431 if (j
== __perf_cpu_map__nr(b
))
441 * orig either gets freed and replaced with a new map, or reused
442 * with no reference count change (similar to "realloc")
443 * other has its reference count increased.
446 struct perf_cpu_map
*perf_cpu_map__merge(struct perf_cpu_map
*orig
,
447 struct perf_cpu_map
*other
)
449 struct perf_cpu
*tmp_cpus
;
452 struct perf_cpu_map
*merged
;
454 if (perf_cpu_map__is_subset(orig
, other
))
456 if (perf_cpu_map__is_subset(other
, orig
)) {
457 perf_cpu_map__put(orig
);
458 return perf_cpu_map__get(other
);
461 tmp_len
= __perf_cpu_map__nr(orig
) + __perf_cpu_map__nr(other
);
462 tmp_cpus
= malloc(tmp_len
* sizeof(struct perf_cpu
));
466 /* Standard merge algorithm from wikipedia */
468 while (i
< __perf_cpu_map__nr(orig
) && j
< __perf_cpu_map__nr(other
)) {
469 if (__perf_cpu_map__cpu(orig
, i
).cpu
<= __perf_cpu_map__cpu(other
, j
).cpu
) {
470 if (__perf_cpu_map__cpu(orig
, i
).cpu
== __perf_cpu_map__cpu(other
, j
).cpu
)
472 tmp_cpus
[k
++] = __perf_cpu_map__cpu(orig
, i
++);
474 tmp_cpus
[k
++] = __perf_cpu_map__cpu(other
, j
++);
477 while (i
< __perf_cpu_map__nr(orig
))
478 tmp_cpus
[k
++] = __perf_cpu_map__cpu(orig
, i
++);
480 while (j
< __perf_cpu_map__nr(other
))
481 tmp_cpus
[k
++] = __perf_cpu_map__cpu(other
, j
++);
482 assert(k
<= tmp_len
);
484 merged
= cpu_map__trim_new(k
, tmp_cpus
);
486 perf_cpu_map__put(orig
);
490 struct perf_cpu_map
*perf_cpu_map__intersect(struct perf_cpu_map
*orig
,
491 struct perf_cpu_map
*other
)
493 struct perf_cpu
*tmp_cpus
;
496 struct perf_cpu_map
*merged
= NULL
;
498 if (perf_cpu_map__is_subset(other
, orig
))
499 return perf_cpu_map__get(orig
);
500 if (perf_cpu_map__is_subset(orig
, other
))
501 return perf_cpu_map__get(other
);
503 tmp_len
= max(__perf_cpu_map__nr(orig
), __perf_cpu_map__nr(other
));
504 tmp_cpus
= malloc(tmp_len
* sizeof(struct perf_cpu
));
509 while (i
< __perf_cpu_map__nr(orig
) && j
< __perf_cpu_map__nr(other
)) {
510 if (__perf_cpu_map__cpu(orig
, i
).cpu
< __perf_cpu_map__cpu(other
, j
).cpu
)
512 else if (__perf_cpu_map__cpu(orig
, i
).cpu
> __perf_cpu_map__cpu(other
, j
).cpu
)
516 tmp_cpus
[k
++] = __perf_cpu_map__cpu(orig
, i
++);
520 merged
= cpu_map__trim_new(k
, tmp_cpus
);