Skip to content

Commit 7e75178

Browse files
dwmw2Peter Zijlstra
authored andcommitted
x86/smpboot: Support parallel startup of secondary CPUs
In parallel startup mode the APs are kicked alive by the control CPU quickly after each other and run through the early startup code in parallel. The real-mode startup code is already serialized with a bit-spinlock to protect the real-mode stack. In parallel startup mode the smpboot_control variable obviously cannot contain the Linux CPU number so the APs have to determine their Linux CPU number on their own. This is required to find the CPUs per CPU offset in order to find the idle task stack and other per CPU data. To achieve this, export the cpuid_to_apicid[] array so that each AP can find its own CPU number by searching therein based on its APIC ID. Introduce a flag in the top bits of smpboot_control which indicates that the AP should find its CPU number by reading the APIC ID from the APIC. This is required because CPUID based APIC ID retrieval can only provide the initial APIC ID, which might have been overruled by the firmware. Some AMD APUs come up with APIC ID = initial APIC ID + 0x10, so the APIC ID to CPU number lookup would fail miserably if based on CPUID. Also virtualization can make its own APIC ID assignements. The only requirement is that the APIC IDs are consistent with the APCI/MADT table. For the boot CPU or in case parallel bringup is disabled the control bits are empty and the CPU number is directly available in bit 0-23 of smpboot_control. [ tglx: Initial proof of concept patch with bitlock and APIC ID lookup ] [ dwmw2: Rework and testing, commit message, CPUID 0x1 and CPU0 support ] [ seanc: Fix stray override of initial_gs in common_cpu_up() ] [ Oleksandr Natalenko: reported suspend/resume issue fixed in x86_acpi_suspend_lowlevel ] [ tglx: Make it read the APIC ID from the APIC instead of using CPUID, split the bitlock part out ] Co-developed-by: Thomas Gleixner <[email protected]> Co-developed-by: Brian Gerst <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Brian Gerst <[email protected]> Signed-off-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 f6f1ae9 commit 7e75178

File tree

7 files changed

+83
-4
lines changed

7 files changed

+83
-4
lines changed

arch/x86/include/asm/apic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ extern int local_apic_timer_c2_ok;
5555
extern int disable_apic;
5656
extern unsigned int lapic_timer_period;
5757

