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 */ |