Skip to content

Commit ebdb203

Browse files
committed
x86/cpu: Provide cpu_init/parse_topology()
Topology evaluation is a complete disaster and impenetrable mess. It's scattered all over the place with some vendor implementations doing early evaluation and some not. The most horrific part is the permanent overwriting of smt_max_siblings and __max_die_per_package, instead of establishing them once on the boot CPU and validating the result on the APs. The goals are: - One topology evaluation entry point - Proper sharing of pointlessly duplicated code - Proper structuring of the evaluation logic and preferences. - Evaluating important system wide information only once on the boot CPU - Making the 0xb/0x1f leaf parsing less convoluted and actually fixing the short comings of leaf 0x1f evaluation. Start to consolidate the topology evaluation code by providing the entry points for the early boot CPU evaluation and for the final parsing on the boot CPU and the APs. Move the trivial pieces into that new code: - The initialization of cpuinfo_x86::topo - The evaluation of CPUID leaf 1, which presets topo::initial_apicid - topo_apicid is set to topo::initial_apicid when invoked from early boot. When invoked for the final evaluation on the boot CPU it reads the actual APIC ID, which makes apic_get_initial_apicid() obsolete once everything is converted over. Provide a temporary helper function topo_converted() which shields off the not yet converted CPU vendors from invoking code which would break them. This shielding covers all vendor CPUs which support SMP, but not the historical pure UP ones as they only need the topology info init and eventually the initial APIC initialization. Provide two new members in cpuinfo_x86::topo to store the maximum number of SMT siblings and the number of dies per package and add them to the debugfs readout. These two members will be used to populate this information on the boot CPU and to validate the APs against it. Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Juergen Gross <[email protected]> Tested-by: Sohil Mehta <[email protected]> Tested-by: Michael Kelley <[email protected]> Tested-by: Peter Zijlstra (Intel) <[email protected]> Tested-by: Zhang Rui <[email protected]> Tested-by: Wang Wendy <[email protected]> Tested-by: K Prateek Nayak <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 43d86e3 commit ebdb203

File tree

7 files changed

+296
-18
lines changed

7 files changed

+296
-18
lines changed

arch/x86/include/asm/topology.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,25 @@ static inline void setup_node_to_cpumask_map(void) { }
102102

103103
#include <asm-generic/topology.h>
104104

105+
/* Topology information */
106+
enum x86_topology_domains {
107+
TOPO_SMT_DOMAIN,
108+
TOPO_CORE_DOMAIN,
109+
TOPO_MODULE_DOMAIN,
110+
TOPO_TILE_DOMAIN,
111+
TOPO_DIE_DOMAIN,
112+
TOPO_DIEGRP_DOMAIN,
113+
TOPO_PKG_DOMAIN,
114+
TOPO_MAX_DOMAIN,
115+
};
116+
117+
struct x86_topology_system {
118+
unsigned int dom_shifts[TOPO_MAX_DOMAIN];
119+
unsigned int dom_size[TOPO_MAX_DOMAIN];
120+
};
121+
122+
extern struct x86_topology_system x86_topo_system;
123+
105124
extern const struct cpumask *cpu_coregroup_mask(int cpu);
106125
extern const struct cpumask *cpu_clustergroup_mask(int cpu);
107126

arch/x86/kernel/cpu/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ KMSAN_SANITIZE_common.o := n
1717
# As above, instrumenting secondary CPU boot code causes boot hangs.
1818
KCSAN_SANITIZE_common.o := n
1919

20-
obj-y := cacheinfo.o scattered.o topology.o
20+
obj-y := cacheinfo.o scattered.o
21+
obj-y += topology_common.o topology.o
2122
obj-y += common.o
2223
obj-y += rdrand.o
2324
obj-y += match.o

arch/x86/kernel/cpu/common.c

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,8 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
15911591
setup_force_cpu_cap(X86_FEATURE_CPUID);
15921592
cpu_parse_early_param();
15931593

