xref: /openbmc/linux/tools/lib/perf/cpumap.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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  
perf_cpu_map__set_nr(struct perf_cpu_map * map,int nr_cpus)13  void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus)
14  {
15  	RC_CHK_ACCESS(map)->nr = nr_cpus;
16  }
17  
perf_cpu_map__alloc(int nr_cpus)18  struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus)
19  {
20  	RC_STRUCT(perf_cpu_map) *cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus);
21  	struct perf_cpu_map *result;
22  
23  	if (ADD_RC_CHK(result, cpus)) {
24  		cpus->nr = nr_cpus;
25  		refcount_set(&cpus->refcnt, 1);
26  	}
27  	return result;
28  }
29  
perf_cpu_map__dummy_new(void)30  struct perf_cpu_map *perf_cpu_map__dummy_new(void)
31  {
32  	struct perf_cpu_map *cpus = perf_cpu_map__alloc(1);
33  
34  	if (cpus)
35  		RC_CHK_ACCESS(cpus)->map[0].cpu = -1;
36  
37  	return cpus;
38  }
39  
cpu_map__delete(struct perf_cpu_map * map)40  static void cpu_map__delete(struct perf_cpu_map *map)
41  {
42  	if (map) {
43  		WARN_ONCE(refcount_read(perf_cpu_map__refcnt(map)) != 0,
44  			  "cpu_map refcnt unbalanced\n");
45  		RC_CHK_FREE(map);
46  	}
47  }
48  
perf_cpu_map__get(struct perf_cpu_map * map)49  struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map)
50  {
51  	struct perf_cpu_map *result;
52  
53  	if (RC_CHK_GET(result, map))
54  		refcount_inc(perf_cpu_map__refcnt(map));
55  
56  	return result;
57  }
58  
perf_cpu_map__put(struct perf_cpu_map * map)59  void perf_cpu_map__put(struct perf_cpu_map *map)
60  {
61  	if (map) {
62  		if (refcount_dec_and_test(perf_cpu_map__refcnt(map)))
63  			cpu_map__delete(map);
64  		else
65  			RC_CHK_PUT(map);
66  	}
67  }
68  
cpu_map__default_new(void)69  static struct perf_cpu_map *cpu_map__default_new(void)
70  {
71  	struct perf_cpu_map *cpus;
72  	int nr_cpus;
73  
74  	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
75  	if (nr_cpus < 0)
76  		return NULL;
77  
78  	cpus = perf_cpu_map__alloc(nr_cpus);
79  	if (cpus != NULL) {
80  		int i;
81  
82  		for (i = 0; i < nr_cpus; ++i)
83  			RC_CHK_ACCESS(cpus)->map[i].cpu = i;
84  	}
85  
86  	return cpus;
87  }
88  
perf_cpu_map__default_new(void)89  struct perf_cpu_map *perf_cpu_map__default_new(void)
90  {
91  	return cpu_map__default_new();
92  }
93  
94  
cmp_cpu(const void * a,const void * b)95  static int cmp_cpu(const void *a, const void *b)
96  {
97  	const struct perf_cpu *cpu_a = a, *cpu_b = b;
98  
99  	return cpu_a->cpu - cpu_b->cpu;
100  }
101  
__perf_cpu_map__cpu(const struct perf_cpu_map * cpus,int idx)102  static struct perf_cpu __perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
103  {
104  	return RC_CHK_ACCESS(cpus)->map[idx];
105  }
106  
cpu_map__trim_new(int nr_cpus,const struct perf_cpu * tmp_cpus)107  static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, const struct perf_cpu *tmp_cpus)
108  {
109  	size_t payload_size = nr_cpus * sizeof(struct perf_cpu);
110  	struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr_cpus);
111  	int i, j;
112  
113  	if (cpus != NULL) {
114  		memcpy(RC_CHK_ACCESS(cpus)->map, tmp_cpus, payload_size);
115  		qsort(RC_CHK_ACCESS(cpus)->map, nr_cpus, sizeof(struct perf_cpu), cmp_cpu);
116  		/* Remove dups */
117  		j = 0;
118  		for (i = 0; i < nr_cpus; i++) {
119  			if (i == 0 ||
120  			    __perf_cpu_map__cpu(cpus, i).cpu !=
121  			    __perf_cpu_map__cpu(cpus, i - 1).cpu) {
122  				RC_CHK_ACCESS(cpus)->map[j++].cpu =
123  					__perf_cpu_map__cpu(cpus, i).cpu;
124  			}
125  		}
126  		perf_cpu_map__set_nr(cpus, j);
127  		assert(j <= nr_cpus);
128  	}
129  	return cpus;
130  }
131  
perf_cpu_map__read(FILE * file)132  struct perf_cpu_map *perf_cpu_map__read(FILE *file)
133  {
134  	struct perf_cpu_map *cpus = NULL;
135  	int nr_cpus = 0;
136  	struct perf_cpu *tmp_cpus = NULL, *tmp;
137  	int max_entries = 0;
138  	int n, cpu, prev;
139  	char sep;
140  
141  	sep = 0;
142  	prev = -1;
143  	for (;;) {
144  		n = fscanf(file, "%u%c", &cpu, &sep);
145  		if (n <= 0)
146  			break;
147  		if (prev >= 0) {
148  			int new_max = nr_cpus + cpu - prev - 1;
149  
150  			WARN_ONCE(new_max >= MAX_NR_CPUS, "Perf can support %d CPUs. "
151  							  "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
152  
153  			if (new_max >= max_entries) {
154  				max_entries = new_max + MAX_NR_CPUS / 2;
155  				tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu));
156  				if (tmp == NULL)
157  					goto out_free_tmp;
158  				tmp_cpus = tmp;
159  			}
160  
161  			while (++prev < cpu)
162  				tmp_cpus[nr_cpus++].cpu = prev;
163  		}
164  		if (nr_cpus == max_entries) {
165  			max_entries += MAX_NR_CPUS;
166  			tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu));
167  			if (tmp == NULL)
168  				goto out_free_tmp;
169  			tmp_cpus = tmp;
170  		}
171  
172  		tmp_cpus[nr_cpus++].cpu = cpu;
173  		if (n == 2 && sep == '-')
174  			prev = cpu;
175  		else
176  			prev = -1;
177  		if (n == 1 || sep == '\n')
178  			break;
179  	}
180  
181  	if (nr_cpus > 0)
182  		cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
183  	else
184  		cpus = cpu_map__default_new();
185  out_free_tmp:
186  	free(tmp_cpus);
187  	return cpus;
188  }
189  
cpu_map__read_all_cpu_map(void)190  static struct perf_cpu_map *cpu_map__read_all_cpu_map(void)
191  {
192  	struct perf_cpu_map *cpus = NULL;
193  	FILE *onlnf;
194  
195  	onlnf = fopen("/sys/devices/system/cpu/online", "r");
196  	if (!onlnf)
197  		return cpu_map__default_new();
198  
199  	cpus = perf_cpu_map__read(onlnf);
200  	fclose(onlnf);
201  	return cpus;
202  }
203  
perf_cpu_map__new(const char * cpu_list)204  struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list)
205  {
206  	struct perf_cpu_map *cpus = NULL;
207  	unsigned long start_cpu, end_cpu = 0;
208  	char *p = NULL;
209  	int i, nr_cpus = 0;
210  	struct perf_cpu *tmp_cpus = NULL, *tmp;
211  	int max_entries = 0;
212  
213  	if (!cpu_list)
214  		return cpu_map__read_all_cpu_map();
215  
216  	/*
217  	 * must handle the case of empty cpumap to cover
218  	 * TOPOLOGY header for NUMA nodes with no CPU
219  	 * ( e.g., because of CPU hotplug)
220  	 */
221  	if (!isdigit(*cpu_list) && *cpu_list != '\0')
222  		goto out;
223  
224  	while (isdigit(*cpu_list)) {
225  		p = NULL;
226  		start_cpu = strtoul(cpu_list, &p, 0);
227  		if (start_cpu >= INT_MAX
228  		    || (*p != '\0' && *p != ',' && *p != '-'))
229  			goto invalid;
230  
231  		if (*p == '-') {
232  			cpu_list = ++p;
233  			p = NULL;
234  			end_cpu = strtoul(cpu_list, &p, 0);
235  
236  			if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
237  				goto invalid;
238  
239  			if (end_cpu < start_cpu)
240  				goto invalid;
241  		} else {
242  			end_cpu = start_cpu;
243  		}
244  
245  		WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. "
246  						  "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
247  
248  		for (; start_cpu <= end_cpu; start_cpu++) {
249  			/* check for duplicates */
250  			for (i = 0; i < nr_cpus; i++)
251  				if (tmp_cpus[i].cpu == (int)start_cpu)
252  					goto invalid;
253  
254  			if (nr_cpus == max_entries) {
255  				max_entries += MAX_NR_CPUS;
256  				tmp = realloc(tmp_cpus, max_entries * sizeof(struct perf_cpu));
257  				if (tmp == NULL)
258  					goto invalid;
259  				tmp_cpus = tmp;
260  			}
261  			tmp_cpus[nr_cpus++].cpu = (int)start_cpu;
262  		}
263  		if (*p)
264  			++p;
265  
266  		cpu_list = p;
267  	}
268  
269  	if (nr_cpus > 0)
270  		cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
271  	else if (*cpu_list != '\0')
272  		cpus = cpu_map__default_new();
273  	else
274  		cpus = perf_cpu_map__dummy_new();
275  invalid:
276  	free(tmp_cpus);
277  out:
278  	return cpus;
279  }
280  
__perf_cpu_map__nr(const struct perf_cpu_map * cpus)281  static int __perf_cpu_map__nr(const struct perf_cpu_map *cpus)
282  {
283  	return RC_CHK_ACCESS(cpus)->nr;
284  }
285  
perf_cpu_map__cpu(const struct perf_cpu_map * cpus,int idx)286  struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
287  {
288  	struct perf_cpu result = {
289  		.cpu = -1
290  	};
291  
292  	if (cpus && idx < __perf_cpu_map__nr(cpus))
293  		return __perf_cpu_map__cpu(cpus, idx);
294  
295  	return result;
296  }
297  
perf_cpu_map__nr(const struct perf_cpu_map * cpus)298  int perf_cpu_map__nr(const struct perf_cpu_map *cpus)
299  {
300  	return cpus ? __perf_cpu_map__nr(cpus) : 1;
301  }
302  
perf_cpu_map__empty(const struct perf_cpu_map * map)303  bool perf_cpu_map__empty(const struct perf_cpu_map *map)
304  {
305  	return map ? __perf_cpu_map__cpu(map, 0).cpu == -1 : true;
306  }
307  
perf_cpu_map__idx(const struct perf_cpu_map * cpus,struct perf_cpu cpu)308  int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu)
309  {
310  	int low, high;
311  
312  	if (!cpus)
313  		return -1;
314  
315  	low = 0;
316  	high = __perf_cpu_map__nr(cpus);
317  	while (low < high) {
318  		int idx = (low + high) / 2;
319  		struct perf_cpu cpu_at_idx = __perf_cpu_map__cpu(cpus, idx);
320  
321  		if (cpu_at_idx.cpu == cpu.cpu)
322  			return idx;
323  
324  		if (cpu_at_idx.cpu > cpu.cpu)
325  			high = idx;
326  		else
327  			low = idx + 1;
328  	}
329  
330  	return -1;
331  }
332  
perf_cpu_map__has(const struct perf_cpu_map * cpus,struct perf_cpu cpu)333  bool perf_cpu_map__has(const struct perf_cpu_map *cpus, struct perf_cpu cpu)
334  {
335  	return perf_cpu_map__idx(cpus, cpu) != -1;
336  }
337  
perf_cpu_map__equal(const struct perf_cpu_map * lhs,const struct perf_cpu_map * rhs)338  bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_map *rhs)
339  {
340  	int nr;
341  
342  	if (lhs == rhs)
343  		return true;
344  
345  	if (!lhs || !rhs)
346  		return false;
347  
348  	nr = __perf_cpu_map__nr(lhs);
349  	if (nr != __perf_cpu_map__nr(rhs))
350  		return false;
351  
352  	for (int idx = 0; idx < nr; idx++) {
353  		if (__perf_cpu_map__cpu(lhs, idx).cpu != __perf_cpu_map__cpu(rhs, idx).cpu)
354  			return false;
355  	}
356  	return true;
357  }
358  
perf_cpu_map__has_any_cpu(const struct perf_cpu_map * map)359  bool perf_cpu_map__has_any_cpu(const struct perf_cpu_map *map)
360  {
361  	return map && __perf_cpu_map__cpu(map, 0).cpu == -1;
362  }
363  
perf_cpu_map__max(const struct perf_cpu_map * map)364  struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map)
365  {
366  	struct perf_cpu result = {
367  		.cpu = -1
368  	};
369  
370  	// cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well.
371  	return __perf_cpu_map__nr(map) > 0
372  		? __perf_cpu_map__cpu(map, __perf_cpu_map__nr(map) - 1)
373  		: result;
374  }
375  
376  /** Is 'b' a subset of 'a'. */
perf_cpu_map__is_subset(const struct perf_cpu_map * a,const struct perf_cpu_map * b)377  bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b)
378  {
379  	if (a == b || !b)
380  		return true;
381  	if (!a || __perf_cpu_map__nr(b) > __perf_cpu_map__nr(a))
382  		return false;
383  
384  	for (int i = 0, j = 0; i < __perf_cpu_map__nr(a); i++) {
385  		if (__perf_cpu_map__cpu(a, i).cpu > __perf_cpu_map__cpu(b, j).cpu)
386  			return false;
387  		if (__perf_cpu_map__cpu(a, i).cpu == __perf_cpu_map__cpu(b, j).cpu) {
388  			j++;
389  			if (j == __perf_cpu_map__nr(b))
390  				return true;
391  		}
392  	}
393  	return false;
394  }
395  
396  /*
397   * Merge two cpumaps
398   *
399   * orig either gets freed and replaced with a new map, or reused
400   * with no reference count change (similar to "realloc")
401   * other has its reference count increased.
402   */
403  
perf_cpu_map__merge(struct perf_cpu_map * orig,struct perf_cpu_map * other)404  struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig,
405  					 struct perf_cpu_map *other)
406  {
407  	struct perf_cpu *tmp_cpus;
408  	int tmp_len;
409  	int i, j, k;
410  	struct perf_cpu_map *merged;
411  
412  	if (perf_cpu_map__is_subset(orig, other))
413  		return orig;
414  	if (perf_cpu_map__is_subset(other, orig)) {
415  		perf_cpu_map__put(orig);
416  		return perf_cpu_map__get(other);
417  	}
418  
419  	tmp_len = __perf_cpu_map__nr(orig) + __perf_cpu_map__nr(other);
420  	tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu));
421  	if (!tmp_cpus)
422  		return NULL;
423  
424  	/* Standard merge algorithm from wikipedia */
425  	i = j = k = 0;
426  	while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) {
427  		if (__perf_cpu_map__cpu(orig, i).cpu <= __perf_cpu_map__cpu(other, j).cpu) {
428  			if (__perf_cpu_map__cpu(orig, i).cpu == __perf_cpu_map__cpu(other, j).cpu)
429  				j++;
430  			tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++);
431  		} else
432  			tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++);
433  	}
434  
435  	while (i < __perf_cpu_map__nr(orig))
436  		tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++);
437  
438  	while (j < __perf_cpu_map__nr(other))
439  		tmp_cpus[k++] = __perf_cpu_map__cpu(other, j++);
440  	assert(k <= tmp_len);
441  
442  	merged = cpu_map__trim_new(k, tmp_cpus);
443  	free(tmp_cpus);
444  	perf_cpu_map__put(orig);
445  	return merged;
446  }
447  
perf_cpu_map__intersect(struct perf_cpu_map * orig,struct perf_cpu_map * other)448  struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig,
449  					     struct perf_cpu_map *other)
450  {
451  	struct perf_cpu *tmp_cpus;
452  	int tmp_len;
453  	int i, j, k;
454  	struct perf_cpu_map *merged = NULL;
455  
456  	if (perf_cpu_map__is_subset(other, orig))
457  		return perf_cpu_map__get(orig);
458  	if (perf_cpu_map__is_subset(orig, other))
459  		return perf_cpu_map__get(other);
460  
461  	tmp_len = max(__perf_cpu_map__nr(orig), __perf_cpu_map__nr(other));
462  	tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu));
463  	if (!tmp_cpus)
464  		return NULL;
465  
466  	i = j = k = 0;
467  	while (i < __perf_cpu_map__nr(orig) && j < __perf_cpu_map__nr(other)) {
468  		if (__perf_cpu_map__cpu(orig, i).cpu < __perf_cpu_map__cpu(other, j).cpu)
469  			i++;
470  		else if (__perf_cpu_map__cpu(orig, i).cpu > __perf_cpu_map__cpu(other, j).cpu)
471  			j++;
472  		else {
473  			j++;
474  			tmp_cpus[k++] = __perf_cpu_map__cpu(orig, i++);
475  		}
476  	}
477  	if (k)
478  		merged = cpu_map__trim_new(k, tmp_cpus);
479  	free(tmp_cpus);
480  	return merged;
481  }
482