summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-19 17:44:54 +0000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-20 16:09:45 +1000
commit344eb010b2e399069bac474a9fd0ba04908a2601 (patch)
tree5d3e5a2c62651c437318a23b9934fec027576cb2
parent78b782cb788cadbda151ecb61753c109602a250c (diff)
powerpc/powernv: Add CPU hotplug support
Unplugged CPU go into NAP mode in a loop until woken up Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/Kconfig2
-rw-r--r--arch/powerpc/platforms/powernv/smp.c78
2 files changed, 78 insertions, 2 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 62711421cd64..8523bd1b8d7e 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -323,7 +323,7 @@ config SWIOTLB
config HOTPLUG_CPU
bool "Support for enabling/disabling CPUs"
- depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC)
+ depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC || PPC_POWERNV)
---help---
Say Y here to be able to disable and re-enable individual
CPUs at runtime on SMP machines.
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 36c715102c58..4f4ec3797eb6 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -33,7 +33,14 @@
#include "powernv.h"
-static void __devinit pnv_smp_setup_cpu(int cpu)
+#ifdef DEBUG
+#include <asm/udbg.h>
+#define DBG(fmt...) udbg_printf(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+static void __cpuinit pnv_smp_setup_cpu(int cpu)
{
if (cpu != boot_cpuid)
xics_setup_cpu();
@@ -55,6 +62,67 @@ static int pnv_smp_cpu_bootable(unsigned int nr)
return 1;
}
+#ifdef CONFIG_HOTPLUG_CPU
+
+static int pnv_smp_cpu_disable(void)
+{
+ int cpu = smp_processor_id();
+
+ /* This is identical to pSeries... might consolidate by
+ * moving migrate_irqs_away to a ppc_md with default to
+ * the generic fixup_irqs. --BenH.
+ */
+ set_cpu_online(cpu, false);
+ vdso_data->processorCount--;
+ if (cpu == boot_cpuid)
+ boot_cpuid = cpumask_any(cpu_online_mask);
+ xics_migrate_irqs_away();
+ return 0;
+}
+
+static void pnv_smp_cpu_kill_self(void)
+{
+ unsigned int cpu;
+
+ /* If powersave_nap is enabled, use NAP mode, else just
+ * spin aimlessly
+ */
+ if (!powersave_nap) {
+ generic_mach_cpu_die();
+ return;
+ }
+
+ /* Standard hot unplug procedure */
+ local_irq_disable();
+ idle_task_exit();
+ current->active_mm = NULL; /* for sanity */
+ cpu = smp_processor_id();
+ DBG("CPU%d offline\n", cpu);
+ generic_set_cpu_dead(cpu);
+ smp_wmb();
+
+ /* We don't want to take decrementer interrupts while we are offline,
+ * so clear LPCR:PECE1. We keep PECE2 enabled.
+ */
+ mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
+ while (!generic_check_cpu_restart(cpu)) {
+ power7_idle();
+ if (!generic_check_cpu_restart(cpu)) {
+ DBG("CPU%d Unexpected exit while offline !\n", cpu);
+ /* We may be getting an IPI, so we re-enable
+ * interrupts to process it, it will be ignored
+ * since we aren't online (hopefully)
+ */
+ local_irq_enable();
+ local_irq_disable();
+ }
+ }
+ mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1);
+ DBG("CPU%d coming online...\n", cpu);
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
+
static struct smp_ops_t pnv_smp_ops = {
.message_pass = smp_muxed_ipi_message_pass,
.cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */
@@ -62,6 +130,10 @@ static struct smp_ops_t pnv_smp_ops = {
.kick_cpu = smp_generic_kick_cpu,
.setup_cpu = pnv_smp_setup_cpu,
.cpu_bootable = pnv_smp_cpu_bootable,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_disable = pnv_smp_cpu_disable,
+ .cpu_die = generic_cpu_die,
+#endif /* CONFIG_HOTPLUG_CPU */
};
/* This is called very early during platform setup_arch */
@@ -80,4 +152,8 @@ void __init pnv_smp_init(void)
smp_ops->take_timebase = rtas_take_timebase;
}
#endif /* CONFIG_PPC_RTAS */
+
+#ifdef CONFIG_HOTPLUG_CPU
+ ppc_md.cpu_die = pnv_smp_cpu_kill_self;
+#endif
}