8eb15d5
commit 285994a62c80f1d72c6924282bcb59608098d5ec
8eb15d5
Author: Catalin Marinas <catalin.marinas@arm.com>
8eb15d5
Date:   Wed Mar 11 12:20:39 2015 +0000
8eb15d5
8eb15d5
    arm64: Invalidate the TLB corresponding to intermediate page table levels
8eb15d5
    
8eb15d5
    The ARM architecture allows the caching of intermediate page table
8eb15d5
    levels and page table freeing requires a sequence like:
8eb15d5
    
8eb15d5
    	pmd_clear()
8eb15d5
    	TLB invalidation
8eb15d5
    	pte page freeing
8eb15d5
    
8eb15d5
    With commit 5e5f6dc10546 (arm64: mm: enable HAVE_RCU_TABLE_FREE logic),
8eb15d5
    the page table freeing batching was moved from tlb_remove_page() to
8eb15d5
    tlb_remove_table(). The former takes care of TLB invalidation as this is
8eb15d5
    also shared with pte clearing and page cache page freeing. The latter,
8eb15d5
    however, does not invalidate the TLBs for intermediate page table levels
8eb15d5
    as it probably relies on the architecture code to do it if required.
8eb15d5
    When the mm->mm_users < 2, tlb_remove_table() does not do any batching
8eb15d5
    and page table pages are freed before tlb_finish_mmu() which performs
8eb15d5
    the actual TLB invalidation.
8eb15d5
    
8eb15d5
    This patch introduces __tlb_flush_pgtable() for arm64 and calls it from
8eb15d5
    the {pte,pmd,pud}_free_tlb() directly without relying on deferred page
8eb15d5
    table freeing.
8eb15d5
    
8eb15d5
    Fixes: 5e5f6dc10546 arm64: mm: enable HAVE_RCU_TABLE_FREE logic
8eb15d5
    Reported-by: Jon Masters <jcm@redhat.com>
8eb15d5
    Tested-by: Jon Masters <jcm@redhat.com>
8eb15d5
    Tested-by: Steve Capper <steve.capper@linaro.org>
8eb15d5
    Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
8eb15d5
8eb15d5
diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h
8eb15d5
index c028fe3..53d9c35 100644
8eb15d5
--- a/arch/arm64/include/asm/tlb.h
8eb15d5
+++ b/arch/arm64/include/asm/tlb.h
8eb15d5
@@ -48,6 +48,7 @@ static inline void tlb_flush(struct mmu_gather *tlb)
8eb15d5
 static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
8eb15d5
 				  unsigned long addr)
8eb15d5
 {
8eb15d5
+	__flush_tlb_pgtable(tlb->mm, addr);
8eb15d5
 	pgtable_page_dtor(pte);
8eb15d5
 	tlb_remove_entry(tlb, pte);
8eb15d5
 }
8eb15d5
@@ -56,6 +57,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
8eb15d5
 static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
8eb15d5
 				  unsigned long addr)
8eb15d5
 {
8eb15d5
+	__flush_tlb_pgtable(tlb->mm, addr);
8eb15d5
 	tlb_remove_entry(tlb, virt_to_page(pmdp));
8eb15d5
 }
8eb15d5
 #endif
8eb15d5
@@ -64,6 +66,7 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
8eb15d5
 static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
8eb15d5
 				  unsigned long addr)
8eb15d5
 {
8eb15d5
+	__flush_tlb_pgtable(tlb->mm, addr);
8eb15d5
 	tlb_remove_entry(tlb, virt_to_page(pudp));
8eb15d5
 }
8eb15d5
 #endif
8eb15d5
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
8eb15d5
index 4abe9b9..c3bb05b 100644
8eb15d5
--- a/arch/arm64/include/asm/tlbflush.h
8eb15d5
+++ b/arch/arm64/include/asm/tlbflush.h
8eb15d5
@@ -144,6 +144,19 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
8eb15d5
 }
8eb15d5
 
8eb15d5
 /*
8eb15d5
+ * Used to invalidate the TLB (walk caches) corresponding to intermediate page
8eb15d5
+ * table levels (pgd/pud/pmd).
8eb15d5
+ */
8eb15d5
+static inline void __flush_tlb_pgtable(struct mm_struct *mm,
8eb15d5
+				       unsigned long uaddr)
8eb15d5
+{
8eb15d5
+	unsigned long addr = uaddr >> 12 | ((unsigned long)ASID(mm) << 48);
8eb15d5
+
8eb15d5
+	dsb(ishst);
8eb15d5
+	asm("tlbi	vae1is, %0" : : "r" (addr));
8eb15d5
+	dsb(ish);
8eb15d5
+}
8eb15d5
+/*
8eb15d5
  * On AArch64, the cache coherency is handled via the set_pte_at() function.
8eb15d5
  */
8eb15d5
 static inline void update_mmu_cache(struct vm_area_struct *vma,