powerpc/mm/hash64: use _PAGE_PTE when checking for pte_present
authorAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Tue, 5 May 2020 07:17:10 +0000 (12:47 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 5 May 2020 11:20:14 +0000 (21:20 +1000)
This makes the pte_present check stricter by checking for additional _PAGE_PTE
bit. A level 1 pte pointer (THP pte) can be switched to a pointer to level 0 pte
page table page by following two operations.

1) THP split.
2) madvise(MADV_DONTNEED) in parallel to page fault.

A lockless page table walk need to make sure we can handle such changes
gracefully.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200505071729.54912-4-aneesh.kumar@linux.ibm.com
arch/powerpc/include/asm/book3s/64/pgtable.h
arch/powerpc/mm/book3s64/hash_utils.c

index 368b136..03521a8 100644 (file)
@@ -553,6 +553,12 @@ static inline pte_t pte_clear_savedwrite(pte_t pte)
 }
 #endif /* CONFIG_NUMA_BALANCING */
 
+static inline bool pte_hw_valid(pte_t pte)
+{
+       return (pte_raw(pte) & cpu_to_be64(_PAGE_PRESENT | _PAGE_PTE)) ==
+               cpu_to_be64(_PAGE_PRESENT | _PAGE_PTE);
+}
+
 static inline int pte_present(pte_t pte)
 {
        /*
@@ -561,12 +567,11 @@ static inline int pte_present(pte_t pte)
         * invalid during ptep_set_access_flags. Hence we look for _PAGE_INVALID
         * if we find _PAGE_PRESENT cleared.
         */
-       return !!(pte_raw(pte) & cpu_to_be64(_PAGE_PRESENT | _PAGE_INVALID));
-}
 
-static inline bool pte_hw_valid(pte_t pte)
-{
-       return !!(pte_raw(pte) & cpu_to_be64(_PAGE_PRESENT));
+       if (pte_hw_valid(pte))
+               return true;
+       return (pte_raw(pte) & cpu_to_be64(_PAGE_INVALID | _PAGE_PTE)) ==
+               cpu_to_be64(_PAGE_INVALID | _PAGE_PTE);
 }
 
 #ifdef CONFIG_PPC_MEM_KEYS
index e951e87..525eac4 100644 (file)
@@ -1350,8 +1350,15 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,
                goto bail;
        }
 
-       /* Add _PAGE_PRESENT to the required access perm */
-       access |= _PAGE_PRESENT;
+       /*
+        * Add _PAGE_PRESENT to the required access perm. If there are parallel
+        * updates to the pte that can possibly clear _PAGE_PTE, catch that too.
+        *
+        * We can safely use the return pte address in rest of the function
+        * because we do set H_PAGE_BUSY which prevents further updates to pte
+        * from generic code.
+        */
+       access |= _PAGE_PRESENT | _PAGE_PTE;
 
        /*
         * Pre-check access permissions (will be re-checked atomically