aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-08-04 23:07:37 -0400
committerDavid S. Miller <davem@davemloft.net>2014-08-04 23:16:00 -0400
commit4ca9a23765da3260058db3431faf5b4efd8cf926 (patch)
treea7bafd8e1e1ed1de0e1afa82f4a45fbd485ff41e
parentfe418231b195c205701c0cc550a03f6c9758fd9e (diff)
sparc64: Guard against flushing openfirmware mappings.
Based almost entirely upon a patch by Christopher Alexander Tobias Schulze. In commit db64fe02258f1507e13fe5212a989922323685ce ("mm: rewrite vmap layer") lazy VMAP tlb flushing was added to the vmalloc layer. This causes problems on sparc64. Sparc64 has two VMAP mapped regions and they are not contiguous with eachother. First we have the malloc mapping area, then another unrelated region, then the vmalloc region. This "another unrelated region" is where the firmware is mapped. If the lazy TLB flushing logic in the vmalloc code triggers after we've had both a module unload and a vfree or similar, it will pass an address range that goes from somewhere inside the malloc region to somewhere inside the vmalloc region, and thus covering the openfirmware area entirely. The sparc64 kernel learns about openfirmware's dynamic mappings in this region early in the boot, and then services TLB misses in this area. But openfirmware has some locked TLB entries which are not mentioned in those dynamic mappings and we should thus not disturb them. These huge lazy TLB flush ranges causes those openfirmware locked TLB entries to be removed, resulting in all kinds of problems including hard hangs and crashes during reboot/reset. Besides causing problems like this, such huge TLB flush ranges are also incredibly inefficient. A plea has been made with the author of the VMAP lazy TLB flushing code, but for now we'll put a safety guard into our flush_tlb_kernel_range() implementation. Since the implementation has become non-trivial, stop defining it as a macro and instead make it a function in a C source file. Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--arch/sparc/include/asm/tlbflush_64.h12
-rw-r--r--arch/sparc/mm/init_64.c23
2 files changed, 25 insertions, 10 deletions
diff --git a/arch/sparc/include/asm/tlbflush_64.h b/arch/sparc/include/asm/tlbflush_64.h
index 816d8202fa0a..dea1cfa2122b 100644
--- a/arch/sparc/include/asm/tlbflush_64.h
+++ b/arch/sparc/include/asm/tlbflush_64.h
@@ -34,6 +34,8 @@ static inline void flush_tlb_range(struct vm_area_struct *vma,
34{ 34{
35} 35}
36 36
37void flush_tlb_kernel_range(unsigned long start, unsigned long end);
38
37#define __HAVE_ARCH_ENTER_LAZY_MMU_MODE 39#define __HAVE_ARCH_ENTER_LAZY_MMU_MODE
38 40
39void flush_tlb_pending(void); 41void flush_tlb_pending(void);
@@ -48,11 +50,6 @@ void __flush_tlb_kernel_range(unsigned long start, unsigned long end);
48 50
49#ifndef CONFIG_SMP 51#ifndef CONFIG_SMP
50 52
51#define flush_tlb_kernel_range(start,end) \
52do { flush_tsb_kernel_range(start,end); \
53 __flush_tlb_kernel_range(start,end); \
54} while (0)
55
56static inline void global_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr) 53static inline void global_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr)
57{ 54{
58 __flush_tlb_page(CTX_HWBITS(mm->context), vaddr); 55 __flush_tlb_page(CTX_HWBITS(mm->context), vaddr);
@@ -63,11 +60,6 @@ static inline void global_flush_tlb_page(struct mm_struct *mm, unsigned long vad
63void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end); 60void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end);
64void smp_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr); 61void smp_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr);
65 62
66#define flush_tlb_kernel_range(start, end) \
67do { flush_tsb_kernel_range(start,end); \
68 smp_flush_tlb_kernel_range(start, end); \
69} while (0)
70
71#define global_flush_tlb_page(mm, vaddr) \ 63#define global_flush_tlb_page(mm, vaddr) \
72 smp_flush_tlb_page(mm, vaddr) 64 smp_flush_tlb_page(mm, vaddr)
73 65
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index db5ddde0b335..2cfb0f25e0ed 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2707,3 +2707,26 @@ void hugetlb_setup(struct pt_regs *regs)
2707 } 2707 }
2708} 2708}
2709#endif 2709#endif
2710
2711#ifdef CONFIG_SMP
2712#define do_flush_tlb_kernel_range smp_flush_tlb_kernel_range
2713#else
2714#define do_flush_tlb_kernel_range __flush_tlb_kernel_range
2715#endif
2716
2717void flush_tlb_kernel_range(unsigned long start, unsigned long end)
2718{
2719 if (start < HI_OBP_ADDRESS && end > LOW_OBP_ADDRESS) {
2720 if (start < LOW_OBP_ADDRESS) {
2721 flush_tsb_kernel_range(start, LOW_OBP_ADDRESS);
2722 do_flush_tlb_kernel_range(start, LOW_OBP_ADDRESS);
2723 }
2724 if (end > HI_OBP_ADDRESS) {
2725 flush_tsb_kernel_range(end, HI_OBP_ADDRESS);
2726 do_flush_tlb_kernel_range(end, HI_OBP_ADDRESS);
2727 }
2728 } else {
2729 flush_tsb_kernel_range(start, end);
2730 do_flush_tlb_kernel_range(start, end);
2731 }
2732}