cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cputopo.c (7992B)


      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
     34static 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
     75try_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
    106try_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;
    134done:
    135	if (fp)
    136		fclose(fp);
    137	free(buf);
    138	return ret;
    139}
    140
    141void 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
    160static bool has_die_topology(void)
    161{
    162	char filename[MAXPATHLEN];
    163	struct utsname uts;
    164
    165	if (uname(&uts) < 0)
    166		return false;
    167
    168	if (strncmp(uts.machine, "x86_64", 6) &&
    169	    strncmp(uts.machine, "s390x", 5))
    170		return false;
    171
    172	scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT,
    173		  sysfs__mountpoint(), 0);
    174	if (access(filename, F_OK) == -1)
    175		return false;
    176
    177	return true;
    178}
    179
    180struct cpu_topology *cpu_topology__new(void)
    181{
    182	struct cpu_topology *tp = NULL;
    183	void *addr;
    184	u32 nr, i, nr_addr;
    185	size_t sz;
    186	long ncpus;
    187	int ret = -1;
    188	struct perf_cpu_map *map;
    189	bool has_die = has_die_topology();
    190
    191	ncpus = cpu__max_present_cpu().cpu;
    192
    193	/* build online CPU map */
    194	map = perf_cpu_map__new(NULL);
    195	if (map == NULL) {
    196		pr_debug("failed to get system cpumap\n");
    197		return NULL;
    198	}
    199
    200	nr = (u32)(ncpus & UINT_MAX);
    201
    202	sz = nr * sizeof(char *);
    203	if (has_die)
    204		nr_addr = 3;
    205	else
    206		nr_addr = 2;
    207	addr = calloc(1, sizeof(*tp) + nr_addr * sz);
    208	if (!addr)
    209		goto out_free;
    210
    211	tp = addr;
    212	addr += sizeof(*tp);
    213	tp->package_cpus_list = addr;
    214	addr += sz;
    215	if (has_die) {
    216		tp->die_cpus_list = addr;
    217		addr += sz;
    218	}
    219	tp->core_cpus_list = addr;
    220
    221	for (i = 0; i < nr; i++) {
    222		if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i }))
    223			continue;
    224
    225		ret = build_cpu_topology(tp, i);
    226		if (ret < 0)
    227			break;
    228	}
    229
    230out_free:
    231	perf_cpu_map__put(map);
    232	if (ret) {
    233		cpu_topology__delete(tp);
    234		tp = NULL;
    235	}
    236	return tp;
    237}
    238
    239static int load_numa_node(struct numa_topology_node *node, int nr)
    240{
    241	char str[MAXPATHLEN];
    242	char field[32];
    243	char *buf = NULL, *p;
    244	size_t len = 0;
    245	int ret = -1;
    246	FILE *fp;
    247	u64 mem;
    248
    249	node->node = (u32) nr;
    250
    251	scnprintf(str, MAXPATHLEN, NODE_MEMINFO_FMT,
    252		  sysfs__mountpoint(), nr);
    253	fp = fopen(str, "r");
    254	if (!fp)
    255		return -1;
    256
    257	while (getline(&buf, &len, fp) > 0) {
    258		/* skip over invalid lines */
    259		if (!strchr(buf, ':'))
    260			continue;
    261		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2)
    262			goto err;
    263		if (!strcmp(field, "MemTotal:"))
    264			node->mem_total = mem;
    265		if (!strcmp(field, "MemFree:"))
    266			node->mem_free = mem;
    267		if (node->mem_total && node->mem_free)
    268			break;
    269	}
    270
    271	fclose(fp);
    272	fp = NULL;
    273
    274	scnprintf(str, MAXPATHLEN, NODE_CPULIST_FMT,
    275		  sysfs__mountpoint(), nr);
    276
    277	fp = fopen(str, "r");
    278	if (!fp)
    279		return -1;
    280
    281	if (getline(&buf, &len, fp) <= 0)
    282		goto err;
    283
    284	p = strchr(buf, '\n');
    285	if (p)
    286		*p = '\0';
    287
    288	node->cpus = buf;
    289	fclose(fp);
    290	return 0;
    291
    292err:
    293	free(buf);
    294	if (fp)
    295		fclose(fp);
    296	return ret;
    297}
    298
    299struct numa_topology *numa_topology__new(void)
    300{
    301	struct perf_cpu_map *node_map = NULL;
    302	struct numa_topology *tp = NULL;
    303	char path[MAXPATHLEN];
    304	char *buf = NULL;
    305	size_t len = 0;
    306	u32 nr, i;
    307	FILE *fp;
    308	char *c;
    309
    310	scnprintf(path, MAXPATHLEN, NODE_ONLINE_FMT,
    311		  sysfs__mountpoint());
    312
    313	fp = fopen(path, "r");
    314	if (!fp)
    315		return NULL;
    316
    317	if (getline(&buf, &len, fp) <= 0)
    318		goto out;
    319
    320	c = strchr(buf, '\n');
    321	if (c)
    322		*c = '\0';
    323
    324	node_map = perf_cpu_map__new(buf);
    325	if (!node_map)
    326		goto out;
    327
    328	nr = (u32) perf_cpu_map__nr(node_map);
    329
    330	tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr);
    331	if (!tp)
    332		goto out;
    333
    334	tp->nr = nr;
    335
    336	for (i = 0; i < nr; i++) {
    337		if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) {
    338			numa_topology__delete(tp);
    339			tp = NULL;
    340			break;
    341		}
    342	}
    343
    344out:
    345	free(buf);
    346	fclose(fp);
    347	perf_cpu_map__put(node_map);
    348	return tp;
    349}
    350
    351void numa_topology__delete(struct numa_topology *tp)
    352{
    353	u32 i;
    354
    355	for (i = 0; i < tp->nr; i++)
    356		zfree(&tp->nodes[i].cpus);
    357
    358	free(tp);
    359}
    360
    361static int load_hybrid_node(struct hybrid_topology_node *node,
    362			    struct perf_pmu *pmu)
    363{
    364	const char *sysfs;
    365	char path[PATH_MAX];
    366	char *buf = NULL, *p;
    367	FILE *fp;
    368	size_t len = 0;
    369
    370	node->pmu_name = strdup(pmu->name);
    371	if (!node->pmu_name)
    372		return -1;
    373
    374	sysfs = sysfs__mountpoint();
    375	if (!sysfs)
    376		goto err;
    377
    378	snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
    379	fp = fopen(path, "r");
    380	if (!fp)
    381		goto err;
    382
    383	if (getline(&buf, &len, fp) <= 0) {
    384		fclose(fp);
    385		goto err;
    386	}
    387
    388	p = strchr(buf, '\n');
    389	if (p)
    390		*p = '\0';
    391
    392	fclose(fp);
    393	node->cpus = buf;
    394	return 0;
    395
    396err:
    397	zfree(&node->pmu_name);
    398	free(buf);
    399	return -1;
    400}
    401
    402struct hybrid_topology *hybrid_topology__new(void)
    403{
    404	struct perf_pmu *pmu;
    405	struct hybrid_topology *tp = NULL;
    406	u32 nr, i = 0;
    407
    408	nr = perf_pmu__hybrid_pmu_num();
    409	if (nr == 0)
    410		return NULL;
    411
    412	tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0]) * nr);
    413	if (!tp)
    414		return NULL;
    415
    416	tp->nr = nr;
    417	perf_pmu__for_each_hybrid_pmu(pmu) {
    418		if (load_hybrid_node(&tp->nodes[i], pmu)) {
    419			hybrid_topology__delete(tp);
    420			return NULL;
    421		}
    422		i++;
    423	}
    424
    425	return tp;
    426}
    427
    428void hybrid_topology__delete(struct hybrid_topology *tp)
    429{
    430	u32 i;
    431
    432	for (i = 0; i < tp->nr; i++) {
    433		zfree(&tp->nodes[i].pmu_name);
    434		zfree(&tp->nodes[i].cpus);
    435	}
    436
    437	free(tp);
    438}