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