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__dummy_new(void)13 struct perf_cpu_map *perf_cpu_map__dummy_new(void)
14 {
15 struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
16
17 if (cpus != NULL) {
18 cpus->nr = 1;
19 cpus->map[0] = -1;
20 refcount_set(&cpus->refcnt, 1);
21 }
22
23 return cpus;
24 }
25
cpu_map__delete(struct perf_cpu_map * map)26 static void cpu_map__delete(struct perf_cpu_map *map)
27 {
28 if (map) {
29 WARN_ONCE(refcount_read(&map->refcnt) != 0,
30 "cpu_map refcnt unbalanced\n");
31 free(map);
32 }
33 }
34
perf_cpu_map__get(struct perf_cpu_map * map)35 struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map)
36 {
37 if (map)
38 refcount_inc(&map->refcnt);
39 return map;
40 }
41
perf_cpu_map__put(struct perf_cpu_map * map)42 void perf_cpu_map__put(struct perf_cpu_map *map)
43 {
44 if (map && refcount_dec_and_test(&map->refcnt))
45 cpu_map__delete(map);
46 }
47
cpu_map__default_new(void)48 static struct perf_cpu_map *cpu_map__default_new(void)
49 {
50 struct perf_cpu_map *cpus;
51 int nr_cpus;
52
53 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
54 if (nr_cpus < 0)
55 return NULL;
56
57 cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
58 if (cpus != NULL) {
59 int i;
60
61 for (i = 0; i < nr_cpus; ++i)
62 cpus->map[i] = i;
63
64 cpus->nr = nr_cpus;
65 refcount_set(&cpus->refcnt, 1);
66 }
67
68 return cpus;
69 }
70
cpu_map__trim_new(int nr_cpus,int * tmp_cpus)71 static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
72 {
73 size_t payload_size = nr_cpus * sizeof(int);
74 struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
75
76 if (cpus != NULL) {
77 cpus->nr = nr_cpus;
78 memcpy(cpus->map, tmp_cpus, payload_size);
79 refcount_set(&cpus->refcnt, 1);
80 }
81
82 return cpus;
83 }
84
perf_cpu_map__read(FILE * file)85 struct perf_cpu_map *perf_cpu_map__read(FILE *file)
86 {
87 struct perf_cpu_map *cpus = NULL;
88 int nr_cpus = 0;
89 int *tmp_cpus = NULL, *tmp;
90 int max_entries = 0;
91 int n, cpu, prev;
92 char sep;
93
94 sep = 0;
95 prev = -1;
96 for (;;) {
97 n = fscanf(file, "%u%c", &cpu, &sep);
98 if (n <= 0)
99 break;
100 if (prev >= 0) {
101 int new_max = nr_cpus + cpu - prev - 1;
102
103 WARN_ONCE(new_max >= MAX_NR_CPUS, "Perf can support %d CPUs. "
104 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
105
106 if (new_max >= max_entries) {
107 max_entries = new_max + MAX_NR_CPUS / 2;
108 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
109 if (tmp == NULL)
110 goto out_free_tmp;
111 tmp_cpus = tmp;
112 }
113
114 while (++prev < cpu)
115 tmp_cpus[nr_cpus++] = prev;
116 }
117 if (nr_cpus == max_entries) {
118 max_entries += MAX_NR_CPUS;
119 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
120 if (tmp == NULL)
121 goto out_free_tmp;
122 tmp_cpus = tmp;
123 }
124
125 tmp_cpus[nr_cpus++] = cpu;
126 if (n == 2 && sep == '-')
127 prev = cpu;
128 else
129 prev = -1;
130 if (n == 1 || sep == '\n')
131 break;
132 }
133
134 if (nr_cpus > 0)
135 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
136 else
137 cpus = cpu_map__default_new();
138 out_free_tmp:
139 free(tmp_cpus);
140 return cpus;
141 }
142
cpu_map__read_all_cpu_map(void)143 static struct perf_cpu_map *cpu_map__read_all_cpu_map(void)
144 {
145 struct perf_cpu_map *cpus = NULL;
146 FILE *onlnf;
147
148 onlnf = fopen("/sys/devices/system/cpu/online", "r");
149 if (!onlnf)
150 return cpu_map__default_new();
151
152 cpus = perf_cpu_map__read(onlnf);
153 fclose(onlnf);
154 return cpus;
155 }
156
perf_cpu_map__new(const char * cpu_list)157 struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list)
158 {
159 struct perf_cpu_map *cpus = NULL;
160 unsigned long start_cpu, end_cpu = 0;
161 char *p = NULL;
162 int i, nr_cpus = 0;
163 int *tmp_cpus = NULL, *tmp;
164 int max_entries = 0;
165
166 if (!cpu_list)
167 return cpu_map__read_all_cpu_map();
168
169 /*
170 * must handle the case of empty cpumap to cover
171 * TOPOLOGY header for NUMA nodes with no CPU
172 * ( e.g., because of CPU hotplug)
173 */
174 if (!isdigit(*cpu_list) && *cpu_list != '\0')
175 goto out;
176
177 while (isdigit(*cpu_list)) {
178 p = NULL;
179 start_cpu = strtoul(cpu_list, &p, 0);
180 if (start_cpu >= INT_MAX
181 || (*p != '\0' && *p != ',' && *p != '-'))
182 goto invalid;
183
184 if (*p == '-') {
185 cpu_list = ++p;
186 p = NULL;
187 end_cpu = strtoul(cpu_list, &p, 0);
188
189 if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
190 goto invalid;
191
192 if (end_cpu < start_cpu)
193 goto invalid;
194 } else {
195 end_cpu = start_cpu;
196 }
197
198 WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. "
199 "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
200
201 for (; start_cpu <= end_cpu; start_cpu++) {
202 /* check for duplicates */
203 for (i = 0; i < nr_cpus; i++)
204 if (tmp_cpus[i] == (int)start_cpu)
205 goto invalid;
206
207 if (nr_cpus == max_entries) {
208 max_entries += MAX_NR_CPUS;
209 tmp = realloc(tmp_cpus, max_entries * sizeof(int));
210 if (tmp == NULL)
211 goto invalid;
212 tmp_cpus = tmp;
213 }
214 tmp_cpus[nr_cpus++] = (int)start_cpu;
215 }
216 if (*p)
217 ++p;
218
219 cpu_list = p;
220 }
221
222 if (nr_cpus > 0)
223 cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
224 else if (*cpu_list != '\0')
225 cpus = cpu_map__default_new();
226 else
227 cpus = perf_cpu_map__dummy_new();
228 invalid:
229 free(tmp_cpus);
230 out:
231 return cpus;
232 }
233
perf_cpu_map__cpu(const struct perf_cpu_map * cpus,int idx)234 int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
235 {
236 if (idx < cpus->nr)
237 return cpus->map[idx];
238
239 return -1;
240 }
241
perf_cpu_map__nr(const struct perf_cpu_map * cpus)242 int perf_cpu_map__nr(const struct perf_cpu_map *cpus)
243 {
244 return cpus ? cpus->nr : 1;
245 }
246
perf_cpu_map__empty(const struct perf_cpu_map * map)247 bool perf_cpu_map__empty(const struct perf_cpu_map *map)
248 {
249 return map ? map->map[0] == -1 : true;
250 }
251
perf_cpu_map__idx(struct perf_cpu_map * cpus,int cpu)252 int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu)
253 {
254 int i;
255
256 for (i = 0; i < cpus->nr; ++i) {
257 if (cpus->map[i] == cpu)
258 return i;
259 }
260
261 return -1;
262 }
263
perf_cpu_map__max(struct perf_cpu_map * map)264 int perf_cpu_map__max(struct perf_cpu_map *map)
265 {
266 int i, max = -1;
267
268 for (i = 0; i < map->nr; i++) {
269 if (map->map[i] > max)
270 max = map->map[i];
271 }
272
273 return max;
274 }
275