Skip to content

Commit 2b030aa

Browse files
atishp04palmer-dabbelt
authored andcommitted
RISCV: Support cpu hotplug.
This patch enable support for cpu hotplug in RISC-V. In absensece of generic cpu stop functions, WFI is used to put the cpu in low power state during offline. An IPI is sent to bring it out of WFI during online operation. Tested both on QEMU and HighFive Unleashed board with 4 cpus. Test result follows. $ echo 0 > /sys/devices/system/cpu/cpu2/online [ 31.828562] CPU2: shutdown $ cat /proc/cpuinfo hart : 1 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 hart : 3 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 hart : 4 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 $ echo 0 > /sys/devices/system/cpu/cpu4/online [ 52.968495] CPU4: shutdown $ cat /proc/cpuinfo hart : 1 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 hart : 3 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 $ echo 1 > /sys/devices/system/cpu/cpu4/online [ 64.298250] CPU4: online $ cat /proc/cpuinfo hart : 1 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 hart : 3 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 hart : 4 isa : rv64imafdc mmu : sv39 uarch : sifive,rocket0 Signed-off-by: Atish Patra <[email protected]> Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 1b35cd8 commit 2b030aa

File tree

8 files changed

+123
-10
lines changed

8 files changed

+123
-10
lines changed

arch/riscv/Kconfig

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ config RISCV
1313
select CLONE_BACKWARDS
1414
select COMMON_CLK
1515
select GENERIC_CLOCKEVENTS
16-
select GENERIC_CPU_DEVICES
1716
select GENERIC_IRQ_SHOW
1817
select GENERIC_PCI_IOMAP
1918
select GENERIC_STRNCPY_FROM_USER
@@ -175,6 +174,16 @@ config NR_CPUS
175174
depends on SMP
176175
default "8"
177176

177+
config HOTPLUG_CPU
178+
bool "Support for hot-pluggable CPUs"
179+
select GENERIC_IRQ_MIGRATION
180+
help
181+
182+
Say Y here to experiment with turning CPUs off and on. CPUs
183+
can be controlled through /sys/devices/system/cpu.
184+
185+
Say N if you want to disable CPU hotplug.
186+
178187
config CPU_SUPPORTS_32BIT_KERNEL
179188
bool
180189
config CPU_SUPPORTS_64BIT_KERNEL

arch/riscv/include/asm/csr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
/* Interrupt Enable and Interrupt Pending flags */
5555
#define SIE_SSIE _AC(0x00000002, UL) /* Software Interrupt Enable */
5656
#define SIE_STIE _AC(0x00000020, UL) /* Timer Interrupt Enable */
57+
#define SIE_SEIE _AC(0x000000200, UL) /* External Interrupt Enable */
5758

5859
#define EXC_INST_MISALIGNED 0
5960
#define EXC_INST_ACCESS 1

arch/riscv/include/asm/smp.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424

2525
#ifdef CONFIG_SMP
2626

27-
/* SMP initialization hook for setup_arch */
28-
void init_clockevent(void);
29-
3027
/* SMP initialization hook for setup_arch */
3128
void __init setup_smp(void);
3229

@@ -47,6 +44,12 @@ void arch_send_call_function_single_ipi(int cpu);
4744
/* Interprocessor interrupt handler */
4845
irqreturn_t handle_ipi(void);
4946

47+
#ifdef CONFIG_HOTPLUG_CPU
48+
extern int __cpu_disable(void);
49+
extern void __cpu_die(unsigned int cpu);
50+
extern void cpu_play_dead(void);
51+
extern void boot_sec_cpu(void);
52+
#endif
5053
#endif /* CONFIG_SMP */
5154

5255
#endif /* _ASM_RISCV_SMP_H */

arch/riscv/kernel/head.S

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ relocate:
149149
j .Lsecondary_park
150150
END(_start)
151151

152+
.section .text
153+
.global boot_sec_cpu
154+
155+
boot_sec_cpu:
156+
/* clear all pending flags */
157+
csrw sip, zero
158+
/* Mask all interrupts */
159+
csrw sie, zero
160+
fence
161+
162+
tail smp_callin
163+
152164
__PAGE_ALIGNED_BSS
153165
/* Empty zero page */
154166
.balign PAGE_SIZE

arch/riscv/kernel/process.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ void arch_cpu_idle(void)
4242
local_irq_enable();
4343
}
4444

45+
#ifdef CONFIG_HOTPLUG_CPU
46+
void arch_cpu_idle_dead(void)
47+
{
48+
cpu_play_dead();
49+
}
50+
#endif
51+
4552
void show_regs(struct pt_regs *regs)
4653
{
4754
show_regs_print_info(KERN_DEFAULT);

arch/riscv/kernel/setup.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ EXPORT_SYMBOL(empty_zero_page);
5959

6060
/* The lucky hart to first increment this variable will boot the other cores */
6161
atomic_t hart_lottery;
62+
static DEFINE_PER_CPU(struct cpu, cpu_devices);
6263

6364
#ifdef CONFIG_BLK_DEV_INITRD
6465
static void __init setup_initrd(void)
@@ -218,6 +219,22 @@ void __init setup_arch(char **cmdline_p)
218219
riscv_fill_hwcap();
219220
}
220221

