mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

This patch is also an issue report. get_cpu_topology will always save into cpupower_topology a cores size of 0. The code to handle this looks like it was commented out, and what is commented out is missing a curly bracket. https://elixir.bootlin.com/linux/v6.13.5/source/tools/power/cpupower/lib/cpupower.c#L206-L212 Inspiration was taken from psutil to implement this by querying core_cpu_list. Instead of using a hashmap, I used a sorted array, and counted the number of valid unique strings. The counting of this takes place before the qsort for .pkg as the following code says it is dependent on the order of that sort. The previous code claimed Intel CPUs are not numbered correctly. I was not able to reproduce that issue and removed that comment and the code. This commit was tested with the libcpupower SWIG Python bindings and performed correctly on 4 different setups. The most notable is the Framework Intel laptop; a hybrid system of 4 P cores (8 threads) and 8 E cores (8 threads). The 4 setups: A 4 core virt-manager VM running Fedora 41 4c/4t (specs not listed) was tested as a sanity test for VMs. A Lenovo Ryzen 7 Pro 7840HS 8c/16t. A Supermico Intel(R) Xeon(R) Gold 6330 CPU w/ 56c/112t with 2 CPU sockets. A Framework 12th Gen Intel(R) Core(TM) i5-1240P with hybrid cores. CPU(s): 16 On-line CPU(s) list: 0-15 Vendor ID: AuthenticAMD Model name: AMD Ryzen 7 PRO 7840HS w/ Radeon 780M Graphics CPU family: 25 Model: 116 Thread(s) per core: 2 Core(s) per socket: 8 Socket(s): 1 Stepping: 1 CPU(s): 112 On-line CPU(s) list: 0-111 Vendor ID: GenuineIntel BIOS Vendor ID: Intel(R) Corporation Model name: Intel(R) Xeon(R) Gold 6330 CPU @ 2.00GHz BIOS Model name: Intel(R) Xeon(R) Gold 6330 CPU @ 2.00GHz CPU @ 2.0GHz BIOS CPU family: 179 CPU family: 6 Model: 106 Thread(s) per core: 2 Core(s) per socket: 28 Socket(s): 2 Stepping: 6 CPU(s): 16 On-line CPU(s) list: 0-15 Vendor ID: GenuineIntel Model name: 12th Gen Intel(R) Core(TM) i5-1240P CPU family: 6 Model: 154 Thread(s) per core: 2 Core(s) per socket: 12 Socket(s): 1 Stepping: 3 Link: https://lore.kernel.org/r/20250305210901.24177-1-jwyatt@redhat.com Signed-off-by: "John B. Wyatt IV" <jwyatt@redhat.com> Signed-off-by: "John B. Wyatt IV" <sageofredondo@gmail.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
251 lines
5.6 KiB
C
251 lines
5.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "cpupower.h"
|
|
#include "cpupower_intern.h"
|
|
|
|
int is_valid_path(const char *path)
|
|
{
|
|
if (access(path, F_OK) == -1)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
|
|
{
|
|
ssize_t numread;
|
|
int fd;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
if (fd == -1)
|
|
return 0;
|
|
|
|
numread = read(fd, buf, buflen - 1);
|
|
if (numread < 1) {
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
buf[numread] = '\0';
|
|
close(fd);
|
|
|
|
return (unsigned int) numread;
|
|
}
|
|
|
|
unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen)
|
|
{
|
|
ssize_t numwritten;
|
|
int fd;
|
|
|
|
fd = open(path, O_WRONLY);
|
|
if (fd == -1)
|
|
return 0;
|
|
|
|
numwritten = write(fd, buf, buflen - 1);
|
|
if (numwritten < 1) {
|
|
perror(path);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return (unsigned int) numwritten;
|
|
}
|
|
|
|
/*
|
|
* Detect whether a CPU is online
|
|
*
|
|
* Returns:
|
|
* 1 -> if CPU is online
|
|
* 0 -> if CPU is offline
|
|
* negative errno values in error case
|
|
*/
|
|
int cpupower_is_cpu_online(unsigned int cpu)
|
|
{
|
|
char path[SYSFS_PATH_MAX];
|
|
int fd;
|
|
ssize_t numread;
|
|
unsigned long long value;
|
|
char linebuf[MAX_LINE_LEN];
|
|
char *endp;
|
|
struct stat statbuf;
|
|
|
|
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
|
|
|
|
if (stat(path, &statbuf) != 0)
|
|
return 0;
|
|
|
|
/*
|
|
* kernel without CONFIG_HOTPLUG_CPU
|
|
* -> cpuX directory exists, but not cpuX/online file
|
|
*/
|
|
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
|
|
if (stat(path, &statbuf) != 0)
|
|
return 1;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
if (fd == -1)
|
|
return -errno;
|
|
|
|
numread = read(fd, linebuf, MAX_LINE_LEN - 1);
|
|
if (numread < 1) {
|
|
close(fd);
|
|
return -EIO;
|
|
}
|
|
linebuf[numread] = '\0';
|
|
close(fd);
|
|
|
|
value = strtoull(linebuf, &endp, 0);
|
|
if (value > 1)
|
|
return -EINVAL;
|
|
|
|
return value;
|
|
}
|
|
|
|
/* returns -1 on failure, 0 on success */
|
|
static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
|
|
{
|
|
char linebuf[MAX_LINE_LEN];
|
|
char *endp;
|
|
char path[SYSFS_PATH_MAX];
|
|
|
|
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
|
|
cpu, fname);
|
|
if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
|
|
return -1;
|
|
*result = strtol(linebuf, &endp, 0);
|
|
if (endp == linebuf || errno == ERANGE)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int __compare(const void *t1, const void *t2)
|
|
{
|
|
struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
|
|
struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
|
|
if (top1->pkg < top2->pkg)
|
|
return -1;
|
|
else if (top1->pkg > top2->pkg)
|
|
return 1;
|
|
else if (top1->core < top2->core)
|
|
return -1;
|
|
else if (top1->core > top2->core)
|
|
return 1;
|
|
else if (top1->cpu < top2->cpu)
|
|
return -1;
|
|
else if (top1->cpu > top2->cpu)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int __compare_core_cpu_list(const void *t1, const void *t2)
|
|
{
|
|
struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
|
|
struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
|
|
|
|
return strcmp(top1->core_cpu_list, top2->core_cpu_list);
|
|
}
|
|
|
|
/*
|
|
* Returns amount of cpus, negative on error, cpu_top must be
|
|
* passed to cpu_topology_release to free resources
|
|
*
|
|
* Array is sorted after ->cpu_smt_list ->pkg, ->core
|
|
*/
|
|
int get_cpu_topology(struct cpupower_topology *cpu_top)
|
|
{
|
|
int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
|
|
char path[SYSFS_PATH_MAX];
|
|
char *last_cpu_list;
|
|
|
|
cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
|
|
if (cpu_top->core_info == NULL)
|
|
return -ENOMEM;
|
|
cpu_top->pkgs = cpu_top->cores = 0;
|
|
for (cpu = 0; cpu < cpus; cpu++) {
|
|
cpu_top->core_info[cpu].cpu = cpu;
|
|
cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
|
|
if(sysfs_topology_read_file(
|
|
cpu,
|
|
"physical_package_id",
|
|
&(cpu_top->core_info[cpu].pkg)) < 0) {
|
|
cpu_top->core_info[cpu].pkg = -1;
|
|
cpu_top->core_info[cpu].core = -1;
|
|
continue;
|
|
}
|
|
if(sysfs_topology_read_file(
|
|
cpu,
|
|
"core_id",
|
|
&(cpu_top->core_info[cpu].core)) < 0) {
|
|
cpu_top->core_info[cpu].pkg = -1;
|
|
cpu_top->core_info[cpu].core = -1;
|
|
continue;
|
|
}
|
|
if (cpu_top->core_info[cpu].core == -1) {
|
|
strncpy(cpu_top->core_info[cpu].core_cpu_list, "-1", CPULIST_BUFFER);
|
|
continue;
|
|
}
|
|
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
|
|
cpu, "core_cpus_list");
|
|
if (cpupower_read_sysfs(
|
|
path,
|
|
cpu_top->core_info[cpu].core_cpu_list,
|
|
CPULIST_BUFFER) < 1) {
|
|
printf("Warning CPU%u has a 0 size core_cpus_list string", cpu);
|
|
}
|
|
}
|
|
|
|
/* Count the number of distinct cpu lists to get the physical core
|
|
* count.
|
|
*/
|
|
qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
|
|
__compare_core_cpu_list);
|
|
|
|
last_cpu_list = cpu_top->core_info[0].core_cpu_list;
|
|
cpu_top->cores = 1;
|
|
for (cpu = 1; cpu < cpus; cpu++) {
|
|
if (strcmp(cpu_top->core_info[cpu].core_cpu_list, last_cpu_list) != 0 &&
|
|
cpu_top->core_info[cpu].pkg != -1) {
|
|
last_cpu_list = cpu_top->core_info[cpu].core_cpu_list;
|
|
cpu_top->cores++;
|
|
}
|
|
}
|
|
|
|
qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
|
|
__compare);
|
|
|
|
/* Count the number of distinct pkgs values. This works
|
|
because the primary sort of the core_info struct was just
|
|
done by pkg value. */
|
|
last_pkg = cpu_top->core_info[0].pkg;
|
|
for(cpu = 1; cpu < cpus; cpu++) {
|
|
if (cpu_top->core_info[cpu].pkg != last_pkg &&
|
|
cpu_top->core_info[cpu].pkg != -1) {
|
|
|
|
last_pkg = cpu_top->core_info[cpu].pkg;
|
|
cpu_top->pkgs++;
|
|
}
|
|
}
|
|
if (!(cpu_top->core_info[0].pkg == -1))
|
|
cpu_top->pkgs++;
|
|
|
|
return cpus;
|
|
}
|
|
|
|
void cpu_topology_release(struct cpupower_topology cpu_top)
|
|
{
|
|
free(cpu_top.core_info);
|
|
}
|