From: Zwane Mwaikambo Patch provides a generic hotplug cpu implementation, with the only current user being pmac. This doesn't replace real hotplug code as is currently used by LPAR systems. Ben i can add the additional pmac specific code to put the processor into a sleeping state seperately. Thanks to Nathan for testing. Signed-off-by: Zwane Mwaikambo Signed-off-by: Andrew Morton --- 25-akpm/arch/ppc64/Kconfig | 2 25-akpm/arch/ppc64/kernel/idle.c | 4 + 25-akpm/arch/ppc64/kernel/irq.c | 29 ++++++++++ 25-akpm/arch/ppc64/kernel/pSeries_setup.c | 5 + 25-akpm/arch/ppc64/kernel/pmac_setup.c | 3 + 25-akpm/arch/ppc64/kernel/pmac_smp.c | 5 + 25-akpm/arch/ppc64/kernel/setup.c | 3 - 25-akpm/arch/ppc64/kernel/smp.c | 80 ++++++++++++++++++++++++++++++ 25-akpm/arch/ppc64/kernel/sysfs.c | 6 -- 25-akpm/include/asm-ppc64/machdep.h | 1 25-akpm/include/asm-ppc64/smp.h | 9 +++ 11 files changed, 136 insertions(+), 11 deletions(-) diff -puN arch/ppc64/Kconfig~ppc64-generic-hotplug-cpu-support arch/ppc64/Kconfig --- 25/arch/ppc64/Kconfig~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/Kconfig 2005-02-22 18:16:19.000000000 -0800 @@ -313,7 +313,7 @@ source "drivers/pci/Kconfig" config HOTPLUG_CPU bool "Support for hot-pluggable CPUs" - depends on SMP && EXPERIMENTAL && PPC_PSERIES + depends on SMP && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC) select HOTPLUG ---help--- Say Y here to be able to turn CPUs off and on. diff -puN arch/ppc64/kernel/idle.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/idle.c --- 25/arch/ppc64/kernel/idle.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/idle.c 2005-02-22 18:16:19.000000000 -0800 @@ -293,6 +293,10 @@ static int native_idle(void) power4_idle(); if (need_resched()) schedule(); + + if (cpu_is_offline(smp_processor_id()) && + system_state == SYSTEM_RUNNING) + cpu_die(); } return 0; } diff -puN arch/ppc64/kernel/irq.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/irq.c --- 25/arch/ppc64/kernel/irq.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/irq.c 2005-02-22 18:16:19.000000000 -0800 @@ -116,6 +116,35 @@ skip: return 0; } +#ifdef CONFIG_HOTPLUG_CPU +void fixup_irqs(cpumask_t map) +{ + unsigned int irq; + static int warned; + + for_each_irq(irq) { + cpumask_t mask; + + if (irq_desc[irq].status & IRQ_PER_CPU) + continue; + + cpus_and(mask, irq_affinity[irq], map); + if (any_online_cpu(mask) == NR_CPUS) { + printk("Breaking affinity for irq %i\n", irq); + mask = map; + } + if (irq_desc[irq].handler->set_affinity) + irq_desc[irq].handler->set_affinity(irq, mask); + else if (irq_desc[irq].action && !(warned++)) + printk("Cannot set affinity for irq %i\n", irq); + } + + local_irq_enable(); + mdelay(1); + local_irq_disable(); +} +#endif + extern int noirqdebug; /* diff -puN arch/ppc64/kernel/pmac_setup.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/pmac_setup.c --- 25/arch/ppc64/kernel/pmac_setup.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/pmac_setup.c 2005-02-22 18:16:19.000000000 -0800 @@ -439,6 +439,9 @@ static int __init pmac_probe(int platfor } struct machdep_calls __initdata pmac_md = { +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = generic_mach_cpu_die, +#endif .probe = pmac_probe, .setup_arch = pmac_setup_arch, .init_early = pmac_init_early, diff -puN arch/ppc64/kernel/pmac_smp.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/pmac_smp.c --- 25/arch/ppc64/kernel/pmac_smp.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/pmac_smp.c 2005-02-22 18:16:19.000000000 -0800 @@ -308,4 +308,9 @@ struct smp_ops_t core99_smp_ops __pmacda void __init pmac_setup_smp(void) { smp_ops = &core99_smp_ops; +#ifdef CONFIG_HOTPLUG_CPU + smp_ops->cpu_enable = generic_cpu_enable; + smp_ops->cpu_disable = generic_cpu_disable; + smp_ops->cpu_die = generic_cpu_die; +#endif } diff -puN arch/ppc64/kernel/pSeries_setup.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/pSeries_setup.c --- 25/arch/ppc64/kernel/pSeries_setup.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/pSeries_setup.c 2005-02-22 18:16:19.000000000 -0800 @@ -320,8 +320,9 @@ static void __init pSeries_discover_pic } } -static void pSeries_cpu_die(void) +static void pSeries_mach_cpu_die(void) { + idle_task_exit(); local_irq_disable(); /* Some hardware requires clearing the CPPR, while other hardware does not * it is safe either way @@ -599,7 +600,7 @@ struct machdep_calls __initdata pSeries_ .power_off = rtas_power_off, .halt = rtas_halt, .panic = rtas_os_term, - .cpu_die = pSeries_cpu_die, + .cpu_die = pSeries_mach_cpu_die, .get_boot_time = pSeries_get_boot_time, .get_rtc_time = pSeries_get_rtc_time, .set_rtc_time = pSeries_set_rtc_time, diff -puN arch/ppc64/kernel/setup.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/setup.c --- 25/arch/ppc64/kernel/setup.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/setup.c 2005-02-22 18:16:19.000000000 -0800 @@ -1377,9 +1377,6 @@ early_param("xmon", early_xmon); void cpu_die(void) { - idle_task_exit(); if (ppc_md.cpu_die) ppc_md.cpu_die(); - local_irq_disable(); - for (;;); } diff -puN arch/ppc64/kernel/smp.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/smp.c --- 25/arch/ppc64/kernel/smp.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/smp.c 2005-02-22 18:16:19.000000000 -0800 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -406,10 +407,89 @@ void __devinit smp_prepare_boot_cpu(void current_set[boot_cpuid] = current->thread_info; } +#ifdef CONFIG_HOTPLUG_CPU +/* State of each CPU during hotplug phases */ +DEFINE_PER_CPU(int, cpu_state) = { 0 }; + +int generic_cpu_disable(void) +{ + unsigned int cpu = smp_processor_id(); + + if (cpu == boot_cpuid) + return -EBUSY; + + systemcfg->processorCount--; + cpu_clear(cpu, cpu_online_map); + fixup_irqs(cpu_online_map); + return 0; +} + +int generic_cpu_enable(unsigned int cpu) +{ + /* Do the normal bootup if we haven't + * already bootstrapped. */ + if (system_state != SYSTEM_RUNNING) + return -ENOSYS; + + /* get the target out of it's holding state */ + per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; + wmb(); + + while (!cpu_online(cpu)) + cpu_relax(); + + fixup_irqs(cpu_online_map); + /* counter the irq disable in fixup_irqs */ + local_irq_enable(); + return 0; +} + +void generic_cpu_die(unsigned int cpu) +{ + int i; + + for (i = 0; i < 100; i++) { + rmb(); + if (per_cpu(cpu_state, cpu) == CPU_DEAD) + return; + msleep(100); + } + printk(KERN_ERR "CPU%d didn't die...\n", cpu); +} + +void generic_mach_cpu_die(void) +{ + unsigned int cpu; + + local_irq_disable(); + cpu = smp_processor_id(); + printk(KERN_DEBUG "CPU%d offline\n", cpu); + __get_cpu_var(cpu_state) = CPU_DEAD; + wmb(); + while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE) + cpu_relax(); + + flush_tlb_pending(); + cpu_set(cpu, cpu_online_map); + local_irq_enable(); +} +#endif + +static int __devinit cpu_enable(unsigned int cpu) +{ + if (smp_ops->cpu_enable) + return smp_ops->cpu_enable(cpu); + + return -ENOSYS; +} + int __devinit __cpu_up(unsigned int cpu) { int c; + if (!cpu_enable(cpu)) + return 0; + /* At boot, don't bother with non-present cpus -JSCHOPP */ if (system_state < SYSTEM_RUNNING && !cpu_present(cpu)) return -ENOENT; diff -puN arch/ppc64/kernel/sysfs.c~ppc64-generic-hotplug-cpu-support arch/ppc64/kernel/sysfs.c --- 25/arch/ppc64/kernel/sysfs.c~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/sysfs.c 2005-02-22 18:16:19.000000000 -0800 @@ -18,7 +18,7 @@ #include #include #include - +#include static DEFINE_PER_CPU(struct cpu, cpu_devices); @@ -413,9 +413,7 @@ static int __init topology_init(void) * CPU. For instance, the boot cpu might never be valid * for hotplugging. */ -#ifdef CONFIG_HOTPLUG_CPU - if (systemcfg->platform != PLATFORM_PSERIES_LPAR) -#endif + if (!ppc_md.cpu_die) c->no_control = 1; if (cpu_online(cpu) || (c->no_control == 0)) { diff -puN include/asm-ppc64/machdep.h~ppc64-generic-hotplug-cpu-support include/asm-ppc64/machdep.h --- 25/include/asm-ppc64/machdep.h~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/include/asm-ppc64/machdep.h 2005-02-22 18:16:19.000000000 -0800 @@ -30,6 +30,7 @@ struct smp_ops_t { void (*setup_cpu)(int nr); void (*take_timebase)(void); void (*give_timebase)(void); + int (*cpu_enable)(unsigned int nr); int (*cpu_disable)(void); void (*cpu_die)(unsigned int nr); }; diff -puN include/asm-ppc64/smp.h~ppc64-generic-hotplug-cpu-support include/asm-ppc64/smp.h --- 25/include/asm-ppc64/smp.h~ppc64-generic-hotplug-cpu-support 2005-02-22 18:16:19.000000000 -0800 +++ 25-akpm/include/asm-ppc64/smp.h 2005-02-22 18:16:19.000000000 -0800 @@ -29,7 +29,7 @@ extern int boot_cpuid; extern int boot_cpuid_phys; -extern void cpu_die(void) __attribute__((noreturn)); +extern void cpu_die(void); #ifdef CONFIG_SMP @@ -37,6 +37,13 @@ extern void smp_send_debugger_break(int struct pt_regs; extern void smp_message_recv(int, struct pt_regs *); +#ifdef CONFIG_HOTPLUG_CPU +extern void fixup_irqs(cpumask_t map); +int generic_cpu_disable(void); +int generic_cpu_enable(unsigned int cpu); +void generic_cpu_die(unsigned int cpu); +void generic_mach_cpu_die(void); +#endif #define __smp_processor_id() (get_paca()->paca_index) #define hard_smp_processor_id() (get_paca()->hw_cpu_id) _