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