]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/perf/util/cputopo.c
Merge tag 'fuse-update-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
[mirror_ubuntu-jammy-kernel.git] / tools / perf / util / cputopo.c
CommitLineData
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
31static 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
68try_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 99try_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;
127done:
128 if (fp)
129 fclose(fp);
130 free(buf);
131 return ret;
132}
133
134void 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
155static 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
174struct 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
224out_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
233static 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
286err:
287 free(buf);
288 if (fp)
289 fclose(fp);
290 return ret;
291}
292
293struct 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
338out:
339 free(buf);
340 fclose(fp);
38f01d8d 341 perf_cpu_map__put(node_map);
48e6c5ac
JO
342 return tp;
343}
344
345void 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}