[ARM SMP] Add hotplug CPU infrastructure
This patch adds the infrastructure to support hotplug CPU on ARM platforms. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
committed by
Russell King
parent
273c2cdb2b
commit
a054a81159
@@ -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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
__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 @@
|
|||||||
#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)
|
|||||||
/* 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)
|
|||||||
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;
|
|||||||
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 {
|
|||||||
};
|
};
|
||||||
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 */
|
||||||
|
Reference in New Issue
Block a user