Skip to content

Commit f7fb3b2

Browse files
committed
x86/cpu: Provide an AMD/HYGON specific topology parser
AMD/HYGON uses various methods for topology evaluation: - Leaf 0x80000008 and 0x8000001e based with an optional leaf 0xb, which is the preferred variant for modern CPUs. Leaf 0xb will be superseded by leaf 0x80000026 soon, which is just another variant of the Intel 0x1f leaf for whatever reasons. - Subleaf 0x80000008 and NODEID_MSR base - Legacy fallback That code is following the principle of random bits and pieces all over the place which results in multiple evaluations and impenetrable code flows in the same way as the Intel parsing did. Provide a sane implementation by clearly separating the three variants and bringing them in the proper preference order in one place. This provides the parsing for both AMD and HYGON because there is no point in having a separate HYGON parser which only differs by 3 lines of code. Any further divergence between AMD and HYGON can be handled in different functions, while still sharing the existing parsers. 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: Zhang Rui <[email protected]> Tested-by: Wang Wendy <[email protected]> Tested-by: K Prateek Nayak <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 7e3ec62 commit f7fb3b2

File tree

9 files changed

+215
-7
lines changed

9 files changed

+215
-7
lines changed

arch/x86/include/asm/topology.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ int topology_update_package_map(unsigned int apicid, unsigned int cpu);
163163
int topology_update_die_map(unsigned int dieid, unsigned int cpu);
164164
int topology_phys_to_logical_pkg(unsigned int pkg);
165165

166+
extern unsigned int __amd_nodes_per_pkg;
167+
166168
static inline unsigned int topology_amd_nodes_per_pkg(void)
167169
{
168170
return __max_die_per_package;

arch/x86/kernel/cpu/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ KMSAN_SANITIZE_common.o := n
1818
KCSAN_SANITIZE_common.o := n
1919

2020
obj-y := cacheinfo.o scattered.o
21-
obj-y += topology_common.o topology_ext.o topology.o
21+
obj-y += topology_common.o topology_ext.o topology_amd.o topology.o
2222
obj-y += common.o
2323
obj-y += rdrand.o
2424
obj-y += match.o

arch/x86/kernel/cpu/amd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
351351
if (!err)
352352
c->x86_coreid_bits = get_count_order(c->x86_max_cores);
353353

354-
cacheinfo_amd_init_llc_id(c);
354+
cacheinfo_amd_init_llc_id(c, c->topo.die_id);
355355

356356
} else if (cpu_has(c, X86_FEATURE_NODEID_MSR)) {
357357
u64 value;

arch/x86/kernel/cpu/cacheinfo.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c)
661661
return i;
662662
}
663663

664-
void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c)
664+
void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, u16 die_id)
665665
{
666666
/*
667667
* We may have multiple LLCs if L3 caches exist, so check if we
@@ -672,7 +672,7 @@ void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c)
672672

673673
if (c->x86 < 0x17) {
674674
/* LLC is at the node level. */
675-
c->topo.llc_id = c->topo.die_id;
675+
c->topo.llc_id = die_id;
676676
} else if (c->x86 == 0x17 && c->x86_model <= 0x1F) {
677677
/*
678678
* LLC is at the core complex level.

arch/x86/kernel/cpu/cpu.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ extern void init_hygon_cacheinfo(struct cpuinfo_x86 *c);
7979
extern int detect_extended_topology(struct cpuinfo_x86 *c);
8080
extern void check_null_seg_clears_base(struct cpuinfo_x86 *c);
8181

82-
void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c);
82+
void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, u16 die_id);
8383
void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c);
8484

8585
unsigned int aperfmperf_get_khz(int cpu);

arch/x86/kernel/cpu/debugfs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ static int cpu_debug_show(struct seq_file *m, void *p)
2626
seq_printf(m, "logical_die_id: %u\n", c->topo.logical_die_id);
2727
seq_printf(m, "llc_id: %u\n", c->topo.llc_id);
2828
seq_printf(m, "l2c_id: %u\n", c->topo.l2c_id);
29+
seq_printf(m, "amd_node_id: %u\n", c->topo.amd_node_id);
30+
seq_printf(m, "amd_nodes_per_pkg: %u\n", topology_amd_nodes_per_pkg());
2931
seq_printf(m, "max_cores: %u\n", c->x86_max_cores);
3032
seq_printf(m, "max_die_per_pkg: %u\n", __max_die_per_package);
3133
seq_printf(m, "smp_num_siblings: %u\n", smp_num_siblings);

arch/x86/kernel/cpu/topology.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ struct topo_scan {
99

1010
/* Legacy CPUID[1]:EBX[23:16] number of logical processors */
1111
unsigned int ebx1_nproc_shift;
12+
13+
/* AMD specific node ID which cannot be mapped into APIC space. */
14+
u16 amd_nodes_per_pkg;
15+
u16 amd_node_id;
1216
};
1317

