diff options
-rw-r--r-- | arch/powerpc/mm/tlb-radix.c | 96 |
1 files changed, 75 insertions, 21 deletions
diff --git a/arch/powerpc/mm/tlb-radix.c b/arch/powerpc/mm/tlb-radix.c index 67a6e86d3e7e..a734e486664d 100644 --- a/arch/powerpc/mm/tlb-radix.c +++ b/arch/powerpc/mm/tlb-radix.c | |||
@@ -689,22 +689,17 @@ EXPORT_SYMBOL(radix__flush_tlb_kernel_range); | |||
689 | static unsigned long tlb_single_page_flush_ceiling __read_mostly = 33; | 689 | static unsigned long tlb_single_page_flush_ceiling __read_mostly = 33; |
690 | static unsigned long tlb_local_single_page_flush_ceiling __read_mostly = POWER9_TLB_SETS_RADIX * 2; | 690 | static unsigned long tlb_local_single_page_flush_ceiling __read_mostly = POWER9_TLB_SETS_RADIX * 2; |
691 | 691 | ||
692 | void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | 692 | static inline void __radix__flush_tlb_range(struct mm_struct *mm, |
693 | unsigned long end) | 693 | unsigned long start, unsigned long end, |
694 | bool flush_all_sizes) | ||
694 | 695 | ||
695 | { | 696 | { |
696 | struct mm_struct *mm = vma->vm_mm; | ||
697 | unsigned long pid; | 697 | unsigned long pid; |
698 | unsigned int page_shift = mmu_psize_defs[mmu_virtual_psize].shift; | 698 | unsigned int page_shift = mmu_psize_defs[mmu_virtual_psize].shift; |
699 | unsigned long page_size = 1UL << page_shift; | 699 | unsigned long page_size = 1UL << page_shift; |
700 | unsigned long nr_pages = (end - start) >> page_shift; | 700 | unsigned long nr_pages = (end - start) >> page_shift; |
701 | bool local, full; | 701 | bool local, full; |
702 | 702 | ||
703 | #ifdef CONFIG_HUGETLB_PAGE | ||
704 | if (is_vm_hugetlb_page(vma)) | ||
705 | return radix__flush_hugetlb_tlb_range(vma, start, end); | ||
706 | #endif | ||
707 | |||
708 | pid = mm->context.id; | 703 | pid = mm->context.id; |
709 | if (unlikely(pid == MMU_NO_CONTEXT)) | 704 | if (unlikely(pid == MMU_NO_CONTEXT)) |
710 | return; | 705 | return; |
@@ -738,37 +733,64 @@ is_local: | |||
738 | _tlbie_pid(pid, RIC_FLUSH_TLB); | 733 | _tlbie_pid(pid, RIC_FLUSH_TLB); |
739 | } | 734 | } |
740 | } else { | 735 | } else { |
741 | bool hflush = false; | 736 | bool hflush = flush_all_sizes; |
737 | bool gflush = flush_all_sizes; | ||
742 | unsigned long hstart, hend; | 738 | unsigned long hstart, hend; |
739 | unsigned long gstart, gend; | ||
743 | 740 | ||
744 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 741 | if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) |
745 | hstart = (start + HPAGE_PMD_SIZE - 1) >> HPAGE_PMD_SHIFT; | ||
746 | hend = end >> HPAGE_PMD_SHIFT; | ||
747 | if (hstart < hend) { | ||
748 | hstart <<= HPAGE_PMD_SHIFT; | ||
749 | hend <<= HPAGE_PMD_SHIFT; | ||
750 | hflush = true; | 742 | hflush = true; |
743 | |||
744 | if (hflush) { | ||
745 | hstart = (start + PMD_SIZE - 1) & PMD_MASK; | ||
746 | hend = end & PMD_MASK; | ||
747 | if (hstart == hend) | ||
748 | hflush = false; | ||
749 | } | ||
750 | |||
751 | if (gflush) { | ||
752 | gstart = (start + PUD_SIZE - 1) & PUD_MASK; | ||
753 | gend = end & PUD_MASK; | ||
754 | if (gstart == gend) | ||
755 | gflush = false; | ||
751 | } | 756 | } |
752 | #endif | ||
753 | 757 | ||
754 | asm volatile("ptesync": : :"memory"); | 758 | asm volatile("ptesync": : :"memory"); |
755 | if (local) { | 759 | if (local) { |
756 | __tlbiel_va_range(start, end, pid, page_size, mmu_virtual_psize); | 760 | __tlbiel_va_range(start, end, pid, page_size, mmu_virtual_psize); |
757 | if (hflush) | 761 | if (hflush) |
758 | __tlbiel_va_range(hstart, hend, pid, | 762 | __tlbiel_va_range(hstart, hend, pid, |
759 | HPAGE_PMD_SIZE, MMU_PAGE_2M); | 763 | PMD_SIZE, MMU_PAGE_2M); |
764 | if (gflush) | ||
765 | __tlbiel_va_range(gstart, gend, pid, | ||
766 | PUD_SIZE, MMU_PAGE_1G); | ||
760 | asm volatile("ptesync": : :"memory"); | 767 | asm volatile("ptesync": : :"memory"); |
761 | } else { | 768 | } else { |
762 | __tlbie_va_range(start, end, pid, page_size, mmu_virtual_psize); | 769 | __tlbie_va_range(start, end, pid, page_size, mmu_virtual_psize); |
763 | if (hflush) | 770 | if (hflush) |
764 | __tlbie_va_range(hstart, hend, pid, | 771 | __tlbie_va_range(hstart, hend, pid, |
765 | HPAGE_PMD_SIZE, MMU_PAGE_2M); | 772 | PMD_SIZE, MMU_PAGE_2M); |
773 | if (gflush) | ||
774 | __tlbie_va_range(gstart, gend, pid, | ||
775 | PUD_SIZE, MMU_PAGE_1G); | ||
766 | fixup_tlbie(); | 776 | fixup_tlbie(); |
767 | asm volatile("eieio; tlbsync; ptesync": : :"memory"); | 777 | asm volatile("eieio; tlbsync; ptesync": : :"memory"); |
768 | } | 778 | } |
769 | } | 779 | } |
770 | preempt_enable(); | 780 | preempt_enable(); |
771 | } | 781 | } |
782 | |||
783 | void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
784 | unsigned long end) | ||
785 | |||
786 | { | ||
787 | #ifdef CONFIG_HUGETLB_PAGE | ||
788 | if (is_vm_hugetlb_page(vma)) | ||
789 | return radix__flush_hugetlb_tlb_range(vma, start, end); | ||
790 | #endif | ||
791 | |||
792 | __radix__flush_tlb_range(vma->vm_mm, start, end, false); | ||
793 | } | ||
772 | EXPORT_SYMBOL(radix__flush_tlb_range); | 794 | EXPORT_SYMBOL(radix__flush_tlb_range); |
773 | 795 | ||
774 | static int radix_get_mmu_psize(int page_size) | 796 | static int radix_get_mmu_psize(int page_size) |
@@ -837,6 +859,8 @@ void radix__tlb_flush(struct mmu_gather *tlb) | |||
837 | int psize = 0; | 859 | int psize = 0; |
838 | struct mm_struct *mm = tlb->mm; | 860 | struct mm_struct *mm = tlb->mm; |
839 | int page_size = tlb->page_size; | 861 | int page_size = tlb->page_size; |
862 | unsigned long start = tlb->start; | ||
863 | unsigned long end = tlb->end; | ||
840 | 864 | ||
841 | /* | 865 | /* |
842 | * if page size is not something we understand, do a full mm flush | 866 | * if page size is not something we understand, do a full mm flush |
@@ -847,15 +871,45 @@ void radix__tlb_flush(struct mmu_gather *tlb) | |||
847 | */ | 871 | */ |
848 | if (tlb->fullmm) { | 872 | if (tlb->fullmm) { |
849 | __flush_all_mm(mm, true); | 873 | __flush_all_mm(mm, true); |
874 | #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLB_PAGE) | ||
875 | } else if (mm_tlb_flush_nested(mm)) { | ||
876 | /* | ||
877 | * If there is a concurrent invalidation that is clearing ptes, | ||
878 | * then it's possible this invalidation will miss one of those | ||
879 | * cleared ptes and miss flushing the TLB. If this invalidate | ||
880 | * returns before the other one flushes TLBs, that can result | ||
881 | * in it returning while there are still valid TLBs inside the | ||
882 | * range to be invalidated. | ||
883 | * | ||
884 | * See mm/memory.c:tlb_finish_mmu() for more details. | ||
885 | * | ||
886 | * The solution to this is ensure the entire range is always | ||
887 | * flushed here. The problem for powerpc is that the flushes | ||
888 | * are page size specific, so this "forced flush" would not | ||
889 | * do the right thing if there are a mix of page sizes in | ||
890 | * the range to be invalidated. So use __flush_tlb_range | ||
891 | * which invalidates all possible page sizes in the range. | ||
892 | * | ||
893 | * PWC flush probably is not be required because the core code | ||
894 | * shouldn't free page tables in this path, but accounting | ||
895 | * for the possibility makes us a bit more robust. | ||
896 | * | ||
897 | * need_flush_all is an uncommon case because page table | ||
898 | * teardown should be done with exclusive locks held (but | ||
899 | * after locks are dropped another invalidate could come | ||
900 | * in), it could be optimized further if necessary. | ||
901 | */ | ||
902 | if (!tlb->need_flush_all) | ||
903 | __radix__flush_tlb_range(mm, start, end, true); | ||
904 | else | ||
905 | radix__flush_all_mm(mm); | ||
906 | #endif | ||
850 | } else if ( (psize = radix_get_mmu_psize(page_size)) == -1) { | 907 | } else if ( (psize = radix_get_mmu_psize(page_size)) == -1) { |
851 | if (!tlb->need_flush_all) | 908 | if (!tlb->need_flush_all) |
852 | radix__flush_tlb_mm(mm); | 909 | radix__flush_tlb_mm(mm); |
853 | else | 910 | else |
854 | radix__flush_all_mm(mm); | 911 | radix__flush_all_mm(mm); |
855 | } else { | 912 | } else { |
856 | unsigned long start = tlb->start; | ||
857 | unsigned long end = tlb->end; | ||
858 | |||
859 | if (!tlb->need_flush_all) | 913 | if (!tlb->need_flush_all) |
860 | radix__flush_tlb_range_psize(mm, start, end, psize); | 914 | radix__flush_tlb_range_psize(mm, start, end, psize); |
861 | else | 915 | else |