Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpupower: Implement CPU physical core querying

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>

authored by

John B. Wyatt IV and committed by
Shuah Khan
f89cb9cb 0014f65e

+43 -8
+40 -8
tools/power/cpupower/lib/cpupower.c
··· 10 10 #include <stdio.h> 11 11 #include <errno.h> 12 12 #include <stdlib.h> 13 + #include <string.h> 13 14 14 15 #include "cpupower.h" 15 16 #include "cpupower_intern.h" ··· 151 150 return 0; 152 151 } 153 152 153 + static int __compare_core_cpu_list(const void *t1, const void *t2) 154 + { 155 + struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; 156 + struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2; 157 + 158 + return strcmp(top1->core_cpu_list, top2->core_cpu_list); 159 + } 160 + 154 161 /* 155 162 * Returns amount of cpus, negative on error, cpu_top must be 156 163 * passed to cpu_topology_release to free resources 157 164 * 158 - * Array is sorted after ->pkg, ->core, then ->cpu 165 + * Array is sorted after ->cpu_smt_list ->pkg, ->core 159 166 */ 160 167 int get_cpu_topology(struct cpupower_topology *cpu_top) 161 168 { 162 169 int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF); 170 + char path[SYSFS_PATH_MAX]; 171 + char *last_cpu_list; 163 172 164 173 cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus); 165 174 if (cpu_top->core_info == NULL) ··· 194 183 cpu_top->core_info[cpu].core = -1; 195 184 continue; 196 185 } 186 + if (cpu_top->core_info[cpu].core == -1) { 187 + strncpy(cpu_top->core_info[cpu].core_cpu_list, "-1", CPULIST_BUFFER); 188 + continue; 189 + } 190 + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s", 191 + cpu, "core_cpus_list"); 192 + if (cpupower_read_sysfs( 193 + path, 194 + cpu_top->core_info[cpu].core_cpu_list, 195 + CPULIST_BUFFER) < 1) { 196 + printf("Warning CPU%u has a 0 size core_cpus_list string", cpu); 197 + } 198 + } 199 + 200 + /* Count the number of distinct cpu lists to get the physical core 201 + * count. 202 + */ 203 + qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), 204 + __compare_core_cpu_list); 205 + 206 + last_cpu_list = cpu_top->core_info[0].core_cpu_list; 207 + cpu_top->cores = 1; 208 + for (cpu = 1; cpu < cpus; cpu++) { 209 + if (strcmp(cpu_top->core_info[cpu].core_cpu_list, last_cpu_list) != 0 && 210 + cpu_top->core_info[cpu].pkg != -1) { 211 + last_cpu_list = cpu_top->core_info[cpu].core_cpu_list; 212 + cpu_top->cores++; 213 + } 197 214 } 198 215 199 216 qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), ··· 242 203 if (!(cpu_top->core_info[0].pkg == -1)) 243 204 cpu_top->pkgs++; 244 205 245 - /* Intel's cores count is not consecutively numbered, there may 246 - * be a core_id of 3, but none of 2. Assume there always is 0 247 - * Get amount of cores by counting duplicates in a package 248 - for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) { 249 - if (cpu_top->core_info[cpu].core == 0) 250 - cpu_top->cores++; 251 - */ 252 206 return cpus; 253 207 } 254 208
+3
tools/power/cpupower/lib/cpupower.h
··· 2 2 #ifndef __CPUPOWER_CPUPOWER_H__ 3 3 #define __CPUPOWER_CPUPOWER_H__ 4 4 5 + #define CPULIST_BUFFER 5 6 + 5 7 struct cpupower_topology { 6 8 /* Amount of CPU cores, packages and threads per core in the system */ 7 9 unsigned int cores; ··· 18 16 int pkg; 19 17 int core; 20 18 int cpu; 19 + char core_cpu_list[CPULIST_BUFFER]; 21 20 22 21 /* flags */ 23 22 unsigned int is_online:1;