ARM: 6279/1: highmem: fix SMP preemption bug in kmap_high_l1_vipt
authorGary King <gking@nvidia.com>
Thu, 29 Jul 2010 16:37:20 +0000 (17:37 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 30 Jul 2010 22:16:07 +0000 (23:16 +0100)
smp_processor_id() must not be called from a preemptible context (this
is checked by CONFIG_DEBUG_PREEMPT).  kmap_high_l1_vipt() was doing so.
This lead to a problem where the wrong per_cpu kmap_high_l1_vipt_depth
could be incremented, causing a BUG_ON(*depth <= 0); in
kunmap_high_l1_vipt().

The solution is to move the call to smp_processor_id() after the call
to preempt_disable().

Originally by: Andrew Howe <ahowe@nvidia.com>

Signed-off-by: Gary King <gking@nvidia.com>
Acked-by: Nicolas Pitre <nico.as.pitre@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mm/highmem.c

index 086816b205b8cf25c9d178c9d56c44a654e2ff9f..6ab244062b4ab2f720e84d870011faac64878c81 100644 (file)
@@ -163,19 +163,22 @@ static DEFINE_PER_CPU(int, kmap_high_l1_vipt_depth);
 
 void *kmap_high_l1_vipt(struct page *page, pte_t *saved_pte)
 {
-       unsigned int idx, cpu = smp_processor_id();
-       int *depth = &per_cpu(kmap_high_l1_vipt_depth, cpu);
+       unsigned int idx, cpu;
+       int *depth;
        unsigned long vaddr, flags;
        pte_t pte, *ptep;
 
+       if (!in_interrupt())
+               preempt_disable();
+
+       cpu = smp_processor_id();
+       depth = &per_cpu(kmap_high_l1_vipt_depth, cpu);
+
        idx = KM_L1_CACHE + KM_TYPE_NR * cpu;
        vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
        ptep = TOP_PTE(vaddr);
        pte = mk_pte(page, kmap_prot);
 
-       if (!in_interrupt())
-               preempt_disable();
-
        raw_local_irq_save(flags);
        (*depth)++;
        if (pte_val(*ptep) == pte_val(pte)) {