aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2013-10-24 15:30:16 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2013-10-25 06:33:20 -0400
commit652af899799354049b273af897b798b8f03fdd88 (patch)
treeb251c256f8589168401dd0cc794f2574f48fcf44 /arch/arm64
parentcd1aebf5277a3a154a9e4c0ea4b3acabb62e5cab (diff)
arm64: factor out spin-table boot method
The arm64 kernel has an internal holding pen, which is necessary for some systems where we can't bring CPUs online individually and must hold multiple CPUs in a safe area until the kernel is able to handle them. The current SMP infrastructure for arm64 is closely coupled to this holding pen, and alternative boot methods must launch CPUs into the pen, where they sit before they are launched into the kernel proper. With PSCI (and possibly other future boot methods), we can bring CPUs online individually, and need not perform the secondary_holding_pen dance. Instead, this patch factors the holding pen management code out to the spin-table boot method code, as it is the only boot method requiring the pen. A new entry point for secondaries, secondary_entry is added for other boot methods to use, which bypasses the holding pen and its associated overhead when bringing CPUs online. The smp.pen.text section is also removed, as the pen can live in head.text without problem. The cpu_operations structure is extended with two new functions, cpu_boot and cpu_postboot, for bringing a cpu into the kernel and performing any post-boot cleanup required by a bootmethod (e.g. resetting the secondary_holding_pen_release to INVALID_HWID). Documentation is added for cpu_operations. 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/include/asm/cpu_ops.h16
-rw-r--r--arch/arm64/include/asm/smp.h3
-rw-r--r--arch/arm64/kernel/head.S12
-rw-r--r--arch/arm64/kernel/psci.c18
-rw-r--r--arch/arm64/kernel/smp.c65
-rw-r--r--arch/arm64/kernel/smp_spin_table.c75
-rw-r--r--arch/arm64/kernel/vmlinux.lds.S1
7 files changed, 117 insertions, 73 deletions
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h
index 3c60c8d1d928..67bc4fd83798 100644
--- a/arch/arm64/include/asm/cpu_ops.h
+++ b/arch/arm64/include/asm/cpu_ops.h
@@ -21,10 +21,26 @@
21 21
22struct device_node; 22struct device_node;
23 23
24/**
25 * struct cpu_operations - Callback operations for hotplugging CPUs.
26 *
27 * @name: Name of the property as appears in a devicetree cpu node's
28 * enable-method property.
29 * @cpu_init: Reads any data necessary for a specific enable-method from the
30 * devicetree, for a given cpu node and proposed logical id.
31 * @cpu_prepare: Early one-time preparation step for a cpu. If there is a
32 * mechanism for doing so, tests whether it is possible to boot
33 * the given CPU.
34 * @cpu_boot: Boots a cpu into the kernel.
35 * @cpu_postboot: Optionally, perform any post-boot cleanup or necesary
36 * synchronisation. Called from the cpu being booted.
37 */
24struct cpu_operations { 38struct cpu_operations {
25 const char *name; 39 const char *name;
26 int (*cpu_init)(struct device_node *, unsigned int); 40 int (*cpu_init)(struct device_node *, unsigned int);
27 int (*cpu_prepare)(unsigned int); 41 int (*cpu_prepare)(unsigned int);
42 int (*cpu_boot)(unsigned int);
43 void (*cpu_postboot)(void);
28}; 44};
29 45
30extern const struct cpu_operations *cpu_ops[NR_CPUS]; 46extern const struct cpu_operations *cpu_ops[NR_CPUS];
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index 7e34295f78e3..d64187ce69a2 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -60,8 +60,7 @@ struct secondary_data {
60 void *stack; 60 void *stack;
61}; 61};
62extern struct secondary_data secondary_data; 62extern struct secondary_data secondary_data;
63extern void secondary_holding_pen(void); 63extern void secondary_entry(void);
64extern volatile unsigned long secondary_holding_pen_release;
65 64
66extern void arch_send_call_function_single_ipi(int cpu); 65extern void arch_send_call_function_single_ipi(int cpu);
67extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); 66extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 7090c126797c..bf7efdf2d7e7 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -225,7 +225,6 @@ ENTRY(__boot_cpu_mode)
225 .quad PAGE_OFFSET 225 .quad PAGE_OFFSET
226 226
227#ifdef CONFIG_SMP 227#ifdef CONFIG_SMP
228 .pushsection .smp.pen.text, "ax"
229 .align 3 228 .align 3
2301: .quad . 2291: .quad .
231 .quad secondary_holding_pen_release 230 .quad secondary_holding_pen_release
@@ -250,7 +249,16 @@ pen: ldr x4, [x3]
250 wfe 249 wfe
251 b pen 250 b pen
252ENDPROC(secondary_holding_pen) 251ENDPROC(secondary_holding_pen)
253 .popsection 252
253 /*
254 * Secondary entry point that jumps straight into the kernel. Only to
255 * be used where CPUs are brought online dynamically by the kernel.
256 */
257ENTRY(secondary_entry)
258 bl __calc_phys_offset // x2=phys offset
259 bl el2_setup // Drop to EL1
260 b secondary_startup
261ENDPROC(secondary_entry)
254 262
255ENTRY(secondary_startup) 263ENTRY(secondary_startup)
256 /* 264 /*
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index ccec2ca67755..fb56b6158344 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -239,26 +239,28 @@ static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu)
239 239
240static int __init cpu_psci_cpu_prepare(unsigned int cpu) 240static int __init cpu_psci_cpu_prepare(unsigned int cpu)
241{ 241{
242 int err;
243
244 if (!psci_ops.cpu_on) { 242 if (!psci_ops.cpu_on) {
245 pr_err("no cpu_on method, not booting CPU%d\n", cpu); 243 pr_err("no cpu_on method, not booting CPU%d\n", cpu);
246 return -ENODEV; 244 return -ENODEV;
247 } 245 }
248 246
249 err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_holding_pen));
250 if (err) {
251 pr_err("failed to boot CPU%d (%d)\n", cpu, err);
252 return err;
253 }
254
255 return 0; 247 return 0;
256} 248}
257 249
250static int cpu_psci_cpu_boot(unsigned int cpu)
251{
252 int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry));
253 if (err)
254 pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err);
255
256 return err;
257}
258
258const struct cpu_operations cpu_psci_ops = { 259const struct cpu_operations cpu_psci_ops = {
259 .name = "psci", 260 .name = "psci",
260 .cpu_init = cpu_psci_cpu_init, 261 .cpu_init = cpu_psci_cpu_init,
261 .cpu_prepare = cpu_psci_cpu_prepare, 262 .cpu_prepare = cpu_psci_cpu_prepare,
263 .cpu_boot = cpu_psci_cpu_boot,
262}; 264};
263 265
264#endif 266#endif
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 8965fb7dee89..6806bc40b630 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -55,7 +55,6 @@
55 * where to place its SVC stack 55 * where to place its SVC stack
56 */ 56 */
57struct secondary_data secondary_data; 57struct secondary_data secondary_data;
58volatile unsigned long secondary_holding_pen_release = INVALID_HWID;
59 58
60enum ipi_msg_type { 59enum ipi_msg_type {
61 IPI_RESCHEDULE, 60 IPI_RESCHEDULE,
@@ -64,61 +63,16 @@ enum ipi_msg_type {
64 IPI_CPU_STOP, 63 IPI_CPU_STOP,
65}; 64};
66 65
67static DEFINE_RAW_SPINLOCK(boot_lock);
68
69/*
70 * Write secondary_holding_pen_release in a way that is guaranteed to be
71 * visible to all observers, irrespective of whether they're taking part
72 * in coherency or not. This is necessary for the hotplug code to work
73 * reliably.
74 */
75static void write_pen_release(u64 val)
76{
77 void *start = (void *)&secondary_holding_pen_release;
78 unsigned long size = sizeof(secondary_holding_pen_release);
79
80 secondary_holding_pen_release = val;
81 __flush_dcache_area(start, size);
82}
83
84/* 66/*
85 * Boot a secondary CPU, and assign it the specified idle task. 67 * Boot a secondary CPU, and assign it the specified idle task.
86 * This also gives us the initial stack to use for this CPU. 68 * This also gives us the initial stack to use for this CPU.
87 */ 69 */
88static int boot_secondary(unsigned int cpu, struct task_struct *idle) 70static int boot_secondary(unsigned int cpu, struct task_struct *idle)
89{ 71{
90 unsigned long timeout; 72 if (cpu_ops[cpu]->cpu_boot)
91 73 return cpu_ops[cpu]->cpu_boot(cpu);
92 /*
93 * Set synchronisation state between this boot processor
94 * and the secondary one
95 */
96 raw_spin_lock(&boot_lock);
97
98 /*
99 * Update the pen release flag.
100 */
101 write_pen_release(cpu_logical_map(cpu));
102 74
103 /* 75 return -EOPNOTSUPP;
104 * Send an event, causing the secondaries to read pen_release.
105 */
106 sev();
107
108 timeout = jiffies + (1 * HZ);
109 while (time_before(jiffies, timeout)) {
110 if (secondary_holding_pen_release == INVALID_HWID)
111 break;
112 udelay(10);
113 }
114
115 /*
116 * Now the secondary core is starting up let it run its
117 * calibrations, then wait for it to finish
118 */
119 raw_spin_unlock(&boot_lock);
120
121 return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0;
122} 76}
123 77
124static DECLARE_COMPLETION(cpu_running); 78static DECLARE_COMPLETION(cpu_running);
@@ -188,17 +142,8 @@ asmlinkage void secondary_start_kernel(void)
188 preempt_disable(); 142 preempt_disable();
189 trace_hardirqs_off(); 143 trace_hardirqs_off();
190 144
191 /* 145 if (cpu_ops[cpu]->cpu_postboot)
192 * Let the primary processor know we're out of the 146 cpu_ops[cpu]->cpu_postboot();
193 * pen, then head off into the C entry point
194 */
195 write_pen_release(INVALID_HWID);
196
197 /*
198 * Synchronise with the boot thread.
199 */
200 raw_spin_lock(&boot_lock);
201 raw_spin_unlock(&boot_lock);
202 147
203 /* 148 /*
204 * OK, now it's safe to let the boot CPU continue. Wait for 149 * OK, now it's safe to let the boot CPU continue. Wait for
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c
index a8b76e4eccff..27f08367a6e7 100644
--- a/arch/arm64/kernel/smp_spin_table.c
+++ b/arch/arm64/kernel/smp_spin_table.c
@@ -16,14 +16,37 @@
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */ 17 */
18 18
19#include <linux/delay.h>
19#include <linux/init.h> 20#include <linux/init.h>
20#include <linux/of.h> 21#include <linux/of.h>
21#include <linux/smp.h> 22#include <linux/smp.h>
22 23
23#include <asm/cacheflush.h> 24#include <asm/cacheflush.h>
24#include <asm/cpu_ops.h> 25#include <asm/cpu_ops.h>
26#include <asm/cputype.h>
27#include <asm/smp_plat.h>
28
29extern void secondary_holding_pen(void);
30volatile unsigned long secondary_holding_pen_release = INVALID_HWID;
25 31
26static phys_addr_t cpu_release_addr[NR_CPUS]; 32static phys_addr_t cpu_release_addr[NR_CPUS];
33static DEFINE_RAW_SPINLOCK(boot_lock);
34
35/*
36 * Write secondary_holding_pen_release in a way that is guaranteed to be
37 * visible to all observers, irrespective of whether they're taking part
38 * in coherency or not. This is necessary for the hotplug code to work
39 * reliably.
40 */
41static void write_pen_release(u64 val)
42{
43 void *start = (void *)&secondary_holding_pen_release;
44 unsigned long size = sizeof(secondary_holding_pen_release);
45
46 secondary_holding_pen_release = val;
47 __flush_dcache_area(start, size);
48}
49
27 50
28static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu) 51static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu)
29{ 52{
@@ -60,8 +83,60 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu)
60 return 0; 83 return 0;
61} 84}
62 85
86static int smp_spin_table_cpu_boot(unsigned int cpu)
87{
88 unsigned long timeout;
89
90 /*
91 * Set synchronisation state between this boot processor
92 * and the secondary one
93 */
94 raw_spin_lock(&boot_lock);
95
96 /*
97 * Update the pen release flag.
98 */
99 write_pen_release(cpu_logical_map(cpu));
100
101 /*
102 * Send an event, causing the secondaries to read pen_release.
103 */
104 sev();
105
106 timeout = jiffies + (1 * HZ);
107 while (time_before(jiffies, timeout)) {
108 if (secondary_holding_pen_release == INVALID_HWID)
109 break;
110 udelay(10);
111 }
112
113 /*
114 * Now the secondary core is starting up let it run its
115 * calibrations, then wait for it to finish
116 */
117 raw_spin_unlock(&boot_lock);
118
119 return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0;
120}
121
122void smp_spin_table_cpu_postboot(void)
123{
124 /*
125 * Let the primary processor know we're out of the pen.
126 */
127 write_pen_release(INVALID_HWID);
128
129 /*
130 * Synchronise with the boot thread.
131 */
132 raw_spin_lock(&boot_lock);
133 raw_spin_unlock(&boot_lock);
134}
135
63const struct cpu_operations smp_spin_table_ops = { 136const struct cpu_operations smp_spin_table_ops = {
64 .name = "spin-table", 137 .name = "spin-table",
65 .cpu_init = smp_spin_table_cpu_init, 138 .cpu_init = smp_spin_table_cpu_init,
66 .cpu_prepare = smp_spin_table_cpu_prepare, 139 .cpu_prepare = smp_spin_table_cpu_prepare,
140 .cpu_boot = smp_spin_table_cpu_boot,
141 .cpu_postboot = smp_spin_table_cpu_postboot,
67}; 142};
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index f8ab9d8e2ea3..991ffddf49df 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -54,7 +54,6 @@ SECTIONS
54 } 54 }
55 .text : { /* Real text segment */ 55 .text : { /* Real text segment */
56 _stext = .; /* Text and read-only data */ 56 _stext = .; /* Text and read-only data */
57 *(.smp.pen.text)
58 __exception_text_start = .; 57 __exception_text_start = .;
59 *(.exception.text) 58 *(.exception.text)
60 __exception_text_end = .; 59 __exception_text_end = .;