1418
bool topo_is_converted(struct cpuinfo_x86 *c);
@@ -17,6 +21,8 @@ void cpu_parse_topology(struct cpuinfo_x86 *c);
1721
void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
1822
unsigned int shift, unsigned int ncpus);
1923
bool cpu_parse_topology_ext(struct topo_scan *tscan);
24+
void cpu_parse_topology_amd(struct topo_scan *tscan);
25+
void cpu_topology_fixup_amd(struct topo_scan *tscan);
2026

2127
static inline u32 topo_shift_apicid(u32 apicid, enum x86_topology_domains dom)
2228
{

arch/x86/kernel/cpu/topology_amd.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/cpu.h>
3+
4+
#include <asm/apic.h>
5+
#include <asm/memtype.h>
6+
#include <asm/processor.h>
7+
8+
#include "cpu.h"
9+
10+
static bool parse_8000_0008(struct topo_scan *tscan)
11+
{
12+
struct {
13+
// ecx
14+
u32 cpu_nthreads : 8, // Number of physical threads - 1
15+
: 4, // Reserved
16+
apicid_coreid_len : 4, // Number of thread core ID bits (shift) in APIC ID
17+
perf_tsc_len : 2, // Performance time-stamp counter size
18+
: 14; // Reserved
19+
} ecx;
20+
unsigned int sft;
21+
22+
if (tscan->c->extended_cpuid_level < 0x80000008)
23+
return false;
24+
25+
cpuid_leaf_reg(0x80000008, CPUID_ECX, &ecx);
26+
27+
/* If the thread bits are 0, then get the shift value from ecx.cpu_nthreads */
28+
sft = ecx.apicid_coreid_len;
29+
if (!sft)
30+
sft = get_count_order(ecx.cpu_nthreads + 1);
31+
32+
topology_set_dom(tscan, TOPO_SMT_DOMAIN, sft, ecx.cpu_nthreads + 1);
33+
return true;
34+
}
35+
36+
static void store_node(struct topo_scan *tscan, unsigned int nr_nodes, u16 node_id)
37+
{
38+
/*
39+
* Starting with Fam 17h the DIE domain could probably be used to
40+
* retrieve the node info on AMD/HYGON. Analysis of CPUID dumps
41+
* suggests it's the topmost bit(s) of the CPU cores area, but
42+
* that's guess work and neither enumerated nor documented.
43+
*
44+
* Up to Fam 16h this does not work at all and the legacy node ID
45+
* has to be used.
46+
*/
47+
tscan->amd_nodes_per_pkg = nr_nodes;
48+
tscan->amd_node_id = node_id;
49+
}
50+
51+
static bool parse_8000_001e(struct topo_scan *tscan, bool has_0xb)
52+
{
53+
struct {
54+
// eax
55+
u32 ext_apic_id : 32; // Extended APIC ID
56+
// ebx
57+
u32 core_id : 8, // Unique per-socket logical core unit ID
58+
core_nthreads : 8, // #Threads per core (zero-based)
59+
: 16; // Reserved
60+
// ecx
61+
u32 node_id : 8, // Node (die) ID of invoking logical CPU
62+
nnodes_per_socket : 3, // #nodes in invoking logical CPU's package/socket
63+
: 21; // Reserved
64+
// edx
65+
u32 : 32; // Reserved
66+
} leaf;
67+
68+
if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
69+
return false;
70+
71+
cpuid_leaf(0x8000001e, &leaf);
72+
73+
tscan->c->topo.initial_apicid = leaf.ext_apic_id;
74+
75+
/*
76+
* If leaf 0xb is available, then SMT shift is set already. If not
77+
* take it from ecx.threads_per_core and use topo_update_dom() -
78+
* topology_set_dom() would propagate and overwrite the already
79+
* propagated CORE level.
80+
*/
81+
if (!has_0xb) {
82+
unsigned int nthreads = leaf.core_nthreads + 1;
83+
84+
topology_update_dom(tscan, TOPO_SMT_DOMAIN, get_count_order(nthreads), nthreads);
85+
}
86+
87+
store_node(tscan, leaf.nnodes_per_socket + 1, leaf.node_id);
88+
89+
if (tscan->c->x86_vendor == X86_VENDOR_AMD) {
90+
if (tscan->c->x86 == 0x15)
91+
tscan->c->topo.cu_id = leaf.core_id;
92+
93+
cacheinfo_amd_init_llc_id(tscan->c, leaf.node_id);
94+
} else {
95+
/*
96+
* Package ID is ApicId[6..] on certain Hygon CPUs. See
97+
* commit e0ceeae708ce for explanation. The topology info
98+
* is screwed up: The package shift is always 6 and the
99+
* node ID is bit [4:5].
100+
*/
101+
if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && tscan->c->x86_model <= 0x3) {
102+
topology_set_dom(tscan, TOPO_CORE_DOMAIN, 6,
103+
tscan->dom_ncpus[TOPO_CORE_DOMAIN]);
104+
}
105+
cacheinfo_hygon_init_llc_id(tscan->c);
106+
}
107+
return true;
108+
}
109+
110+
static bool parse_fam10h_node_id(struct topo_scan *tscan)
111+
{
112+
struct {
113+
union {
114+
u64 node_id : 3,
115+
nodes_per_pkg : 3,
116+
unused : 58;
117+
u64 msr;
118+
};
119+
} nid;
120+
121+
if (!boot_cpu_has(X86_FEATURE_NODEID_MSR))
122+
return false;
123+
124+
rdmsrl(MSR_FAM10H_NODE_ID, nid.msr);
125+
store_node(tscan, nid.nodes_per_pkg + 1, nid.node_id);
126+
tscan->c->topo.llc_id = nid.node_id;
127+
return true;
128+
}
129+
130+
static void legacy_set_llc(struct topo_scan *tscan)
131+
{
132+
unsigned int apicid = tscan->c->topo.initial_apicid;
133+
134+
/* parse_8000_0008() set everything up except llc_id */
135+
tscan->c->topo.llc_id = apicid >> tscan->dom_shifts[TOPO_CORE_DOMAIN];
136+
}
137+
138+
static void parse_topology_amd(struct topo_scan *tscan)
139+
{
140+
bool has_0xb = false;
141+
142+
/*
143+
* If the extended topology leaf 0x8000_001e is available
144+
* try to get SMT and CORE shift from leaf 0xb first, then
145+
* try to get the CORE shift from leaf 0x8000_0008.
146+
*/
147+
if (cpu_feature_enabled(X86_FEATURE_TOPOEXT))
148+
has_0xb = cpu_parse_topology_ext(tscan);
149+
150+
if (!has_0xb && !parse_8000_0008(tscan))
151+
return;
152+
153+
/* Prefer leaf 0x8000001e if available */
154+
if (parse_8000_001e(tscan, has_0xb))
155+
return;
156+
157+
/* Try the NODEID MSR */
158+
if (parse_fam10h_node_id(tscan))
159+
return;
160+
161+
legacy_set_llc(tscan);
162+
}
163+
164+
void cpu_parse_topology_amd(struct topo_scan *tscan)
165+
{
166+
tscan->amd_nodes_per_pkg = 1;
167+
parse_topology_amd(tscan);
168+
169+
if (tscan->amd_nodes_per_pkg > 1)
170+
set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM);
171+
}
172+
173+
void cpu_topology_fixup_amd(struct topo_scan *tscan)
174+
{
175+
struct cpuinfo_x86 *c = tscan->c;
176+
177+
/*
178+
* Adjust the core_id relative to the node when there is more than
179+
* one node.
180+
*/
181+
if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1)
182+
c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg;
183+
}

