]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
use x86 cpu park to speedup smp_init in kexec situation
authorThomas Gleixner <tglx@linutronix.de>
Fri, 12 Feb 2021 17:30:28 +0000 (18:30 +0100)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Tue, 16 Feb 2021 13:19:36 +0000 (13:19 +0000)
On Fri, Feb 12 2021 at 01:29, Thomas Gleixner wrote:
> On Thu, Feb 11 2021 at 22:58, David Woodhouse wrote:
> I have no problem with making that jump based. It does not matter at
> all. But you can't use the idle task stack before the CR3 switch in
> secondary_startup_64 - unless I'm missing something.
>
> And there's more than 'call verify_cpu' on the way which uses stack. Let
> me stare more tomorrow with brain awake.

The below boots on top of mainline. It probably breaks i386 and XEN and
whatever :)

I didn't come around to play with your patches yet and won't be able to
do so before next week.

Thanks,

        Thomas

arch/x86/include/asm/realmode.h
arch/x86/include/asm/smp.h
arch/x86/kernel/acpi/sleep.c
arch/x86/kernel/apic/apic.c
arch/x86/kernel/head_64.S
arch/x86/kernel/smpboot.c
arch/x86/realmode/init.c
arch/x86/realmode/rm/trampoline_64.S
kernel/smpboot.c

index 5db5d083c87322ec4913252d1c22752b85d517f4..e1cc4bc746bc872faaaa972fc2d35dca068fcd0f 100644 (file)
@@ -51,6 +51,7 @@ struct trampoline_header {
        u64 efer;
        u32 cr4;
        u32 flags;
+       u32 lock;
 #endif
 };
 
@@ -64,6 +65,8 @@ extern unsigned long initial_stack;
 extern unsigned long initial_vc_handler;
 #endif
 
+extern u32 *trampoline_lock;
+
 extern unsigned char real_mode_blob[];
 extern unsigned char real_mode_relocs[];
 
index c0538f82c9a220cfafbb20d7d954eff6eff5053e..b7ce698f4e5b4ff2ec65db5a6dfd97f9167856fe 100644 (file)
@@ -187,5 +187,12 @@ extern void nmi_selftest(void);
 #define nmi_selftest() do { } while (0)
 #endif
 
-#endif /* __ASSEMBLY__ */
+extern unsigned int smpboot_control;
+
+#endif /* !__ASSEMBLY__ */
+
+/* Control bits for startup_64 */
+#define        STARTUP_USE_APICID      0x10000
+#define        STARTUP_USE_CPUID_0B    0x20000
+
 #endif /* _ASM_X86_SMP_H */
index cc1fea76aab05dfabb69ca59e3bff20f0643914d..69f4ba736689b976e04d5753d5d4f87706ef8f57 100644 (file)
@@ -114,6 +114,7 @@ int x86_acpi_suspend_lowlevel(void)
        early_gdt_descr.address =
                        (unsigned long)get_cpu_gdt_rw(smp_processor_id());
        initial_gs = per_cpu_offset(smp_processor_id());
+       smpboot_control = 0;
 #endif
        initial_code = (unsigned long)wakeup_long64;
        saved_magic = 0x123456789abcdef0L;
index 6bd20c0de8bc605c9f9d82a1c76060e71b13d3fa..f823cbf53bfb219f675dc2f70017d8f1504d09c7 100644 (file)
@@ -2326,7 +2326,7 @@ static int nr_logical_cpuids = 1;
 /*
  * Used to store mapping between logical CPU IDs and APIC IDs.
  */
