Loading...
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
34static 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
75try_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
106try_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;
134done:
135 if (fp)
136 fclose(fp);
137 free(buf);
138 return ret;
139}
140
141void 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
160bool cpu_topology__smt_on(const struct cpu_topology *topology)
161{
162 for (u32 i = 0; i < topology->core_cpus_lists; i++) {
163 const char *cpu_list = topology->core_cpus_list[i];
164
165 /*
166 * If there is a need to separate siblings in a core then SMT is
167 * enabled.
168 */
169 if (strchr(cpu_list, ',') || strchr(cpu_list, '-'))
170 return true;
171 }
172 return false;
173}
174
175bool cpu_topology__core_wide(const struct cpu_topology *topology,
176 const char *user_requested_cpu_list)
177{
178 struct perf_cpu_map *user_requested_cpus;
179
180 /*
181 * If user_requested_cpu_list is empty then all CPUs are recorded and so
182 * core_wide is true.
183 */
184 if (!user_requested_cpu_list)
185 return true;
186
187 user_requested_cpus = perf_cpu_map__new(user_requested_cpu_list);
188 /* Check that every user requested CPU is the complete set of SMT threads on a core. */
189 for (u32 i = 0; i < topology->core_cpus_lists; i++) {
190 const char *core_cpu_list = topology->core_cpus_list[i];
191 struct perf_cpu_map *core_cpus = perf_cpu_map__new(core_cpu_list);
192 struct perf_cpu cpu;
193 int idx;
194 bool has_first, first = true;
195
196 perf_cpu_map__for_each_cpu(cpu, idx, core_cpus) {
197 if (first) {
198 has_first = perf_cpu_map__has(user_requested_cpus, cpu);
199 first = false;
200 } else {
201 /*
202 * If the first core CPU is user requested then
203 * all subsequent CPUs in the core must be user
204 * requested too. If the first CPU isn't user
205 * requested then none of the others must be
206 * too.
207 */
208 if (perf_cpu_map__has(user_requested_cpus, cpu) != has_first) {
209 perf_cpu_map__put(core_cpus);
210 perf_cpu_map__put(user_requested_cpus);
211 return false;
212 }
213 }
214 }
215 perf_cpu_map__put(core_cpus);
216 }
217 perf_cpu_map__put(user_requested_cpus);
218 return true;
219}
220
221static bool has_die_topology(void)
222{
223 char filename[MAXPATHLEN];
224 struct utsname uts;
225
226 if (uname(&uts) < 0)
227 return false;
228
229 if (strncmp(uts.machine, "x86_64", 6) &&
230 strncmp(uts.machine, "s390x", 5))
231 return false;
232
233 scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
234 sysfs__mountpoint(), 0);
235 if (access(filename, F_OK) == -1)
236 return false;
237
238 return true;
239}
240
241struct cpu_topology *cpu_topology__new(void)
242{
243 struct cpu_topology *tp = NULL;
244 void *addr;
245 u32 nr, i, nr_addr;
246 size_t sz;
247 long ncpus;
248 int ret = -1;
249 struct perf_cpu_map *map;
250 bool has_die = has_die_topology();
251
252 ncpus = cpu__max_present_cpu().cpu;
253
254 /* build online CPU map */
255 map = perf_cpu_map__new(NULL);
256 if (map == NULL) {
257 pr_debug("failed to get system cpumap\n");
258 return NULL;
259 }
260
261 nr = (u32)(ncpus & UINT_MAX);
262
263 sz = nr * sizeof(char *);
264 if (has_die)
265 nr_addr = 3;
266 else
267 nr_addr = 2;
268 addr = calloc(1, sizeof(*tp) + nr_addr * sz);
269 if (!addr)
270 goto out_free;
271
272 tp = addr;
273 addr += sizeof(*tp);
274 tp->package_cpus_list = addr;
275 addr += sz;
276 if (has_die) {
277 tp->die_cpus_list = addr;
278 addr += sz;
279 }
280 tp->core_cpus_list = addr;
281
282 for (i = 0; i < nr; i++) {
283 if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))
284 continue;
285
286 ret = build_cpu_topology(tp, i);
287 if (ret < 0)
288 break;
289 }
290
291out_free:
292 perf_cpu_map__put(map);
293 if (ret) {
294 cpu_topology__delete(tp);
295 tp = NULL;
296 }
297 return tp;
298}
299
300static int load_numa_node(struct numa_topology_node *node, int nr)
301{
302 char str[MAXPATHLEN];
303 char field[32];
304 char *buf = NULL, *p;
305 size_t len = 0;
306 int ret = -1;
307 FILE *fp;
308 u64 mem;
309
310 node->node = (u32) nr;
311
312 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
313 sysfs__mountpoint(), nr);
314 fp = fopen(str, "r");
315 if (!fp)
316 return -1;
317
318 while (getline(&buf, &len, fp) > 0) {
319 /* skip over invalid lines */
320 if (!strchr(buf, ':'))
321 continue;
322 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
323 goto err;
324 if (!strcmp(field, "MemTotal:"))
325 node->mem_total = mem;
326 if (!strcmp(field, "MemFree:"))
327 node->mem_free = mem;
328 if (node->mem_total && node->mem_free)
329 break;
330 }
331
332 fclose(fp);
333 fp = NULL;
334
335 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
336 sysfs__mountpoint(), nr);
337
338 fp = fopen(str, "r");
339 if (!fp)
340 return -1;
341
342 if (getline(&buf, &len, fp) <= 0)
343 goto err;
344
345 p = strchr(buf, '\n');
346 if (p)
347 *p = '\0';
348
349 node->cpus = buf;
350 fclose(fp);
351 return 0;
352
353err:
354 free(buf);
355 if (fp)
356 fclose(fp);
357 return ret;
358}
359
360struct numa_topology *numa_topology__new(void)
361{
362 struct perf_cpu_map *node_map = NULL;
363 struct numa_topology *tp = NULL;
364 char path[MAXPATHLEN];
365 char *buf = NULL;
366 size_t len = 0;
367 u32 nr, i;
368 FILE *fp;
369 char *c;
370
371 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
372 sysfs__mountpoint());
373
374 fp = fopen(path, "r");
375 if (!fp)
376 return NULL;
377
378 if (getline(&buf, &len, fp) <= 0)
379 goto out;
380
381 c = strchr(buf, '\n');
382 if (c)
383 *c = '\0';
384
385 node_map = perf_cpu_map__new(buf);
386 if (!node_map)
387 goto out;
388
389 nr = (u32) perf_cpu_map__nr(node_map);
390
391 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
392 if (!tp)
393 goto out;
394
395 tp->nr = nr;
396
397 for (i = 0; i < nr; i++) {
398 if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) {
399 numa_topology__delete(tp);
400 tp = NULL;
401 break;
402 }
403 }
404
405out:
406 free(buf);
407 fclose(fp);
408 perf_cpu_map__put(node_map);
409 return tp;
410}
411
412void numa_topology__delete(struct numa_topology *tp)
413{
414 u32 i;
415
416 for (i = 0; i < tp->nr; i++)
417 zfree(&tp->nodes[i].cpus);
418
419 free(tp);
420}
421
422static int load_hybrid_node(struct hybrid_topology_node *node,
423 struct perf_pmu *pmu)
424{
425 const char *sysfs;
426 char path[PATH_MAX];
427 char *buf = NULL, *p;
428 FILE *fp;
429 size_t len = 0;
430
431 node->pmu_name = strdup(pmu->name);
432 if (!node->pmu_name)
433 return -1;
434
435 sysfs = sysfs__mountpoint();
436 if (!sysfs)
437 goto err;
438
439 snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
440 fp = fopen(path, "r");
441 if (!fp)
442 goto err;
443
444 if (getline(&buf, &len, fp) <= 0) {
445 fclose(fp);
446 goto err;
447 }
448
449 p = strchr(buf, '\n');
450 if (p)
451 *p = '\0';
452
453 fclose(fp);
454 node->cpus = buf;
455 return 0;
456
457err:
458 zfree(&node->pmu_name);
459 free(buf);
460 return -1;
461}
462
463struct hybrid_topology *hybrid_topology__new(void)
464{
465 struct perf_pmu *pmu;
466 struct hybrid_topology *tp = NULL;
467 u32 nr, i = 0;
468
469 nr = perf_pmu__hybrid_pmu_num();
470 if (nr == 0)
471 return NULL;
472
473 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
474 if (!tp)
475 return NULL;
476
477 tp->nr = nr;
478 perf_pmu__for_each_hybrid_pmu(pmu) {
479 if (load_hybrid_node(&tp->nodes[i], pmu)) {
480 hybrid_topology__delete(tp);
481 return NULL;
482 }
483 i++;
484 }
485
486 return tp;
487}
488
489void hybrid_topology__delete(struct hybrid_topology *tp)
490{
491 u32 i;
492
493 for (i = 0; i < tp->nr; i++) {
494 zfree(&tp->nodes[i].pmu_name);
495 zfree(&tp->nodes[i].cpus);
496 }
497
498 free(tp);
499}
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
16#define CORE_SIB_FMT \
17 "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
18#define DIE_SIB_FMT \
19 "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
20#define THRD_SIB_FMT \
21 "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
22#define THRD_SIB_FMT_NEW \
23 "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
24#define NODE_ONLINE_FMT \
25 "%s/devices/system/node/online"
26#define NODE_MEMINFO_FMT \
27 "%s/devices/system/node/node%d/meminfo"
28#define NODE_CPULIST_FMT \
29 "%s/devices/system/node/node%d/cpulist"
30
31static int build_cpu_topology(struct cpu_topology *tp, int cpu)
32{
33 FILE *fp;
34 char filename[MAXPATHLEN];
35 char *buf = NULL, *p;
36 size_t len = 0;
37 ssize_t sret;
38 u32 i = 0;
39 int ret = -1;
40
41 scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
42 sysfs__mountpoint(), cpu);
43 fp = fopen(filename, "r");
44 if (!fp)
45 goto try_dies;
46
47 sret = getline(&buf, &len, fp);
48 fclose(fp);
49 if (sret <= 0)
50 goto try_dies;
51
52 p = strchr(buf, '\n');
53 if (p)
54 *p = '\0';
55
56 for (i = 0; i < tp->core_sib; i++) {
57 if (!strcmp(buf, tp->core_siblings[i]))
58 break;
59 }
60 if (i == tp->core_sib) {
61 tp->core_siblings[i] = buf;
62 tp->core_sib++;
63 buf = NULL;
64 len = 0;
65 }
66 ret = 0;
67
68try_dies:
69 if (!tp->die_siblings)
70 goto try_threads;
71
72 scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
73 sysfs__mountpoint(), cpu);
74 fp = fopen(filename, "r");
75 if (!fp)
76 goto try_threads;
77
78 sret = getline(&buf, &len, fp);
79 fclose(fp);
80 if (sret <= 0)
81 goto try_threads;
82
83 p = strchr(buf, '\n');
84 if (p)
85 *p = '\0';
86
87 for (i = 0; i < tp->die_sib; i++) {
88 if (!strcmp(buf, tp->die_siblings[i]))
89 break;
90 }
91 if (i == tp->die_sib) {
92 tp->die_siblings[i] = buf;
93 tp->die_sib++;
94 buf = NULL;
95 len = 0;
96 }
97 ret = 0;
98
99try_threads:
100 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
101 sysfs__mountpoint(), cpu);
102 if (access(filename, F_OK) == -1) {
103 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
104 sysfs__mountpoint(), cpu);
105 }
106 fp = fopen(filename, "r");
107 if (!fp)
108 goto done;
109
110 if (getline(&buf, &len, fp) <= 0)
111 goto done;
112
113 p = strchr(buf, '\n');
114 if (p)
115 *p = '\0';
116
117 for (i = 0; i < tp->thread_sib; i++) {
118 if (!strcmp(buf, tp->thread_siblings[i]))
119 break;
120 }
121 if (i == tp->thread_sib) {
122 tp->thread_siblings[i] = buf;
123 tp->thread_sib++;
124 buf = NULL;
125 }
126 ret = 0;
127done:
128 if (fp)
129 fclose(fp);
130 free(buf);
131 return ret;
132}
133
134void cpu_topology__delete(struct cpu_topology *tp)
135{
136 u32 i;
137
138 if (!tp)
139 return;
140
141 for (i = 0 ; i < tp->core_sib; i++)
142 zfree(&tp->core_siblings[i]);
143
144 if (tp->die_sib) {
145 for (i = 0 ; i < tp->die_sib; i++)
146 zfree(&tp->die_siblings[i]);
147 }
148
149 for (i = 0 ; i < tp->thread_sib; i++)
150 zfree(&tp->thread_siblings[i]);
151
152 free(tp);
153}
154
155static bool has_die_topology(void)
156{
157 char filename[MAXPATHLEN];
158 struct utsname uts;
159
160 if (uname(&uts) < 0)
161 return false;
162
163 if (strncmp(uts.machine, "x86_64", 6))
164 return false;
165
166 scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
167 sysfs__mountpoint(), 0);
168 if (access(filename, F_OK) == -1)
169 return false;
170
171 return true;
172}
173
174struct cpu_topology *cpu_topology__new(void)
175{
176 struct cpu_topology *tp = NULL;
177 void *addr;
178 u32 nr, i, nr_addr;
179 size_t sz;
180 long ncpus;
181 int ret = -1;
182 struct perf_cpu_map *map;
183 bool has_die = has_die_topology();
184
185 ncpus = cpu__max_present_cpu();
186
187 /* build online CPU map */
188 map = perf_cpu_map__new(NULL);
189 if (map == NULL) {
190 pr_debug("failed to get system cpumap\n");
191 return NULL;
192 }
193
194 nr = (u32)(ncpus & UINT_MAX);
195
196 sz = nr * sizeof(char *);
197 if (has_die)
198 nr_addr = 3;
199 else
200 nr_addr = 2;
201 addr = calloc(1, sizeof(*tp) + nr_addr * sz);
202 if (!addr)
203 goto out_free;
204
205 tp = addr;
206 addr += sizeof(*tp);
207 tp->core_siblings = addr;
208 addr += sz;
209 if (has_die) {
210 tp->die_siblings = addr;
211 addr += sz;
212 }
213 tp->thread_siblings = addr;
214
215 for (i = 0; i < nr; i++) {
216 if (!cpu_map__has(map, i))
217 continue;
218
219 ret = build_cpu_topology(tp, i);
220 if (ret < 0)
221 break;
222 }
223
224out_free:
225 perf_cpu_map__put(map);
226 if (ret) {
227 cpu_topology__delete(tp);
228 tp = NULL;
229 }
230 return tp;
231}
232
233static int load_numa_node(struct numa_topology_node *node, int nr)
234{
235 char str[MAXPATHLEN];
236 char field[32];
237 char *buf = NULL, *p;
238 size_t len = 0;
239 int ret = -1;
240 FILE *fp;
241 u64 mem;
242
243 node->node = (u32) nr;
244
245 scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
246 sysfs__mountpoint(), nr);
247 fp = fopen(str, "r");
248 if (!fp)
249 return -1;
250
251 while (getline(&buf, &len, fp) > 0) {
252 /* skip over invalid lines */
253 if (!strchr(buf, ':'))
254 continue;
255 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
256 goto err;
257 if (!strcmp(field, "MemTotal:"))
258 node->mem_total = mem;
259 if (!strcmp(field, "MemFree:"))
260 node->mem_free = mem;
261 if (node->mem_total && node->mem_free)
262 break;
263 }
264
265 fclose(fp);
266 fp = NULL;
267
268 scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
269 sysfs__mountpoint(), nr);
270
271 fp = fopen(str, "r");
272 if (!fp)
273 return -1;
274
275 if (getline(&buf, &len, fp) <= 0)
276 goto err;
277
278 p = strchr(buf, '\n');
279 if (p)
280 *p = '\0';
281
282 node->cpus = buf;
283 fclose(fp);
284 return 0;
285
286err:
287 free(buf);
288 if (fp)
289 fclose(fp);
290 return ret;
291}
292
293struct numa_topology *numa_topology__new(void)
294{
295 struct perf_cpu_map *node_map = NULL;
296 struct numa_topology *tp = NULL;
297 char path[MAXPATHLEN];
298 char *buf = NULL;
299 size_t len = 0;
300 u32 nr, i;
301 FILE *fp;
302 char *c;
303
304 scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
305 sysfs__mountpoint());
306
307 fp = fopen(path, "r");
308 if (!fp)
309 return NULL;
310
311 if (getline(&buf, &len, fp) <= 0)
312 goto out;
313
314 c = strchr(buf, '\n');
315 if (c)
316 *c = '\0';
317
318 node_map = perf_cpu_map__new(buf);
319 if (!node_map)
320 goto out;
321
322 nr = (u32) node_map->nr;
323
324 tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
325 if (!tp)
326 goto out;
327
328 tp->nr = nr;
329
330 for (i = 0; i < nr; i++) {
331 if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
332 numa_topology__delete(tp);
333 tp = NULL;
334 break;
335 }
336 }
337
338out:
339 free(buf);
340 fclose(fp);
341 perf_cpu_map__put(node_map);
342 return tp;
343}
344
345void numa_topology__delete(struct numa_topology *tp)
346{
347 u32 i;
348
349 for (i = 0; i < tp->nr; i++)
350 zfree(&tp->nodes[i].cpus);
351
352 free(tp);
353}