Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
160static 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
179struct 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
229out_free:
230 perf_cpu_map__put(map);
231 if (ret) {
232 cpu_topology__delete(tp);
233 tp = NULL;
234 }
235 return tp;
236}
237
238static 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
291err:
292 free(buf);
293 if (fp)
294 fclose(fp);
295 return ret;
296}
297
298struct 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
343out:
344 free(buf);
345 fclose(fp);
346 perf_cpu_map__put(node_map);
347 return tp;
348}
349
350void 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
360static 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
395err:
396 zfree(&node->pmu_name);
397 free(buf);
398 return -1;
399}
400
401struct 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
427void 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}