summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2009-06-23 10:00:31 +0100
committerRalf Baechle <ralf@linux-mips.org>2009-06-24 18:34:40 +0100
commit1b2bc75c1bde6581d2694cb3ed7fb06b69685008 (patch)
tree800fc23052bccb1fbf8acfbaabbf5648c69daa9e
parent4ac4aa5cc3b00cc558575065ae71043e92d1a69a (diff)
MIPS: Add arch generic CPU hotplug
Each platform has to add support for CPU hotplugging itself by providing suitable definitions for the cpu_disable and cpu_die of the smp_ops methods and setting SYS_SUPPORTS_HOTPLUG_CPU. A platform should only set SYS_SUPPORTS_HOTPLUG_CPU once all it's smp_ops definitions have the necessary changes. This patch contains the changes to the dummy smp_ops definition for uni-processor systems. Parts of the code contributed by Cavium Inc. Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/Kconfig11
-rw-r--r--arch/mips/include/asm/smp-ops.h4
-rw-r--r--arch/mips/include/asm/smp.h19
-rw-r--r--arch/mips/kernel/process.c13
-rw-r--r--arch/mips/kernel/smp-up.c16
-rw-r--r--arch/mips/kernel/smp.c17
-rw-r--r--arch/mips/kernel/topology.c5
7 files changed, 78 insertions, 7 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index b29f0280d712..eb7e8d795c6a 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -784,8 +784,17 @@ config SYS_HAS_EARLY_PRINTK
bool
config HOTPLUG_CPU
+ bool "Support for hot-pluggable CPUs"
+ depends on SMP && HOTPLUG && SYS_SUPPORTS_HOTPLUG_CPU
+ help
+ Say Y here to allow turning CPUs off and on. CPUs can be
+ controlled through /sys/devices/system/cpu.
+ (Note: power management support will enable this option
+ automatically on SMP systems. )
+ Say N if you want to disable CPU hotplug.
+
+config SYS_SUPPORTS_HOTPLUG_CPU
bool
- default n
config I8259
bool
diff --git a/arch/mips/include/asm/smp-ops.h b/arch/mips/include/asm/smp-ops.h
index 64ffc0290b84..fd545547b8aa 100644
--- a/arch/mips/include/asm/smp-ops.h
+++ b/arch/mips/include/asm/smp-ops.h
@@ -26,6 +26,10 @@ struct plat_smp_ops {
void (*boot_secondary)(int cpu, struct task_struct *idle);
void (*smp_setup)(void);
void (*prepare_cpus)(unsigned int max_cpus);
+#ifdef CONFIG_HOTPLUG_CPU
+ int (*cpu_disable)(void);
+ void (*cpu_die)(unsigned int cpu);
+#endif
};
extern void register_smp_ops(struct plat_smp_ops *ops);
diff --git a/arch/mips/include/asm/smp.h b/arch/mips/include/asm/smp.h
index 2f83fa8631db..01f813dc3888 100644
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -41,6 +41,7 @@ extern int __cpu_logical_map[NR_CPUS];
/* Octeon - Tell another core to flush its icache */
#define SMP_ICACHE_FLUSH 0x4
+extern cpumask_t cpu_callin_map;
extern void asmlinkage smp_bootstrap(void);
@@ -56,6 +57,24 @@ static inline void smp_send_reschedule(int cpu)
mp_ops->send_ipi_single(cpu, SMP_RESCHEDULE_YOURSELF);
}
+#ifdef CONFIG_HOTPLUG_CPU
+static inline int __cpu_disable(void)
+{
+ extern struct plat_smp_ops *mp_ops; /* private */
+
+ return mp_ops->cpu_disable();
+}
+
+static inline void __cpu_die(unsigned int cpu)
+{
+ extern struct plat_smp_ops *mp_ops; /* private */
+
+ mp_ops->cpu_die(cpu);
+}
+
+extern void play_dead(void);
+#endif
+
extern asmlinkage void smp_call_function_interrupt(void);
extern void arch_send_call_function_single_ipi(int cpu);
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 1eaaa450e20c..c09d681b7181 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -50,10 +50,15 @@
*/
void __noreturn cpu_idle(void)
{
+ int cpu;
+
+ /* CPU is going idle. */
+ cpu = smp_processor_id();
+
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
- while (!need_resched()) {
+ while (!need_resched() && cpu_online(cpu)) {
#ifdef CONFIG_MIPS_MT_SMTC
extern void smtc_idle_loop_hook(void);
@@ -62,6 +67,12 @@ void __noreturn cpu_idle(void)
if (cpu_wait)
(*cpu_wait)();
}
+#ifdef CONFIG_HOTPLUG_CPU
+ if (!cpu_online(cpu) && !cpu_isset(cpu, cpu_callin_map) &&
+ (system_state == SYSTEM_RUNNING ||
+ system_state == SYSTEM_BOOTING))
+ play_dead();
+#endif
tick_nohz_restart_sched_tick();
preempt_enable_no_resched();
schedule();
diff --git a/arch/mips/kernel/smp-up.c b/arch/mips/kernel/smp-up.c
index 878e3733bbb2..2508d55d68fd 100644
--- a/arch/mips/kernel/smp-up.c
+++ b/arch/mips/kernel/smp-up.c
@@ -55,6 +55,18 @@ static void __init up_prepare_cpus(unsigned int max_cpus)
{
}
+#ifdef CONFIG_HOTPLUG_CPU
+static int up_cpu_disable(void)
+{
+ return -ENOSYS;
+}
+
+static void up_cpu_die(unsigned int cpu)
+{
+ BUG();
+}
+#endif
+
struct plat_smp_ops up_smp_ops = {
.send_ipi_single = up_send_ipi_single,
.send_ipi_mask = up_send_ipi_mask,
@@ -64,4 +76,8 @@ struct plat_smp_ops up_smp_ops = {
.boot_secondary = up_boot_secondary,
.smp_setup = up_smp_setup,
.prepare_cpus = up_prepare_cpus,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_disable = up_cpu_disable,
+ .cpu_die = up_cpu_die,
+#endif
};
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index 58f4679bbd43..bc7d9b05e2f4 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -45,7 +45,7 @@
#include <asm/mipsmtregs.h>
#endif /* CONFIG_MIPS_MT_SMTC */
-static volatile cpumask_t cpu_callin_map; /* Bitmask of started secondaries */
+volatile cpumask_t cpu_callin_map; /* Bitmask of started secondaries */
int __cpu_number_map[NR_CPUS]; /* Map physical to logical */
int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */
@@ -201,6 +201,8 @@ void __devinit smp_prepare_boot_cpu(void)
* and keep control until "cpu_online(cpu)" is set. Note: cpu is
* physical, not logical.
*/
+static struct task_struct *cpu_idle_thread[NR_CPUS];
+
int __cpuinit __cpu_up(unsigned int cpu)
{
struct task_struct *idle;
@@ -210,9 +212,16 @@ int __cpuinit __cpu_up(unsigned int cpu)
* The following code is purely to make sure
* Linux can schedule processes on this slave.
*/
- idle = fork_idle(cpu);
- if (IS_ERR(idle))
- panic(KERN_ERR "Fork failed for CPU %d", cpu);
+ if (!cpu_idle_thread[cpu]) {
+ idle = fork_idle(cpu);
+ cpu_idle_thread[cpu] = idle;
+
+ if (IS_ERR(idle))
+ panic(KERN_ERR "Fork failed for CPU %d", cpu);
+ } else {
+ idle = cpu_idle_thread[cpu];
+ init_idle(idle, cpu);
+ }
mp_ops->boot_secondary(cpu, idle);
diff --git a/arch/mips/kernel/topology.c b/arch/mips/kernel/topology.c
index 660e44ed44d7..cf3eb61fad12 100644
--- a/arch/mips/kernel/topology.c
+++ b/arch/mips/kernel/topology.c
@@ -17,7 +17,10 @@ static int __init topology_init(void)
#endif /* CONFIG_NUMA */
for_each_present_cpu(i) {
- ret = register_cpu(&per_cpu(cpu_devices, i), i);
+ struct cpu *c = &per_cpu(cpu_devices, i);
+
+ c->hotpluggable = 1;
+ ret = register_cpu(c, i);
if (ret)
printk(KERN_WARNING "topology_init: register_cpu %d "
"failed (%d)\n", i, ret);