1594+
cpu_init_topology(c);
1595+
15941596
if (this_cpu->c_early_init)
15951597
this_cpu->c_early_init(c);
15961598

@@ -1601,6 +1603,7 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
16011603
this_cpu->c_bsp_init(c);
16021604
} else {
16031605
setup_clear_cpu_cap(X86_FEATURE_CPUID);
1606+
cpu_init_topology(c);
16041607
}
16051608

16061609
get_cpu_address_sizes(c);
@@ -1748,18 +1751,6 @@ static void generic_identify(struct cpuinfo_x86 *c)
17481751

17491752
get_cpu_address_sizes(c);
17501753

1751-
if (c->cpuid_level >= 0x00000001) {
1752-
c->topo.initial_apicid = (cpuid_ebx(1) >> 24) & 0xFF;
1753-
#ifdef CONFIG_X86_32
1754-
# ifdef CONFIG_SMP
1755-
c->topo.apicid = apic->phys_pkg_id(c->topo.initial_apicid, 0);
1756-
# else
1757-
c->topo.apicid = c->topo.initial_apicid;
1758-
# endif
1759-
#endif
1760-
c->topo.pkg_id = c->topo.initial_apicid;
1761-
}
1762-
17631754
get_model_name(c); /* Default name */
17641755

17651756
/*
@@ -1818,9 +1809,6 @@ static void identify_cpu(struct cpuinfo_x86 *c)
18181809
c->x86_model_id[0] = '\0'; /* Unset */
18191810
c->x86_max_cores = 1;
18201811
c->x86_coreid_bits = 0;
1821-
c->topo.cu_id = 0xff;
1822-
c->topo.llc_id = BAD_APICID;
1823-
c->topo.l2c_id = BAD_APICID;
18241812
#ifdef CONFIG_X86_64
18251813
c->x86_clflush_size = 64;
18261814
c->x86_phys_bits = 36;
@@ -1839,17 +1827,19 @@ static void identify_cpu(struct cpuinfo_x86 *c)
18391827

18401828
generic_identify(c);
18411829

1830+
cpu_parse_topology(c);
1831+
18421832
if (this_cpu->c_identify)
18431833
this_cpu->c_identify(c);
18441834

18451835
/* Clear/Set all flags overridden by options, after probe */
18461836
apply_forced_caps(c);
18471837

18481838
#ifdef CONFIG_X86_64
1849-
c->topo.apicid = apic->phys_pkg_id(c->topo.initial_apicid, 0);
1839+
if (!topo_is_converted(c))
1840+
c->topo.apicid = apic->phys_pkg_id(c->topo.initial_apicid, 0);
18501841
#endif
18511842

1852-
18531843
/*
18541844
* Set default APIC and TSC_DEADLINE MSR fencing flag. AMD and
18551845
* Hygon will clear it in ->c_init() below.

arch/x86/kernel/cpu/cpu.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
#ifndef ARCH_X86_CPU_H
33
#define ARCH_X86_CPU_H
44

5+
#include <asm/cpu.h>
6+
#include <asm/topology.h>
7+
8+
#include "topology.h"
9+
510
/* attempt to consolidate cpu attributes */
611
struct cpu_dev {
712
const char *c_vendor;
@@ -96,4 +101,5 @@ static inline bool spectre_v2_in_eibrs_mode(enum spectre_v2_mitigation mode)
96101
mode == SPECTRE_V2_EIBRS_RETPOLINE ||
97102
mode == SPECTRE_V2_EIBRS_LFENCE;
98103
}
104+
99105
#endif /* ARCH_X86_CPU_H */

arch/x86/kernel/cpu/debugfs.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <asm/apic.h>
66
#include <asm/processor.h>
77