58+
extern int cpuid_to_apicid[];
59+
5860
extern enum apic_intr_mode_id apic_intr_mode;
5961
enum apic_intr_mode_id {
6062
APIC_PIC,

arch/x86/include/asm/apicdef.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@
138138
#define APIC_EILVT_MASKED (1 << 16)
139139

140140
#define APIC_BASE (fix_to_virt(FIX_APIC_BASE))
141-
#define APIC_BASE_MSR 0x800
141+
#define APIC_BASE_MSR 0x800
142+
#define APIC_X2APIC_ID_MSR 0x802
142143
#define XAPIC_ENABLE (1UL << 11)
143144
#define X2APIC_ENABLE (1UL << 10)
144145

@@ -162,6 +163,7 @@
162163
#define APIC_CPUID(apicid) ((apicid) & XAPIC_DEST_CPUS_MASK)
163164
#define NUM_APIC_CLUSTERS ((BAD_APICID + 1) >> XAPIC_DEST_CPUS_SHIFT)
164165

166+
#ifndef __ASSEMBLY__
165167
/*
166168
* the local APIC register structure, memory mapped. Not terribly well
167169
* tested, but we might eventually use this one in the future - the
@@ -435,4 +437,5 @@ enum apic_delivery_modes {
435437
APIC_DELIVERY_MODE_EXTINT = 7,
436438
};
437439

440+
#endif /* !__ASSEMBLY__ */
438441
#endif /* _ASM_X86_APICDEF_H */

arch/x86/include/asm/smp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,10 @@ extern unsigned long apic_mmio_base;
200200

201201
#endif /* !__ASSEMBLY__ */
202202

203+
/* Control bits for startup_64 */
204+
#define STARTUP_READ_APICID 0x80000000
205+
206+
/* Top 8 bits are reserved for control */
207+
#define STARTUP_PARALLEL_MASK 0xFF000000
208+
203209
#endif /* _ASM_X86_SMP_H */

arch/x86/kernel/acpi/sleep.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <asm/cacheflush.h>
1717
#include <asm/realmode.h>
1818
#include <asm/hypervisor.h>
19+
#include <asm/smp.h>
1920

2021
#include <linux/ftrace.h>
2122
#include "../../realmode/rm/wakeup.h"
@@ -127,7 +128,13 @@ int x86_acpi_suspend_lowlevel(void)
127128
* value is in the actual %rsp register.
128129
*/
129130
current->thread.sp = (unsigned long)temp_stack + sizeof(temp_stack);
130-
smpboot_control = smp_processor_id();
131+
/*
132+
* Ensure the CPU knows which one it is when it comes back, if
133+
* it isn't in parallel mode and expected to work that out for
134+
* itself.
135+
*/
136+
if (!(smpboot_control & STARTUP_PARALLEL_MASK))
137+
smpboot_control = smp_processor_id();
131138
#endif
132139
initial_code = (unsigned long)wakeup_long64;
133140
saved_magic = 0x123456789abcdef0L;

arch/x86/kernel/apic/apic.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2380,7 +2380,7 @@ static int nr_logical_cpuids = 1;
23802380
/*
23812381
* Used to store mapping between logical CPU IDs and APIC IDs.
23822382
*/
2383-
static int cpuid_to_apicid[] = {
2383+
int cpuid_to_apicid[] = {
23842384
[0 ... NR_CPUS - 1] = -1,
23852385
};
23862386

arch/x86/kernel/head_64.S

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
#include "../entry/calling.h"
2525
#include <asm/export.h>
2626
#include <asm/nospec-branch.h>
27+
#include <asm/apicdef.h>
2728
#include <asm/fixmap.h>
29+
#include <asm/smp.h>
2830

2931
/*
3032
* We are not able to switch in one step to the final KERNEL ADDRESS SPACE
@@ -234,8 +236,67 @@ SYM_INNER_LABEL(secondary_startup_64_no_verify, SYM_L_GLOBAL)
234236
ANNOTATE_NOENDBR // above
235237

236238
#ifdef CONFIG_SMP
239+
/*
240+
* For parallel boot, the APIC ID is read from the APIC, and then
241+
* used to look up the CPU number. For booting a single CPU, the
242+
* CPU number is encoded in smpboot_control.
243+
*
244+
* Bit 31 STARTUP_READ_APICID (Read APICID from APIC)
245+
* Bit 0-23 CPU# if STARTUP_xx flags are not set
246+
*/
237247
movl smpboot_control(%rip), %ecx
248+
testl $STARTUP_READ_APICID, %ecx
249+
jnz .Lread_apicid
250+
/*
251+
* No control bit set, single CPU bringup. CPU number is provided
252+
* in bit 0-23. This is also the boot CPU case (CPU number 0).
253+
*/
254+
andl $(~STARTUP_PARALLEL_MASK), %ecx
255+
jmp .Lsetup_cpu
256+
257+
.Lread_apicid:
258+
/* Check whether X2APIC mode is already enabled */
259+
mov $MSR_IA32_APICBASE, %ecx
260+
rdmsr
261+
testl $X2APIC_ENABLE, %eax
262+
jnz .Lread_apicid_msr
263+
264+
/* Read the APIC ID from the fix-mapped MMIO space. */
265+
movq apic_mmio_base(%rip), %rcx
266+
addq $APIC_ID, %rcx
267+
movl (%rcx), %eax
268+
shr $24, %eax
269+
jmp .Llookup_AP
270+
271+
.Lread_apicid_msr:
272+
mov $APIC_X2APIC_ID_MSR, %ecx
273+
rdmsr
274+
275+
.Llookup_AP:
276+
/* EAX contains the APIC ID of the current CPU */
277+
xorq %rcx, %rcx
278+
leaq cpuid_to_apicid(%rip), %rbx
279+
280+
.Lfind_cpunr:
281+
cmpl (%rbx,%rcx,4), %eax
282+
jz .Lsetup_cpu
283+
inc %ecx
284+
#ifdef CONFIG_FORCE_NR_CPUS
285+
cmpl $NR_CPUS, %ecx
286+
#else
287+
cmpl nr_cpu_ids(%rip), %ecx
288+
#endif
289+
jb .Lfind_cpunr
290+
291+
/* APIC ID not found in the table. Drop the trampoline lock and bail. */
292+
movq trampoline_lock(%rip), %rax
293+
movl $0, (%rax)
294+
295+
1: cli
296+
hlt
297+
jmp 1b
238298

299+
.Lsetup_cpu:
239300
/* Get the per cpu offset for the given CPU# which is in ECX */
240301
movq __per_cpu_offset(,%rcx,8), %rdx
241302
#else

arch/x86/kernel/smpboot.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,7 @@ static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
996996
if (IS_ENABLED(CONFIG_X86_32)) {
997997
early_gdt_descr.address = (unsigned long)get_cpu_gdt_rw(cpu);
998998
initial_stack = idle->thread.sp;
999-
} else {
999+
} else if (!(smpboot_control & STARTUP_PARALLEL_MASK)) {
10001000
smpboot_control = cpu;
10011001
}
10021002

0 commit comments

Comments
 (0)