Skip to content

Commit 2875fe0

Browse files
atishp04palmer-dabbelt
authored andcommitted
RISC-V: Add cpu_ops and modify default booting method
Currently, all non-booting harts start booting after the booting hart updates the per-hart stack pointer. This is done in a way that, it's difficult to implement any other booting method without breaking the backward compatibility. Define a cpu_ops method that allows to introduce other booting methods in future. Modify the current booting method to be compatible with cpu_ops. Signed-off-by: Atish Patra <[email protected]> Reviewed-by: Anup Patel <[email protected]> Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent e011995 commit 2875fe0

File tree

5 files changed

+147
-21
lines changed

5 files changed

+147
-21
lines changed

arch/riscv/include/asm/cpu_ops.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
4+
* Based on arch/arm64/include/asm/cpu_ops.h
5+
*/
6+
#ifndef __ASM_CPU_OPS_H
7+
#define __ASM_CPU_OPS_H
8+
9+
#include <linux/init.h>
10+
#include <linux/sched.h>
11+
#include <linux/threads.h>
12+
13+
/**
14+
* struct cpu_operations - Callback operations for hotplugging CPUs.
15+
*
16+
* @name: Name of the boot protocol.
17+
* @cpu_prepare: Early one-time preparation step for a cpu. If there
18+
* is a mechanism for doing so, tests whether it is
19+
* possible to boot the given HART.
20+
* @cpu_start: Boots a cpu into the kernel.
21+
*/
22+
struct cpu_operations {
23+
const char *name;
24+
int (*cpu_prepare)(unsigned int cpu);
25+
int (*cpu_start)(unsigned int cpu,
26+
struct task_struct *tidle);
27+
};
28+
29+
extern const struct cpu_operations *cpu_ops[NR_CPUS];
30+
void __init cpu_set_ops(int cpu);
31+
void cpu_update_secondary_bootdata(unsigned int cpuid,
32+
struct task_struct *tidle);
33+
34+
#endif /* ifndef __ASM_CPU_OPS_H */

arch/riscv/kernel/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ obj-$(CONFIG_RISCV_M_MODE) += clint.o
3434
obj-$(CONFIG_FPU) += fpu.o
3535
obj-$(CONFIG_SMP) += smpboot.o
3636
obj-$(CONFIG_SMP) += smp.o
37+
obj-$(CONFIG_SMP) += cpu_ops.o
38+
obj-$(CONFIG_SMP) += cpu_ops_spinwait.o
3739
obj-$(CONFIG_MODULES) += module.o
3840
obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o
3941

arch/riscv/kernel/cpu_ops.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
4+
*/
5+
6+
#include <linux/errno.h>
7+
#include <linux/mm.h>
8+
#include <linux/of.h>
9+
#include <linux/string.h>
10+
#include <linux/sched.h>
11+
#include <linux/sched/task_stack.h>
12+
#include <asm/cpu_ops.h>
13+
#include <asm/sbi.h>
14+
#include <asm/smp.h>
15+
16+
const struct cpu_operations *cpu_ops[NR_CPUS] __ro_after_init;
17+
18+
void *__cpu_up_stack_pointer[NR_CPUS];
19+
void *__cpu_up_task_pointer[NR_CPUS];
20+
21+
extern const struct cpu_operations cpu_ops_spinwait;
22+
23+
void cpu_update_secondary_bootdata(unsigned int cpuid,
24+
struct task_struct *tidle)
25+
{
26+
int hartid = cpuid_to_hartid_map(cpuid);
27+
28+
/* Make sure tidle is updated */
29+
smp_mb();
30+
WRITE_ONCE(__cpu_up_stack_pointer[hartid],
31+
task_stack_page(tidle) + THREAD_SIZE);
32+
WRITE_ONCE(__cpu_up_task_pointer[hartid], tidle);
33+
}
34+
35+
void __init cpu_set_ops(int cpuid)
36+
{
37+
cpu_ops[cpuid] = &cpu_ops_spinwait;
38+
}

arch/riscv/kernel/cpu_ops_spinwait.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
4+
*/
5+
6+
#include <linux/errno.h>
7+
#include <linux/of.h>
8+
#include <linux/string.h>
9+
#include <asm/cpu_ops.h>
10+
#include <asm/sbi.h>
11+
#include <asm/smp.h>
12+
13+
const struct cpu_operations cpu_ops_spinwait;
14+
15+
static int spinwait_cpu_prepare(unsigned int cpuid)
16+
{
17+
if (!cpu_ops_spinwait.cpu_start) {
18+
pr_err("cpu start method not defined for CPU [%d]\n", cpuid);
19+
return -ENODEV;
20+
}
21+
return 0;
22+
}
23+
24+
static int spinwait_cpu_start(unsigned int cpuid, struct task_struct *tidle)
25+
{
26+
/*
27+
* In this protocol, all cpus boot on their own accord. _start
28+
* selects the first cpu to boot the kernel and causes the remainder
29+
* of the cpus to spin in a loop waiting for their stack pointer to be
30+
* setup by that main cpu. Writing to bootdata
31+
* (i.e __cpu_up_stack_pointer) signals to the spinning cpus that they
32+
* can continue the boot process.
33+
*/
34+
cpu_update_secondary_bootdata(cpuid, tidle);
35+
36+
return 0;
37+
}
38+
39+
const struct cpu_operations cpu_ops_spinwait = {
40+
.name = "spinwait",
41+
.cpu_prepare = spinwait_cpu_prepare,
42+
.cpu_start = spinwait_cpu_start,
43+
};

