OSDN Git Service

s390/smp: reallocate IPL CPU lowcore
authorAlexander Gordeev <agordeev@linux.ibm.com>
Thu, 6 May 2021 14:26:52 +0000 (16:26 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Mon, 7 Jun 2021 15:07:00 +0000 (17:07 +0200)
The lowcore for IPL CPU is special. It is allocated early
in the boot process using memblock and never freed since.
The reason is pcpu_alloc_lowcore() and pcpu_free_lowcore()
routines use page allocator which is not available when
the IPL CPU is getting initialized.

Similar problem is already addressed for stacks - once the
virtual memory is available the early boot stacks get re-
allocated. Doing the same for lowcore will allow freeing
the IPL CPU lowcore and make no difference between the
boot and secondary CPUs.

Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/kernel/setup.c
arch/s390/kernel/smp.c

index f069083..146d017 100644 (file)
@@ -341,27 +341,6 @@ int __init arch_early_irq_init(void)
        return 0;
 }
 
-static int __init stack_realloc(void)
-{
-       unsigned long old, new;
-
-       old = S390_lowcore.async_stack - STACK_INIT_OFFSET;
-       new = stack_alloc();
-       if (!new)
-               panic("Couldn't allocate async stack");
-       WRITE_ONCE(S390_lowcore.async_stack, new + STACK_INIT_OFFSET);
-       free_pages(old, THREAD_SIZE_ORDER);
-
-       old = S390_lowcore.mcck_stack - STACK_INIT_OFFSET;
-       new = stack_alloc();
-       if (!new)
-               panic("Couldn't allocate machine check stack");
-       WRITE_ONCE(S390_lowcore.mcck_stack, new + STACK_INIT_OFFSET);
-       memblock_free_late(old, THREAD_SIZE);
-       return 0;
-}
-early_initcall(stack_realloc);
-
 void __init arch_call_rest_init(void)
 {
        unsigned long stack;
index 4b43996..1e1d61a 100644 (file)
@@ -1234,3 +1234,55 @@ out:
        return rc;
 }
 subsys_initcall(s390_smp_init);
+
+static __always_inline void set_new_lowcore(struct lowcore *lc)
+{
+       struct lowcore *old_lc = &S390_lowcore;
+       struct lowcore *new_lc = lc;
+       u32 pfx;
+       register struct lowcore *reg2 asm ("2") = new_lc;
+       register unsigned long   reg3 asm ("3") = sizeof(*reg2);
+       register struct lowcore *reg4 asm ("4") = old_lc;
+       register unsigned long   reg5 asm ("5") = sizeof(*reg4);
+
+       asm volatile(
+               "       st      2,%[pfx]\n"
+               "       mvcl    2,4\n"
+               "       spx     %[pfx]\n"
+               : "+&d" (reg2), "+&d" (reg3),
+                 "+&d" (reg4), "+&d" (reg5), [pfx] "=Q" (pfx)
+               : : "memory", "cc");
+}
+
+static int __init smp_reinit_ipl_cpu(void)
+{
+       unsigned long async_stack, nodat_stack, mcck_stack;
+       struct lowcore *lc, *lc_ipl;
+       unsigned long flags;
+
+       lc_ipl = lowcore_ptr[0];
+       lc = (struct lowcore *) __get_free_pages(GFP_KERNEL | GFP_DMA, LC_ORDER);
+       nodat_stack = __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
+       async_stack = stack_alloc();
+       mcck_stack = stack_alloc();
+       if (!lc || !nodat_stack || !async_stack || !mcck_stack)
+               panic("Couldn't allocate memory");
+
+       local_irq_save(flags);
+       local_mcck_disable();
+       set_new_lowcore(lc);
+       S390_lowcore.nodat_stack = nodat_stack + STACK_INIT_OFFSET;
+       S390_lowcore.async_stack = async_stack + STACK_INIT_OFFSET;
+       S390_lowcore.mcck_stack = mcck_stack + STACK_INIT_OFFSET;
+       lowcore_ptr[0] = lc;
+       pcpu_devices[0].lowcore = lc;
+       local_mcck_enable();
+       local_irq_restore(flags);
+
+       free_pages(lc_ipl->async_stack - STACK_INIT_OFFSET, THREAD_SIZE_ORDER);
+       memblock_free_late(lc_ipl->mcck_stack - STACK_INIT_OFFSET, THREAD_SIZE);
+       memblock_free_late((unsigned long) lc_ipl, sizeof(*lc_ipl));
+
+       return 0;
+}
+early_initcall(smp_reinit_ipl_cpu);