1 // SPDX-License-Identifier: GPL-2.0
2 #include <sys/param.h>
3 #include <sys/utsname.h>
4 #include <inttypes.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <api/fs/fs.h>
8 #include <linux/zalloc.h>
9 #include <perf/cpumap.h>
10
11 #include "cputopo.h"
12 #include "cpumap.h"
13 #include "debug.h"
14 #include "env.h"
15 #include "pmu-hybrid.h"
16
17 #define PACKAGE_CPUS_FMT \
18 "%s/devices/system/cpu/cpu%d/topology/package_cpus_list"
19 #define PACKAGE_CPUS_FMT_OLD \
20 "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
21 #define DIE_CPUS_FMT \
22 "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
23 #define CORE_CPUS_FMT \
24 "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
25 #define CORE_CPUS_FMT_OLD \
26 "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
27 #define NODE_ONLINE_FMT \
28 "%s/devices/system/node/online"
29 #define NODE_MEMINFO_FMT \
30 "%s/devices/system/node/node%d/meminfo"
31 #define NODE_CPULIST_FMT \
32 "%s/devices/system/node/node%d/cpulist"
33
build_cpu_topology(struct cpu_topology * tp,int cpu)34 static int build_cpu_topology(struct cpu_topology *tp, int cpu)
35 {
36 FILE *fp;
37 char filename[MAXPATHLEN];
38 char *buf = NULL, *p;
39 size_t len = 0;
40 ssize_t sret;
41 u32 i = 0;
42 int ret = -1;
43
44 scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT,
45 sysfs__mountpoint(), cpu);
46 if (access(filename, F_OK) == -1) {
47 scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT_OLD,
48 sysfs__mountpoint(), cpu);
49 }
50 fp = fopen(filename, "r");
51 if (!fp)
52 goto try_dies;
53
54 sret = getline(&buf, &len, fp);
55 fclose(fp);
56 if (sret <= 0)
57 goto try_dies;
58
59 p = strchr(buf, '\n');
60 if (p)
61 *p = '\0';
62
63 for (i = 0; i < tp->package_cpus_lists; i++) {
64 if (!strcmp(buf, tp->package_cpus_list[i]))
65 break;
66 }
67 if (i == tp->package_cpus_lists) {
68 tp->package_cpus_list[i] = buf;
69 tp->package_cpus_lists++;
70 buf = NULL;
71 len = 0;
72 }
73 ret = 0;
74
75 try_dies:
76 if (!tp->die_cpus_list)
77 goto try_threads;
78
79 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
80 sysfs__mountpoint(), cpu);
81 fp = fopen(filename, "r");
82 if (!fp)
83 goto try_threads;
84
85 sret = getline(&buf, &len, fp);
86 fclose(fp);
87 if (sret <= 0)
88 goto try_threads;
89
90 p = strchr(buf, '\n');
91 if (p)
92 *p = '\0';
93
94 for (i = 0; i < tp->die_cpus_lists; i++) {
95 if (!strcmp(buf, tp->die_cpus_list[i]))
96 break;
97 }
98 if (i == tp->die_cpus_lists) {
99 tp->die_cpus_list[i] = buf;
100 tp->die_cpus_lists++;
101 buf = NULL;
102 len = 0;
103 }
104 ret = 0;
105
106 try_threads:
107 scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT,
108 sysfs__mountpoint(), cpu);
109 if (access(filename, F_OK) == -1) {
110 scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT_OLD,
111 sysfs__mountpoint(), cpu);
112 }
113 fp = fopen(filename, "r");
114 if (!fp)
115 goto done;
116
117 if (getline(&buf, &len, fp) <= 0)
118 goto done;
119
120 p = strchr(buf, '\n');
121 if (p)
122 *p = '\0';
123
124 for (i = 0; i < tp->core_cpus_lists; i++) {
125 if (!strcmp(buf, tp->core_cpus_list[i]))
126 break;
127 }
128 if (i == tp->core_cpus_lists) {
129 tp->core_cpus_list[i] = buf;
130 tp->core_cpus_lists++;
131 buf = NULL;
132 }
133 ret = 0;
134 done:
135 if (fp)
136 fclose(fp);
137 free(buf);
138 return ret;
139 }
140
cpu_topology__delete(struct cpu_topology * tp)141 void cpu_topology__delete(struct cpu_topology *tp)
142 {
143 u32 i;
144
145 if (!tp)
146 return;
147
148 for (i = 0 ; i < tp->package_cpus_lists; i++)
149 zfree(&tp->package_cpus_list[i]);
150
151 for (i = 0 ; i < tp->die_cpus_lists; i++)
152 zfree(&tp->die_cpus_list[i]);
153
154 for (i = 0 ; i < tp->core_cpus_lists; i++)
155 zfree(&tp->core_cpus_list[i]);
156
157 free(tp);
158 }
159
has_die_topology(void)160 static bool has_die_topology(void)
161 {
162 char filename[MAXPATHLEN];
163 struct utsname uts;
164
165 if (uname(&uts) < 0)
166 return false;
167
168 if (strncmp(uts.machine, "x86_64", 6))
169 return false;
170
171 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
172 sysfs__mountpoint(), 0);
173 if (access(filename, F_OK) == -1)
174 return false;
175
176 return true;
177 }
178
cpu_topology__new(void)179 struct cpu_topology *cpu_topology__new(void)
180 {
181 struct cpu_topology *tp = NULL;
182 void *addr;
183 u32 nr, i, nr_addr;
184 size_t sz;
185 long ncpus;
186 int ret = -1;
187 struct perf_cpu_map *map;
188 bool has_die = has_die_topology();
189
190 ncpus = cpu__max_present_cpu();
191
192 /* build online CPU map */
193 map = perf_cpu_map__new(NULL);
194 if (map == NULL) {
195 pr_debug("failed to get system cpumap\n");
196 return NULL;
197 }
198
199 nr = (u32)(ncpus & UINT_MAX);
200
201 sz = nr * sizeof(char *);
202 if (has_die)
203 nr_addr = 3;
204 else
205 nr_addr = 2;
206 addr = calloc(1, sizeof(*tp) + nr_addr * sz);
207 if (!addr)
208 goto out_free;
209
210 tp = addr;
211 addr += sizeof(*tp);
212 tp->package_cpus_list = addr;
213 addr += sz;
214 if (has_die) {
215 tp->die_cpus_list = addr;
216 addr += sz;
217 }
218 tp->core_cpus_list = addr;
219
220 for (i = 0; i < nr; i++) {
221 if (!cpu_map__has(map, i))
222 continue;
223
224 ret = build_cpu_topology(tp, i);
225 if (ret < 0)
226 break;
227 }
228
229 out_free:
230 perf_cpu_map__put(map);
231 if (ret) {
232 cpu_topology__delete(tp);
233 tp = NULL;
234 }
235 return tp;
236 }
237
load_numa_node(struct numa_topology_node * node,int nr)238 static int load_numa_node(struct numa_topology_node *node, int nr)
239 {
240 char str[MAXPATHLEN];
241 char field[32];
242 char *buf = NULL, *p;
243 size_t len = 0;
244 int ret = -1;
245 FILE *fp;
246 u64 mem;
247
248 node->node = (u32) nr;
249
250 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
251 sysfs__mountpoint(), nr);
252 fp = fopen(str, "r");
253 if (!fp)
254 return -1;
255
256 while (getline(&buf, &len, fp) > 0) {
257 /* skip over invalid lines */
258 if (!strchr(buf, ':'))
259 continue;
260 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
261 goto err;
262 if (!strcmp(field, "MemTotal:"))
263 node->mem_total = mem;
264 if (!strcmp(field, "MemFree:"))
265 node->mem_free = mem;
266 if (node->mem_total && node->mem_free)
267 break;
268 }
269
270 fclose(fp);
271 fp = NULL;
272
273 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
274 sysfs__mountpoint(), nr);
275
276 fp = fopen(str, "r");
277 if (!fp)
278 return -1;
279
280 if (getline(&buf, &len, fp) <= 0)
281 goto err;
282
283 p = strchr(buf, '\n');
284 if (p)
285 *p = '\0';
286
287 node->cpus = buf;
288 fclose(fp);
289 return 0;
290
291 err:
292 free(buf);
293 if (fp)
294 fclose(fp);
295 return ret;
296 }
297
numa_topology__new(void)298 struct numa_topology *numa_topology__new(void)
299 {
300 struct perf_cpu_map *node_map = NULL;
301 struct numa_topology *tp = NULL;
302 char path[MAXPATHLEN];
303 char *buf = NULL;
304 size_t len = 0;
305 u32 nr, i;
306 FILE *fp;
307 char *c;
308
309 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
310 sysfs__mountpoint());
311
312 fp = fopen(path, "r");
313 if (!fp)
314 return NULL;
315
316 if (getline(&buf, &len, fp) <= 0)
317 goto out;
318
319 c = strchr(buf, '\n');
320 if (c)
321 *c = '\0';
322
323 node_map = perf_cpu_map__new(buf);
324 if (!node_map)
325 goto out;
326
327 nr = (u32) node_map->nr;
328
329 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
330 if (!tp)
331 goto out;
332
333 tp->nr = nr;
334
335 for (i = 0; i < nr; i++) {
336 if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
337 numa_topology__delete(tp);
338 tp = NULL;
339 break;
340 }
341 }
342
343 out:
344 free(buf);
345 fclose(fp);
346 perf_cpu_map__put(node_map);
347 return tp;
348 }
349
numa_topology__delete(struct numa_topology * tp)350 void numa_topology__delete(struct numa_topology *tp)
351 {
352 u32 i;
353
354 for (i = 0; i < tp->nr; i++)
355 zfree(&tp->nodes[i].cpus);
356
357 free(tp);
358 }
359
load_hybrid_node(struct hybrid_topology_node * node,struct perf_pmu * pmu)360 static int load_hybrid_node(struct hybrid_topology_node *node,
361 struct perf_pmu *pmu)
362 {
363 const char *sysfs;
364 char path[PATH_MAX];
365 char *buf = NULL, *p;
366 FILE *fp;
367 size_t len = 0;
368
369 node->pmu_name = strdup(pmu->name);
370 if (!node->pmu_name)
371 return -1;
372
373 sysfs = sysfs__mountpoint();
374 if (!sysfs)
375 goto err;
376
377 snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
378 fp = fopen(path, "r");
379 if (!fp)
380 goto err;
381
382 if (getline(&buf, &len, fp) <= 0) {
383 fclose(fp);
384 goto err;
385 }
386
387 p = strchr(buf, '\n');
388 if (p)
389 *p = '\0';
390
391 fclose(fp);
392 node->cpus = buf;
393 return 0;
394
395 err:
396 zfree(&node->pmu_name);
397 free(buf);
398 return -1;
399 }
400
hybrid_topology__new(void)401 struct hybrid_topology *hybrid_topology__new(void)
402 {
403 struct perf_pmu *pmu;
404 struct hybrid_topology *tp = NULL;
405 u32 nr, i = 0;
406
407 nr = perf_pmu__hybrid_pmu_num();
408 if (nr == 0)
409 return NULL;
410
411 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
412 if (!tp)
413 return NULL;
414
415 tp->nr = nr;
416 perf_pmu__for_each_hybrid_pmu(pmu) {
417 if (load_hybrid_node(&tp->nodes[i], pmu)) {
418 hybrid_topology__delete(tp);
419 return NULL;
420 }
421 i++;
422 }
423
424 return tp;
425 }
426
hybrid_topology__delete(struct hybrid_topology * tp)427 void hybrid_topology__delete(struct hybrid_topology *tp)
428 {
429 u32 i;
430
431 for (i = 0; i < tp->nr; i++) {
432 zfree(&tp->nodes[i].pmu_name);
433 zfree(&tp->nodes[i].cpus);
434 }
435
436 free(tp);
437 }
438