1 // SPDX-License-Identifier: GPL-2.0
3 #include <sys/utsname.h>
8 #include <linux/zalloc.h>
9 #include <perf/cpumap.h>
15 #include "pmu-hybrid.h"
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"
34 static int build_cpu_topology(struct cpu_topology *tp, int cpu)
37 char filename[MAXPATHLEN];
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);
50 fp = fopen(filename, "r");
54 sret = getline(&buf, &len, fp);
59 p = strchr(buf, '\n');
63 for (i = 0; i < tp->package_cpus_lists; i++) {
64 if (!strcmp(buf, tp->package_cpus_list[i]))
67 if (i == tp->package_cpus_lists) {
68 tp->package_cpus_list[i] = buf;
69 tp->package_cpus_lists++;
76 if (!tp->die_cpus_list)
79 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
80 sysfs__mountpoint(), cpu);
81 fp = fopen(filename, "r");
85 sret = getline(&buf, &len, fp);
90 p = strchr(buf, '\n');
94 for (i = 0; i < tp->die_cpus_lists; i++) {
95 if (!strcmp(buf, tp->die_cpus_list[i]))
98 if (i == tp->die_cpus_lists) {
99 tp->die_cpus_list[i] = buf;
100 tp->die_cpus_lists++;
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);
113 fp = fopen(filename, "r");
117 if (getline(&buf, &len, fp) <= 0)
120 p = strchr(buf, '\n');
124 for (i = 0; i < tp->core_cpus_lists; i++) {
125 if (!strcmp(buf, tp->core_cpus_list[i]))
128 if (i == tp->core_cpus_lists) {
129 tp->core_cpus_list[i] = buf;
130 tp->core_cpus_lists++;
141 void cpu_topology__delete(struct cpu_topology *tp)
148 for (i = 0 ; i < tp->package_cpus_lists; i++)
149 zfree(&tp->package_cpus_list[i]);
151 for (i = 0 ; i < tp->die_cpus_lists; i++)
152 zfree(&tp->die_cpus_list[i]);
154 for (i = 0 ; i < tp->core_cpus_lists; i++)
155 zfree(&tp->core_cpus_list[i]);
160 static bool has_die_topology(void)
162 char filename[MAXPATHLEN];
168 if (strncmp(uts.machine, "x86_64", 6) &&
169 strncmp(uts.machine, "s390x", 5))
172 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
173 sysfs__mountpoint(), 0);
174 if (access(filename, F_OK) == -1)
180 struct cpu_topology *cpu_topology__new(void)
182 struct cpu_topology *tp = NULL;
188 struct perf_cpu_map *map;
189 bool has_die = has_die_topology();
191 ncpus = cpu__max_present_cpu().cpu;
193 /* build online CPU map */
194 map = perf_cpu_map__new(NULL);
196 pr_debug("failed to get system cpumap\n");
200 nr = (u32)(ncpus & UINT_MAX);
202 sz = nr * sizeof(char *);
207 addr = calloc(1, sizeof(*tp) + nr_addr * sz);
213 tp->package_cpus_list = addr;
216 tp->die_cpus_list = addr;
219 tp->core_cpus_list = addr;
221 for (i = 0; i < nr; i++) {
222 if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))
225 ret = build_cpu_topology(tp, i);
231 perf_cpu_map__put(map);
233 cpu_topology__delete(tp);
239 static int load_numa_node(struct numa_topology_node *node, int nr)
241 char str[MAXPATHLEN];
243 char *buf = NULL, *p;
249 node->node = (u32) nr;
251 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
252 sysfs__mountpoint(), nr);
253 fp = fopen(str, "r");
257 while (getline(&buf, &len, fp) > 0) {
258 /* skip over invalid lines */
259 if (!strchr(buf, ':'))
261 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
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)
274 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
275 sysfs__mountpoint(), nr);
277 fp = fopen(str, "r");
281 if (getline(&buf, &len, fp) <= 0)
284 p = strchr(buf, '\n');
299 struct numa_topology *numa_topology__new(void)
301 struct perf_cpu_map *node_map = NULL;
302 struct numa_topology *tp = NULL;
303 char path[MAXPATHLEN];
310 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
311 sysfs__mountpoint());
313 fp = fopen(path, "r");
317 if (getline(&buf, &len, fp) <= 0)
320 c = strchr(buf, '\n');
324 node_map = perf_cpu_map__new(buf);
328 nr = (u32) node_map->nr;
330 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
336 for (i = 0; i < nr; i++) {
337 if (load_numa_node(&tp->nodes[i], node_map->map[i].cpu)) {
338 numa_topology__delete(tp);
347 perf_cpu_map__put(node_map);
351 void numa_topology__delete(struct numa_topology *tp)
355 for (i = 0; i < tp->nr; i++)
356 zfree(&tp->nodes[i].cpus);
361 static int load_hybrid_node(struct hybrid_topology_node *node,
362 struct perf_pmu *pmu)
366 char *buf = NULL, *p;
370 node->pmu_name = strdup(pmu->name);
374 sysfs = sysfs__mountpoint();
378 snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
379 fp = fopen(path, "r");
383 if (getline(&buf, &len, fp) <= 0) {
388 p = strchr(buf, '\n');
397 zfree(&node->pmu_name);
402 struct hybrid_topology *hybrid_topology__new(void)
404 struct perf_pmu *pmu;
405 struct hybrid_topology *tp = NULL;
408 nr = perf_pmu__hybrid_pmu_num();
412 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
417 perf_pmu__for_each_hybrid_pmu(pmu) {
418 if (load_hybrid_node(&tp->nodes[i], pmu)) {
419 hybrid_topology__delete(tp);
428 void hybrid_topology__delete(struct hybrid_topology *tp)
432 for (i = 0; i < tp->nr; i++) {
433 zfree(&tp->nodes[i].pmu_name);
434 zfree(&tp->nodes[i].cpus);