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>
This commit is contained in:
Родитель
39d2211d20
Коммит
0f334a3e8c
|
@ -48,9 +48,9 @@ enum fixed_addresses {
|
|||
#define FIX_N_COLOURS 8
|
||||
FIX_CMAP_BEGIN,
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * NR_CPUS),
|
||||
FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * NR_CPUS * 2),
|
||||
#else
|
||||
FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS,
|
||||
FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * 2),
|
||||
#endif
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
/* reserved pte's for temporary kernel mappings */
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче