mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-13 01:04:35 +08:00
edc0a2b595
Traditionally, all CPUs in a system have identical numbers of SMT siblings. That changes with hybrid processors where some logical CPUs have a sibling and others have none. Today, the CPU boot code sets the global variable smp_num_siblings when every CPU thread is brought up. The last thread to boot will overwrite it with the number of siblings of *that* thread. That last thread to boot will "win". If the thread is a Pcore, smp_num_siblings == 2. If it is an Ecore, smp_num_siblings == 1. smp_num_siblings describes if the *system* supports SMT. It should specify the maximum number of SMT threads among all cores. Ensure that smp_num_siblings represents the system-wide maximum number of siblings by always increasing its value. Never allow it to decrease. On MeteorLake-P platform, this fixes a problem that the Ecore CPUs are not updated in any cpu sibling map because the system is treated as an UP system when probing Ecore CPUs. Below shows part of the CPU topology information before and after the fix, for both Pcore and Ecore CPU (cpu0 is Pcore, cpu 12 is Ecore). ... -/sys/devices/system/cpu/cpu0/topology/package_cpus:000fff -/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-11 +/sys/devices/system/cpu/cpu0/topology/package_cpus:3fffff +/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-21 ... -/sys/devices/system/cpu/cpu12/topology/package_cpus:001000 -/sys/devices/system/cpu/cpu12/topology/package_cpus_list:12 +/sys/devices/system/cpu/cpu12/topology/package_cpus:3fffff +/sys/devices/system/cpu/cpu12/topology/package_cpus_list:0-21 Notice that the "before" 'package_cpus_list' has only one CPU. This means that userspace tools like lscpu will see a little laptop like an 11-socket system: -Core(s) per socket: 1 -Socket(s): 11 +Core(s) per socket: 16 +Socket(s): 1 This is also expected to make the scheduler do rather wonky things too. [ dhansen: remove CPUID detail from changelog, add end user effects ] CC: stable@kernel.org Fixes:bbb65d2d36
("x86: use cpuid vector 0xb when available for detecting cpu topology") Fixes:95f3d39ccf
("x86/cpu/topology: Provide detect_extended_topology_early()") Suggested-by: Len Brown <len.brown@intel.com> Signed-off-by: Zhang Rui <rui.zhang@intel.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/all/20230323015640.27906-1-rui.zhang%40intel.com
169 lines
4.2 KiB
C
169 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Check for extended topology enumeration cpuid leaf 0xb and if it
|
|
* exists, use it for populating initial_apicid and cpu topology
|
|
* detection.
|
|
*/
|
|
|
|
#include <linux/cpu.h>
|
|
#include <asm/apic.h>
|
|
#include <asm/memtype.h>
|
|
#include <asm/processor.h>
|
|
|
|
#include "cpu.h"
|
|
|
|
/* leaf 0xb SMT level */
|
|
#define SMT_LEVEL 0
|
|
|
|
/* extended topology sub-leaf types */
|
|
#define INVALID_TYPE 0
|
|
#define SMT_TYPE 1
|
|
#define CORE_TYPE 2
|
|
#define DIE_TYPE 5
|
|
|
|
#define LEAFB_SUBTYPE(ecx) (((ecx) >> 8) & 0xff)
|
|
#define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f)
|
|
#define LEVEL_MAX_SIBLINGS(ebx) ((ebx) & 0xffff)
|
|
|
|
unsigned int __max_die_per_package __read_mostly = 1;
|
|
EXPORT_SYMBOL(__max_die_per_package);
|
|
|
|
#ifdef CONFIG_SMP
|
|
/*
|
|
* Check if given CPUID extended topology "leaf" is implemented
|
|
*/
|
|
static int check_extended_topology_leaf(int leaf)
|
|
{
|
|
unsigned int eax, ebx, ecx, edx;
|
|
|
|
cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
|
|
|
|
if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
/*
|
|
* Return best CPUID Extended Topology Leaf supported
|
|
*/
|
|
static int detect_extended_topology_leaf(struct cpuinfo_x86 *c)
|
|
{
|
|
if (c->cpuid_level >= 0x1f) {
|
|
if (check_extended_topology_leaf(0x1f) == 0)
|
|
return 0x1f;
|
|
}
|
|
|
|
if (c->cpuid_level >= 0xb) {
|
|
if (check_extended_topology_leaf(0xb) == 0)
|
|
return 0xb;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
int detect_extended_topology_early(struct cpuinfo_x86 *c)
|
|
{
|
|
#ifdef CONFIG_SMP
|
|
unsigned int eax, ebx, ecx, edx;
|
|
int leaf;
|
|
|
|
leaf = detect_extended_topology_leaf(c);
|
|
if (leaf < 0)
|
|
return -1;
|
|
|
|
set_cpu_cap(c, X86_FEATURE_XTOPOLOGY);
|
|
|
|
cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
|
|
/*
|
|
* initial apic id, which also represents 32-bit extended x2apic id.
|
|
*/
|
|
c->initial_apicid = edx;
|
|
smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check for extended topology enumeration cpuid leaf, and if it
|
|
* exists, use it for populating initial_apicid and cpu topology
|
|
* detection.
|
|
*/
|
|
int detect_extended_topology(struct cpuinfo_x86 *c)
|
|
{
|
|
#ifdef CONFIG_SMP
|
|
unsigned int eax, ebx, ecx, edx, sub_index;
|
|
unsigned int ht_mask_width, core_plus_mask_width, die_plus_mask_width;
|
|
unsigned int core_select_mask, core_level_siblings;
|
|
unsigned int die_select_mask, die_level_siblings;
|
|
unsigned int pkg_mask_width;
|
|
bool die_level_present = false;
|
|
int leaf;
|
|
|
|
leaf = detect_extended_topology_leaf(c);
|
|
if (leaf < 0)
|
|
return -1;
|
|
|
|
/*
|
|
* Populate HT related information from sub-leaf level 0.
|
|
*/
|
|
cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
|
|
c->initial_apicid = edx;
|
|
core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
|
|
smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
|
|
core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
|
|
die_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
|
|
pkg_mask_width = die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
|
|
|
|
sub_index = 1;
|
|
while (true) {
|
|
cpuid_count(leaf, sub_index, &eax, &ebx, &ecx, &edx);
|
|
|
|
/*
|
|
* Check for the Core type in the implemented sub leaves.
|
|
*/
|
|
if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) {
|
|
core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
|
|
core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
|
|
die_level_siblings = core_level_siblings;
|
|
die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
|
|
}
|
|
if (LEAFB_SUBTYPE(ecx) == DIE_TYPE) {
|
|
die_level_present = true;
|
|
die_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
|
|
die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
|
|
}
|
|
|
|
if (LEAFB_SUBTYPE(ecx) != INVALID_TYPE)
|
|
pkg_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
|
|
else
|
|
break;
|
|
|
|
sub_index++;
|
|
}
|
|
|
|
core_select_mask = (~(-1 << pkg_mask_width)) >> ht_mask_width;
|
|
die_select_mask = (~(-1 << die_plus_mask_width)) >>
|
|
core_plus_mask_width;
|
|
|
|
c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid,
|
|
ht_mask_width) & core_select_mask;
|
|
|
|
if (die_level_present) {
|
|
c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid,
|
|
core_plus_mask_width) & die_select_mask;
|
|
}
|
|
|
|
c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid,
|
|
pkg_mask_width);
|
|
/*
|
|
* Reinit the apicid, now that we have extended initial_apicid.
|
|
*/
|
|
c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
|
|
|
|
c->x86_max_cores = (core_level_siblings / smp_num_siblings);
|
|
__max_die_per_package = (die_level_siblings / core_level_siblings);
|
|
#endif
|
|
return 0;
|
|
}
|