brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · 8bbeb2d Raw
507 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 35static 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 76try_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 107try_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;135done:136	if (fp)137		fclose(fp);138	free(buf);139	return ret;140}141 142void 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 161bool 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 is168		 * enabled.169		 */170		if (strchr(cpu_list, ',') || strchr(cpu_list, '-'))171			return true;172	}173	return false;174}175 176bool 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 so183	 * 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 then204				 * all subsequent CPUs in the core must be user205				 * requested too. If the first CPU isn't user206				 * requested then none of the others must be207				 * 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 222static 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 242const 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 256struct 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	else282		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 306out_free:307	perf_cpu_map__put(map);308	if (ret) {309		cpu_topology__delete(tp);310		tp = NULL;311	}312	return tp;313}314 315static 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 368err:369	free(buf);370	if (fp)371		fclose(fp);372	return ret;373}374 375struct 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 420out:421	free(buf);422	fclose(fp);423	perf_cpu_map__put(node_map);424	return tp;425}426 427void 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 437static 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 465err:466	zfree(&node->pmu_name);467	free(buf);468	return -1;469}470 471struct 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 496void 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