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