162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <sys/types.h> 762306a36Sopenharmony_ci#include <sys/stat.h> 862306a36Sopenharmony_ci#include <fcntl.h> 962306a36Sopenharmony_ci#include <unistd.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <errno.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "cpupower.h" 1562306a36Sopenharmony_ci#include "cpupower_intern.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciint is_valid_path(const char *path) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci if (access(path, F_OK) == -1) 2062306a36Sopenharmony_ci return 0; 2162306a36Sopenharmony_ci return 1; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciunsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci ssize_t numread; 2762306a36Sopenharmony_ci int fd; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci fd = open(path, O_RDONLY); 3062306a36Sopenharmony_ci if (fd == -1) 3162306a36Sopenharmony_ci return 0; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci numread = read(fd, buf, buflen - 1); 3462306a36Sopenharmony_ci if (numread < 1) { 3562306a36Sopenharmony_ci close(fd); 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci buf[numread] = '\0'; 4062306a36Sopenharmony_ci close(fd); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return (unsigned int) numread; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciunsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci ssize_t numwritten; 4862306a36Sopenharmony_ci int fd; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci fd = open(path, O_WRONLY); 5162306a36Sopenharmony_ci if (fd == -1) 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci numwritten = write(fd, buf, buflen - 1); 5562306a36Sopenharmony_ci if (numwritten < 1) { 5662306a36Sopenharmony_ci perror(path); 5762306a36Sopenharmony_ci close(fd); 5862306a36Sopenharmony_ci return -1; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci close(fd); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return (unsigned int) numwritten; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Detect whether a CPU is online 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * Returns: 7062306a36Sopenharmony_ci * 1 -> if CPU is online 7162306a36Sopenharmony_ci * 0 -> if CPU is offline 7262306a36Sopenharmony_ci * negative errno values in error case 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ciint cpupower_is_cpu_online(unsigned int cpu) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 7762306a36Sopenharmony_ci int fd; 7862306a36Sopenharmony_ci ssize_t numread; 7962306a36Sopenharmony_ci unsigned long long value; 8062306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 8162306a36Sopenharmony_ci char *endp; 8262306a36Sopenharmony_ci struct stat statbuf; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (stat(path, &statbuf) != 0) 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* 9062306a36Sopenharmony_ci * kernel without CONFIG_HOTPLUG_CPU 9162306a36Sopenharmony_ci * -> cpuX directory exists, but not cpuX/online file 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); 9462306a36Sopenharmony_ci if (stat(path, &statbuf) != 0) 9562306a36Sopenharmony_ci return 1; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci fd = open(path, O_RDONLY); 9862306a36Sopenharmony_ci if (fd == -1) 9962306a36Sopenharmony_ci return -errno; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci numread = read(fd, linebuf, MAX_LINE_LEN - 1); 10262306a36Sopenharmony_ci if (numread < 1) { 10362306a36Sopenharmony_ci close(fd); 10462306a36Sopenharmony_ci return -EIO; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci linebuf[numread] = '\0'; 10762306a36Sopenharmony_ci close(fd); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci value = strtoull(linebuf, &endp, 0); 11062306a36Sopenharmony_ci if (value > 1) 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return value; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* returns -1 on failure, 0 on success */ 11762306a36Sopenharmony_cistatic int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci char linebuf[MAX_LINE_LEN]; 12062306a36Sopenharmony_ci char *endp; 12162306a36Sopenharmony_ci char path[SYSFS_PATH_MAX]; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s", 12462306a36Sopenharmony_ci cpu, fname); 12562306a36Sopenharmony_ci if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0) 12662306a36Sopenharmony_ci return -1; 12762306a36Sopenharmony_ci *result = strtol(linebuf, &endp, 0); 12862306a36Sopenharmony_ci if (endp == linebuf || errno == ERANGE) 12962306a36Sopenharmony_ci return -1; 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int __compare(const void *t1, const void *t2) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; 13662306a36Sopenharmony_ci struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2; 13762306a36Sopenharmony_ci if (top1->pkg < top2->pkg) 13862306a36Sopenharmony_ci return -1; 13962306a36Sopenharmony_ci else if (top1->pkg > top2->pkg) 14062306a36Sopenharmony_ci return 1; 14162306a36Sopenharmony_ci else if (top1->core < top2->core) 14262306a36Sopenharmony_ci return -1; 14362306a36Sopenharmony_ci else if (top1->core > top2->core) 14462306a36Sopenharmony_ci return 1; 14562306a36Sopenharmony_ci else if (top1->cpu < top2->cpu) 14662306a36Sopenharmony_ci return -1; 14762306a36Sopenharmony_ci else if (top1->cpu > top2->cpu) 14862306a36Sopenharmony_ci return 1; 14962306a36Sopenharmony_ci else 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Returns amount of cpus, negative on error, cpu_top must be 15562306a36Sopenharmony_ci * passed to cpu_topology_release to free resources 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * Array is sorted after ->pkg, ->core, then ->cpu 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ciint get_cpu_topology(struct cpupower_topology *cpu_top) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus); 16462306a36Sopenharmony_ci if (cpu_top->core_info == NULL) 16562306a36Sopenharmony_ci return -ENOMEM; 16662306a36Sopenharmony_ci cpu_top->pkgs = cpu_top->cores = 0; 16762306a36Sopenharmony_ci for (cpu = 0; cpu < cpus; cpu++) { 16862306a36Sopenharmony_ci cpu_top->core_info[cpu].cpu = cpu; 16962306a36Sopenharmony_ci cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu); 17062306a36Sopenharmony_ci if(sysfs_topology_read_file( 17162306a36Sopenharmony_ci cpu, 17262306a36Sopenharmony_ci "physical_package_id", 17362306a36Sopenharmony_ci &(cpu_top->core_info[cpu].pkg)) < 0) { 17462306a36Sopenharmony_ci cpu_top->core_info[cpu].pkg = -1; 17562306a36Sopenharmony_ci cpu_top->core_info[cpu].core = -1; 17662306a36Sopenharmony_ci continue; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci if(sysfs_topology_read_file( 17962306a36Sopenharmony_ci cpu, 18062306a36Sopenharmony_ci "core_id", 18162306a36Sopenharmony_ci &(cpu_top->core_info[cpu].core)) < 0) { 18262306a36Sopenharmony_ci cpu_top->core_info[cpu].pkg = -1; 18362306a36Sopenharmony_ci cpu_top->core_info[cpu].core = -1; 18462306a36Sopenharmony_ci continue; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), 18962306a36Sopenharmony_ci __compare); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Count the number of distinct pkgs values. This works 19262306a36Sopenharmony_ci because the primary sort of the core_info struct was just 19362306a36Sopenharmony_ci done by pkg value. */ 19462306a36Sopenharmony_ci last_pkg = cpu_top->core_info[0].pkg; 19562306a36Sopenharmony_ci for(cpu = 1; cpu < cpus; cpu++) { 19662306a36Sopenharmony_ci if (cpu_top->core_info[cpu].pkg != last_pkg && 19762306a36Sopenharmony_ci cpu_top->core_info[cpu].pkg != -1) { 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci last_pkg = cpu_top->core_info[cpu].pkg; 20062306a36Sopenharmony_ci cpu_top->pkgs++; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci if (!(cpu_top->core_info[0].pkg == -1)) 20462306a36Sopenharmony_ci cpu_top->pkgs++; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Intel's cores count is not consecutively numbered, there may 20762306a36Sopenharmony_ci * be a core_id of 3, but none of 2. Assume there always is 0 20862306a36Sopenharmony_ci * Get amount of cores by counting duplicates in a package 20962306a36Sopenharmony_ci for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) { 21062306a36Sopenharmony_ci if (cpu_top->core_info[cpu].core == 0) 21162306a36Sopenharmony_ci cpu_top->cores++; 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci return cpus; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_civoid cpu_topology_release(struct cpupower_topology cpu_top) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci free(cpu_top.core_info); 21962306a36Sopenharmony_ci} 220