8+
#include "cpu.h"
9+
810
static int cpu_debug_show(struct seq_file *m, void *p)
911
{
1012
unsigned long cpu = (unsigned long)m->private;
@@ -42,12 +44,48 @@ static const struct file_operations dfs_cpu_ops = {
4244
.release = single_release,
4345
};
4446

47+
static int dom_debug_show(struct seq_file *m, void *p)
48+
{
49+
static const char *domain_names[TOPO_MAX_DOMAIN] = {
50+
[TOPO_SMT_DOMAIN] = "Thread",
51+
[TOPO_CORE_DOMAIN] = "Core",
52+
[TOPO_MODULE_DOMAIN] = "Module",
53+
[TOPO_TILE_DOMAIN] = "Tile",
54+
[TOPO_DIE_DOMAIN] = "Die",
55+
[TOPO_DIEGRP_DOMAIN] = "DieGrp",
56+
[TOPO_PKG_DOMAIN] = "Package",
57+
};
58+
unsigned int dom, nthreads = 1;
59+
60+
for (dom = 0; dom < TOPO_MAX_DOMAIN; dom++) {
61+
nthreads *= x86_topo_system.dom_size[dom];
62+
seq_printf(m, "domain: %-10s shift: %u dom_size: %5u max_threads: %5u\n",
63+
domain_names[dom], x86_topo_system.dom_shifts[dom],
64+
x86_topo_system.dom_size[dom], nthreads);
65+
}
66+
return 0;
67+
}
68+
69+
static int dom_debug_open(struct inode *inode, struct file *file)
70+
{
71+
return single_open(file, dom_debug_show, inode->i_private);
72+
}
73+
74+
static const struct file_operations dfs_dom_ops = {
75+
.open = dom_debug_open,
76+
.read = seq_read,
77+
.llseek = seq_lseek,
78+
.release = single_release,
79+
};
80+
4581
static __init int cpu_init_debugfs(void)
4682
{
4783
struct dentry *dir, *base = debugfs_create_dir("topo", arch_debugfs_dir);
4884
unsigned long id;
4985
char name[24];
5086

87+
debugfs_create_file("domains", 0444, base, NULL, &dfs_dom_ops);
88+
5189
dir = debugfs_create_dir("cpus", base);
5290
for_each_possible_cpu(id) {
5391
sprintf(name, "%lu", id);

arch/x86/kernel/cpu/topology.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef ARCH_X86_TOPOLOGY_H
3+
#define ARCH_X86_TOPOLOGY_H
4+
5+
struct topo_scan {
6+
struct cpuinfo_x86 *c;
7+
unsigned int dom_shifts[TOPO_MAX_DOMAIN];
8+
unsigned int dom_ncpus[TOPO_MAX_DOMAIN];
9+
};
10+
11+
bool topo_is_converted(struct cpuinfo_x86 *c);
12+
void cpu_init_topology(struct cpuinfo_x86 *c);
13+
void cpu_parse_topology(struct cpuinfo_x86 *c);
14+
void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
15+
unsigned int shift, unsigned int ncpus);
16+
17+
static inline u32 topo_shift_apicid(u32 apicid, enum x86_topology_domains dom)
18+
{
19+
if (dom == TOPO_SMT_DOMAIN)
20+
return apicid;
21+
return apicid >> x86_topo_system.dom_shifts[dom - 1];
22+
}
23+
24+
static inline u32 topo_relative_domain_id(u32 apicid, enum x86_topology_domains dom)
25+
{
26+
if (dom != TOPO_SMT_DOMAIN)
27+
apicid >>= x86_topo_system.dom_shifts[dom - 1];
28+
return apicid & (x86_topo_system.dom_size[dom] - 1);
29+
}
30+
31+
static inline u32 topo_domain_mask(enum x86_topology_domains dom)
32+
{
33+
return (1U << x86_topo_system.dom_shifts[dom]) - 1;
34+
}
35+
36+
#endif /* ARCH_X86_TOPOLOGY_H */

0 commit comments

Comments
 (0)