summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJohn David Anglin <dave.anglin@bell.net>2017-07-25 17:11:26 -0400
committerHelge Deller <deller@gmx.de>2017-07-25 23:36:53 +0200
commitae7a609c34b6fb12328c553b5f9aab26ae74a28e (patch)
tree83a8ff3a784d77197e6da678831b9db683cac99a /arch
parent56188832a50f09998cb570ba3771a1d25c193c0e (diff)
downloadlinux-ae7a609c34b6fb12328c553b5f9aab26ae74a28e.tar.gz
linux-ae7a609c34b6fb12328c553b5f9aab26ae74a28e.tar.bz2
linux-ae7a609c34b6fb12328c553b5f9aab26ae74a28e.zip
parisc: Prevent TLB speculation on flushed pages on CPUs that only support equivalent aliases
Helge noticed that we flush the TLB page in flush_cache_page but not in flush_cache_range or flush_cache_mm. For a long time, we have had random segmentation faults building packages on machines with PA8800/8900 processors. These machines only support equivalent aliases. We don't see these faults on machines that don't require strict coherency. So, it appears TLB speculation sometimes leads to cache corruption on machines that require coherency. This patch adds TLB flushes to flush_cache_range and flush_cache_mm when coherency is required. We only flush the TLB in flush_cache_page when coherency is required. The patch also optimizes flush_cache_range. It turns out we always have the right context to use flush_user_dcache_range_asm and flush_user_icache_range_asm. The patch has been tested for some time on rp3440, rp3410 and A500-44. It's been boot tested on c8000. No random segmentation faults were observed during testing. Signed-off-by: John David Anglin <dave.anglin@bell.net> Cc: stable@vger.kernel.org # 4.9+ Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/parisc/kernel/cache.c34
1 files changed, 14 insertions, 20 deletions
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index c32a09095216..3a64ebb39ac7 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -539,6 +539,10 @@ void flush_cache_mm(struct mm_struct *mm)
struct vm_area_struct *vma;
pgd_t *pgd;
+ /* Flush the TLB to avoid speculation if coherency is required. */
+ if (parisc_requires_coherency())
+ flush_tlb_all();
+
/* Flushing the whole cache on each cpu takes forever on
rp3440, etc. So, avoid it if the mm isn't too big. */
if (mm_total_size(mm) >= parisc_cache_flush_threshold) {
@@ -577,33 +581,22 @@ void flush_cache_mm(struct mm_struct *mm)
void flush_cache_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
- unsigned long addr;
- pgd_t *pgd;
-
BUG_ON(!vma->vm_mm->context);
+ /* Flush the TLB to avoid speculation if coherency is required. */
+ if (parisc_requires_coherency())
+ flush_tlb_range(vma, start, end);
+
if ((end - start) >= parisc_cache_flush_threshold) {
flush_cache_all();
return;
}
- if (vma->vm_mm->context == mfsp(3)) {
- flush_user_dcache_range_asm(start, end);
- if (vma->vm_flags & VM_EXEC)
- flush_user_icache_range_asm(start, end);
- return;
- }
+ BUG_ON(vma->vm_mm->context != mfsp(3));
- pgd = vma->vm_mm->pgd;
- for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) {
- unsigned long pfn;
- pte_t *ptep = get_ptep(pgd, addr);
- if (!ptep)
- continue;
- pfn = pte_pfn(*ptep);
- if (pfn_valid(pfn))
- __flush_cache_page(vma, addr, PFN_PHYS(pfn));
- }
+ flush_user_dcache_range_asm(start, end);
+ if (vma->vm_flags & VM_EXEC)
+ flush_user_icache_range_asm(start, end);
}
void
@@ -612,7 +605,8 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long
BUG_ON(!vma->vm_mm->context);
if (pfn_valid(pfn)) {
- flush_tlb_page(vma, vmaddr);
+ if (parisc_requires_coherency())
+ flush_tlb_page(vma, vmaddr);
__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
}
}