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