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