blob: ec77e2a7b3ca1875ef610d0f6ce3be17fc75a266 [file] [log] [blame]
Jiri Olsa5135d5e2019-02-19 10:58:13 +01001// SPDX-License-Identifier: GPL-2.0
2#include <sys/param.h>
Kan Liangacae8b32019-06-04 15:50:41 -07003#include <sys/utsname.h>
Jiri Olsa48e6c5a2019-02-19 10:58:14 +01004#include <inttypes.h>
Arnaldo Carvalho de Melo215a0d32019-07-04 11:21:24 -03005#include <stdlib.h>
Arnaldo Carvalho de Melo5e51b0b2019-08-22 10:48:31 -03006#include <string.h>
Jiri Olsae19a01c2019-02-19 10:58:15 +01007#include <api/fs/fs.h>
Arnaldo Carvalho de Melo7f7c5362019-07-04 11:32:27 -03008#include <linux/zalloc.h>
Jiri Olsa9c3516d2019-07-21 13:24:30 +02009#include <perf/cpumap.h>
Jiri Olsa5135d5e2019-02-19 10:58:13 +010010
11#include "cputopo.h"
12#include "cpumap.h"
Arnaldo Carvalho de Melo5e51b0b2019-08-22 10:48:31 -030013#include "debug.h"
Jiri Olsa48e6c5a2019-02-19 10:58:14 +010014#include "env.h"
Jin Yaof7d74ce2021-05-14 20:29:47 +080015#include "pmu-hybrid.h"
Jiri Olsa5135d5e2019-02-19 10:58:13 +010016
Jiri Olsa5135d5e2019-02-19 10:58:13 +010017#define CORE_SIB_FMT \
Jiri Olsae19a01c2019-02-19 10:58:15 +010018 "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
Kan Liangacae8b32019-06-04 15:50:41 -070019#define DIE_SIB_FMT \
20 "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
Jiri Olsa5135d5e2019-02-19 10:58:13 +010021#define THRD_SIB_FMT \
Jiri Olsae19a01c2019-02-19 10:58:15 +010022 "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
Kan Liang0ccdb842019-06-04 15:50:44 -070023#define THRD_SIB_FMT_NEW \
24 "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
Jiri Olsae19a01c2019-02-19 10:58:15 +010025#define NODE_ONLINE_FMT \
26 "%s/devices/system/node/online"
27#define NODE_MEMINFO_FMT \
28 "%s/devices/system/node/node%d/meminfo"
29#define NODE_CPULIST_FMT \
30 "%s/devices/system/node/node%d/cpulist"
Jiri Olsa5135d5e2019-02-19 10:58:13 +010031
32static int build_cpu_topology(struct cpu_topology *tp, int cpu)
33{
34 FILE *fp;
35 char filename[MAXPATHLEN];
36 char *buf = NULL, *p;
37 size_t len = 0;
38 ssize_t sret;
39 u32 i = 0;
40 int ret = -1;
41
Jiri Olsae19a01c2019-02-19 10:58:15 +010042 scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
43 sysfs__mountpoint(), cpu);
Jiri Olsa5135d5e2019-02-19 10:58:13 +010044 fp = fopen(filename, "r");
45 if (!fp)
Kan Liangacae8b32019-06-04 15:50:41 -070046 goto try_dies;
Jiri Olsa5135d5e2019-02-19 10:58:13 +010047
48 sret = getline(&buf, &len, fp);
49 fclose(fp);
50 if (sret <= 0)
Kan Liangacae8b32019-06-04 15:50:41 -070051 goto try_dies;
Jiri Olsa5135d5e2019-02-19 10:58:13 +010052
53 p = strchr(buf, '\n');
54 if (p)
55 *p = '\0';
56
57 for (i = 0; i < tp->core_sib; i++) {
58 if (!strcmp(buf, tp->core_siblings[i]))
59 break;
60 }
61 if (i == tp->core_sib) {
62 tp->core_siblings[i] = buf;
63 tp->core_sib++;
64 buf = NULL;
65 len = 0;
66 }
67 ret = 0;
68
Kan Liangacae8b32019-06-04 15:50:41 -070069try_dies:
70 if (!tp->die_siblings)
71 goto try_threads;
72
73 scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
74 sysfs__mountpoint(), cpu);
75 fp = fopen(filename, "r");
76 if (!fp)
77 goto try_threads;
78
79 sret = getline(&buf, &len, fp);
80 fclose(fp);
81 if (sret <= 0)
82 goto try_threads;
83
84 p = strchr(buf, '\n');
85 if (p)
86 *p = '\0';
87
88 for (i = 0; i < tp->die_sib; i++) {
89 if (!strcmp(buf, tp->die_siblings[i]))
90 break;
91 }
92 if (i == tp->die_sib) {
93 tp->die_siblings[i] = buf;
94 tp->die_sib++;
95 buf = NULL;
96 len = 0;
97 }
98 ret = 0;
99
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100100try_threads:
Kan Liang0ccdb842019-06-04 15:50:44 -0700101 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
Jiri Olsae19a01c2019-02-19 10:58:15 +0100102 sysfs__mountpoint(), cpu);
Kan Liang0ccdb842019-06-04 15:50:44 -0700103 if (access(filename, F_OK) == -1) {
104 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
105 sysfs__mountpoint(), cpu);
106 }
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100107 fp = fopen(filename, "r");
108 if (!fp)
109 goto done;
110
111 if (getline(&buf, &len, fp) <= 0)
112 goto done;
113
114 p = strchr(buf, '\n');
115 if (p)
116 *p = '\0';
117
118 for (i = 0; i < tp->thread_sib; i++) {
119 if (!strcmp(buf, tp->thread_siblings[i]))
120 break;
121 }
122 if (i == tp->thread_sib) {
123 tp->thread_siblings[i] = buf;
124 tp->thread_sib++;
125 buf = NULL;
126 }
127 ret = 0;
128done:
129 if (fp)
130 fclose(fp);
131 free(buf);
132 return ret;
133}
134
135void cpu_topology__delete(struct cpu_topology *tp)
136{
137 u32 i;
138
139 if (!tp)
140 return;
141
142 for (i = 0 ; i < tp->core_sib; i++)
143 zfree(&tp->core_siblings[i]);
144
Kan Liangacae8b32019-06-04 15:50:41 -0700145 if (tp->die_sib) {
146 for (i = 0 ; i < tp->die_sib; i++)
147 zfree(&tp->die_siblings[i]);
148 }
149
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100150 for (i = 0 ; i < tp->thread_sib; i++)
151 zfree(&tp->thread_siblings[i]);
152
153 free(tp);
154}
155
Kan Liangacae8b32019-06-04 15:50:41 -0700156static bool has_die_topology(void)
157{
158 char filename[MAXPATHLEN];
159 struct utsname uts;
160
161 if (uname(&uts) < 0)
162 return false;
163
164 if (strncmp(uts.machine, "x86_64", 6))
165 return false;
166
167 scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
168 sysfs__mountpoint(), 0);
169 if (access(filename, F_OK) == -1)
170 return false;
171
172 return true;
173}
174
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100175struct cpu_topology *cpu_topology__new(void)
176{
177 struct cpu_topology *tp = NULL;
178 void *addr;
Kan Liangacae8b32019-06-04 15:50:41 -0700179 u32 nr, i, nr_addr;
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100180 size_t sz;
181 long ncpus;
182 int ret = -1;
Jiri Olsaf8548392019-07-21 13:23:49 +0200183 struct perf_cpu_map *map;
Kan Liangacae8b32019-06-04 15:50:41 -0700184 bool has_die = has_die_topology();
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100185
186 ncpus = cpu__max_present_cpu();
187
188 /* build online CPU map */
Jiri Olsa9c3516d2019-07-21 13:24:30 +0200189 map = perf_cpu_map__new(NULL);
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100190 if (map == NULL) {
191 pr_debug("failed to get system cpumap\n");
192 return NULL;
193 }
194
195 nr = (u32)(ncpus & UINT_MAX);
196
197 sz = nr * sizeof(char *);
Kan Liangacae8b32019-06-04 15:50:41 -0700198 if (has_die)
199 nr_addr = 3;
200 else
201 nr_addr = 2;
202 addr = calloc(1, sizeof(*tp) + nr_addr * sz);
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100203 if (!addr)
204 goto out_free;
205
206 tp = addr;
207 addr += sizeof(*tp);
208 tp->core_siblings = addr;
209 addr += sz;
Kan Liangacae8b32019-06-04 15:50:41 -0700210 if (has_die) {
211 tp->die_siblings = addr;
212 addr += sz;
213 }
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100214 tp->thread_siblings = addr;
215
216 for (i = 0; i < nr; i++) {
217 if (!cpu_map__has(map, i))
218 continue;
219
220 ret = build_cpu_topology(tp, i);
221 if (ret < 0)
222 break;
223 }
224
225out_free:
Jiri Olsa38f01d82019-07-21 13:24:17 +0200226 perf_cpu_map__put(map);
Jiri Olsa5135d5e2019-02-19 10:58:13 +0100227 if (ret) {
228 cpu_topology__delete(tp);
229 tp = NULL;
230 }
231 return tp;
232}
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100233
234static int load_numa_node(struct numa_topology_node *node, int nr)
235{
236 char str[MAXPATHLEN];
237 char field[32];
238 char *buf = NULL, *p;
239 size_t len = 0;
240 int ret = -1;
241 FILE *fp;
242 u64 mem;
243
244 node->node = (u32) nr;
245
Jiri Olsae19a01c2019-02-19 10:58:15 +0100246 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
247 sysfs__mountpoint(), nr);
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100248 fp = fopen(str, "r");
249 if (!fp)
250 return -1;
251
252 while (getline(&buf, &len, fp) > 0) {
253 /* skip over invalid lines */
254 if (!strchr(buf, ':'))
255 continue;
256 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
257 goto err;
258 if (!strcmp(field, "MemTotal:"))
259 node->mem_total = mem;
260 if (!strcmp(field, "MemFree:"))
261 node->mem_free = mem;
262 if (node->mem_total && node->mem_free)
263 break;
264 }
265
266 fclose(fp);
267 fp = NULL;
268
Jiri Olsae19a01c2019-02-19 10:58:15 +0100269 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
270 sysfs__mountpoint(), nr);
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100271
272 fp = fopen(str, "r");
273 if (!fp)
274 return -1;
275
276 if (getline(&buf, &len, fp) <= 0)
277 goto err;
278
279 p = strchr(buf, '\n');
280 if (p)
281 *p = '\0';
282
283 node->cpus = buf;
284 fclose(fp);
285 return 0;
286
287err:
288 free(buf);
289 if (fp)
290 fclose(fp);
291 return ret;
292}
293
294struct numa_topology *numa_topology__new(void)
295{
Jiri Olsaf8548392019-07-21 13:23:49 +0200296 struct perf_cpu_map *node_map = NULL;
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100297 struct numa_topology *tp = NULL;
Jiri Olsae19a01c2019-02-19 10:58:15 +0100298 char path[MAXPATHLEN];
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100299 char *buf = NULL;
300 size_t len = 0;
301 u32 nr, i;
302 FILE *fp;
303 char *c;
304
Jiri Olsae19a01c2019-02-19 10:58:15 +0100305 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
306 sysfs__mountpoint());
307
308 fp = fopen(path, "r");
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100309 if (!fp)
310 return NULL;
311
312 if (getline(&buf, &len, fp) <= 0)
313 goto out;
314
315 c = strchr(buf, '\n');
316 if (c)
317 *c = '\0';
318
Jiri Olsa9c3516d2019-07-21 13:24:30 +0200319 node_map = perf_cpu_map__new(buf);
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100320 if (!node_map)
321 goto out;
322
323 nr = (u32) node_map->nr;
324
325 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
326 if (!tp)
327 goto out;
328
329 tp->nr = nr;
330
331 for (i = 0; i < nr; i++) {
332 if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
333 numa_topology__delete(tp);
334 tp = NULL;
335 break;
336 }
337 }
338
339out:
340 free(buf);
341 fclose(fp);
Jiri Olsa38f01d82019-07-21 13:24:17 +0200342 perf_cpu_map__put(node_map);
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100343 return tp;
344}
345
346void numa_topology__delete(struct numa_topology *tp)
347{
348 u32 i;
349
350 for (i = 0; i < tp->nr; i++)
Arnaldo Carvalho de Melod8f9da22019-07-04 12:06:20 -0300351 zfree(&tp->nodes[i].cpus);
Jiri Olsa48e6c5a2019-02-19 10:58:14 +0100352
353 free(tp);
354}
Jin Yaof7d74ce2021-05-14 20:29:47 +0800355
356static int load_hybrid_node(struct hybrid_topology_node *node,
357 struct perf_pmu *pmu)
358{
359 const char *sysfs;
360 char path[PATH_MAX];
361 char *buf = NULL, *p;
362 FILE *fp;
363 size_t len = 0;
364
365 node->pmu_name = strdup(pmu->name);
366 if (!node->pmu_name)
367 return -1;
368
369 sysfs = sysfs__mountpoint();
370 if (!sysfs)
371 goto err;
372
373 snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
374 fp = fopen(path, "r");
375 if (!fp)
376 goto err;
377
378 if (getline(&buf, &len, fp) <= 0) {
379 fclose(fp);
380 goto err;
381 }
382
383 p = strchr(buf, '\n');
384 if (p)
385 *p = '\0';
386
387 fclose(fp);
388 node->cpus = buf;
389 return 0;
390
391err:
392 zfree(&node->pmu_name);
393 free(buf);
394 return -1;
395}
396
397struct hybrid_topology *hybrid_topology__new(void)
398{
399 struct perf_pmu *pmu;
400 struct hybrid_topology *tp = NULL;
401 u32 nr, i = 0;
402
403 nr = perf_pmu__hybrid_pmu_num();
404 if (nr == 0)
405 return NULL;
406
407 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
408 if (!tp)
409 return NULL;
410
411 tp->nr = nr;
412 perf_pmu__for_each_hybrid_pmu(pmu) {
413 if (load_hybrid_node(&tp->nodes[i], pmu)) {
414 hybrid_topology__delete(tp);
415 return NULL;
416 }
417 i++;
418 }
419
420 return tp;
421}
422
423void hybrid_topology__delete(struct hybrid_topology *tp)
424{
425 u32 i;
426
427 for (i = 0; i < tp->nr; i++) {
428 zfree(&tp->nodes[i].pmu_name);
429 zfree(&tp->nodes[i].cpus);
430 }
431
432 free(tp);
433}