diff options
author | Mark Rutland <mark.rutland@arm.com> | 2013-10-24 15:30:18 -0400 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2013-10-25 06:33:21 -0400 |
commit | 9327e2c6bb8cb0131b38a07847cd58c78dc095e9 (patch) | |
tree | a326739547f5521d1c1b2d228081f52466609e6c /arch/arm64 | |
parent | e8765b265a69c83504afc6901d6e137b1811d1f0 (diff) |
arm64: add CPU_HOTPLUG infrastructure
This patch adds the basic infrastructure necessary to support
CPU_HOTPLUG on arm64, based on the arm implementation. Actual hotplug
support will depend on an implementation's cpu_operations (e.g. PSCI).
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r-- | arch/arm64/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm64/include/asm/cpu_ops.h | 9 | ||||
-rw-r--r-- | arch/arm64/include/asm/irq.h | 1 | ||||
-rw-r--r-- | arch/arm64/include/asm/smp.h | 5 | ||||
-rw-r--r-- | arch/arm64/kernel/cputable.c | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/irq.c | 61 | ||||
-rw-r--r-- | arch/arm64/kernel/process.c | 7 | ||||
-rw-r--r-- | arch/arm64/kernel/smp.c | 96 |
8 files changed, 187 insertions, 1 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 9e8233b3f188..86ebfe499d6a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
@@ -161,6 +161,13 @@ config NR_CPUS | |||
161 | default "8" if ARCH_XGENE | 161 | default "8" if ARCH_XGENE |
162 | default "4" | 162 | default "4" |
163 | 163 | ||
164 | config HOTPLUG_CPU | ||
165 | bool "Support for hot-pluggable CPUs" | ||
166 | depends on SMP | ||
167 | help | ||
168 | Say Y here to experiment with turning CPUs off and on. CPUs | ||
169 | can be controlled through /sys/devices/system/cpu. | ||
170 | |||
164 | source kernel/Kconfig.preempt | 171 | source kernel/Kconfig.preempt |
165 | 172 | ||
166 | config HZ | 173 | config HZ |
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h index 1a98dbfc4a5f..c4cdb5e5b73d 100644 --- a/arch/arm64/include/asm/cpu_ops.h +++ b/arch/arm64/include/asm/cpu_ops.h | |||
@@ -34,6 +34,11 @@ struct device_node; | |||
34 | * @cpu_boot: Boots a cpu into the kernel. | 34 | * @cpu_boot: Boots a cpu into the kernel. |
35 | * @cpu_postboot: Optionally, perform any post-boot cleanup or necesary | 35 | * @cpu_postboot: Optionally, perform any post-boot cleanup or necesary |
36 | * synchronisation. Called from the cpu being booted. | 36 | * synchronisation. Called from the cpu being booted. |
37 | * @cpu_disable: Prepares a cpu to die. May fail for some mechanism-specific | ||
38 | * reason, which will cause the hot unplug to be aborted. Called | ||
39 | * from the cpu to be killed. | ||
40 | * @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the | ||
41 | * cpu being killed. | ||
37 | */ | 42 | */ |
38 | struct cpu_operations { | 43 | struct cpu_operations { |
39 | const char *name; | 44 | const char *name; |
@@ -41,6 +46,10 @@ struct cpu_operations { | |||
41 | int (*cpu_prepare)(unsigned int); | 46 | int (*cpu_prepare)(unsigned int); |
42 | int (*cpu_boot)(unsigned int); | 47 | int (*cpu_boot)(unsigned int); |
43 | void (*cpu_postboot)(void); | 48 | void (*cpu_postboot)(void); |
49 | #ifdef CONFIG_HOTPLUG_CPU | ||
50 | int (*cpu_disable)(unsigned int cpu); | ||
51 | void (*cpu_die)(unsigned int cpu); | ||
52 | #endif | ||
44 | }; | 53 | }; |
45 | 54 | ||
46 | extern const struct cpu_operations *cpu_ops[NR_CPUS]; | 55 | extern const struct cpu_operations *cpu_ops[NR_CPUS]; |
diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h index 0332fc077f6e..e1f7ecdde11f 100644 --- a/arch/arm64/include/asm/irq.h +++ b/arch/arm64/include/asm/irq.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <asm-generic/irq.h> | 4 | #include <asm-generic/irq.h> |
5 | 5 | ||
6 | extern void (*handle_arch_irq)(struct pt_regs *); | 6 | extern void (*handle_arch_irq)(struct pt_regs *); |
7 | extern void migrate_irqs(void); | ||
7 | extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); | 8 | extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); |
8 | 9 | ||
9 | #endif | 10 | #endif |
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index d64187ce69a2..a498f2cd2c2a 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h | |||
@@ -65,4 +65,9 @@ extern void secondary_entry(void); | |||
65 | extern void arch_send_call_function_single_ipi(int cpu); | 65 | extern void arch_send_call_function_single_ipi(int cpu); |
66 | extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); | 66 | extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); |
67 | 67 | ||
68 | extern int __cpu_disable(void); | ||
69 | |||
70 | extern void __cpu_die(unsigned int cpu); | ||
71 | extern void cpu_die(void); | ||
72 | |||
68 | #endif /* ifndef __ASM_SMP_H */ | 73 | #endif /* ifndef __ASM_SMP_H */ |
diff --git a/arch/arm64/kernel/cputable.c b/arch/arm64/kernel/cputable.c index 63cfc4a43f4e..fd3993cb060f 100644 --- a/arch/arm64/kernel/cputable.c +++ b/arch/arm64/kernel/cputable.c | |||
@@ -22,7 +22,7 @@ | |||
22 | 22 | ||
23 | extern unsigned long __cpu_setup(void); | 23 | extern unsigned long __cpu_setup(void); |
24 | 24 | ||
25 | struct cpu_info __initdata cpu_table[] = { | 25 | struct cpu_info cpu_table[] = { |
26 | { | 26 | { |
27 | .cpu_id_val = 0x000f0000, | 27 | .cpu_id_val = 0x000f0000, |
28 | .cpu_id_mask = 0x000f0000, | 28 | .cpu_id_mask = 0x000f0000, |
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index ecb3354292ed..473e5dbf8f39 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c | |||
@@ -81,3 +81,64 @@ void __init init_IRQ(void) | |||
81 | if (!handle_arch_irq) | 81 | if (!handle_arch_irq) |
82 | panic("No interrupt controller found."); | 82 | panic("No interrupt controller found."); |
83 | } | 83 | } |
84 | |||
85 | #ifdef CONFIG_HOTPLUG_CPU | ||
86 | static bool migrate_one_irq(struct irq_desc *desc) | ||
87 | { | ||
88 | struct irq_data *d = irq_desc_get_irq_data(desc); | ||
89 | const struct cpumask *affinity = d->affinity; | ||
90 | struct irq_chip *c; | ||
91 | bool ret = false; | ||
92 | |||
93 | /* | ||
94 | * If this is a per-CPU interrupt, or the affinity does not | ||
95 | * include this CPU, then we have nothing to do. | ||
96 | */ | ||
97 | if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity)) | ||
98 | return false; | ||
99 | |||
100 | if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) { | ||
101 | affinity = cpu_online_mask; | ||
102 | ret = true; | ||
103 | } | ||
104 | |||
105 | c = irq_data_get_irq_chip(d); | ||
106 | if (!c->irq_set_affinity) | ||
107 | pr_debug("IRQ%u: unable to set affinity\n", d->irq); | ||
108 | else if (c->irq_set_affinity(d, affinity, true) == IRQ_SET_MASK_OK && ret) | ||
109 | cpumask_copy(d->affinity, affinity); | ||
110 | |||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * The current CPU has been marked offline. Migrate IRQs off this CPU. | ||
116 | * If the affinity settings do not allow other CPUs, force them onto any | ||
117 | * available CPU. | ||
118 | * | ||
119 | * Note: we must iterate over all IRQs, whether they have an attached | ||
120 | * action structure or not, as we need to get chained interrupts too. | ||
121 | */ | ||
122 | void migrate_irqs(void) | ||
123 | { | ||
124 | unsigned int i; | ||
125 | struct irq_desc *desc; | ||
126 | unsigned long flags; | ||
127 | |||
128 | local_irq_save(flags); | ||
129 | |||
130 | for_each_irq_desc(i, desc) { | ||
131 | bool affinity_broken; | ||
132 | |||
133 | raw_spin_lock(&desc->lock); | ||
134 | affinity_broken = migrate_one_irq(desc); | ||
135 | raw_spin_unlock(&desc->lock); | ||
136 | |||
137 | if (affinity_broken) | ||
138 | pr_warn_ratelimited("IRQ%u no longer affine to CPU%u\n", | ||
139 | i, smp_processor_id()); | ||
140 | } | ||
141 | |||
142 | local_irq_restore(flags); | ||
143 | } | ||
144 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 7ae8a1f00c3c..de17c89985db 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c | |||
@@ -102,6 +102,13 @@ void arch_cpu_idle(void) | |||
102 | local_irq_enable(); | 102 | local_irq_enable(); |
103 | } | 103 | } |
104 | 104 | ||
105 | #ifdef CONFIG_HOTPLUG_CPU | ||
106 | void arch_cpu_idle_dead(void) | ||
107 | { | ||
108 | cpu_die(); | ||
109 | } | ||
110 | #endif | ||
111 | |||
105 | void machine_shutdown(void) | 112 | void machine_shutdown(void) |
106 | { | 113 | { |
107 | #ifdef CONFIG_SMP | 114 | #ifdef CONFIG_SMP |
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index e992734d21ec..5b4ddbd653c3 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c | |||
@@ -167,6 +167,102 @@ asmlinkage void secondary_start_kernel(void) | |||
167 | cpu_startup_entry(CPUHP_ONLINE); | 167 | cpu_startup_entry(CPUHP_ONLINE); |
168 | } | 168 | } |
169 | 169 | ||
170 | #ifdef CONFIG_HOTPLUG_CPU | ||
171 | static int op_cpu_disable(unsigned int cpu) | ||
172 | { | ||
173 | /* | ||
174 | * If we don't have a cpu_die method, abort before we reach the point | ||
175 | * of no return. CPU0 may not have an cpu_ops, so test for it. | ||
176 | */ | ||
177 | if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_die) | ||
178 | return -EOPNOTSUPP; | ||
179 | |||
180 | /* | ||
181 | * We may need to abort a hot unplug for some other mechanism-specific | ||
182 | * reason. | ||
183 | */ | ||
184 | if (cpu_ops[cpu]->cpu_disable) | ||
185 | return cpu_ops[cpu]->cpu_disable(cpu); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * __cpu_disable runs on the processor to be shutdown. | ||
192 | */ | ||
193 | int __cpu_disable(void) | ||
194 | { | ||
195 | unsigned int cpu = smp_processor_id(); | ||
196 | int ret; | ||
197 | |||
198 | ret = op_cpu_disable(cpu); | ||
199 | if (ret) | ||
200 | return ret; | ||
201 | |||
202 | /* | ||
203 | * Take this CPU offline. Once we clear this, we can't return, | ||
204 | * and we must not schedule until we're ready to give up the cpu. | ||
205 | */ | ||
206 | set_cpu_online(cpu, false); | ||
207 | |||
208 | /* | ||
209 | * OK - migrate IRQs away from this CPU | ||
210 | */ | ||
211 | migrate_irqs(); | ||
212 | |||
213 | /* | ||
214 | * Remove this CPU from the vm mask set of all processes. | ||
215 | */ | ||
216 | clear_tasks_mm_cpumask(cpu); | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static DECLARE_COMPLETION(cpu_died); | ||
222 | |||
223 | /* | ||
224 | * called on the thread which is asking for a CPU to be shutdown - | ||
225 | * waits until shutdown has completed, or it is timed out. | ||
226 | */ | ||
227 | void __cpu_die(unsigned int cpu) | ||
228 | { | ||
229 | if (!wait_for_completion_timeout(&cpu_died, msecs_to_jiffies(5000))) { | ||
230 | pr_crit("CPU%u: cpu didn't die\n", cpu); | ||
231 | return; | ||
232 | } | ||
233 | pr_notice("CPU%u: shutdown\n", cpu); | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * Called from the idle thread for the CPU which has been shutdown. | ||
238 | * | ||
239 | * Note that we disable IRQs here, but do not re-enable them | ||
240 | * before returning to the caller. This is also the behaviour | ||
241 | * of the other hotplug-cpu capable cores, so presumably coming | ||
242 | * out of idle fixes this. | ||
243 | */ | ||
244 | void cpu_die(void) | ||
245 | { | ||
246 | unsigned int cpu = smp_processor_id(); | ||
247 | |||
248 | idle_task_exit(); | ||
249 | |||
250 | local_irq_disable(); | ||
251 | |||
252 | /* Tell __cpu_die() that this CPU is now safe to dispose of */ | ||
253 | complete(&cpu_died); | ||
254 | |||
255 | /* | ||
256 | * Actually shutdown the CPU. This must never fail. The specific hotplug | ||
257 | * mechanism must perform all required cache maintenance to ensure that | ||
258 | * no dirty lines are lost in the process of shutting down the CPU. | ||
259 | */ | ||
260 | cpu_ops[cpu]->cpu_die(cpu); | ||
261 | |||
262 | BUG(); | ||
263 | } | ||
264 | #endif | ||
265 | |||
170 | void __init smp_cpus_done(unsigned int max_cpus) | 266 | void __init smp_cpus_done(unsigned int max_cpus) |
171 | { | 267 | { |
172 | pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); | 268 | pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); |