aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn David Anglin <dave.anglin@bell.net>2018-02-27 08:16:07 -0500
committerHelge Deller <deller@gmx.de>2018-03-02 04:03:28 -0500
commit0adb24e03a124b79130c9499731936b11ce2677d (patch)
tree0a5ea091ef738f86dc43b85b40275282c767b3bf
parentd8a5b80568a9cb66810e75b182018e9edb68e8ff (diff)
parisc: Fix ordering of cache and TLB flushes
The change to flush_kernel_vmap_range() wasn't sufficient to avoid the SMP stalls.  The problem is some drivers call these routines with interrupts disabled.  Interrupts need to be enabled for flush_tlb_all() and flush_cache_all() to work.  This version adds checks to ensure interrupts are not disabled before calling routines that need IPI interrupts.  When interrupts are disabled, we now drop into slower code. The attached change fixes the ordering of cache and TLB flushes in several cases.  When we flush the cache using the existing PTE/TLB entries, we need to flush the TLB after doing the cache flush.  We don't need to do this when we flush the entire instruction and data caches as these flushes don't use the existing TLB entries.  The same is true for tmpalias region flushes. The flush_kernel_vmap_range() and invalidate_kernel_vmap_range() routines have been updated. Secondly, we added a new purge_kernel_dcache_range_asm() routine to pacache.S and use it in invalidate_kernel_vmap_range().  Nominally, purges are faster than flushes as the cache lines don't have to be written back to memory. Hopefully, this is sufficient to resolve the remaining problems due to cache speculation.  So far, testing indicates that this is the case.  I did work up a patch using tmpalias flushes, but there is a performance hit because we need the physical address for each page, and we also need to sequence access to the tmpalias flush code.  This increases the probability of stalls. 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>
-rw-r--r--arch/parisc/include/asm/cacheflush.h1
-rw-r--r--arch/parisc/kernel/cache.c57
-rw-r--r--arch/parisc/kernel/pacache.S22
3 files changed, 54 insertions, 26 deletions
diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h
index 3742508cc534..bd5ce31936f5 100644
--- a/arch/parisc/include/asm/cacheflush.h
+++ b/arch/parisc/include/asm/cacheflush.h
@@ -26,6 +26,7 @@ void flush_user_icache_range_asm(unsigned long, unsigned long);
26void flush_kernel_icache_range_asm(unsigned long, unsigned long); 26void flush_kernel_icache_range_asm(unsigned long, unsigned long);
27void flush_user_dcache_range_asm(unsigned long, unsigned long); 27void flush_user_dcache_range_asm(unsigned long, unsigned long);
28void flush_kernel_dcache_range_asm(unsigned long, unsigned long); 28void flush_kernel_dcache_range_asm(unsigned long, unsigned long);
29void purge_kernel_dcache_range_asm(unsigned long, unsigned long);
29void flush_kernel_dcache_page_asm(void *); 30void flush_kernel_dcache_page_asm(void *);
30void flush_kernel_icache_page(void *); 31void flush_kernel_icache_page(void *);
31 32
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 19c0c141bc3f..79089778725b 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -465,10 +465,10 @@ EXPORT_SYMBOL(copy_user_page);
465int __flush_tlb_range(unsigned long sid, unsigned long start, 465int __flush_tlb_range(unsigned long sid, unsigned long start,
466 unsigned long end) 466 unsigned long end)
467{ 467{
468 unsigned long flags, size; 468 unsigned long flags;
469 469
470 size = (end - start); 470 if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
471 if (size >= parisc_tlb_flush_threshold) { 471 end - start >= parisc_tlb_flush_threshold) {
472 flush_tlb_all(); 472 flush_tlb_all();
473 return 1; 473 return 1;
474 } 474 }
@@ -539,13 +539,11 @@ void flush_cache_mm(struct mm_struct *mm)
539 struct vm_area_struct *vma; 539 struct vm_area_struct *vma;
540 pgd_t *pgd; 540 pgd_t *pgd;
541 541
542 /* Flush the TLB to avoid speculation if coherency is required. */
543 if (parisc_requires_coherency())
544 flush_tlb_all();
545
546 /* Flushing the whole cache on each cpu takes forever on 542 /* Flushing the whole cache on each cpu takes forever on
547 rp3440, etc. So, avoid it if the mm isn't too big. */ 543 rp3440, etc. So, avoid it if the mm isn't too big. */
548 if (mm_total_size(mm) >= parisc_cache_flush_threshold) { 544 if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
545 mm_total_size(mm) >= parisc_cache_flush_threshold) {
546 flush_tlb_all();
549 flush_cache_all(); 547 flush_cache_all();
550 return; 548 return;
551 } 549 }
@@ -553,9 +551,9 @@ void flush_cache_mm(struct mm_struct *mm)
553 if (mm->context == mfsp(3)) { 551 if (mm->context == mfsp(3)) {
554 for (vma = mm->mmap; vma; vma = vma->vm_next) { 552 for (vma = mm->mmap; vma; vma = vma->vm_next) {
555 flush_user_dcache_range_asm(vma->vm_start, vma->vm_end); 553 flush_user_dcache_range_asm(vma->vm_start, vma->vm_end);
556 if ((vma->vm_flags & VM_EXEC) == 0) 554 if (vma->vm_flags & VM_EXEC)
557 continue; 555 flush_user_icache_range_asm(vma->vm_start, vma->vm_end);
558 flush_user_icache_range_asm(vma->vm_start, vma->vm_end); 556 flush_tlb_range(vma, vma->vm_start, vma->vm_end);
559 } 557 }
560 return; 558 return;
561 } 559 }
@@ -581,14 +579,9 @@ void flush_cache_mm(struct mm_struct *mm)
581void flush_cache_range(struct vm_area_struct *vma, 579void flush_cache_range(struct vm_area_struct *vma,
582 unsigned long start, unsigned long end) 580 unsigned long start, unsigned long end)
583{ 581{
584 BUG_ON(!vma->vm_mm->context); 582 if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
585 583 end - start >= parisc_cache_flush_threshold) {
586 /* Flush the TLB to avoid speculation if coherency is required. */
587 if (parisc_requires_coherency())
588 flush_tlb_range(vma, start, end); 584 flush_tlb_range(vma, start, end);
589
590 if ((end - start) >= parisc_cache_flush_threshold
591 || vma->vm_mm->context != mfsp(3)) {
592 flush_cache_all(); 585 flush_cache_all();
593 return; 586 return;
594 } 587 }
@@ -596,6 +589,7 @@ void flush_cache_range(struct vm_area_struct *vma,
596 flush_user_dcache_range_asm(start, end); 589 flush_user_dcache_range_asm(start, end);
597 if (vma->vm_flags & VM_EXEC) 590 if (vma->vm_flags & VM_EXEC)
598 flush_user_icache_range_asm(start, end); 591 flush_user_icache_range_asm(start, end);
592 flush_tlb_range(vma, start, end);
599} 593}
600 594
601void 595void
@@ -604,8 +598,7 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long
604 BUG_ON(!vma->vm_mm->context); 598 BUG_ON(!vma->vm_mm->context);
605 599
606 if (pfn_valid(pfn)) { 600 if (pfn_valid(pfn)) {
607 if (parisc_requires_coherency()) 601 flush_tlb_page(vma, vmaddr);
608 flush_tlb_page(vma, vmaddr);
609 __flush_cache_page(vma, vmaddr, PFN_PHYS(pfn)); 602 __flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
610 } 603 }
611} 604}
@@ -613,21 +606,33 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long
613void flush_kernel_vmap_range(void *vaddr, int size) 606void flush_kernel_vmap_range(void *vaddr, int size)
614{ 607{
615 unsigned long start = (unsigned long)vaddr; 608 unsigned long start = (unsigned long)vaddr;
609 unsigned long end = start + size;
616 610
617 if ((unsigned long)size > parisc_cache_flush_threshold) 611 if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
612 (unsigned long)size >= parisc_cache_flush_threshold) {
613 flush_tlb_kernel_range(start, end);
618 flush_data_cache(); 614 flush_data_cache();
619 else 615 return;
620 flush_kernel_dcache_range_asm(start, start + size); 616 }
617
618 flush_kernel_dcache_range_asm(start, end);
619 flush_tlb_kernel_range(start, end);
621} 620}
622EXPORT_SYMBOL(flush_kernel_vmap_range); 621EXPORT_SYMBOL(flush_kernel_vmap_range);
623 622
624void invalidate_kernel_vmap_range(void *vaddr, int size) 623void invalidate_kernel_vmap_range(void *vaddr, int size)
625{ 624{
626 unsigned long start = (unsigned long)vaddr; 625 unsigned long start = (unsigned long)vaddr;
626 unsigned long end = start + size;
627 627
628 if ((unsigned long)size > parisc_cache_flush_threshold) 628 if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
629 (unsigned long)size >= parisc_cache_flush_threshold) {
630 flush_tlb_kernel_range(start, end);
629 flush_data_cache(); 631 flush_data_cache();
630 else 632 return;
631 flush_kernel_dcache_range_asm(start, start + size); 633 }
634
635 purge_kernel_dcache_range_asm(start, end);
636 flush_tlb_kernel_range(start, end);
632} 637}
633EXPORT_SYMBOL(invalidate_kernel_vmap_range); 638EXPORT_SYMBOL(invalidate_kernel_vmap_range);
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index 2d40c4ff3f69..67b0f7532e83 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -1110,6 +1110,28 @@ ENTRY_CFI(flush_kernel_dcache_range_asm)
1110 .procend 1110 .procend
1111ENDPROC_CFI(flush_kernel_dcache_range_asm) 1111ENDPROC_CFI(flush_kernel_dcache_range_asm)
1112 1112
1113ENTRY_CFI(purge_kernel_dcache_range_asm)
1114 .proc
1115 .callinfo NO_CALLS
1116 .entry
1117
1118 ldil L%dcache_stride, %r1
1119 ldw R%dcache_stride(%r1), %r23
1120 ldo -1(%r23), %r21
1121 ANDCM %r26, %r21, %r26
1122
11231: cmpb,COND(<<),n %r26, %r25,1b
1124 pdc,m %r23(%r26)
1125
1126 sync
1127 syncdma
1128 bv %r0(%r2)
1129 nop
1130 .exit
1131
1132 .procend
1133ENDPROC_CFI(purge_kernel_dcache_range_asm)
1134
1113ENTRY_CFI(flush_user_icache_range_asm) 1135ENTRY_CFI(flush_user_icache_range_asm)
1114 .proc 1136 .proc
1115 .callinfo NO_CALLS 1137 .callinfo NO_CALLS