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