diff options
author | Kevin Cernekee <cernekee@gmail.com> | 2009-09-07 11:11:31 -0700 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2009-11-02 12:00:04 +0100 |
commit | 0f334a3e8c3586f81286345eb077ed32b375e8d6 (patch) | |
tree | 02278a0f6ef99b43c56af12ba44d964f099c70c3 /arch/mips/mm/init.c | |
parent | 39d2211d20518677511043d7ee16bbca6d0c5070 (diff) | |
download | op-kernel-dev-0f334a3e8c3586f81286345eb077ed32b375e8d6.zip op-kernel-dev-0f334a3e8c3586f81286345eb077ed32b375e8d6.tar.gz |
MIPS: Fix machine check exception in kmap_coherent()
On an SMP system with cache aliases, the following sequence of events may
happen:
1) copy_user_highpage() runs on CPU0, invoking kmap_coherent() to create a
temporary mapping in the fixmap region
2) copy_page() starts on CPU0
3) CPU1 sends CPU0 an IPI asking CPU0 to run local_r4k_flush_cache_page()
4) CPU0 takes the interrupt, interrupting copy_page()
5) local_r4k_flush_cache_page() on CPU0 calls kmap_coherent() again
6) The second invocation of kmap_coherent() on CPU0 tries to use the
same fixmap virtual address that was being used by copy_user_highpage()
7) CPU0 throws a machine check exception for the TLB address conflict
Fixed by creating an extra set of fixmap entries for use in interrupt
handlers. This prevents fixmap VA conflicts between copy_user_highpage()
running in user context, and local_r4k_flush_cache_page() invoked from an
SMP IPI.
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm/init.c')
-rw-r--r-- | arch/mips/mm/init.c | 6 |
1 files changed, 5 insertions, 1 deletions
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 15aa190..8d1f4f3 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -27,6 +27,7 @@ #include <linux/swap.h> #include <linux/proc_fs.h> #include <linux/pfn.h> +#include <linux/hardirq.h> #include <asm/asm-offsets.h> #include <asm/bootinfo.h> @@ -132,7 +133,10 @@ void *kmap_coherent(struct page *page, unsigned long addr) inc_preempt_count(); idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1); #ifdef CONFIG_MIPS_MT_SMTC - idx += FIX_N_COLOURS * smp_processor_id(); + idx += FIX_N_COLOURS * smp_processor_id() + + (in_interrupt() ? (FIX_N_COLOURS * NR_CPUS) : 0); +#else + idx += in_interrupt() ? FIX_N_COLOURS : 0; #endif vaddr = __fix_to_virt(FIX_CMAP_END - idx); pte = mk_pte(page, PAGE_KERNEL); |