xref: /linux/tools/perf/util/cputopo.c (revision 5643b1a59e581ac3f66d36caba8124313cc446c0)
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 <api/fs/fs.h>
7 #include <linux/zalloc.h>
8 #include <perf/cpumap.h>
9 
10 #include "cputopo.h"
11 #include "cpumap.h"
12 #include "env.h"
13 
14 #define CORE_SIB_FMT \
15 	"%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
16 #define DIE_SIB_FMT \
17 	"%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
18 #define THRD_SIB_FMT \
19 	"%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
20 #define THRD_SIB_FMT_NEW \
21 	"%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
22 #define NODE_ONLINE_FMT \
23 	"%s/devices/system/node/online"
24 #define NODE_MEMINFO_FMT \
25 	"%s/devices/system/node/node%d/meminfo"
26 #define NODE_CPULIST_FMT \
27 	"%s/devices/system/node/node%d/cpulist"
28 
29 static int build_cpu_topology(struct cpu_topology *tp, int cpu)
30 {
31 	FILE *fp;
32 	char filename[MAXPATHLEN];
33 	char *buf = NULL, *p;
34 	size_t len = 0;
35 	ssize_t sret;
36 	u32 i = 0;
37 	int ret = -1;
38 
39 	scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
40 		  sysfs__mountpoint(), cpu);
41 	fp = fopen(filename, "r");
42 	if (!fp)
43 		goto try_dies;
44 
45 	sret = getline(&buf, &len, fp);
46 	fclose(fp);
47 	if (sret <= 0)
48 		goto try_dies;
49 
50 	p = strchr(buf, '\n');
51 	if (p)
52 		*p = '\0';
53 
54 	for (i = 0; i < tp->core_sib; i++) {
55 		if (!strcmp(buf, tp->core_siblings[i]))
56 			break;
57 	}
58 	if (i == tp->core_sib) {
59 		tp->core_siblings[i] = buf;
60 		tp->core_sib++;
61 		buf = NULL;
62 		len = 0;
63 	}
64 	ret = 0;
65 
66 try_dies:
67 	if (!tp->die_siblings)
68 		goto try_threads;
69 
70 	scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
71 		  sysfs__mountpoint(), cpu);
72 	fp = fopen(filename, "r");
73 	if (!fp)
74 		goto try_threads;
75 
76 	sret = getline(&buf, &len, fp);
77 	fclose(fp);
78 	if (sret <= 0)
79 		goto try_threads;
80 
81 	p = strchr(buf, '\n');
82 	if (p)
83 		*p = '\0';
84 
85 	for (i = 0; i < tp->die_sib; i++) {
86 		if (!strcmp(buf, tp->die_siblings[i]))
87 			break;
88 	}
89 	if (i == tp->die_sib) {
90 		tp->die_siblings[i] = buf;
91 		tp->die_sib++;
92 		buf = NULL;
93 		len = 0;
94 	}
95 	ret = 0;
96 
97 try_threads:
98 	scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
99 		  sysfs__mountpoint(), cpu);
100 	if (access(filename, F_OK) == -1) {
101 		scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
102 			  sysfs__mountpoint(), cpu);
103 	}
104 	fp = fopen(filename, "r");
105 	if (!fp)
106 		goto done;
107 
108 	if (getline(&buf, &len, fp) <= 0)
109 		goto done;
110 
111 	p = strchr(buf, '\n');
112 	if (p)
113 		*p = '\0';
114 
115 	for (i = 0; i < tp->thread_sib; i++) {
116 		if (!strcmp(buf, tp->thread_siblings[i]))
117 			break;
118 	}
119 	if (i == tp->thread_sib) {
120 		tp->thread_siblings[i] = buf;
121 		tp->thread_sib++;
122 		buf = NULL;
123 	}
124 	ret = 0;
125 done:
126 	if (fp)
127 		fclose(fp);
128 	free(buf);
129 	return ret;
130 }
131 
132 void cpu_topology__delete(struct cpu_topology *tp)
133 {
134 	u32 i;
135 
136 	if (!tp)
137 		return;
138 
139 	for (i = 0 ; i < tp->core_sib; i++)
140 		zfree(&tp->core_siblings[i]);
141 
142 	if (tp->die_sib) {
143 		for (i = 0 ; i < tp->die_sib; i++)
144 			zfree(&tp->die_siblings[i]);
145 	}
146 
147 	for (i = 0 ; i < tp->thread_sib; i++)
148 		zfree(&tp->thread_siblings[i]);
149 
150 	free(tp);
151 }
152 
153 static bool has_die_topology(void)
154 {
155 	char filename[MAXPATHLEN];
156 	struct utsname uts;
157 
158 	if (uname(&uts) < 0)
159 		return false;
160 
161 	if (strncmp(uts.machine, "x86_64", 6))
162 		return false;
163 
164 	scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
165 		  sysfs__mountpoint(), 0);
166 	if (access(filename, F_OK) == -1)
167 		return false;
168 
169 	return true;
170 }
171 
172 struct cpu_topology *cpu_topology__new(void)
173 {
174 	struct cpu_topology *tp = NULL;
175 	void *addr;
176 	u32 nr, i, nr_addr;
177 	size_t sz;
178 	long ncpus;
179 	int ret = -1;
180 	struct perf_cpu_map *map;
181 	bool has_die = has_die_topology();
182 
183 	ncpus = cpu__max_present_cpu();
184 
185 	/* build online CPU map */
186 	map = perf_cpu_map__new(NULL);
187 	if (map == NULL) {
188 		pr_debug("failed to get system cpumap\n");
189 		return NULL;
190 	}
191 
192 	nr = (u32)(ncpus & UINT_MAX);
193 
194 	sz = nr * sizeof(char *);
195 	if (has_die)
196 		nr_addr = 3;
197 	else
198 		nr_addr = 2;
199 	addr = calloc(1, sizeof(*tp) + nr_addr * sz);
200 	if (!addr)
201 		goto out_free;
202 
203 	tp = addr;
204 	addr += sizeof(*tp);
205 	tp->core_siblings = addr;
206 	addr += sz;
207 	if (has_die) {
208 		tp->die_siblings = addr;
209 		addr += sz;
210 	}
211 	tp->thread_siblings = addr;
212 
213 	for (i = 0; i < nr; i++) {
214 		if (!cpu_map__has(map, i))
215 			continue;
216 
217 		ret = build_cpu_topology(tp, i);
218 		if (ret < 0)
219 			break;
220 	}
221 
222 out_free:
223 	perf_cpu_map__put(map);
224 	if (ret) {
225 		cpu_topology__delete(tp);
226 		tp = NULL;
227 	}
228 	return tp;
229 }
230 
231 static int load_numa_node(struct numa_topology_node *node, int nr)
232 {
233 	char str[MAXPATHLEN];
234 	char field[32];
235 	char *buf = NULL, *p;
236 	size_t len = 0;
237 	int ret = -1;
238 	FILE *fp;
239 	u64 mem;
240 
241 	node->node = (u32) nr;
242 
243 	scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
244 		  sysfs__mountpoint(), nr);
245 	fp = fopen(str, "r");
246 	if (!fp)
247 		return -1;
248 
249 	while (getline(&buf, &len, fp) > 0) {
250 		/* skip over invalid lines */
251 		if (!strchr(buf, ':'))
252 			continue;
253 		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
254 			goto err;
255 		if (!strcmp(field, "MemTotal:"))
256 			node->mem_total = mem;
257 		if (!strcmp(field, "MemFree:"))
258 			node->mem_free = mem;
259 		if (node->mem_total && node->mem_free)
260 			break;
261 	}
262 
263 	fclose(fp);
264 	fp = NULL;
265 
266 	scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
267 		  sysfs__mountpoint(), nr);
268 
269 	fp = fopen(str, "r");
270 	if (!fp)
271 		return -1;
272 
273 	if (getline(&buf, &len, fp) <= 0)
274 		goto err;
275 
276 	p = strchr(buf, '\n');
277 	if (p)
278 		*p = '\0';
279 
280 	node->cpus = buf;
281 	fclose(fp);
282 	return 0;
283 
284 err:
285 	free(buf);
286 	if (fp)
287 		fclose(fp);
288 	return ret;
289 }
290 
291 struct numa_topology *numa_topology__new(void)
292 {
293 	struct perf_cpu_map *node_map = NULL;
294 	struct numa_topology *tp = NULL;
295 	char path[MAXPATHLEN];
296 	char *buf = NULL;
297 	size_t len = 0;
298 	u32 nr, i;
299 	FILE *fp;
300 	char *c;
301 
302 	scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
303 		  sysfs__mountpoint());
304 
305 	fp = fopen(path, "r");
306 	if (!fp)
307 		return NULL;
308 
309 	if (getline(&buf, &len, fp) <= 0)
310 		goto out;
311 
312 	c = strchr(buf, '\n');
313 	if (c)
314 		*c = '\0';
315 
316 	node_map = perf_cpu_map__new(buf);
317 	if (!node_map)
318 		goto out;
319 
320 	nr = (u32) node_map->nr;
321 
322 	tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
323 	if (!tp)
324 		goto out;
325 
326 	tp->nr = nr;
327 
328 	for (i = 0; i < nr; i++) {
329 		if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
330 			numa_topology__delete(tp);
331 			tp = NULL;
332 			break;
333 		}
334 	}
335 
336 out:
337 	free(buf);
338 	fclose(fp);
339 	perf_cpu_map__put(node_map);
340 	return tp;
341 }
342 
343 void numa_topology__delete(struct numa_topology *tp)
344 {
345 	u32 i;
346 
347 	for (i = 0; i < tp->nr; i++)
348 		zfree(&tp->nodes[i].cpus);
349 
350 	free(tp);
351 }
352