arch/x86/kernel/cpu/topology_common.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111

1212
struct x86_topology_system x86_topo_system __ro_after_init;
1313

14+
unsigned int __amd_nodes_per_pkg __ro_after_init;
15+
EXPORT_SYMBOL_GPL(__amd_nodes_per_pkg);
16+
1417
void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
1518
unsigned int shift, unsigned int ncpus)
1619
{
17-
tscan->dom_shifts[dom] = shift;
18-
tscan->dom_ncpus[dom] = ncpus;
20+
topology_update_dom(tscan, dom, shift, ncpus);
1921

2022
/* Propagate to the upper levels */
2123
for (dom++; dom < TOPO_MAX_DOMAIN; dom++) {
@@ -153,6 +155,13 @@ static void topo_set_ids(struct topo_scan *tscan)
153155
/* Package relative core ID */
154156
c->topo.core_id = (apicid & topo_domain_mask(TOPO_PKG_DOMAIN)) >>
155157
x86_topo_system.dom_shifts[TOPO_SMT_DOMAIN];
158+
159+
/* Temporary workaround */
160+
if (tscan->amd_nodes_per_pkg)
161+
c->topo.amd_node_id = c->topo.die_id = tscan->amd_node_id;
162+
163+
if (c->x86_vendor == X86_VENDOR_AMD)
164+
cpu_topology_fixup_amd(tscan);
156165
}
157166

158167
static void topo_set_max_cores(struct topo_scan *tscan)
@@ -237,4 +246,10 @@ void __init cpu_init_topology(struct cpuinfo_x86 *c)
237246
*/
238247
__max_die_per_package = tscan.dom_ncpus[TOPO_DIE_DOMAIN] /
239248
tscan.dom_ncpus[TOPO_DIE_DOMAIN - 1];
249+
/*
250+
* AMD systems have Nodes per package which cannot be mapped to
251+
* APIC ID.
252+
*/
253+
if (c->x86_vendor == X86_VENDOR_AMD || c->x86_vendor == X86_VENDOR_HYGON)
254+
__amd_nodes_per_pkg = __max_die_per_package = tscan.amd_nodes_per_pkg;
240255
}

0 commit comments

Comments
 (0)