perf cpumap: Migrate to libperf cpumap api
[platform/kernel/linux-starfive.git] / tools / perf / util / cputopo.c
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
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
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
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             strncmp(uts.machine, "s390x", 5))
170                 return false;
171
172         scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
173                   sysfs__mountpoint(), 0);
174         if (access(filename, F_OK) == -1)
175                 return false;
176
177         return true;
178 }
179
180 struct cpu_topology *cpu_topology__new(void)
181 {
182         struct cpu_topology *tp = NULL;
183         void *addr;
184         u32 nr, i, nr_addr;
185         size_t sz;
186         long ncpus;
187         int ret = -1;
188         struct perf_cpu_map *map;
189         bool has_die = has_die_topology();
190
191         ncpus = cpu__max_present_cpu().cpu;
192
193         /* build online CPU map */
194         map = perf_cpu_map__new(NULL);
195         if (map == NULL) {
196                 pr_debug("failed to get system cpumap\n");
197                 return NULL;
198         }
199
200         nr = (u32)(ncpus & UINT_MAX);
201
202         sz = nr * sizeof(char *);
203         if (has_die)
204                 nr_addr = 3;
205         else
206                 nr_addr = 2;
207         addr = calloc(1, sizeof(*tp) + nr_addr * sz);
208         if (!addr)
209                 goto out_free;
210
211         tp = addr;
212         addr += sizeof(*tp);
213         tp->package_cpus_list = addr;
214         addr += sz;
215         if (has_die) {
216                 tp->die_cpus_list = addr;
217                 addr += sz;
218         }
219         tp->core_cpus_list = addr;
220
221         for (i = 0; i < nr; i++) {
222                 if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))
223                         continue;
224
225                 ret = build_cpu_topology(tp, i);
226                 if (ret < 0)
227                         break;
228         }
229
230 out_free:
231         perf_cpu_map__put(map);
232         if (ret) {
233                 cpu_topology__delete(tp);
234                 tp = NULL;
235         }
236         return tp;
237 }
238
239 static int load_numa_node(struct numa_topology_node *node, int nr)
240 {
241         char str[MAXPATHLEN];
242         char field[32];
243         char *buf = NULL, *p;
244         size_t len = 0;
245         int ret = -1;
246         FILE *fp;
247         u64 mem;
248
249         node->node = (u32) nr;
250
251         scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
252                   sysfs__mountpoint(), nr);
253         fp = fopen(str, "r");
254         if (!fp)
255                 return -1;
256
257         while (getline(&buf, &len, fp) > 0) {
258                 /* skip over invalid lines */
259                 if (!strchr(buf, ':'))
260                         continue;
261                 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
262                         goto err;
263                 if (!strcmp(field, "MemTotal:"))
264                         node->mem_total = mem;
265                 if (!strcmp(field, "MemFree:"))
266                         node->mem_free = mem;
267                 if (node->mem_total && node->mem_free)
268                         break;
269         }
270
271         fclose(fp);
272         fp = NULL;
273
274         scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
275                   sysfs__mountpoint(), nr);
276
277         fp = fopen(str, "r");
278         if (!fp)
279                 return -1;
280
281         if (getline(&buf, &len, fp) <= 0)
282                 goto err;
283
284         p = strchr(buf, '\n');
285         if (p)
286                 *p = '\0';
287
288         node->cpus = buf;
289         fclose(fp);
290         return 0;
291
292 err:
293         free(buf);
294         if (fp)
295                 fclose(fp);
296         return ret;
297 }
298
299 struct numa_topology *numa_topology__new(void)
300 {
301         struct perf_cpu_map *node_map = NULL;
302         struct numa_topology *tp = NULL;
303         char path[MAXPATHLEN];
304         char *buf = NULL;
305         size_t len = 0;
306         u32 nr, i;
307         FILE *fp;
308         char *c;
309
310         scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
311                   sysfs__mountpoint());
312
313         fp = fopen(path, "r");
314         if (!fp)
315                 return NULL;
316
317         if (getline(&buf, &len, fp) <= 0)
318                 goto out;
319
320         c = strchr(buf, '\n');
321         if (c)
322                 *c = '\0';
323
324         node_map = perf_cpu_map__new(buf);
325         if (!node_map)
326                 goto out;
327
328         nr = (u32) perf_cpu_map__nr(node_map);
329
330         tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
331         if (!tp)
332                 goto out;
333
334         tp->nr = nr;
335
336         for (i = 0; i < nr; i++) {
337                 if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) {
338                         numa_topology__delete(tp);
339                         tp = NULL;
340                         break;
341                 }
342         }
343
344 out:
345         free(buf);
346         fclose(fp);
347         perf_cpu_map__put(node_map);
348         return tp;
349 }
350
351 void numa_topology__delete(struct numa_topology *tp)
352 {
353         u32 i;
354
355         for (i = 0; i < tp->nr; i++)
356                 zfree(&tp->nodes[i].cpus);
357
358         free(tp);
359 }
360
361 static int load_hybrid_node(struct hybrid_topology_node *node,
362                             struct perf_pmu *pmu)
363 {
364         const char *sysfs;
365         char path[PATH_MAX];
366         char *buf = NULL, *p;
367         FILE *fp;
368         size_t len = 0;
369
370         node->pmu_name = strdup(pmu->name);
371         if (!node->pmu_name)
372                 return -1;
373
374         sysfs = sysfs__mountpoint();
375         if (!sysfs)
376                 goto err;
377
378         snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
379         fp = fopen(path, "r");
380         if (!fp)
381                 goto err;
382
383         if (getline(&buf, &len, fp) <= 0) {
384                 fclose(fp);
385                 goto err;
386         }
387
388         p = strchr(buf, '\n');
389         if (p)
390                 *p = '\0';
391
392         fclose(fp);
393         node->cpus = buf;
394         return 0;
395
396 err:
397         zfree(&node->pmu_name);
398         free(buf);
399         return -1;
400 }
401
402 struct hybrid_topology *hybrid_topology__new(void)
403 {
404         struct perf_pmu *pmu;
405         struct hybrid_topology *tp = NULL;
406         u32 nr, i = 0;
407
408         nr = perf_pmu__hybrid_pmu_num();
409         if (nr == 0)
410                 return NULL;
411
412         tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
413         if (!tp)
414                 return NULL;
415
416         tp->nr = nr;
417         perf_pmu__for_each_hybrid_pmu(pmu) {
418                 if (load_hybrid_node(&tp->nodes[i], pmu)) {
419                         hybrid_topology__delete(tp);
420                         return NULL;
421                 }
422                 i++;
423         }
424
425         return tp;
426 }
427
428 void hybrid_topology__delete(struct hybrid_topology *tp)
429 {
430         u32 i;
431
432         for (i = 0; i < tp->nr; i++) {
433                 zfree(&tp->nodes[i].pmu_name);
434                 zfree(&tp->nodes[i].cpus);
435         }
436
437         free(tp);
438 }