Commit a054a811 authored by Russell King's avatar Russell King Committed by Russell King

[ARM SMP] Add hotplug CPU infrastructure

This patch adds the infrastructure to support hotplug CPU on ARM
platforms.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 273c2cdb
...@@ -349,6 +349,13 @@ config NR_CPUS ...@@ -349,6 +349,13 @@ config NR_CPUS
depends on SMP depends on SMP
default "4" default "4"
config HOTPLUG_CPU
bool "Support for hot-pluggable CPUs (EXPERIMENTAL)"
depends on SMP && HOTPLUG && EXPERIMENTAL
help
Say Y here to experiment with turning CPUs off and on. CPUs
can be controlled through /sys/devices/system/cpu.
config PREEMPT config PREEMPT
bool "Preemptible Kernel (EXPERIMENTAL)" bool "Preemptible Kernel (EXPERIMENTAL)"
depends on EXPERIMENTAL depends on EXPERIMENTAL
......
...@@ -1050,3 +1050,34 @@ static int __init noirqdebug_setup(char *str) ...@@ -1050,3 +1050,34 @@ static int __init noirqdebug_setup(char *str)
} }
__setup("noirqdebug", noirqdebug_setup); __setup("noirqdebug", noirqdebug_setup);
#ifdef CONFIG_HOTPLUG_CPU
/*
* The CPU has been marked offline. Migrate IRQs off this CPU. If
* the affinity settings do not allow other CPUs, force them onto any
* available CPU.
*/
void migrate_irqs(void)
{
unsigned int i, cpu = smp_processor_id();
for (i = 0; i < NR_IRQS; i++) {
struct irqdesc *desc = irq_desc + i;
if (desc->cpu == cpu) {
unsigned int newcpu = any_online_cpu(desc->affinity);
if (newcpu == NR_CPUS) {
if (printk_ratelimit())
printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n",
i, cpu);
cpus_setall(desc->affinity);
newcpu = any_online_cpu(desc->affinity);
}
route_irq(desc, i, newcpu);
}
}
}
#endif /* CONFIG_HOTPLUG_CPU */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/cpu.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -105,6 +106,14 @@ void cpu_idle(void) ...@@ -105,6 +106,14 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */ /* endless idle loop with no priority at all */
while (1) { while (1) {
void (*idle)(void) = pm_idle; void (*idle)(void) = pm_idle;
#ifdef CONFIG_HOTPLUG_CPU
if (cpu_is_offline(smp_processor_id())) {
leds_event(led_idle_start);
cpu_die();
}
#endif
if (!idle) if (!idle)
idle = default_idle; idle = default_idle;
preempt_disable(); preempt_disable();
......
...@@ -159,6 +159,91 @@ int __cpuinit __cpu_up(unsigned int cpu) ...@@ -159,6 +159,91 @@ int __cpuinit __cpu_up(unsigned int cpu)
return ret; return ret;
} }
#ifdef CONFIG_HOTPLUG_CPU
/*
* __cpu_disable runs on the processor to be shutdown.
*/
int __cpuexit __cpu_disable(void)
{
unsigned int cpu = smp_processor_id();
struct task_struct *p;
int ret;
ret = mach_cpu_disable(cpu);
if (ret)
return ret;
/*
* Take this CPU offline. Once we clear this, we can't return,
* and we must not schedule until we're ready to give up the cpu.
*/
cpu_clear(cpu, cpu_online_map);
/*
* OK - migrate IRQs away from this CPU
*/
migrate_irqs();
/*
* Flush user cache and TLB mappings, and then remove this CPU
* from the vm mask set of all processes.
*/
flush_cache_all();
local_flush_tlb_all();
read_lock(&tasklist_lock);
for_each_process(p) {
if (p->mm)
cpu_clear(cpu, p->mm->cpu_vm_mask);
}
read_unlock(&tasklist_lock);
return 0;
}
/*
* called on the thread which is asking for a CPU to be shutdown -
* waits until shutdown has completed, or it is timed out.
*/
void __cpuexit __cpu_die(unsigned int cpu)
{
if (!platform_cpu_kill(cpu))
printk("CPU%u: unable to kill\n", cpu);
}
/*
* Called from the idle thread for the CPU which has been shutdown.
*
* Note that we disable IRQs here, but do not re-enable them
* before returning to the caller. This is also the behaviour
* of the other hotplug-cpu capable cores, so presumably coming
* out of idle fixes this.
*/
void __cpuexit cpu_die(void)
{
unsigned int cpu = smp_processor_id();
local_irq_disable();
idle_task_exit();
/*
* actual CPU shutdown procedure is at least platform (if not
* CPU) specific
*/
platform_cpu_die(cpu);
/*
* Do not return to the idle loop - jump back to the secondary
* cpu initialisation. There's some initialisation which needs
* to be repeated to undo the effects of taking the CPU offline.
*/
__asm__("mov sp, %0\n"
" b secondary_start_kernel"
:
: "r" ((void *)current->thread_info + THREAD_SIZE - 8));
}
#endif /* CONFIG_HOTPLUG_CPU */
/* /*
* This is the secondary CPU boot entry. We're using this CPUs * This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables. * idle thread stack, but a set of temporary page tables.
......
...@@ -47,5 +47,6 @@ struct irqaction; ...@@ -47,5 +47,6 @@ struct irqaction;
struct pt_regs; struct pt_regs;
int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
extern void migrate_irqs(void);
#endif #endif
...@@ -66,4 +66,14 @@ struct secondary_data { ...@@ -66,4 +66,14 @@ struct secondary_data {
}; };
extern struct secondary_data secondary_data; extern struct secondary_data secondary_data;
extern int __cpu_disable(void);
extern int mach_cpu_disable(unsigned int cpu);
extern void __cpu_die(unsigned int cpu);
extern void cpu_die(void);
extern void platform_cpu_die(unsigned int cpu);
extern int platform_cpu_kill(unsigned int cpu);
extern void platform_cpu_enable(unsigned int cpu);
#endif /* ifndef __ASM_ARM_SMP_H */ #endif /* ifndef __ASM_ARM_SMP_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment