Merge tag 'linux-watchdog-5.4-rc1' of git://www.linux-watchdog.org/linux-watchdog
[linux-2.6-microblaze.git] / tools / perf / util / cputopo.c
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 <string.h>
7 #include <api/fs/fs.h>
8 #include <linux/zalloc.h>
9 #include <perf/cpumap.h>
10
11 #include "cputopo.h"
12 #include "cpumap.h"
13 #include "debug.h"
14 #include "env.h"
15
16 #define CORE_SIB_FMT \
17         "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
18 #define DIE_SIB_FMT \
19         "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
20 #define THRD_SIB_FMT \
21         "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
22 #define THRD_SIB_FMT_NEW \
23         "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
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"
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
41         scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
42                   sysfs__mountpoint(), cpu);
43         fp = fopen(filename, "r");
44         if (!fp)
45                 goto try_dies;
46
47         sret = getline(&buf, &len, fp);
48         fclose(fp);
49         if (sret <= 0)
50                 goto try_dies;
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
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
99 try_threads:
100         scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
101                   sysfs__mountpoint(), cpu);
102         if (access(filename, F_OK) == -1) {
103                 scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
104                           sysfs__mountpoint(), cpu);
105         }
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
144         if (tp->die_sib) {
145                 for (i = 0 ; i < tp->die_sib; i++)
146                         zfree(&tp->die_siblings[i]);
147         }
148
149         for (i = 0 ; i < tp->thread_sib; i++)
150                 zfree(&tp->thread_siblings[i]);
151
152         free(tp);
153 }
154
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
174 struct cpu_topology *cpu_topology__new(void)
175 {
176         struct cpu_topology *tp = NULL;
177         void *addr;
178         u32 nr, i, nr_addr;
179         size_t sz;
180         long ncpus;
181         int ret = -1;
182         struct perf_cpu_map *map;
183         bool has_die = has_die_topology();
184
185         ncpus = cpu__max_present_cpu();
186
187         /* build online CPU map */
188         map = perf_cpu_map__new(NULL);
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 *);
197         if (has_die)
198                 nr_addr = 3;
199         else
200                 nr_addr = 2;
201         addr = calloc(1, sizeof(*tp) + nr_addr * sz);
202         if (!addr)
203                 goto out_free;
204
205         tp = addr;
206         addr += sizeof(*tp);
207         tp->core_siblings = addr;
208         addr += sz;
209         if (has_die) {
210                 tp->die_siblings = addr;
211                 addr += sz;
212         }
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:
225         perf_cpu_map__put(map);
226         if (ret) {
227                 cpu_topology__delete(tp);
228                 tp = NULL;
229         }
230         return tp;
231 }
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
245         scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
246                   sysfs__mountpoint(), nr);
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
268         scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
269                   sysfs__mountpoint(), nr);
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 {
295         struct perf_cpu_map *node_map = NULL;
296         struct numa_topology *tp = NULL;
297         char path[MAXPATHLEN];
298         char *buf = NULL;
299         size_t len = 0;
300         u32 nr, i;
301         FILE *fp;
302         char *c;
303
304         scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
305                   sysfs__mountpoint());
306
307         fp = fopen(path, "r");
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
318         node_map = perf_cpu_map__new(buf);
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);
341         perf_cpu_map__put(node_map);
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++)
350                 zfree(&tp->nodes[i].cpus);
351
352         free(tp);
353 }