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