18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <sys/types.h>
78c2ecf20Sopenharmony_ci#include <sys/stat.h>
88c2ecf20Sopenharmony_ci#include <fcntl.h>
98c2ecf20Sopenharmony_ci#include <unistd.h>
108c2ecf20Sopenharmony_ci#include <stdio.h>
118c2ecf20Sopenharmony_ci#include <errno.h>
128c2ecf20Sopenharmony_ci#include <stdlib.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "cpupower.h"
158c2ecf20Sopenharmony_ci#include "cpupower_intern.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciunsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	int fd;
208c2ecf20Sopenharmony_ci	ssize_t numread;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	fd = open(path, O_RDONLY);
238c2ecf20Sopenharmony_ci	if (fd == -1)
248c2ecf20Sopenharmony_ci		return 0;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	numread = read(fd, buf, buflen - 1);
278c2ecf20Sopenharmony_ci	if (numread < 1) {
288c2ecf20Sopenharmony_ci		close(fd);
298c2ecf20Sopenharmony_ci		return 0;
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	buf[numread] = '\0';
338c2ecf20Sopenharmony_ci	close(fd);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return (unsigned int) numread;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * Detect whether a CPU is online
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * Returns:
428c2ecf20Sopenharmony_ci *     1 -> if CPU is online
438c2ecf20Sopenharmony_ci *     0 -> if CPU is offline
448c2ecf20Sopenharmony_ci *     negative errno values in error case
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ciint cpupower_is_cpu_online(unsigned int cpu)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	char path[SYSFS_PATH_MAX];
498c2ecf20Sopenharmony_ci	int fd;
508c2ecf20Sopenharmony_ci	ssize_t numread;
518c2ecf20Sopenharmony_ci	unsigned long long value;
528c2ecf20Sopenharmony_ci	char linebuf[MAX_LINE_LEN];
538c2ecf20Sopenharmony_ci	char *endp;
548c2ecf20Sopenharmony_ci	struct stat statbuf;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (stat(path, &statbuf) != 0)
598c2ecf20Sopenharmony_ci		return 0;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/*
628c2ecf20Sopenharmony_ci	 * kernel without CONFIG_HOTPLUG_CPU
638c2ecf20Sopenharmony_ci	 * -> cpuX directory exists, but not cpuX/online file
648c2ecf20Sopenharmony_ci	 */
658c2ecf20Sopenharmony_ci	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
668c2ecf20Sopenharmony_ci	if (stat(path, &statbuf) != 0)
678c2ecf20Sopenharmony_ci		return 1;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	fd = open(path, O_RDONLY);
708c2ecf20Sopenharmony_ci	if (fd == -1)
718c2ecf20Sopenharmony_ci		return -errno;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	numread = read(fd, linebuf, MAX_LINE_LEN - 1);
748c2ecf20Sopenharmony_ci	if (numread < 1) {
758c2ecf20Sopenharmony_ci		close(fd);
768c2ecf20Sopenharmony_ci		return -EIO;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	linebuf[numread] = '\0';
798c2ecf20Sopenharmony_ci	close(fd);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	value = strtoull(linebuf, &endp, 0);
828c2ecf20Sopenharmony_ci	if (value > 1)
838c2ecf20Sopenharmony_ci		return -EINVAL;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return value;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* returns -1 on failure, 0 on success */
898c2ecf20Sopenharmony_cistatic int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	char linebuf[MAX_LINE_LEN];
928c2ecf20Sopenharmony_ci	char *endp;
938c2ecf20Sopenharmony_ci	char path[SYSFS_PATH_MAX];
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
968c2ecf20Sopenharmony_ci			 cpu, fname);
978c2ecf20Sopenharmony_ci	if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
988c2ecf20Sopenharmony_ci		return -1;
998c2ecf20Sopenharmony_ci	*result = strtol(linebuf, &endp, 0);
1008c2ecf20Sopenharmony_ci	if (endp == linebuf || errno == ERANGE)
1018c2ecf20Sopenharmony_ci		return -1;
1028c2ecf20Sopenharmony_ci	return 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int __compare(const void *t1, const void *t2)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
1088c2ecf20Sopenharmony_ci	struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
1098c2ecf20Sopenharmony_ci	if (top1->pkg < top2->pkg)
1108c2ecf20Sopenharmony_ci		return -1;
1118c2ecf20Sopenharmony_ci	else if (top1->pkg > top2->pkg)
1128c2ecf20Sopenharmony_ci		return 1;
1138c2ecf20Sopenharmony_ci	else if (top1->core < top2->core)
1148c2ecf20Sopenharmony_ci		return -1;
1158c2ecf20Sopenharmony_ci	else if (top1->core > top2->core)
1168c2ecf20Sopenharmony_ci		return 1;
1178c2ecf20Sopenharmony_ci	else if (top1->cpu < top2->cpu)
1188c2ecf20Sopenharmony_ci		return -1;
1198c2ecf20Sopenharmony_ci	else if (top1->cpu > top2->cpu)
1208c2ecf20Sopenharmony_ci		return 1;
1218c2ecf20Sopenharmony_ci	else
1228c2ecf20Sopenharmony_ci		return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * Returns amount of cpus, negative on error, cpu_top must be
1278c2ecf20Sopenharmony_ci * passed to cpu_topology_release to free resources
1288c2ecf20Sopenharmony_ci *
1298c2ecf20Sopenharmony_ci * Array is sorted after ->pkg, ->core, then ->cpu
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_ciint get_cpu_topology(struct cpupower_topology *cpu_top)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
1368c2ecf20Sopenharmony_ci	if (cpu_top->core_info == NULL)
1378c2ecf20Sopenharmony_ci		return -ENOMEM;
1388c2ecf20Sopenharmony_ci	cpu_top->pkgs = cpu_top->cores = 0;
1398c2ecf20Sopenharmony_ci	for (cpu = 0; cpu < cpus; cpu++) {
1408c2ecf20Sopenharmony_ci		cpu_top->core_info[cpu].cpu = cpu;
1418c2ecf20Sopenharmony_ci		cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
1428c2ecf20Sopenharmony_ci		if(sysfs_topology_read_file(
1438c2ecf20Sopenharmony_ci			cpu,
1448c2ecf20Sopenharmony_ci			"physical_package_id",
1458c2ecf20Sopenharmony_ci			&(cpu_top->core_info[cpu].pkg)) < 0) {
1468c2ecf20Sopenharmony_ci			cpu_top->core_info[cpu].pkg = -1;
1478c2ecf20Sopenharmony_ci			cpu_top->core_info[cpu].core = -1;
1488c2ecf20Sopenharmony_ci			continue;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci		if(sysfs_topology_read_file(
1518c2ecf20Sopenharmony_ci			cpu,
1528c2ecf20Sopenharmony_ci			"core_id",
1538c2ecf20Sopenharmony_ci			&(cpu_top->core_info[cpu].core)) < 0) {
1548c2ecf20Sopenharmony_ci			cpu_top->core_info[cpu].pkg = -1;
1558c2ecf20Sopenharmony_ci			cpu_top->core_info[cpu].core = -1;
1568c2ecf20Sopenharmony_ci			continue;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
1618c2ecf20Sopenharmony_ci	      __compare);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Count the number of distinct pkgs values. This works
1648c2ecf20Sopenharmony_ci	   because the primary sort of the core_info struct was just
1658c2ecf20Sopenharmony_ci	   done by pkg value. */
1668c2ecf20Sopenharmony_ci	last_pkg = cpu_top->core_info[0].pkg;
1678c2ecf20Sopenharmony_ci	for(cpu = 1; cpu < cpus; cpu++) {
1688c2ecf20Sopenharmony_ci		if (cpu_top->core_info[cpu].pkg != last_pkg &&
1698c2ecf20Sopenharmony_ci				cpu_top->core_info[cpu].pkg != -1) {
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci			last_pkg = cpu_top->core_info[cpu].pkg;
1728c2ecf20Sopenharmony_ci			cpu_top->pkgs++;
1738c2ecf20Sopenharmony_ci		}
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	if (!(cpu_top->core_info[0].pkg == -1))
1768c2ecf20Sopenharmony_ci		cpu_top->pkgs++;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* Intel's cores count is not consecutively numbered, there may
1798c2ecf20Sopenharmony_ci	 * be a core_id of 3, but none of 2. Assume there always is 0
1808c2ecf20Sopenharmony_ci	 * Get amount of cores by counting duplicates in a package
1818c2ecf20Sopenharmony_ci	for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) {
1828c2ecf20Sopenharmony_ci		if (cpu_top->core_info[cpu].core == 0)
1838c2ecf20Sopenharmony_ci	cpu_top->cores++;
1848c2ecf20Sopenharmony_ci	*/
1858c2ecf20Sopenharmony_ci	return cpus;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_civoid cpu_topology_release(struct cpupower_topology cpu_top)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	free(cpu_top.core_info);
1918c2ecf20Sopenharmony_ci}
192