aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s5pv310/platsmp.c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2010-12-18 05:53:12 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2010-12-20 10:09:13 -0500
commit3705ff6da538aff6dba535e2e9cbcbb9456d0d53 (patch)
tree348fcec2be9d41e1839686a6c03f0b5479d7e4f9 /arch/arm/mach-s5pv310/platsmp.c
parented3768a8d9dc2d345d4f27eb44ee1e4825056c08 (diff)
ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has been offlined can online itself before being requested, which results in things going astray on the next online/offline cycle. What happens in the normal online/offline/online cycle is: CPU0 CPU3 requests boot of CPU3 pen_release = 3 flush cache line checks pen_release, reads 3 starts boot pen_release = -1 ... requests CPU3 offline ... ... dies ... checks pen_release, reads -1 requests boot of CPU3 pen_release = 3 flush cache line checks pen_release, reads 3 starts boot pen_release = -1 However, as the write of -1 of pen_release is not fully flushed back to memory, and the checking of pen_release is done with caches disabled, this allows CPU3 the opportunity to read the old value of pen_release: CPU0 CPU3 requests boot of CPU3 pen_release = 3 flush cache line checks pen_release, reads 3 starts boot pen_release = -1 ... requests CPU3 offline ... ... dies ... checks pen_release, reads 3 starts boot pen_release = -1 requests boot of CPU3 pen_release = 3 flush cache line Fix this by grouping the write of pen_release along with its cache line flushing code to ensure that any update to pen_release is always pushed out to physical memory. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-s5pv310/platsmp.c')
-rw-r--r--arch/arm/mach-s5pv310/platsmp.c20
1 files changed, 15 insertions, 5 deletions
diff --git a/arch/arm/mach-s5pv310/platsmp.c b/arch/arm/mach-s5pv310/platsmp.c
index 18aaf5f54033..98c04748ed84 100644
--- a/arch/arm/mach-s5pv310/platsmp.c
+++ b/arch/arm/mach-s5pv310/platsmp.c
@@ -37,6 +37,19 @@ extern void s5pv310_secondary_startup(void);
37 37
38volatile int __cpuinitdata pen_release = -1; 38volatile int __cpuinitdata pen_release = -1;
39 39
40/*
41 * Write pen_release in a way that is guaranteed to be visible to all
42 * observers, irrespective of whether they're taking part in coherency
43 * or not. This is necessary for the hotplug code to work reliably.
44 */
45static void write_pen_release(int val)
46{
47 pen_release = val;
48 smp_wmb();
49 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
50 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
51}
52
40static void __iomem *scu_base_addr(void) 53static void __iomem *scu_base_addr(void)
41{ 54{
42 return (void __iomem *)(S5P_VA_SCU); 55 return (void __iomem *)(S5P_VA_SCU);
@@ -57,8 +70,7 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
57 * let the primary processor know we're out of the 70 * let the primary processor know we're out of the
58 * pen, then head off into the C entry point 71 * pen, then head off into the C entry point
59 */ 72 */
60 pen_release = -1; 73 write_pen_release(-1);
61 smp_wmb();
62 74
63 /* 75 /*
64 * Synchronise with the boot thread. 76 * Synchronise with the boot thread.
@@ -85,9 +97,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
85 * Note that "pen_release" is the hardware CPU ID, whereas 97 * Note that "pen_release" is the hardware CPU ID, whereas
86 * "cpu" is Linux's internal ID. 98 * "cpu" is Linux's internal ID.
87 */ 99 */
88 pen_release = cpu; 100 write_pen_release(cpu);
89 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
90 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
91 101
92 /* 102 /*
93 * Send the secondary CPU a soft interrupt, thereby causing 103 * Send the secondary CPU a soft interrupt, thereby causing