RISC-V: Use __cpu_up_stack/task_pointer only for spinwait method
authorAtish Patra <atishp@rivosinc.com>
Thu, 20 Jan 2022 09:09:15 +0000 (01:09 -0800)
committerPalmer Dabbelt <palmer@rivosinc.com>
Thu, 20 Jan 2022 17:27:08 +0000 (09:27 -0800)
The __cpu_up_stack/task_pointer array is only used for spinwait method
now. The per cpu array based lookup is also fragile for platforms with
discontiguous/sparse hartids. The spinwait method is only used for
M-mode Linux or older firmwares without SBI HSM extension. For general
Linux systems, ordered booting method is preferred anyways to support
cpu hotplug and kexec.

Make sure that __cpu_up_stack/task_pointer is only used for spinwait
method. Take this opportunity to rename it to
__cpu_spinwait_stack/task_pointer to emphasize the purpose as well.

Reviewed-by: Anup Patel <anup@brainfault.org>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
arch/riscv/include/asm/cpu_ops.h
arch/riscv/kernel/cpu_ops.c
arch/riscv/kernel/cpu_ops_spinwait.c
arch/riscv/kernel/head.S
arch/riscv/kernel/head.h

index a8ec3c5c1bd265ddaff29d158d3c5ecf9e959309..134590f1b84359f47da63b838d1300d63d456aca 100644 (file)
@@ -40,7 +40,5 @@ struct cpu_operations {
 
 extern const struct cpu_operations *cpu_ops[NR_CPUS];
 void __init cpu_set_ops(int cpu);
-void cpu_update_secondary_bootdata(unsigned int cpuid,
-                                  struct task_struct *tidle);
 
 #endif /* ifndef __ASM_CPU_OPS_H */
index 3f5a38b03044444dd7e497048fca76dcb6575c7a..c1e30f403c3b463a7fbaf615aa14bdebabefa0fe 100644 (file)
@@ -8,31 +8,15 @@
 #include <linux/of.h>
 #include <linux/string.h>
 #include <linux/sched.h>
-#include <linux/sched/task_stack.h>
 #include <asm/cpu_ops.h>
 #include <asm/sbi.h>
 #include <asm/smp.h>
 
 const struct cpu_operations *cpu_ops[NR_CPUS] __ro_after_init;
 
-void *__cpu_up_stack_pointer[NR_CPUS] __section(".data");
-void *__cpu_up_task_pointer[NR_CPUS] __section(".data");
-
 extern const struct cpu_operations cpu_ops_sbi;
 extern const struct cpu_operations cpu_ops_spinwait;
 
-void cpu_update_secondary_bootdata(unsigned int cpuid,
-                                  struct task_struct *tidle)
-{
-       int hartid = cpuid_to_hartid_map(cpuid);
-
-       /* Make sure tidle is updated */
-       smp_mb();
-       WRITE_ONCE(__cpu_up_stack_pointer[hartid],
-                  task_stack_page(tidle) + THREAD_SIZE);
-       WRITE_ONCE(__cpu_up_task_pointer[hartid], tidle);
-}
-
 void __init cpu_set_ops(int cpuid)
 {
 #if IS_ENABLED(CONFIG_RISCV_SBI)
index b2c957bb68c1fd87d35a03dc759806569109a032..346847f6c41c854ee719f123ecb087803b488fe9 100644 (file)
@@ -6,11 +6,36 @@
 #include <linux/errno.h>
 #include <linux/of.h>
 #include <linux/string.h>
+#include <linux/sched/task_stack.h>
 #include <asm/cpu_ops.h>
 #include <asm/sbi.h>
 #include <asm/smp.h>
 
 const struct cpu_operations cpu_ops_spinwait;
+void *__cpu_spinwait_stack_pointer[NR_CPUS] __section(".data");
+void *__cpu_spinwait_task_pointer[NR_CPUS] __section(".data");
+
+static void cpu_update_secondary_bootdata(unsigned int cpuid,
+                                  struct task_struct *tidle)
+{
+       int hartid = cpuid_to_hartid_map(cpuid);
+
+       /*
+        * The hartid must be less than NR_CPUS to avoid out-of-bound access
+        * errors for __cpu_spinwait_stack/task_pointer. That is not always possible
+        * for platforms with discontiguous hartid numbering scheme. That's why
+        * spinwait booting is not the recommended approach for any platforms
+        * booting Linux in S-mode and can be disabled in the future.
+        */
+       if (hartid == INVALID_HARTID || hartid >= NR_CPUS)
+               return;
+
+       /* Make sure tidle is updated */
+       smp_mb();
+       WRITE_ONCE(__cpu_spinwait_stack_pointer[hartid],
+                  task_stack_page(tidle) + THREAD_SIZE);
+       WRITE_ONCE(__cpu_spinwait_task_pointer[hartid], tidle);
+}
 
 static int spinwait_cpu_prepare(unsigned int cpuid)
 {
@@ -28,7 +53,7 @@ static int spinwait_cpu_start(unsigned int cpuid, struct task_struct *tidle)
         * selects the first cpu to boot the kernel and causes the remainder
         * of the cpus to spin in a loop waiting for their stack pointer to be
         * setup by that main cpu.  Writing to bootdata
-        * (i.e __cpu_up_stack_pointer) signals to the spinning cpus that they
+        * (i.e __cpu_spinwait_stack_pointer) signals to the spinning cpus that they
         * can continue the boot process.
         */
        cpu_update_secondary_bootdata(cpuid, tidle);
index 05ea372e19099f36fb110b8aa4fa93ca2912a8cc..efa9cd499ac696b0fb66fc2e967e5789500c103c 100644 (file)
@@ -347,9 +347,9 @@ clear_bss_done:
        csrw CSR_TVEC, a3
 
        slli a3, a0, LGREG
-       la a1, __cpu_up_stack_pointer
+       la a1, __cpu_spinwait_stack_pointer
        XIP_FIXUP_OFFSET a1
-       la a2, __cpu_up_task_pointer
+       la a2, __cpu_spinwait_task_pointer
        XIP_FIXUP_OFFSET a2
        add a1, a3, a1
        add a2, a3, a2
index aabbc3ac3e48e9a699514f0f33f2d652f4d6bea4..5393cca777901e37ae071f93c147bc9c278031a7 100644 (file)
@@ -16,7 +16,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa);
 asmlinkage void __init __copy_data(void);
 #endif
 
-extern void *__cpu_up_stack_pointer[];
-extern void *__cpu_up_task_pointer[];
+extern void *__cpu_spinwait_stack_pointer[];
+extern void *__cpu_spinwait_task_pointer[];
 
 #endif /* __ASM_HEAD_H */