18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <sys/param.h>
38c2ecf20Sopenharmony_ci#include <sys/utsname.h>
48c2ecf20Sopenharmony_ci#include <inttypes.h>
58c2ecf20Sopenharmony_ci#include <stdlib.h>
68c2ecf20Sopenharmony_ci#include <string.h>
78c2ecf20Sopenharmony_ci#include <api/fs/fs.h>
88c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
98c2ecf20Sopenharmony_ci#include <perf/cpumap.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "cputopo.h"
128c2ecf20Sopenharmony_ci#include "cpumap.h"
138c2ecf20Sopenharmony_ci#include "debug.h"
148c2ecf20Sopenharmony_ci#include "env.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define CORE_SIB_FMT \
178c2ecf20Sopenharmony_ci	"%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
188c2ecf20Sopenharmony_ci#define DIE_SIB_FMT \
198c2ecf20Sopenharmony_ci	"%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
208c2ecf20Sopenharmony_ci#define THRD_SIB_FMT \
218c2ecf20Sopenharmony_ci	"%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
228c2ecf20Sopenharmony_ci#define THRD_SIB_FMT_NEW \
238c2ecf20Sopenharmony_ci	"%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
248c2ecf20Sopenharmony_ci#define NODE_ONLINE_FMT \
258c2ecf20Sopenharmony_ci	"%s/devices/system/node/online"
268c2ecf20Sopenharmony_ci#define NODE_MEMINFO_FMT \
278c2ecf20Sopenharmony_ci	"%s/devices/system/node/node%d/meminfo"
288c2ecf20Sopenharmony_ci#define NODE_CPULIST_FMT \
298c2ecf20Sopenharmony_ci	"%s/devices/system/node/node%d/cpulist"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int build_cpu_topology(struct cpu_topology *tp, int cpu)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	FILE *fp;
348c2ecf20Sopenharmony_ci	char filename[MAXPATHLEN];
358c2ecf20Sopenharmony_ci	char *buf = NULL, *p;
368c2ecf20Sopenharmony_ci	size_t len = 0;
378c2ecf20Sopenharmony_ci	ssize_t sret;
388c2ecf20Sopenharmony_ci	u32 i = 0;
398c2ecf20Sopenharmony_ci	int ret = -1;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT,
428c2ecf20Sopenharmony_ci		  sysfs__mountpoint(), cpu);
438c2ecf20Sopenharmony_ci	fp = fopen(filename, "r");
448c2ecf20Sopenharmony_ci	if (!fp)
458c2ecf20Sopenharmony_ci		goto try_dies;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	sret = getline(&buf, &len, fp);
488c2ecf20Sopenharmony_ci	fclose(fp);
498c2ecf20Sopenharmony_ci	if (sret <= 0)
508c2ecf20Sopenharmony_ci		goto try_dies;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	p = strchr(buf, '\n');
538c2ecf20Sopenharmony_ci	if (p)
548c2ecf20Sopenharmony_ci		*p = '\0';
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	for (i = 0; i < tp->core_sib; i++) {
578c2ecf20Sopenharmony_ci		if (!strcmp(buf, tp->core_siblings[i]))
588c2ecf20Sopenharmony_ci			break;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	if (i == tp->core_sib) {
618c2ecf20Sopenharmony_ci		tp->core_siblings[i] = buf;
628c2ecf20Sopenharmony_ci		tp->core_sib++;
638c2ecf20Sopenharmony_ci		buf = NULL;
648c2ecf20Sopenharmony_ci		len = 0;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci	ret = 0;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_citry_dies:
698c2ecf20Sopenharmony_ci	if (!tp->die_siblings)
708c2ecf20Sopenharmony_ci		goto try_threads;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
738c2ecf20Sopenharmony_ci		  sysfs__mountpoint(), cpu);
748c2ecf20Sopenharmony_ci	fp = fopen(filename, "r");
758c2ecf20Sopenharmony_ci	if (!fp)
768c2ecf20Sopenharmony_ci		goto try_threads;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	sret = getline(&buf, &len, fp);
798c2ecf20Sopenharmony_ci	fclose(fp);
808c2ecf20Sopenharmony_ci	if (sret <= 0)
818c2ecf20Sopenharmony_ci		goto try_threads;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	p = strchr(buf, '\n');
848c2ecf20Sopenharmony_ci	if (p)
858c2ecf20Sopenharmony_ci		*p = '\0';
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	for (i = 0; i < tp->die_sib; i++) {
888c2ecf20Sopenharmony_ci		if (!strcmp(buf, tp->die_siblings[i]))
898c2ecf20Sopenharmony_ci			break;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci	if (i == tp->die_sib) {
928c2ecf20Sopenharmony_ci		tp->die_siblings[i] = buf;
938c2ecf20Sopenharmony_ci		tp->die_sib++;
948c2ecf20Sopenharmony_ci		buf = NULL;
958c2ecf20Sopenharmony_ci		len = 0;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	ret = 0;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_citry_threads:
1008c2ecf20Sopenharmony_ci	scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
1018c2ecf20Sopenharmony_ci		  sysfs__mountpoint(), cpu);
1028c2ecf20Sopenharmony_ci	if (access(filename, F_OK) == -1) {
1038c2ecf20Sopenharmony_ci		scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
1048c2ecf20Sopenharmony_ci			  sysfs__mountpoint(), cpu);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	fp = fopen(filename, "r");
1078c2ecf20Sopenharmony_ci	if (!fp)
1088c2ecf20Sopenharmony_ci		goto done;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (getline(&buf, &len, fp) <= 0)
1118c2ecf20Sopenharmony_ci		goto done;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	p = strchr(buf, '\n');
1148c2ecf20Sopenharmony_ci	if (p)
1158c2ecf20Sopenharmony_ci		*p = '\0';
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	for (i = 0; i < tp->thread_sib; i++) {
1188c2ecf20Sopenharmony_ci		if (!strcmp(buf, tp->thread_siblings[i]))
1198c2ecf20Sopenharmony_ci			break;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci	if (i == tp->thread_sib) {
1228c2ecf20Sopenharmony_ci		tp->thread_siblings[i] = buf;
1238c2ecf20Sopenharmony_ci		tp->thread_sib++;
1248c2ecf20Sopenharmony_ci		buf = NULL;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	ret = 0;
1278c2ecf20Sopenharmony_cidone:
1288c2ecf20Sopenharmony_ci	if (fp)
1298c2ecf20Sopenharmony_ci		fclose(fp);
1308c2ecf20Sopenharmony_ci	free(buf);
1318c2ecf20Sopenharmony_ci	return ret;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_civoid cpu_topology__delete(struct cpu_topology *tp)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	u32 i;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (!tp)
1398c2ecf20Sopenharmony_ci		return;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	for (i = 0 ; i < tp->core_sib; i++)
1428c2ecf20Sopenharmony_ci		zfree(&tp->core_siblings[i]);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (tp->die_sib) {
1458c2ecf20Sopenharmony_ci		for (i = 0 ; i < tp->die_sib; i++)
1468c2ecf20Sopenharmony_ci			zfree(&tp->die_siblings[i]);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	for (i = 0 ; i < tp->thread_sib; i++)
1508c2ecf20Sopenharmony_ci		zfree(&tp->thread_siblings[i]);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	free(tp);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic bool has_die_topology(void)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	char filename[MAXPATHLEN];
1588c2ecf20Sopenharmony_ci	struct utsname uts;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (uname(&uts) < 0)
1618c2ecf20Sopenharmony_ci		return false;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (strncmp(uts.machine, "x86_64", 6))
1648c2ecf20Sopenharmony_ci		return false;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
1678c2ecf20Sopenharmony_ci		  sysfs__mountpoint(), 0);
1688c2ecf20Sopenharmony_ci	if (access(filename, F_OK) == -1)
1698c2ecf20Sopenharmony_ci		return false;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return true;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistruct cpu_topology *cpu_topology__new(void)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct cpu_topology *tp = NULL;
1778c2ecf20Sopenharmony_ci	void *addr;
1788c2ecf20Sopenharmony_ci	u32 nr, i, nr_addr;
1798c2ecf20Sopenharmony_ci	size_t sz;
1808c2ecf20Sopenharmony_ci	long ncpus;
1818c2ecf20Sopenharmony_ci	int ret = -1;
1828c2ecf20Sopenharmony_ci	struct perf_cpu_map *map;
1838c2ecf20Sopenharmony_ci	bool has_die = has_die_topology();
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ncpus = cpu__max_present_cpu();
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* build online CPU map */
1888c2ecf20Sopenharmony_ci	map = perf_cpu_map__new(NULL);
1898c2ecf20Sopenharmony_ci	if (map == NULL) {
1908c2ecf20Sopenharmony_ci		pr_debug("failed to get system cpumap\n");
1918c2ecf20Sopenharmony_ci		return NULL;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	nr = (u32)(ncpus & UINT_MAX);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	sz = nr * sizeof(char *);
1978c2ecf20Sopenharmony_ci	if (has_die)
1988c2ecf20Sopenharmony_ci		nr_addr = 3;
1998c2ecf20Sopenharmony_ci	else
2008c2ecf20Sopenharmony_ci		nr_addr = 2;
2018c2ecf20Sopenharmony_ci	addr = calloc(1, sizeof(*tp) + nr_addr * sz);
2028c2ecf20Sopenharmony_ci	if (!addr)
2038c2ecf20Sopenharmony_ci		goto out_free;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	tp = addr;
2068c2ecf20Sopenharmony_ci	addr += sizeof(*tp);
2078c2ecf20Sopenharmony_ci	tp->core_siblings = addr;
2088c2ecf20Sopenharmony_ci	addr += sz;
2098c2ecf20Sopenharmony_ci	if (has_die) {
2108c2ecf20Sopenharmony_ci		tp->die_siblings = addr;
2118c2ecf20Sopenharmony_ci		addr += sz;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	tp->thread_siblings = addr;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i++) {
2168c2ecf20Sopenharmony_ci		if (!cpu_map__has(map, i))
2178c2ecf20Sopenharmony_ci			continue;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		ret = build_cpu_topology(tp, i);
2208c2ecf20Sopenharmony_ci		if (ret < 0)
2218c2ecf20Sopenharmony_ci			break;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ciout_free:
2258c2ecf20Sopenharmony_ci	perf_cpu_map__put(map);
2268c2ecf20Sopenharmony_ci	if (ret) {
2278c2ecf20Sopenharmony_ci		cpu_topology__delete(tp);
2288c2ecf20Sopenharmony_ci		tp = NULL;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci	return tp;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int load_numa_node(struct numa_topology_node *node, int nr)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	char str[MAXPATHLEN];
2368c2ecf20Sopenharmony_ci	char field[32];
2378c2ecf20Sopenharmony_ci	char *buf = NULL, *p;
2388c2ecf20Sopenharmony_ci	size_t len = 0;
2398c2ecf20Sopenharmony_ci	int ret = -1;
2408c2ecf20Sopenharmony_ci	FILE *fp;
2418c2ecf20Sopenharmony_ci	u64 mem;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	node->node = (u32) nr;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
2468c2ecf20Sopenharmony_ci		  sysfs__mountpoint(), nr);
2478c2ecf20Sopenharmony_ci	fp = fopen(str, "r");
2488c2ecf20Sopenharmony_ci	if (!fp)
2498c2ecf20Sopenharmony_ci		return -1;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	while (getline(&buf, &len, fp) > 0) {
2528c2ecf20Sopenharmony_ci		/* skip over invalid lines */
2538c2ecf20Sopenharmony_ci		if (!strchr(buf, ':'))
2548c2ecf20Sopenharmony_ci			continue;
2558c2ecf20Sopenharmony_ci		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
2568c2ecf20Sopenharmony_ci			goto err;
2578c2ecf20Sopenharmony_ci		if (!strcmp(field, "MemTotal:"))
2588c2ecf20Sopenharmony_ci			node->mem_total = mem;
2598c2ecf20Sopenharmony_ci		if (!strcmp(field, "MemFree:"))
2608c2ecf20Sopenharmony_ci			node->mem_free = mem;
2618c2ecf20Sopenharmony_ci		if (node->mem_total && node->mem_free)
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	fclose(fp);
2668c2ecf20Sopenharmony_ci	fp = NULL;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
2698c2ecf20Sopenharmony_ci		  sysfs__mountpoint(), nr);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	fp = fopen(str, "r");
2728c2ecf20Sopenharmony_ci	if (!fp)
2738c2ecf20Sopenharmony_ci		return -1;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (getline(&buf, &len, fp) <= 0)
2768c2ecf20Sopenharmony_ci		goto err;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	p = strchr(buf, '\n');
2798c2ecf20Sopenharmony_ci	if (p)
2808c2ecf20Sopenharmony_ci		*p = '\0';
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	node->cpus = buf;
2838c2ecf20Sopenharmony_ci	fclose(fp);
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cierr:
2878c2ecf20Sopenharmony_ci	free(buf);
2888c2ecf20Sopenharmony_ci	if (fp)
2898c2ecf20Sopenharmony_ci		fclose(fp);
2908c2ecf20Sopenharmony_ci	return ret;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistruct numa_topology *numa_topology__new(void)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct perf_cpu_map *node_map = NULL;
2968c2ecf20Sopenharmony_ci	struct numa_topology *tp = NULL;
2978c2ecf20Sopenharmony_ci	char path[MAXPATHLEN];
2988c2ecf20Sopenharmony_ci	char *buf = NULL;
2998c2ecf20Sopenharmony_ci	size_t len = 0;
3008c2ecf20Sopenharmony_ci	u32 nr, i;
3018c2ecf20Sopenharmony_ci	FILE *fp;
3028c2ecf20Sopenharmony_ci	char *c;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
3058c2ecf20Sopenharmony_ci		  sysfs__mountpoint());
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	fp = fopen(path, "r");
3088c2ecf20Sopenharmony_ci	if (!fp)
3098c2ecf20Sopenharmony_ci		return NULL;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (getline(&buf, &len, fp) <= 0)
3128c2ecf20Sopenharmony_ci		goto out;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	c = strchr(buf, '\n');
3158c2ecf20Sopenharmony_ci	if (c)
3168c2ecf20Sopenharmony_ci		*c = '\0';
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	node_map = perf_cpu_map__new(buf);
3198c2ecf20Sopenharmony_ci	if (!node_map)
3208c2ecf20Sopenharmony_ci		goto out;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	nr = (u32) node_map->nr;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
3258c2ecf20Sopenharmony_ci	if (!tp)
3268c2ecf20Sopenharmony_ci		goto out;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	tp->nr = nr;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i++) {
3318c2ecf20Sopenharmony_ci		if (load_numa_node(&tp->nodes[i], node_map->map[i])) {
3328c2ecf20Sopenharmony_ci			numa_topology__delete(tp);
3338c2ecf20Sopenharmony_ci			tp = NULL;
3348c2ecf20Sopenharmony_ci			break;
3358c2ecf20Sopenharmony_ci		}
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ciout:
3398c2ecf20Sopenharmony_ci	free(buf);
3408c2ecf20Sopenharmony_ci	fclose(fp);
3418c2ecf20Sopenharmony_ci	perf_cpu_map__put(node_map);
3428c2ecf20Sopenharmony_ci	return tp;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_civoid numa_topology__delete(struct numa_topology *tp)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	u32 i;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	for (i = 0; i < tp->nr; i++)
3508c2ecf20Sopenharmony_ci		zfree(&tp->nodes[i].cpus);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	free(tp);
3538c2ecf20Sopenharmony_ci}
354