-static int cpuid_to_apicid[] = {
+int cpuid_to_apicid[] = {
        [0 ... NR_CPUS - 1] = -1,
 };
 
index 04bddaaba8e25b78aae2c56c1db4eec9d333a529..217672d4e441a93afbde4ac6fef6399f0090aa27 100644 (file)
@@ -25,6 +25,7 @@
 #include <asm/export.h>
 #include <asm/nospec-branch.h>
 #include <asm/fixmap.h>
+#include <asm/smp.h>
 
 /*
  * We are not able to switch in one step to the final KERNEL ADDRESS SPACE
@@ -176,6 +177,64 @@ SYM_INNER_LABEL(secondary_startup_64_no_verify, SYM_L_GLOBAL)
 1:
        UNWIND_HINT_EMPTY
 
+       /*
+        * Is this the boot CPU coming up? If so everything is available
+        * in initial_gs, initial_stack and early_gdt_descr.
+        */
+       movl    smpboot_control(%rip), %eax
+       testl   %eax, %eax
+       jz      .Lsetup_cpu
+
+       /*
+        * Secondary CPUs find out the offsets via the APIC ID. For parallel
+        * boot the APIC ID is retrieved from CPUID, otherwise it's encoded
+        * in smpboot_control:
+        * Bit 0-15     APICID if STARTUP_USE_CPUID_0B is not set
+        * Bit 16       Secondary boot flag
+        * Bit 17       Parallel boot flag
+        */
+       testl   $STARTUP_USE_CPUID_0B, %eax
+       jz      .Lsetup_AP
+
+       mov     $0x0B, %eax
+       xorl    %ecx, %ecx
+       cpuid
+       mov     %edx, %eax
+
+.Lsetup_AP:
+       /* EAX contains the APICID of the current CPU */
+       andl    $0xFFFF, %eax
+       xorl    %ecx, %ecx
+       leaq    cpuid_to_apicid(%rip), %rbx
+
+.Lfind_cpunr:
+       cmpl    (%rbx), %eax
+       jz      .Linit_cpu_data
+       addq    $4, %rbx
+       addq    $8, %rcx
+       jmp     .Lfind_cpunr
+
+.Linit_cpu_data:
+       /* Get the per cpu offset */
+       leaq    __per_cpu_offset(%rip), %rbx
+       addq    %rcx, %rbx
+       movq    (%rbx), %rbx
+       /* Save it for GS BASE setup */
+       movq    %rbx, initial_gs(%rip)
+
+       /* Calculate the GDT address */
+       movq    $gdt_page, %rcx
+       addq    %rbx, %rcx
+       movq    %rcx, early_gdt_descr_base(%rip)
+
+       /* Find the idle task stack */
+       movq    $idle_threads, %rcx
+       addq    %rbx, %rcx
+       movq    (%rcx), %rcx
+       movq    TASK_threadsp(%rcx), %rcx
+       movq    %rcx, initial_stack(%rip)
+
+.Lsetup_cpu:
        /*
         * We must switch to a new descriptor in kernel space for the GDT
         * because soon the kernel won't have access anymore to the userspace
@@ -216,6 +275,14 @@ SYM_INNER_LABEL(secondary_startup_64_no_verify, SYM_L_GLOBAL)
         */
        movq initial_stack(%rip), %rsp
 
+       /* Drop the realmode protection. For the boot CPU the pointer is NULL! */
+       movq    trampoline_lock(%rip), %rax
+       testq   %rax, %rax
+       jz      .Lsetup_idt
+       lock
+       btrl    $0, (%rax)
+
+.Lsetup_idt:
        /* Setup and Load IDT */
        pushq   %rsi
        call    early_setup_idt
@@ -347,6 +414,7 @@ SYM_DATA(initial_vc_handler,        .quad handle_vc_boot_ghcb)
  * reliably detect the end of the stack.
  */
 SYM_DATA(initial_stack, .quad init_thread_union + THREAD_SIZE - SIZEOF_PTREGS)
+SYM_DATA(trampoline_lock, .quad 0);
        __FINITDATA
 
        __INIT
@@ -572,6 +640,9 @@ SYM_DATA_END(level1_fixmap_pgt)
 SYM_DATA(early_gdt_descr,              .word GDT_ENTRIES*8-1)
 SYM_DATA_LOCAL(early_gdt_descr_base,   .quad INIT_PER_CPU_VAR(gdt_page))
 
+       .align 16
+SYM_DATA(smpboot_control,              .long 0)
+
        .align 16
 /* This must match the first entry in level2_kernel_pgt */
 SYM_DATA(phys_base, .quad 0x0)
index 649b8236309b35e41e0808e7ecf322a6f7003a00..6517ca40eb6fe49fb5e97f4037e16ac249a4070e 100644 (file)
@@ -1028,6 +1028,16 @@ int common_cpu_up(unsigned int cpu, struct task_struct *idle)
        return 0;
 }
 
