Skip to content

Commit 0c7ffa3

Browse files
KAGA-KOKOPeter Zijlstra
authored andcommitted
x86/smpboot/64: Implement arch_cpuhp_init_parallel_bringup() and enable it
Implement the validation function which tells the core code whether parallel bringup is possible. The only condition for now is that the kernel does not run in an encrypted guest as these will trap the RDMSR via #VC, which cannot be handled at that point in early startup. There was an earlier variant for AMD-SEV which used the GHBC protocol for retrieving the APIC ID via CPUID, but there is no guarantee that the initial APIC ID in CPUID is the same as the real APIC ID. There is no enforcement from the secure firmware and the hypervisor can assign APIC IDs as it sees fit as long as the ACPI/MADT table is consistent with that assignment. Unfortunately there is no RDMSR GHCB protocol at the moment, so enabling AMD-SEV guests for parallel startup needs some more thought. Intel-TDX provides a secure RDMSR hypercall, but supporting that is outside the scope of this change. Fixup announce_cpu() as e.g. on Hyper-V CPU1 is the secondary sibling of CPU0, which makes the @cpu == 1 logic in announce_cpu() fall apart. [ mikelley: Reported the announce_cpu() fallout Originally-by: David Woodhouse <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Tested-by: Michael Kelley <[email protected]> Tested-by: Oleksandr Natalenko <[email protected]> Tested-by: Helge Deller <[email protected]> # parisc Tested-by: Guilherme G. Piccoli <[email protected]> # Steam Deck Link: https://lore.kernel.org/r/[email protected]
1 parent 7e75178 commit 0c7ffa3

File tree

3 files changed

+75
-21
lines changed

3 files changed

+75
-21
lines changed

arch/x86/Kconfig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,9 @@ config X86
274274
select HAVE_UNSTABLE_SCHED_CLOCK
275275
select HAVE_USER_RETURN_NOTIFIER
276276
select HAVE_GENERIC_VDSO
277+
select HOTPLUG_PARALLEL if SMP && X86_64
277278
select HOTPLUG_SMT if SMP
278-
select HOTPLUG_SPLIT_STARTUP if SMP
279+
select HOTPLUG_SPLIT_STARTUP if SMP && X86_32
279280
select IRQ_FORCED_THREADING
280281
select NEED_PER_CPU_EMBED_FIRST_CHUNK
281282
select NEED_PER_CPU_PAGE_FIRST_CHUNK

arch/x86/kernel/cpu/common.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,11 +2128,7 @@ static inline void setup_getcpu(int cpu)
21282128
}
21292129

21302130
#ifdef CONFIG_X86_64
2131-
static inline void ucode_cpu_init(int cpu)
2132-
{
2133-
if (cpu)
2134-
load_ucode_ap();
2135-
}
2131+
static inline void ucode_cpu_init(int cpu) { }
21362132

21372133
static inline void tss_setup_ist(struct tss_struct *tss)
21382134
{

arch/x86/kernel/smpboot.c

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include <linux/overflow.h>
5959
#include <linux/stackprotector.h>
6060
#include <linux/cpuhotplug.h>
61+
#include <linux/mc146818rtc.h>
6162

6263
#include <asm/acpi.h>
6364
#include <asm/cacheinfo.h>
@@ -75,7 +76,7 @@
7576
#include <asm/fpu/api.h>
7677
#include <asm/setup.h>
7778
#include <asm/uv/uv.h>
78-
#include <linux/mc146818rtc.h>
79+
#include <asm/microcode.h>
7980
#include <asm/i8259.h>
8081
#include <asm/misc.h>
8182
#include <asm/qspinlock.h>
@@ -128,7 +129,6 @@ int arch_update_cpu_topology(void)
128129
return retval;
129130
}
130131

131-
132132
static unsigned int smpboot_warm_reset_vector_count;
133133

134134
static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip)
@@ -226,16 +226,43 @@ static void notrace start_secondary(void *unused)
226226
*/
227227
cr4_init();
228228

229-
#ifdef CONFIG_X86_32
230-
/* switch away from the initial page table */
231-
load_cr3(swapper_pg_dir);
232-
__flush_tlb_all();
233-
#endif
229+
/*
230+
* 32-bit specific. 64-bit reaches this code with the correct page
231+
* table established. Yet another historical divergence.
232+
*/
233+
if (IS_ENABLED(CONFIG_X86_32)) {
234+
/* switch away from the initial page table */
235+
load_cr3(swapper_pg_dir);
236+
__flush_tlb_all();
237+
}
238+
234239
cpu_init_exception_handling();
235240

