xref: /openbmc/linux/tools/perf/util/cputopo.c (revision d40d48e1)
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 		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 
179 struct 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 
229 out_free:
230 	perf_cpu_map__put(map);
231 	if (ret) {
232 		cpu_topology__delete(tp);
233 		tp = NULL;
234 	}
235 	return tp;
236 }
237 
238 static 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 
291 err:
292 	free(buf);
293 	if (fp)
294 		fclose(fp);
295 	return ret;
296 }
297 
298 struct 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 
343 out:
344 	free(buf);
345 	fclose(fp);
346 	perf_cpu_map__put(node_map);
347 	return tp;
348 }
349 
350 void 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 
360 static 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 
395 err:
396 	zfree(&node->pmu_name);
397 	free(buf);
398 	return -1;
399 }
400 
401 struct 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 
427 void 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 }
438