~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/util/cputopo.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 #include "pmu.h"
 16 #include "pmus.h"
 17 
 18 #define PACKAGE_CPUS_FMT \
 19         "%s/devices/system/cpu/cpu%d/topology/package_cpus_list"
 20 #define PACKAGE_CPUS_FMT_OLD \
 21         "%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
 22 #define DIE_CPUS_FMT \
 23         "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
 24 #define CORE_CPUS_FMT \
 25         "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
 26 #define CORE_CPUS_FMT_OLD \
 27         "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
 28 #define NODE_ONLINE_FMT \
 29         "%s/devices/system/node/online"
 30 #define NODE_MEMINFO_FMT \
 31         "%s/devices/system/node/node%d/meminfo"
 32 #define NODE_CPULIST_FMT \
 33         "%s/devices/system/node/node%d/cpulist"
 34 
 35 static int build_cpu_topology(struct cpu_topology *tp, int cpu)
 36 {
 37         FILE *fp;
 38         char filename[MAXPATHLEN];
 39         char *buf = NULL, *p;
 40         size_t len = 0;
 41         ssize_t sret;
 42         u32 i = 0;
 43         int ret = -1;
 44 
 45         scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT,
 46                   sysfs__mountpoint(), cpu);
 47         if (access(filename, F_OK) == -1) {
 48                 scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT_OLD,
 49                         sysfs__mountpoint(), cpu);
 50         }
 51         fp = fopen(filename, "r");
 52         if (!fp)
 53                 goto try_dies;
 54 
 55         sret = getline(&buf, &len, fp);
 56         fclose(fp);
 57         if (sret <= 0)
 58                 goto try_dies;
 59 
 60         p = strchr(buf, '\n');
 61         if (p)
 62                 *p = '\0';
 63 
 64         for (i = 0; i < tp->package_cpus_lists; i++) {
 65                 if (!strcmp(buf, tp->package_cpus_list[i]))
 66                         break;
 67         }
 68         if (i == tp->package_cpus_lists) {
 69                 tp->package_cpus_list[i] = buf;
 70                 tp->package_cpus_lists++;
 71                 buf = NULL;
 72                 len = 0;
 73         }
 74         ret = 0;
 75 
 76 try_dies:
 77         if (!tp->die_cpus_list)
 78                 goto try_threads;
 79 
 80         scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
 81                   sysfs__mountpoint(), cpu);
 82         fp = fopen(filename, "r");
 83         if (!fp)
 84                 goto try_threads;
 85 
 86         sret = getline(&buf, &len, fp);
 87         fclose(fp);
 88         if (sret <= 0)
 89                 goto try_threads;
 90 
 91         p = strchr(buf, '\n');
 92         if (p)
 93                 *p = '\0';
 94 
 95         for (i = 0; i < tp->die_cpus_lists; i++) {
 96                 if (!strcmp(buf, tp->die_cpus_list[i]))
 97                         break;
 98         }
 99         if (i == tp->die_cpus_lists) {
100                 tp->die_cpus_list[i] = buf;
101                 tp->die_cpus_lists++;
102                 buf = NULL;
103                 len = 0;
104         }
105         ret = 0;
106 
107 try_threads:
108         scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT,
109                   sysfs__mountpoint(), cpu);
110         if (access(filename, F_OK) == -1) {
111                 scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT_OLD,
112                           sysfs__mountpoint(), cpu);
113         }
114         fp = fopen(filename, "r");
115         if (!fp)
116                 goto done;
117 
118         if (getline(&buf, &len, fp) <= 0)
119                 goto done;
120 
121         p = strchr(buf, '\n');
122         if (p)
123                 *p = '\0';
124 
125         for (i = 0; i < tp->core_cpus_lists; i++) {
126                 if (!strcmp(buf, tp->core_cpus_list[i]))
127                         break;
128         }
129         if (i == tp->core_cpus_lists) {
130                 tp->core_cpus_list[i] = buf;
131                 tp->core_cpus_lists++;
132                 buf = NULL;
133         }
134         ret = 0;
135 done:
136         if (fp)
137                 fclose(fp);
138         free(buf);
139         return ret;
140 }
141 
142 void cpu_topology__delete(struct cpu_topology *tp)
143 {
144         u32 i;
145 
146         if (!tp)
147                 return;
148 
149         for (i = 0 ; i < tp->package_cpus_lists; i++)
150                 zfree(&tp->package_cpus_list[i]);
151 
152         for (i = 0 ; i < tp->die_cpus_lists; i++)
153                 zfree(&tp->die_cpus_list[i]);
154 
155         for (i = 0 ; i < tp->core_cpus_lists; i++)
156                 zfree(&tp->core_cpus_list[i]);
157 
158         free(tp);
159 }
160 
161 bool cpu_topology__smt_on(const struct cpu_topology *topology)
162 {
163         for (u32 i = 0; i < topology->core_cpus_lists; i++) {
164                 const char *cpu_list = topology->core_cpus_list[i];
165 
166                 /*
167                  * If there is a need to separate siblings in a core then SMT is
168                  * enabled.
169                  */
170                 if (strchr(cpu_list, ',') || strchr(cpu_list, '-'))
171                         return true;
172         }
173         return false;
174 }
175 
176 bool cpu_topology__core_wide(const struct cpu_topology *topology,
177                              const char *user_requested_cpu_list)
178 {
179         struct perf_cpu_map *user_requested_cpus;
180 
181         /*
182          * If user_requested_cpu_list is empty then all CPUs are recorded and so
183          * core_wide is true.
184          */
185         if (!user_requested_cpu_list)
186                 return true;
187 
188         user_requested_cpus = perf_cpu_map__new(user_requested_cpu_list);
189         /* Check that every user requested CPU is the complete set of SMT threads on a core. */
190         for (u32 i = 0; i < topology->core_cpus_lists; i++) {
191                 const char *core_cpu_list = topology->core_cpus_list[i];
192                 struct perf_cpu_map *core_cpus = perf_cpu_map__new(core_cpu_list);
193                 struct perf_cpu cpu;
194                 int idx;
195                 bool has_first, first = true;
196 
197                 perf_cpu_map__for_each_cpu(cpu, idx, core_cpus) {
198                         if (first) {
199                                 has_first = perf_cpu_map__has(user_requested_cpus, cpu);
200                                 first = false;
201                         } else {
202                                 /*
203                                  * If the first core CPU is user requested then
204                                  * all subsequent CPUs in the core must be user
205                                  * requested too. If the first CPU isn't user
206                                  * requested then none of the others must be
207                                  * too.
208                                  */
209                                 if (perf_cpu_map__has(user_requested_cpus, cpu) != has_first) {
210                                         perf_cpu_map__put(core_cpus);
211                                         perf_cpu_map__put(user_requested_cpus);
212                                         return false;
213                                 }
214                         }
215                 }
216                 perf_cpu_map__put(core_cpus);
217         }
218         perf_cpu_map__put(user_requested_cpus);
219         return true;
220 }
221 
222 static bool has_die_topology(void)
223 {
224         char filename[MAXPATHLEN];
225         struct utsname uts;
226 
227         if (uname(&uts) < 0)
228                 return false;
229 
230         if (strncmp(uts.machine, "x86_64", 6) &&
231             strncmp(uts.machine, "s390x", 5))
232                 return false;
233 
234         scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
235                   sysfs__mountpoint(), 0);
236         if (access(filename, F_OK) == -1)
237                 return false;
238 
239         return true;
240 }
241 
242 const struct cpu_topology *online_topology(void)
243 {
244         static const struct cpu_topology *topology;
245 
246         if (!topology) {
247                 topology = cpu_topology__new();
248                 if (!topology) {
249                         pr_err("Error creating CPU topology");
250                         abort();
251                 }
252         }
253         return topology;
254 }
255 
256 struct cpu_topology *cpu_topology__new(void)
257 {
258         struct cpu_topology *tp = NULL;
259         void *addr;
260         u32 nr, i, nr_addr;
261         size_t sz;
262         long ncpus;
263         int ret = -1;
264         struct perf_cpu_map *map;
265         bool has_die = has_die_topology();
266 
267         ncpus = cpu__max_present_cpu().cpu;
268 
269         /* build online CPU map */
270         map = perf_cpu_map__new_online_cpus();
271         if (map == NULL) {
272                 pr_debug("failed to get system cpumap\n");
273                 return NULL;
274         }
275 
276         nr = (u32)(ncpus & UINT_MAX);
277 
278         sz = nr * sizeof(char *);
279         if (has_die)
280                 nr_addr = 3;
281         else
282                 nr_addr = 2;
283         addr = calloc(1, sizeof(*tp) + nr_addr * sz);
284         if (!addr)
285                 goto out_free;
286 
287         tp = addr;
288         addr += sizeof(*tp);
289         tp->package_cpus_list = addr;
290         addr += sz;
291         if (has_die) {
292                 tp->die_cpus_list = addr;
293                 addr += sz;
294         }
295         tp->core_cpus_list = addr;
296 
297         for (i = 0; i < nr; i++) {
298                 if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))
299                         continue;
300 
301                 ret = build_cpu_topology(tp, i);
302                 if (ret < 0)
303                         break;
304         }
305 
306 out_free:
307         perf_cpu_map__put(map);
308         if (ret) {
309                 cpu_topology__delete(tp);
310                 tp = NULL;
311         }
312         return tp;
313 }
314 
315 static int load_numa_node(struct numa_topology_node *node, int nr)
316 {
317         char str[MAXPATHLEN];
318         char field[32];
319         char *buf = NULL, *p;
320         size_t len = 0;
321         int ret = -1;
322         FILE *fp;
323         u64 mem;
324 
325         node->node = (u32) nr;
326 
327         scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
328                   sysfs__mountpoint(), nr);
329         fp = fopen(str, "r");
330         if (!fp)
331                 return -1;
332 
333         while (getline(&buf, &len, fp) > 0) {
334                 /* skip over invalid lines */
335                 if (!strchr(buf, ':'))
336                         continue;
337                 if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
338                         goto err;
339                 if (!strcmp(field, "MemTotal:"))
340                         node->mem_total = mem;
341                 if (!strcmp(field, "MemFree:"))
342                         node->mem_free = mem;
343                 if (node->mem_total && node->mem_free)
344                         break;
345         }
346 
347         fclose(fp);
348         fp = NULL;
349 
350         scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
351                   sysfs__mountpoint(), nr);
352 
353         fp = fopen(str, "r");
354         if (!fp)
355                 return -1;
356 
357         if (getline(&buf, &len, fp) <= 0)
358                 goto err;
359 
360         p = strchr(buf, '\n');
361         if (p)
362                 *p = '\0';
363 
364         node->cpus = buf;
365         fclose(fp);
366         return 0;
367 
368 err:
369         free(buf);
370         if (fp)
371                 fclose(fp);
372         return ret;
373 }
374 
375 struct numa_topology *numa_topology__new(void)
376 {
377         struct perf_cpu_map *node_map = NULL;
378         struct numa_topology *tp = NULL;
379         char path[MAXPATHLEN];
380         char *buf = NULL;
381         size_t len = 0;
382         u32 nr, i;
383         FILE *fp;
384         char *c;
385 
386         scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
387                   sysfs__mountpoint());
388 
389         fp = fopen(path, "r");
390         if (!fp)
391                 return NULL;
392 
393         if (getline(&buf, &len, fp) <= 0)
394                 goto out;
395 
396         c = strchr(buf, '\n');
397         if (c)
398                 *c = '\0';
399 
400         node_map = perf_cpu_map__new(buf);
401         if (!node_map)
402                 goto out;
403 
404         nr = (u32) perf_cpu_map__nr(node_map);
405 
406         tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
407         if (!tp)
408                 goto out;
409 
410         tp->nr = nr;
411 
412         for (i = 0; i < nr; i++) {
413                 if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) {
414                         numa_topology__delete(tp);
415                         tp = NULL;
416                         break;
417                 }
418         }
419 
420 out:
421         free(buf);
422         fclose(fp);
423         perf_cpu_map__put(node_map);
424         return tp;
425 }
426 
427 void numa_topology__delete(struct numa_topology *tp)
428 {
429         u32 i;
430 
431         for (i = 0; i < tp->nr; i++)
432                 zfree(&tp->nodes[i].cpus);
433 
434         free(tp);
435 }
436 
437 static int load_hybrid_node(struct hybrid_topology_node *node,
438                             struct perf_pmu *pmu)
439 {
440         char *buf = NULL, *p;
441         FILE *fp;
442         size_t len = 0;
443 
444         node->pmu_name = strdup(pmu->name);
445         if (!node->pmu_name)
446                 return -1;
447 
448         fp = perf_pmu__open_file(pmu, "cpus");
449         if (!fp)
450                 goto err;
451 
452         if (getline(&buf, &len, fp) <= 0) {
453                 fclose(fp);
454                 goto err;
455         }
456 
457         p = strchr(buf, '\n');
458         if (p)
459                 *p = '\0';
460 
461         fclose(fp);
462         node->cpus = buf;
463         return 0;
464 
465 err:
466         zfree(&node->pmu_name);
467         free(buf);
468         return -1;
469 }
470 
471 struct hybrid_topology *hybrid_topology__new(void)
472 {
473         struct perf_pmu *pmu = NULL;
474         struct hybrid_topology *tp = NULL;
475         int nr = perf_pmus__num_core_pmus(), i = 0;
476 
477         if (nr <= 1)
478                 return NULL;
479 
480         tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
481         if (!tp)
482                 return NULL;
483 
484         tp->nr = nr;
485         while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
486                 if (load_hybrid_node(&tp->nodes[i], pmu)) {
487                         hybrid_topology__delete(tp);
488                         return NULL;
489                 }
490                 i++;
491         }
492 
493         return tp;
494 }
495 
496 void hybrid_topology__delete(struct hybrid_topology *tp)
497 {
498         u32 i;
499 
500         for (i = 0; i < tp->nr; i++) {
501                 zfree(&tp->nodes[i].pmu_name);
502                 zfree(&tp->nodes[i].cpus);
503         }
504 
505         free(tp);
506 }
507 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php