aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2012-03-05 06:49:30 -0500
committerCatalin Marinas <catalin.marinas@arm.com>2012-09-17 08:42:06 -0400
commit08e875c16a16c950e1e6d85755df5f3440844675 (patch)
treee365ab8e1bfbfe855ee5f3cd98a1d2132aa43ff0 /arch/arm64/kernel
parent09b55412469dfe6797244dc5836c17ed0c2f191b (diff)
arm64: SMP support
This patch adds SMP initialisation and spinlocks implementation for AArch64. The spinlock support uses the new load-acquire/store-release instructions to avoid explicit barriers. The architecture also specifies that an event is automatically generated when clearing the exclusive monitor state to wake up processors in WFE, so there is no need for an explicit DSB/SEV instruction sequence. The SEVL instruction is used to set the exclusive monitor locally as there is no conditional WFE and a branch is more expensive. For the SMP booting protocol, see Documentation/arm64/booting.txt. Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Tony Lindgren <tony@atomide.com> Acked-by: Nicolas Pitre <nico@linaro.org> Acked-by: Olof Johansson <olof@lixom.net> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r--arch/arm64/kernel/smp.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
new file mode 100644
index 000000000000..b711525be21f
--- /dev/null
+++ b/arch/arm64/kernel/smp.c
@@ -0,0 +1,469 @@
1/*
2 * SMP initialisation and IPI support
3 * Based on arch/arm/kernel/smp.c
4 *
5 * Copyright (C) 2012 ARM Ltd.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/delay.h>
21#include <linux/init.h>
22#include <linux/spinlock.h>
23#include <linux/sched.h>
24#include <linux/interrupt.h>
25#include <linux/cache.h>
26#include <linux/profile.h>
27#include <linux/errno.h>
28#include <linux/mm.h>
29#include <linux/err.h>
30#include <linux/cpu.h>
31#include <linux/smp.h>
32#include <linux/seq_file.h>
33#include <linux/irq.h>
34#include <linux/percpu.h>
35#include <linux/clockchips.h>
36#include <linux/completion.h>
37#include <linux/of.h>
38
39#include <asm/atomic.h>
40#include <asm/cacheflush.h>
41#include <asm/cputype.h>
42#include <asm/mmu_context.h>
43#include <asm/pgtable.h>
44#include <asm/pgalloc.h>
45#include <asm/processor.h>
46#include <asm/sections.h>
47#include <asm/tlbflush.h>
48#include <asm/ptrace.h>
49#include <asm/mmu_context.h>
50
51/*
52 * as from 2.5, kernels no longer have an init_tasks structure
53 * so we need some other way of telling a new secondary core
54 * where to place its SVC stack
55 */
56struct secondary_data secondary_data;
57volatile unsigned long secondary_holding_pen_release = -1;
58
59enum ipi_msg_type {
60 IPI_RESCHEDULE,
61 IPI_CALL_FUNC,
62 IPI_CALL_FUNC_SINGLE,
63 IPI_CPU_STOP,
64};
65
66static DEFINE_RAW_SPINLOCK(boot_lock);
67
68/*
69 * Write secondary_holding_pen_release in a way that is guaranteed to be
70 * visible to all observers, irrespective of whether they're taking part
71 * in coherency or not. This is necessary for the hotplug code to work
72 * reliably.
73 */
74static void __cpuinit write_pen_release(int val)
75{
76 void *start = (void *)&secondary_holding_pen_release;
77 unsigned long size = sizeof(secondary_holding_pen_release);
78
79 secondary_holding_pen_release = val;
80 __flush_dcache_area(start, size);
81}
82
83/*
84 * Boot a secondary CPU, and assign it the specified idle task.
85 * This also gives us the initial stack to use for this CPU.
86 */
87static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
88{
89 unsigned long timeout;
90
91 /*
92 * Set synchronisation state between this boot processor
93 * and the secondary one
94 */
95 raw_spin_lock(&boot_lock);
96
97 /*
98 * Update the pen release flag.
99 */
100 write_pen_release(cpu);
101
102 /*
103 * Send an event, causing the secondaries to read pen_release.
104 */
105 sev();
106
107 timeout = jiffies + (1 * HZ);
108 while (time_before(jiffies, timeout)) {
109 if (secondary_holding_pen_release == -1UL)
110 break;
111 udelay(10);
112 }
113
114 /*
115 * Now the secondary core is starting up let it run its
116 * calibrations, then wait for it to finish
117 */
118 raw_spin_unlock(&boot_lock);
119
120 return secondary_holding_pen_release != -1 ? -ENOSYS : 0;
121}
122
123static DECLARE_COMPLETION(cpu_running);
124
125int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
126{
127 int ret;
128
129 /*
130 * We need to tell the secondary core where to find its stack and the
131 * page tables.
132 */
133 secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
134 __flush_dcache_area(&secondary_data, sizeof(secondary_data));
135
136 /*
137 * Now bring the CPU into our world.
138 */
139 ret = boot_secondary(cpu, idle);
140 if (ret == 0) {
141 /*
142 * CPU was successfully started, wait for it to come online or
143 * time out.
144 */
145 wait_for_completion_timeout(&cpu_running,
146 msecs_to_jiffies(1000));
147
148 if (!cpu_online(cpu)) {
149 pr_crit("CPU%u: failed to come online\n", cpu);
150 ret = -EIO;
151 }
152 } else {
153 pr_err("CPU%u: failed to boot: %d\n", cpu, ret);
154 }
155
156 secondary_data.stack = NULL;
157
158 return ret;
159}
160
161/*
162 * This is the secondary CPU boot entry. We're using this CPUs
163 * idle thread stack, but a set of temporary page tables.
164 */
165asmlinkage void __cpuinit secondary_start_kernel(void)
166{
167 struct mm_struct *mm = &init_mm;
168 unsigned int cpu = smp_processor_id();
169
170 printk("CPU%u: Booted secondary processor\n", cpu);
171
172 /*
173 * All kernel threads share the same mm context; grab a
174 * reference and switch to it.
175 */
176 atomic_inc(&mm->mm_count);
177 current->active_mm = mm;
178 cpumask_set_cpu(cpu, mm_cpumask(mm));
179
180 /*
181 * TTBR0 is only used for the identity mapping at this stage. Make it
182 * point to zero page to avoid speculatively fetching new entries.
183 */
184 cpu_set_reserved_ttbr0();
185 flush_tlb_all();
186
187 preempt_disable();
188 trace_hardirqs_off();
189
190 /*
191 * Let the primary processor know we're out of the
192 * pen, then head off into the C entry point
193 */
194 write_pen_release(-1);
195
196 /*
197 * Synchronise with the boot thread.
198 */
199 raw_spin_lock(&boot_lock);
200 raw_spin_unlock(&boot_lock);
201
202 /*
203 * Enable local interrupts.
204 */
205 notify_cpu_starting(cpu);
206 local_irq_enable();
207 local_fiq_enable();
208
209 /*
210 * OK, now it's safe to let the boot CPU continue. Wait for
211 * the CPU migration code to notice that the CPU is online
212 * before we continue.
213 */
214 set_cpu_online(cpu, true);
215 while (!cpu_active(cpu))
216 cpu_relax();
217
218 /*
219 * OK, it's off to the idle thread for us
220 */
221 cpu_idle();
222}
223
224void __init smp_cpus_done(unsigned int max_cpus)
225{
226 unsigned long bogosum = loops_per_jiffy * num_online_cpus();
227
228 pr_info("SMP: Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
229 num_online_cpus(), bogosum / (500000/HZ),
230 (bogosum / (5000/HZ)) % 100);
231}
232
233void __init smp_prepare_boot_cpu(void)
234{
235}
236
237static void (*smp_cross_call)(const struct cpumask *, unsigned int);
238static phys_addr_t cpu_release_addr[NR_CPUS];
239
240/*
241 * Enumerate the possible CPU set from the device tree.
242 */
243void __init smp_init_cpus(void)
244{
245 const char *enable_method;
246 struct device_node *dn = NULL;
247 int cpu = 0;
248
249 while ((dn = of_find_node_by_type(dn, "cpu"))) {
250 if (cpu >= NR_CPUS)
251 goto next;
252
253 /*
254 * We currently support only the "spin-table" enable-method.
255 */
256 enable_method = of_get_property(dn, "enable-method", NULL);
257 if (!enable_method || strcmp(enable_method, "spin-table")) {
258 pr_err("CPU %d: missing or invalid enable-method property: %s\n",
259 cpu, enable_method);
260 goto next;
261 }
262
263 /*
264 * Determine the address from which the CPU is polling.
265 */
266 if (of_property_read_u64(dn, "cpu-release-addr",
267 &cpu_release_addr[cpu])) {
268 pr_err("CPU %d: missing or invalid cpu-release-addr property\n",
269 cpu);
270 goto next;
271 }
272
273 set_cpu_possible(cpu, true);
274next:
275 cpu++;
276 }
277
278 /* sanity check */
279 if (cpu > NR_CPUS)
280 pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n",
281 cpu, NR_CPUS);
282}
283
284void __init smp_prepare_cpus(unsigned int max_cpus)
285{
286 int cpu;
287 void **release_addr;
288 unsigned int ncores = num_possible_cpus();
289
290 /*
291 * are we trying to boot more cores than exist?
292 */
293 if (max_cpus > ncores)
294 max_cpus = ncores;
295
296 /*
297 * Initialise the present map (which describes the set of CPUs
298 * actually populated at the present time) and release the
299 * secondaries from the bootloader.
300 */
301 for_each_possible_cpu(cpu) {
302 if (max_cpus == 0)
303 break;
304
305 if (!cpu_release_addr[cpu])
306 continue;
307
308 release_addr = __va(cpu_release_addr[cpu]);
309 release_addr[0] = (void *)__pa(secondary_holding_pen);
310 __flush_dcache_area(release_addr, sizeof(release_addr[0]));
311
312 set_cpu_present(cpu, true);
313 max_cpus--;
314 }
315
316 /*
317 * Send an event to wake up the secondaries.
318 */
319 sev();
320}
321
322
323void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
324{
325 smp_cross_call = fn;
326}
327
328void arch_send_call_function_ipi_mask(const struct cpumask *mask)
329{
330 smp_cross_call(mask, IPI_CALL_FUNC);
331}
332
333void arch_send_call_function_single_ipi(int cpu)
334{
335 smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
336}
337
338static const char *ipi_types[NR_IPI] = {
339#define S(x,s) [x - IPI_RESCHEDULE] = s
340 S(IPI_RESCHEDULE, "Rescheduling interrupts"),
341 S(IPI_CALL_FUNC, "Function call interrupts"),
342 S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"),
343 S(IPI_CPU_STOP, "CPU stop interrupts"),
344};
345
346void show_ipi_list(struct seq_file *p, int prec)
347{
348 unsigned int cpu, i;
349
350 for (i = 0; i < NR_IPI; i++) {
351 seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i + IPI_RESCHEDULE,
352 prec >= 4 ? " " : "");
353 for_each_present_cpu(cpu)
354 seq_printf(p, "%10u ",
355 __get_irq_stat(cpu, ipi_irqs[i]));
356 seq_printf(p, " %s\n", ipi_types[i]);
357 }
358}
359
360u64 smp_irq_stat_cpu(unsigned int cpu)
361{
362 u64 sum = 0;
363 int i;
364
365 for (i = 0; i < NR_IPI; i++)
366 sum += __get_irq_stat(cpu, ipi_irqs[i]);
367
368 return sum;
369}
370
371static DEFINE_RAW_SPINLOCK(stop_lock);
372
373/*
374 * ipi_cpu_stop - handle IPI from smp_send_stop()
375 */
376static void ipi_cpu_stop(unsigned int cpu)
377{
378 if (system_state == SYSTEM_BOOTING ||
379 system_state == SYSTEM_RUNNING) {
380 raw_spin_lock(&stop_lock);
381 pr_crit("CPU%u: stopping\n", cpu);
382 dump_stack();
383 raw_spin_unlock(&stop_lock);
384 }
385
386 set_cpu_online(cpu, false);
387
388 local_fiq_disable();
389 local_irq_disable();
390
391 while (1)
392 cpu_relax();
393}
394
395/*
396 * Main handler for inter-processor interrupts
397 */
398void handle_IPI(int ipinr, struct pt_regs *regs)
399{
400 unsigned int cpu = smp_processor_id();
401 struct pt_regs *old_regs = set_irq_regs(regs);
402
403 if (ipinr >= IPI_RESCHEDULE && ipinr < IPI_RESCHEDULE + NR_IPI)
404 __inc_irq_stat(cpu, ipi_irqs[ipinr - IPI_RESCHEDULE]);
405
406 switch (ipinr) {
407 case IPI_RESCHEDULE:
408 scheduler_ipi();
409 break;
410
411 case IPI_CALL_FUNC:
412 irq_enter();
413 generic_smp_call_function_interrupt();
414 irq_exit();
415 break;
416
417 case IPI_CALL_FUNC_SINGLE:
418 irq_enter();
419 generic_smp_call_function_single_interrupt();
420 irq_exit();
421 break;
422
423 case IPI_CPU_STOP:
424 irq_enter();
425 ipi_cpu_stop(cpu);
426 irq_exit();
427 break;
428
429 default:
430 pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
431 break;
432 }
433 set_irq_regs(old_regs);
434}
435
436void smp_send_reschedule(int cpu)
437{
438 smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
439}
440
441void smp_send_stop(void)
442{
443 unsigned long timeout;
444
445 if (num_online_cpus() > 1) {
446 cpumask_t mask;
447
448 cpumask_copy(&mask, cpu_online_mask);
449 cpu_clear(smp_processor_id(), mask);
450
451 smp_cross_call(&mask, IPI_CPU_STOP);
452 }
453
454 /* Wait up to one second for other CPUs to stop */
455 timeout = USEC_PER_SEC;
456 while (num_online_cpus() > 1 && timeout--)
457 udelay(1);
458
459 if (num_online_cpus() > 1)
460 pr_warning("SMP: failed to stop secondary CPUs\n");
461}
462
463/*
464 * not supported here
465 */
466int setup_profiling_timer(unsigned int multiplier)
467{
468 return -EINVAL;
469}