1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <perf/cpumap.h>
4 #include <linux/refcount.h>
5 #include <internal/cpumap.h>
13 struct perf_cpu_map
*perf_cpu_map__dummy_new(void)
15 struct perf_cpu_map
*cpus
= malloc(sizeof(*cpus
) + sizeof(int));
20 refcount_set(&cpus
->refcnt
, 1);
26 static void cpu_map__delete(struct perf_cpu_map
*map
)
29 WARN_ONCE(refcount_read(&map
->refcnt
) != 0,
30 "cpu_map refcnt unbalanced\n");
35 struct perf_cpu_map
*perf_cpu_map__get(struct perf_cpu_map
*map
)
38 refcount_inc(&map
->refcnt
);
42 void perf_cpu_map__put(struct perf_cpu_map
*map
)
44 if (map
&& refcount_dec_and_test(&map
->refcnt
))
48 static struct perf_cpu_map
*cpu_map__default_new(void)
50 struct perf_cpu_map
*cpus
;
53 nr_cpus
= sysconf(_SC_NPROCESSORS_ONLN
);
57 cpus
= malloc(sizeof(*cpus
) + nr_cpus
* sizeof(int));
61 for (i
= 0; i
< nr_cpus
; ++i
)
65 refcount_set(&cpus
->refcnt
, 1);
71 static int cmp_int(const void *a
, const void *b
)
73 return *(const int *)a
- *(const int*)b
;
76 static struct perf_cpu_map
*cpu_map__trim_new(int nr_cpus
, int *tmp_cpus
)
78 size_t payload_size
= nr_cpus
* sizeof(int);
79 struct perf_cpu_map
*cpus
= malloc(sizeof(*cpus
) + payload_size
);
83 memcpy(cpus
->map
, tmp_cpus
, payload_size
);
84 qsort(cpus
->map
, nr_cpus
, sizeof(int), cmp_int
);
87 for (i
= 0; i
< nr_cpus
; i
++) {
88 if (i
== 0 || cpus
->map
[i
] != cpus
->map
[i
- 1])
89 cpus
->map
[j
++] = cpus
->map
[i
];
93 refcount_set(&cpus
->refcnt
, 1);
99 struct perf_cpu_map
*perf_cpu_map__read(FILE *file
)
101 struct perf_cpu_map
*cpus
= NULL
;
103 int *tmp_cpus
= NULL
, *tmp
;
111 n
= fscanf(file
, "%u%c", &cpu
, &sep
);
115 int new_max
= nr_cpus
+ cpu
- prev
- 1;
117 WARN_ONCE(new_max
>= MAX_NR_CPUS
, "Perf can support %d CPUs. "
118 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS
);
120 if (new_max
>= max_entries
) {
121 max_entries
= new_max
+ MAX_NR_CPUS
/ 2;
122 tmp
= realloc(tmp_cpus
, max_entries
* sizeof(int));
129 tmp_cpus
[nr_cpus
++] = prev
;
131 if (nr_cpus
== max_entries
) {
132 max_entries
+= MAX_NR_CPUS
;
133 tmp
= realloc(tmp_cpus
, max_entries
* sizeof(int));
139 tmp_cpus
[nr_cpus
++] = cpu
;
140 if (n
== 2 && sep
== '-')
144 if (n
== 1 || sep
== '\n')
149 cpus
= cpu_map__trim_new(nr_cpus
, tmp_cpus
);
151 cpus
= cpu_map__default_new();
157 static struct perf_cpu_map
*cpu_map__read_all_cpu_map(void)
159 struct perf_cpu_map
*cpus
= NULL
;
162 onlnf
= fopen("/sys/devices/system/cpu/online", "r");
164 return cpu_map__default_new();
166 cpus
= perf_cpu_map__read(onlnf
);
171 struct perf_cpu_map
*perf_cpu_map__new(const char *cpu_list
)
173 struct perf_cpu_map
*cpus
= NULL
;
174 unsigned long start_cpu
, end_cpu
= 0;
177 int *tmp_cpus
= NULL
, *tmp
;
181 return cpu_map__read_all_cpu_map();
184 * must handle the case of empty cpumap to cover
185 * TOPOLOGY header for NUMA nodes with no CPU
186 * ( e.g., because of CPU hotplug)
188 if (!isdigit(*cpu_list
) && *cpu_list
!= '\0')
191 while (isdigit(*cpu_list
)) {
193 start_cpu
= strtoul(cpu_list
, &p
, 0);
194 if (start_cpu
>= INT_MAX
195 || (*p
!= '\0' && *p
!= ',' && *p
!= '-'))
201 end_cpu
= strtoul(cpu_list
, &p
, 0);
203 if (end_cpu
>= INT_MAX
|| (*p
!= '\0' && *p
!= ','))
206 if (end_cpu
< start_cpu
)
212 WARN_ONCE(end_cpu
>= MAX_NR_CPUS
, "Perf can support %d CPUs. "
213 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS
);
215 for (; start_cpu
<= end_cpu
; start_cpu
++) {
216 /* check for duplicates */
217 for (i
= 0; i
< nr_cpus
; i
++)
218 if (tmp_cpus
[i
] == (int)start_cpu
)
221 if (nr_cpus
== max_entries
) {
222 max_entries
+= MAX_NR_CPUS
;
223 tmp
= realloc(tmp_cpus
, max_entries
* sizeof(int));
228 tmp_cpus
[nr_cpus
++] = (int)start_cpu
;
237 cpus
= cpu_map__trim_new(nr_cpus
, tmp_cpus
);
238 else if (*cpu_list
!= '\0')
239 cpus
= cpu_map__default_new();
241 cpus
= perf_cpu_map__dummy_new();
248 int perf_cpu_map__cpu(const struct perf_cpu_map
*cpus
, int idx
)
250 if (cpus
&& idx
< cpus
->nr
)
251 return cpus
->map
[idx
];
256 int perf_cpu_map__nr(const struct perf_cpu_map
*cpus
)
258 return cpus
? cpus
->nr
: 1;
261 bool perf_cpu_map__empty(const struct perf_cpu_map
*map
)
263 return map
? map
->map
[0] == -1 : true;
266 int perf_cpu_map__idx(struct perf_cpu_map
*cpus
, int cpu
)
270 for (i
= 0; i
< cpus
->nr
; ++i
) {
271 if (cpus
->map
[i
] == cpu
)
278 int perf_cpu_map__max(struct perf_cpu_map
*map
)
282 for (i
= 0; i
< map
->nr
; i
++) {
283 if (map
->map
[i
] > max
)
293 * orig either gets freed and replaced with a new map, or reused
294 * with no reference count change (similar to "realloc")
295 * other has its reference count increased.
298 struct perf_cpu_map
*perf_cpu_map__merge(struct perf_cpu_map
*orig
,
299 struct perf_cpu_map
*other
)
304 struct perf_cpu_map
*merged
;
309 perf_cpu_map__get(other
);
314 if (orig
->nr
== other
->nr
&&
315 !memcmp(orig
->map
, other
->map
, orig
->nr
* sizeof(int)))
318 tmp_len
= orig
->nr
+ other
->nr
;
319 tmp_cpus
= malloc(tmp_len
* sizeof(int));
323 /* Standard merge algorithm from wikipedia */
325 while (i
< orig
->nr
&& j
< other
->nr
) {
326 if (orig
->map
[i
] <= other
->map
[j
]) {
327 if (orig
->map
[i
] == other
->map
[j
])
329 tmp_cpus
[k
++] = orig
->map
[i
++];
331 tmp_cpus
[k
++] = other
->map
[j
++];
335 tmp_cpus
[k
++] = orig
->map
[i
++];
337 while (j
< other
->nr
)
338 tmp_cpus
[k
++] = other
->map
[j
++];
339 assert(k
<= tmp_len
);
341 merged
= cpu_map__trim_new(k
, tmp_cpus
);
343 perf_cpu_map__put(orig
);