Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: 7559/1: smp: switch away from the idmap before updating init_mm.mm_count

When booting a secondary CPU, the primary CPU hands two sets of page
tables via the secondary_data struct:

(1) swapper_pg_dir: a normal, cacheable, shared (if SMP) mapping
of the kernel image (i.e. the tables used by init_mm).

(2) idmap_pgd: an uncached mapping of the .idmap.text ELF
section.

The idmap is generally used when enabling and disabling the MMU, which
includes early CPU boot. In this case, the secondary CPU switches to
swapper as soon as it enters C code:

struct mm_struct *mm = &init_mm;
unsigned int cpu = smp_processor_id();

/*
* All kernel threads share the same mm context; grab a
* reference and switch to it.
*/
atomic_inc(&mm->mm_count);
current->active_mm = mm;
cpumask_set_cpu(cpu, mm_cpumask(mm));
cpu_switch_mm(mm->pgd, mm);

This causes a problem on ARMv7, where the identity mapping is treated as
strongly-ordered leading to architecturally UNPREDICTABLE behaviour of
exclusive accesses, such as those used by atomic_inc.

This patch re-orders the secondary_start_kernel function so that we
switch to swapper before performing any exclusive accesses.

Cc: <stable@vger.kernel.org>
Cc: David McKay <david.mckay@st.com>
Reported-by: Gilles Chanteperdrix <gilles.chanteperdrix@xenomai.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Will Deacon and committed by
Russell King
5f40b909 3581fe0e

+10 -4
+10 -4
arch/arm/kernel/smp.c
··· 294 294 asmlinkage void __cpuinit secondary_start_kernel(void) 295 295 { 296 296 struct mm_struct *mm = &init_mm; 297 - unsigned int cpu = smp_processor_id(); 297 + unsigned int cpu; 298 + 299 + /* 300 + * The identity mapping is uncached (strongly ordered), so 301 + * switch away from it before attempting any exclusive accesses. 302 + */ 303 + cpu_switch_mm(mm->pgd, mm); 304 + enter_lazy_tlb(mm, current); 305 + local_flush_tlb_all(); 298 306 299 307 /* 300 308 * All kernel threads share the same mm context; grab a 301 309 * reference and switch to it. 302 310 */ 311 + cpu = smp_processor_id(); 303 312 atomic_inc(&mm->mm_count); 304 313 current->active_mm = mm; 305 314 cpumask_set_cpu(cpu, mm_cpumask(mm)); 306 - cpu_switch_mm(mm->pgd, mm); 307 - enter_lazy_tlb(mm, current); 308 - local_flush_tlb_all(); 309 315 310 316 printk("CPU%u: Booted secondary processor\n", cpu); 311 317