+static void setup_smpboot_control(unsigned int apicid)
+{
+#ifdef CONFIG_X86_64
+       if (boot_cpu_data.cpuid_level < 0x0B)
+               smpboot_control = apicid | STARTUP_USE_APICID;
+       else
+               smpboot_control = STARTUP_USE_CPUID_0B;
+#endif
+}
+
 /*
  * NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad
  * (ie clustered apic addressing mode), this is a LOGICAL apic ID.
@@ -1042,9 +1052,14 @@ static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle,
        unsigned long boot_error = 0;
 
        idle->thread.sp = (unsigned long)task_pt_regs(idle);
-       early_gdt_descr.address = (unsigned long)get_cpu_gdt_rw(cpu);
        initial_code = (unsigned long)start_secondary;
-       initial_stack  = idle->thread.sp;
+
+       if (IS_ENABLED(CONFIG_X86_32)) {
+               early_gdt_descr.address = (unsigned long)get_cpu_gdt_rw(cpu);
+               initial_stack  = idle->thread.sp;
+       }
+
+       setup_smpboot_control(apicid);
 
        /* Enable the espfix hack for this CPU */
        init_espfix_ap(cpu);
index 22fda7d991590504f956f943d4d4d7bfdcea67ee..51b50605c2907dca1e4d8b53376518dd8d2c7294 100644 (file)
@@ -125,6 +125,9 @@ static void __init setup_real_mode(void)
 
        trampoline_header->flags = 0;
 
+       trampoline_lock = &trampoline_header->lock;
+       *trampoline_lock = 0;
+
        trampoline_pgd = (u64 *) __va(real_mode_header->trampoline_pgd);
        trampoline_pgd[0] = trampoline_pgd_entry.pgd;
        trampoline_pgd[511] = init_top_pgt[511].pgd;
index 84c5d1b33d100b33d61a316483f5c34c435edba5..eb5de7730029d421103c5c08ab91f2ca83657306 100644 (file)
@@ -49,6 +49,19 @@ SYM_CODE_START(trampoline_start)
        mov     %ax, %es
        mov     %ax, %ss
 
+       /*
+        * Make sure only one CPU fiddles with the realmode stack
+        */
+.Llock_rm:
+       btw     $0, tr_lock
+       jnc     2f
+       pause
+       jmp     .Llock_rm
+2:
+       lock
+       btsw    $0, tr_lock
+       jc      .Llock_rm
+
        # Setup stack
        movl    $rm_stack_end, %esp
 
@@ -192,6 +205,7 @@ SYM_DATA_START(trampoline_header)
        SYM_DATA(tr_efer,               .space 8)
        SYM_DATA(tr_cr4,                .space 4)
        SYM_DATA(tr_flags,              .space 4)
+       SYM_DATA(tr_lock,               .space 4)
 SYM_DATA_END(trampoline_header)
 
 #include "trampoline_common.S"
index f25208e8df8365e090cedf887638b3e0b00e92bc..0639cf5775e409a3a13c269303b5b508c7a75160 100644 (file)
@@ -25,7 +25,7 @@
  * For the hotplug case we keep the task structs around and reuse
  * them.
  */
-static DEFINE_PER_CPU(struct task_struct *, idle_threads);
+DEFINE_PER_CPU(struct task_struct *, idle_threads);
 
 struct task_struct *idle_thread_get(unsigned int cpu)
 {