diff options
| -rw-r--r-- | arch/arm/Kconfig | 7 | ||||
| -rw-r--r-- | arch/arm/kernel/irq.c | 31 | ||||
| -rw-r--r-- | arch/arm/kernel/process.c | 9 | ||||
| -rw-r--r-- | arch/arm/kernel/smp.c | 85 | ||||
| -rw-r--r-- | include/asm-arm/irq.h | 1 | ||||
| -rw-r--r-- | include/asm-arm/smp.h | 10 |
6 files changed, 143 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index dc6d8342e5e6..6b12d71978de 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -349,6 +349,13 @@ config NR_CPUS | |||
| 349 | depends on SMP | 349 | depends on SMP |
| 350 | default "4" | 350 | default "4" |
| 351 | 351 | ||
| 352 | config HOTPLUG_CPU | ||
| 353 | bool "Support for hot-pluggable CPUs (EXPERIMENTAL)" | ||
| 354 | depends on SMP && HOTPLUG && EXPERIMENTAL | ||
| 355 | help | ||
| 356 | Say Y here to experiment with turning CPUs off and on. CPUs | ||
| 357 | can be controlled through /sys/devices/system/cpu. | ||
| 358 | |||
| 352 | config PREEMPT | 359 | config PREEMPT |
| 353 | bool "Preemptible Kernel (EXPERIMENTAL)" | 360 | bool "Preemptible Kernel (EXPERIMENTAL)" |
| 354 | depends on EXPERIMENTAL | 361 | depends on EXPERIMENTAL |
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 3284118f356b..9def4404e1f2 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c | |||
| @@ -1050,3 +1050,34 @@ static int __init noirqdebug_setup(char *str) | |||
| 1050 | } | 1050 | } |
| 1051 | 1051 | ||
| 1052 | __setup("noirqdebug", noirqdebug_setup); | 1052 | __setup("noirqdebug", noirqdebug_setup); |
| 1053 | |||
| 1054 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 1055 | /* | ||
| 1056 | * The CPU has been marked offline. Migrate IRQs off this CPU. If | ||
| 1057 | * the affinity settings do not allow other CPUs, force them onto any | ||
| 1058 | * available CPU. | ||
| 1059 | */ | ||
| 1060 | void migrate_irqs(void) | ||
| 1061 | { | ||
| 1062 | unsigned int i, cpu = smp_processor_id(); | ||
| 1063 | |||
| 1064 | for (i = 0; i < NR_IRQS; i++) { | ||
| 1065 | struct irqdesc *desc = irq_desc + i; | ||
| 1066 | |||
| 1067 | if (desc->cpu == cpu) { | ||
| 1068 | unsigned int newcpu = any_online_cpu(desc->affinity); | ||
| 1069 | |||
| 1070 | if (newcpu == NR_CPUS) { | ||
| 1071 | if (printk_ratelimit()) | ||
| 1072 | printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n", | ||
| 1073 | i, cpu); | ||
| 1074 | |||
| 1075 | cpus_setall(desc->affinity); | ||
| 1076 | newcpu = any_online_cpu(desc->affinity); | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | route_irq(desc, i, newcpu); | ||
| 1080 | } | ||
| 1081 | } | ||
| 1082 | } | ||
| 1083 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 409db6d5ec99..ba298277becd 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
| 27 | #include <linux/kallsyms.h> | 27 | #include <linux/kallsyms.h> |
| 28 | #include <linux/init.h> | 28 | #include <linux/init.h> |
| 29 | #include <linux/cpu.h> | ||
| 29 | 30 | ||
| 30 | #include <asm/system.h> | 31 | #include <asm/system.h> |
| 31 | #include <asm/io.h> | 32 | #include <asm/io.h> |
| @@ -105,6 +106,14 @@ void cpu_idle(void) | |||
| 105 | /* endless idle loop with no priority at all */ | 106 | /* endless idle loop with no priority at all */ |
| 106 | while (1) { | 107 | while (1) { |
| 107 | void (*idle)(void) = pm_idle; | 108 | void (*idle)(void) = pm_idle; |
| 109 | |||
| 110 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 111 | if (cpu_is_offline(smp_processor_id())) { | ||
| 112 | leds_event(led_idle_start); | ||
| 113 | cpu_die(); | ||
| 114 | } | ||
| 115 | #endif | ||
| 116 | |||
| 108 | if (!idle) | 117 | if (!idle) |
| 109 | idle = default_idle; | 118 | idle = default_idle; |
| 110 | preempt_disable(); | 119 | preempt_disable(); |
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index b40915dcd533..edb5a406922f 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c | |||
| @@ -159,6 +159,91 @@ int __cpuinit __cpu_up(unsigned int cpu) | |||
| 159 | return ret; | 159 | return ret; |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 163 | /* | ||
| 164 | * __cpu_disable runs on the processor to be shutdown. | ||
| 165 | */ | ||
| 166 | int __cpuexit __cpu_disable(void) | ||
| 167 | { | ||
| 168 | unsigned int cpu = smp_processor_id(); | ||
| 169 | struct task_struct *p; | ||
| 170 | int ret; | ||
| 171 | |||
| 172 | ret = mach_cpu_disable(cpu); | ||
| 173 | if (ret) | ||
| 174 | return ret; | ||
| 175 | |||
| 176 | /* | ||
| 177 | * Take this CPU offline. Once we clear this, we can't return, | ||
| 178 | * and we must not schedule until we're ready to give up the cpu. | ||
| 179 | */ | ||
| 180 | cpu_clear(cpu, cpu_online_map); | ||
| 181 | |||
| 182 | /* | ||
| 183 | * OK - migrate IRQs away from this CPU | ||
| 184 | */ | ||
| 185 | migrate_irqs(); | ||
| 186 | |||
| 187 | /* | ||
| 188 | * Flush user cache and TLB mappings, and then remove this CPU | ||
| 189 | * from the vm mask set of all processes. | ||
| 190 | */ | ||
| 191 | flush_cache_all(); | ||
| 192 | local_flush_tlb_all(); | ||
| 193 | |||
| 194 | read_lock(&tasklist_lock); | ||
| 195 | for_each_process(p) { | ||
| 196 | if (p->mm) | ||
| 197 | cpu_clear(cpu, p->mm->cpu_vm_mask); | ||
| 198 | } | ||
| 199 | read_unlock(&tasklist_lock); | ||
| 200 | |||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* | ||
| 205 | * called on the thread which is asking for a CPU to be shutdown - | ||
| 206 | * waits until shutdown has completed, or it is timed out. | ||
| 207 | */ | ||
| 208 | void __cpuexit __cpu_die(unsigned int cpu) | ||
| 209 | { | ||
| 210 | if (!platform_cpu_kill(cpu)) | ||
| 211 | printk("CPU%u: unable to kill\n", cpu); | ||
| 212 | } | ||
| 213 | |||
| 214 | /* | ||
| 215 | * Called from the idle thread for the CPU which has been shutdown. | ||
| 216 | * | ||
| 217 | * Note that we disable IRQs here, but do not re-enable them | ||
| 218 | * before returning to the caller. This is also the behaviour | ||
| 219 | * of the other hotplug-cpu capable cores, so presumably coming | ||
| 220 | * out of idle fixes this. | ||
| 221 | */ | ||
| 222 | void __cpuexit cpu_die(void) | ||
| 223 | { | ||
| 224 | unsigned int cpu = smp_processor_id(); | ||
| 225 | |||
| 226 | local_irq_disable(); | ||
| 227 | idle_task_exit(); | ||
| 228 | |||
| 229 | /* | ||
| 230 | * actual CPU shutdown procedure is at least platform (if not | ||
| 231 | * CPU) specific | ||
| 232 | */ | ||
| 233 | platform_cpu_die(cpu); | ||
| 234 | |||
| 235 | /* | ||
| 236 | * Do not return to the idle loop - jump back to the secondary | ||
| 237 | * cpu initialisation. There's some initialisation which needs | ||
| 238 | * to be repeated to undo the effects of taking the CPU offline. | ||
| 239 | */ | ||
| 240 | __asm__("mov sp, %0\n" | ||
| 241 | " b secondary_start_kernel" | ||
| 242 | : | ||
| 243 | : "r" ((void *)current->thread_info + THREAD_SIZE - 8)); | ||
| 244 | } | ||
| 245 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
| 246 | |||
| 162 | /* | 247 | /* |
| 163 | * This is the secondary CPU boot entry. We're using this CPUs | 248 | * This is the secondary CPU boot entry. We're using this CPUs |
| 164 | * idle thread stack, but a set of temporary page tables. | 249 | * idle thread stack, but a set of temporary page tables. |
diff --git a/include/asm-arm/irq.h b/include/asm-arm/irq.h index f97912fbb10f..59975ee43cf1 100644 --- a/include/asm-arm/irq.h +++ b/include/asm-arm/irq.h | |||
| @@ -47,5 +47,6 @@ struct irqaction; | |||
| 47 | struct pt_regs; | 47 | struct pt_regs; |
| 48 | int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); | 48 | int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); |
| 49 | 49 | ||
| 50 | extern void migrate_irqs(void); | ||
| 50 | #endif | 51 | #endif |
| 51 | 52 | ||
diff --git a/include/asm-arm/smp.h b/include/asm-arm/smp.h index dbb4d859c586..551cd3c3093c 100644 --- a/include/asm-arm/smp.h +++ b/include/asm-arm/smp.h | |||
| @@ -66,4 +66,14 @@ struct secondary_data { | |||
| 66 | }; | 66 | }; |
| 67 | extern struct secondary_data secondary_data; | 67 | extern struct secondary_data secondary_data; |
| 68 | 68 | ||
| 69 | extern int __cpu_disable(void); | ||
| 70 | extern int mach_cpu_disable(unsigned int cpu); | ||
| 71 | |||
| 72 | extern void __cpu_die(unsigned int cpu); | ||
| 73 | extern void cpu_die(void); | ||
| 74 | |||
| 75 | extern void platform_cpu_die(unsigned int cpu); | ||
| 76 | extern int platform_cpu_kill(unsigned int cpu); | ||
| 77 | extern void platform_cpu_enable(unsigned int cpu); | ||
| 78 | |||
| 69 | #endif /* ifndef __ASM_ARM_SMP_H */ | 79 | #endif /* ifndef __ASM_ARM_SMP_H */ |