222+
static int __init topology_init(void)
223+
{
224+
int i;
225+
226+
for_each_possible_cpu(i) {
227+
struct cpu *cpu = &per_cpu(cpu_devices, i);
228+
#ifdef CONFIG_HOTPLUG_CPU
229+
cpu->hotpluggable = 1;
230+
#endif
231+
register_cpu(cpu, i);
232+
}
233+
234+
return 0;
235+
}
236+
subsys_initcall(topology_init);
237+
221238
static int __init riscv_device_init(void)
222239
{
223240
return of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

arch/riscv/kernel/smpboot.c

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <linux/irq.h>
3131
#include <linux/of.h>
3232
#include <linux/sched/task_stack.h>
33+
#include <linux/sched/hotplug.h>
3334
#include <asm/irq.h>
3435
#include <asm/mmu_context.h>
3536
#include <asm/tlbflush.h>
@@ -82,20 +83,84 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
8283
__cpu_up_stack_pointer[cpu] = task_stack_page(tidle) + THREAD_SIZE;
8384
__cpu_up_task_pointer[cpu] = tidle;
8485

86+
arch_send_call_function_single_ipi(cpu);
8587
while (!cpu_online(cpu))
8688
cpu_relax();
8789

90+
pr_notice("CPU%u: online\n", cpu);
8891
return 0;
8992
}
9093

9194
void __init smp_cpus_done(unsigned int max_cpus)
9295
{
9396
}
9497

98+
#ifdef CONFIG_HOTPLUG_CPU
99+
/*
100+
* __cpu_disable runs on the processor to be shutdown.
101+
*/
102+
int __cpu_disable(void)
103+
{
104+
unsigned int cpu = smp_processor_id();
105+
int ret;
106+
107+
set_cpu_online(cpu, false);
108+
irq_migrate_all_off_this_cpu();
109+
110+
return 0;
111+
}
112+
/*
113+
* called on the thread which is asking for a CPU to be shutdown -
114+
* waits until shutdown has completed, or it is timed out.
115+
*/
116+
void __cpu_die(unsigned int cpu)
117+
{
118+
int err = 0;
119+
120+
if (!cpu_wait_death(cpu, 5)) {
121+
pr_err("CPU %u: didn't die\n", cpu);
122+
return;
123+
}
124+
pr_notice("CPU%u: shutdown\n", cpu);
125+
}
126+
/*
127+
* Called from the idle thread for the CPU which has been shutdown.
128+
*/
129+
void cpu_play_dead(void)
130+
{
131+
int sipval, sieval, scauseval;
132+
int cpu = smp_processor_id();
133+
134+
idle_task_exit();
135+
136+
(void)cpu_report_death();
137+
138+
/* Do not disable software interrupt to restart cpu after WFI */
139+
csr_clear(sie, SIE_STIE | SIE_SEIE);
140+
141+
/* clear all pending flags */
142+
csr_write(sip, 0);
143+
/* clear any previous scause data */
144+
csr_write(scause, 0);
145+
146+
do {
147+
wait_for_interrupt();
148+
sipval = csr_read(sip);
149+
sieval = csr_read(sie);
150+
scauseval = csr_read(scause);
151+
/* only break if wfi returns for an enabled interrupt */
152+
} while ((sipval & sieval) == 0 &&
153+
scauseval != INTERRUPT_CAUSE_SOFTWARE);
154+
155+
boot_sec_cpu();
156+
}
157+
158+
159+
#endif
95160
/*
96161
* C entry point for a secondary processor.
97162
*/
98-
asmlinkage void __init smp_callin(void)
163+
asmlinkage void smp_callin(void)
99164
{
100165
struct mm_struct *mm = &init_mm;
101166

@@ -104,9 +169,8 @@ asmlinkage void __init smp_callin(void)
104169
current->active_mm = mm;
105170

106171
trap_init();
107-
init_clockevent();
108172
notify_cpu_starting(smp_processor_id());
109-
set_cpu_online(smp_processor_id(), 1);
173+
set_cpu_online(smp_processor_id(), true);
110174
local_flush_tlb_all();
111175
local_irq_enable();
112176
preempt_disable();

arch/riscv/kernel/traps.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ int is_valid_bugaddr(unsigned long pc)
166166
}
167167
#endif /* CONFIG_GENERIC_BUG */
168168

169-
void __init trap_init(void)
169+
void trap_init(void)
170170
{
171171
/*
172172
* Set sup0 scratch register to 0, indicating to exception vector
@@ -175,6 +175,6 @@ void __init trap_init(void)
175175
csr_write(sscratch, 0);
176176
/* Set the exception vector address */
177177
csr_write(stvec, &handle_exception);
178-
/* Enable all interrupts */
179-
csr_write(sie, -1);
178+
/* Enable all interrupts but timer interrupt*/
179+
csr_set(sie, SIE_SSIE | SIE_SEIE);
180180
}

0 commit comments

Comments
 (0)