236241
/*
237-
* Synchronization point with the hotplug core. Sets the
238-
* synchronization state to ALIVE and waits for the control CPU to
242+
* 32-bit systems load the microcode from the ASM startup code for
243+
* historical reasons.
244+
*
245+
* On 64-bit systems load it before reaching the AP alive
246+
* synchronization point below so it is not part of the full per
247+
* CPU serialized bringup part when "parallel" bringup is enabled.
248+
*
249+
* That's even safe when hyperthreading is enabled in the CPU as
250+
* the core code starts the primary threads first and leaves the
251+
* secondary threads waiting for SIPI. Loading microcode on
252+
* physical cores concurrently is a safe operation.
253+
*
254+
* This covers both the Intel specific issue that concurrent
255+
* microcode loading on SMT siblings must be prohibited and the
256+
* vendor independent issue`that microcode loading which changes
257+
* CPUID, MSRs etc. must be strictly serialized to maintain
258+
* software state correctness.
259+
*/
260+
if (IS_ENABLED(CONFIG_X86_64))
261+
load_ucode_ap();
262+
263+
/*
264+
* Synchronization point with the hotplug core. Sets this CPUs
265+
* synchronization state to ALIVE and spin-waits for the control CPU to
239266
* release this CPU for further bringup.
240267
*/
241268
cpuhp_ap_sync_alive();
@@ -918,20 +945,20 @@ static int wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_ei
918945
/* reduce the number of lines printed when booting a large cpu count system */
919946
static void announce_cpu(int cpu, int apicid)
920947
{
948+
static int width, node_width, first = 1;
921949
static int current_node = NUMA_NO_NODE;
922950
int node = early_cpu_to_node(cpu);
923-
static int width, node_width;
924951

925952
if (!width)
926953
width = num_digits(num_possible_cpus()) + 1; /* + '#' sign */
927954

928955
if (!node_width)
929956
node_width = num_digits(num_possible_nodes()) + 1; /* + '#' */
930957

931-
if (cpu == 1)
932-
printk(KERN_INFO "x86: Booting SMP configuration:\n");
933-
934958
if (system_state < SYSTEM_RUNNING) {
959+
if (first)
960+
pr_info("x86: Booting SMP configuration:\n");
961+
935962
if (node != current_node) {
936963
if (current_node > (-1))
937964
pr_cont("\n");
@@ -942,11 +969,11 @@ static void announce_cpu(int cpu, int apicid)
942969
}
943970

944971
/* Add padding for the BSP */
945-
if (cpu == 1)
972+
if (first)
946973
pr_cont("%*s", width + 1, " ");
974+
first = 0;
947975

948976
pr_cont("%*s#%d", width - num_digits(cpu), " ", cpu);
949-
950977
} else
951978
pr_info("Booting Node %d Processor %d APIC 0x%x\n",
952979
node, cpu, apicid);
@@ -1236,6 +1263,36 @@ void __init smp_prepare_cpus_common(void)
12361263
set_cpu_sibling_map(0);
12371264
}
12381265

1266+
#ifdef CONFIG_X86_64
1267+
/* Establish whether parallel bringup can be supported. */
1268+
bool __init arch_cpuhp_init_parallel_bringup(void)
1269+
{
1270+
/*
1271+
* Encrypted guests require special handling. They enforce X2APIC
1272+
* mode but the RDMSR to read the APIC ID is intercepted and raises
1273+
* #VC or #VE which cannot be handled in the early startup code.
1274+
*
1275+
* AMD-SEV does not provide a RDMSR GHCB protocol so the early
1276+
* startup code cannot directly communicate with the secure
1277+
* firmware. The alternative solution to retrieve the APIC ID via
1278+
* CPUID(0xb), which is covered by the GHCB protocol, is not viable
1279+
* either because there is no enforcement of the CPUID(0xb)
1280+
* provided "initial" APIC ID to be the same as the real APIC ID.
1281+
*
1282+
* Intel-TDX has a secure RDMSR hypercall, but that needs to be
1283+
* implemented seperately in the low level startup ASM code.
1284+
*/
1285+
if (cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT)) {
1286+
pr_info("Parallel CPU startup disabled due to guest state encryption\n");
1287+
return false;
1288+
}
1289+
1290+
smpboot_control = STARTUP_READ_APICID;
1291+
pr_debug("Parallel CPU startup enabled: 0x%08x\n", smpboot_control);
1292+
return true;
1293+
}
1294+
#endif
1295+
12391296
/*
12401297
* Prepare for SMP bootup.
12411298
* @max_cpus: configured maximum number of CPUs, It is a legacy parameter

0 commit comments

Comments
 (0)