arch/riscv/kernel/smpboot.c

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/sched/task_stack.h>
2626
#include <linux/sched/mm.h>
2727
#include <asm/clint.h>
28+
#include <asm/cpu_ops.h>
2829
#include <asm/irq.h>
2930
#include <asm/mmu_context.h>
3031
#include <asm/tlbflush.h>
@@ -34,8 +35,6 @@
3435

3536
#include "head.h"
3637

37-
void *__cpu_up_stack_pointer[NR_CPUS];
38-
void *__cpu_up_task_pointer[NR_CPUS];
3938
static DECLARE_COMPLETION(cpu_running);
4039

4140
void __init smp_prepare_boot_cpu(void)
@@ -46,6 +45,7 @@ void __init smp_prepare_boot_cpu(void)
4645
void __init smp_prepare_cpus(unsigned int max_cpus)
4746
{
4847
int cpuid;
48+
int ret;
4949

5050
/* This covers non-smp usecase mandated by "nosmp" option */
5151
if (max_cpus == 0)
@@ -54,6 +54,11 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
5454
for_each_possible_cpu(cpuid) {
5555
if (cpuid == smp_processor_id())
5656
continue;
57+
if (cpu_ops[cpuid]->cpu_prepare) {
58+
ret = cpu_ops[cpuid]->cpu_prepare(cpuid);
59+
if (ret)
60+
continue;
61+
}
5762
set_cpu_present(cpuid, true);
5863
}
5964
}
@@ -65,6 +70,8 @@ void __init setup_smp(void)
6570
bool found_boot_cpu = false;
6671
int cpuid = 1;
6772

73+
cpu_set_ops(0);
74+
6875
for_each_of_cpu_node(dn) {
6976
hart = riscv_of_processor_hartid(dn);
7077
if (hart < 0)
@@ -92,36 +99,38 @@ void __init setup_smp(void)
9299
cpuid, nr_cpu_ids);
93100

94101
for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++) {
95-
if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID)
102+
if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID) {
103+
cpu_set_ops(cpuid);
96104
set_cpu_possible(cpuid, true);
105+
}
97106
}
98107
}
99108

109+
int start_secondary_cpu(int cpu, struct task_struct *tidle)
110+
{
111+
if (cpu_ops[cpu]->cpu_start)
112+
return cpu_ops[cpu]->cpu_start(cpu, tidle);
113+
114+
return -EOPNOTSUPP;
115+
}
116+
100117
int __cpu_up(unsigned int cpu, struct task_struct *tidle)
101118
{
102119
int ret = 0;
103-
int hartid = cpuid_to_hartid_map(cpu);
104120
tidle->thread_info.cpu = cpu;
105121

106-
/*
107-
* On RISC-V systems, all harts boot on their own accord. Our _start
108-
* selects the first hart to boot the kernel and causes the remainder
109-
* of the harts to spin in a loop waiting for their stack pointer to be
110-
* setup by that main hart. Writing __cpu_up_stack_pointer signals to
111-
* the spinning harts that they can continue the boot process.
112-
*/
113-
smp_mb();
114-
WRITE_ONCE(__cpu_up_stack_pointer[hartid],
115-
task_stack_page(tidle) + THREAD_SIZE);
116-
WRITE_ONCE(__cpu_up_task_pointer[hartid], tidle);
117-
118-
lockdep_assert_held(&cpu_running);
119-
wait_for_completion_timeout(&cpu_running,
122+
ret = start_secondary_cpu(cpu, tidle);
123+
if (!ret) {
124+
lockdep_assert_held(&cpu_running);
125+
wait_for_completion_timeout(&cpu_running,
120126
msecs_to_jiffies(1000));
121127

122-
if (!cpu_online(cpu)) {
123-
pr_crit("CPU%u: failed to come online\n", cpu);
124-
ret = -EIO;
128+
if (!cpu_online(cpu)) {
129+
pr_crit("CPU%u: failed to come online\n", cpu);
130+
ret = -EIO;
131+
}
132+
} else {
133+
pr_crit("CPU%u: failed to start\n", cpu);
125134
}
126135

127136
return ret;

0 commit comments

Comments
 (0)