]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
powerpc/powernv: Add CPU hotplug support
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Mon, 19 Sep 2011 17:44:54 +0000 (17:44 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Tue, 20 Sep 2011 06:09:45 +0000 (16:09 +1000)
Unplugged CPU go into NAP mode in a loop until woken up

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/Kconfig
arch/powerpc/platforms/powernv/smp.c

index 62711421cd64b234aba1eb5d5706ed3a84a04096..8523bd1b8d7e475385a2bbc763ce1de3fbc5e646 100644 (file)
@@ -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.
index 36c715102c58a027fc089145c49151264d4bc73a..4f4ec3797eb6141d3f1cde8bfbc2b8afb3d3c443 100644 (file)
 
 #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
 }