Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/mm/radix: Use different pte update sequence for different POWER9 revs

POWER9 DD1 requires pte to be marked invalid (V=0) before updating
it with the new value. This makes this distinction for the different
revisions.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Acked-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Aneesh Kumar K.V and committed by
Michael Ellerman
c6d1a767 694c4951

+71 -22
+2 -1
arch/powerpc/include/asm/book3s/32/pgtable.h
··· 223 223 } 224 224 225 225 226 - static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry) 226 + static inline void __ptep_set_access_flags(struct mm_struct *mm, 227 + pte_t *ptep, pte_t entry) 227 228 { 228 229 unsigned long set = pte_val(entry) & 229 230 (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC);
+3 -2
arch/powerpc/include/asm/book3s/64/pgtable.h
··· 565 565 * Generic functions with hash/radix callbacks 566 566 */ 567 567 568 - static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry) 568 + static inline void __ptep_set_access_flags(struct mm_struct *mm, 569 + pte_t *ptep, pte_t entry) 569 570 { 570 571 if (radix_enabled()) 571 - return radix__ptep_set_access_flags(ptep, entry); 572 + return radix__ptep_set_access_flags(mm, ptep, entry); 572 573 return hash__ptep_set_access_flags(ptep, entry); 573 574 } 574 575
+60 -15
arch/powerpc/include/asm/book3s/64/radix.h
··· 11 11 #include <asm/book3s/64/radix-4k.h> 12 12 #endif 13 13 14 + #ifndef __ASSEMBLY__ 15 + #include <asm/book3s/64/tlbflush-radix.h> 16 + #include <asm/cpu_has_feature.h> 17 + #endif 18 + 14 19 /* An empty PTE can still have a R or C writeback */ 15 20 #define RADIX_PTE_NONE_MASK (_PAGE_DIRTY | _PAGE_ACCESSED) 16 21 ··· 110 105 #define RADIX_PUD_TABLE_SIZE (sizeof(pud_t) << RADIX_PUD_INDEX_SIZE) 111 106 #define RADIX_PGD_TABLE_SIZE (sizeof(pgd_t) << RADIX_PGD_INDEX_SIZE) 112 107 113 - static inline unsigned long radix__pte_update(struct mm_struct *mm, 114 - unsigned long addr, 115 - pte_t *ptep, unsigned long clr, 116 - unsigned long set, 117 - int huge) 108 + static inline unsigned long __radix_pte_update(pte_t *ptep, unsigned long clr, 109 + unsigned long set) 118 110 { 119 111 pte_t pte; 120 112 unsigned long old_pte, new_pte; ··· 123 121 124 122 } while (!pte_xchg(ptep, __pte(old_pte), __pte(new_pte))); 125 123 126 - /* We already do a sync in cmpxchg, is ptesync needed ?*/ 124 + return old_pte; 125 + } 126 + 127 + 128 + static inline unsigned long radix__pte_update(struct mm_struct *mm, 129 + unsigned long addr, 130 + pte_t *ptep, unsigned long clr, 131 + unsigned long set, 132 + int huge) 133 + { 134 + unsigned long old_pte; 135 + 136 + if (cpu_has_feature(CPU_FTR_POWER9_DD1)) { 137 + 138 + unsigned long new_pte; 139 + 140 + old_pte = __radix_pte_update(ptep, ~0, 0); 141 + asm volatile("ptesync" : : : "memory"); 142 + /* 143 + * new value of pte 144 + */ 145 + new_pte = (old_pte | set) & ~clr; 146 + 147 + /* 148 + * For now let's do heavy pid flush 149 + * radix__flush_tlb_page_psize(mm, addr, mmu_virtual_psize); 150 + */ 151 + radix__flush_tlb_mm(mm); 152 + 153 + __radix_pte_update(ptep, 0, new_pte); 154 + } else 155 + old_pte = __radix_pte_update(ptep, clr, set); 127 156 asm volatile("ptesync" : : : "memory"); 128 - /* huge pages use the old page table lock */ 129 157 if (!huge) 130 158 assert_pte_locked(mm, addr); 131 159 ··· 166 134 * Set the dirty and/or accessed bits atomically in a linux PTE, this 167 135 * function doesn't need to invalidate tlb. 168 136 */ 169 - static inline void radix__ptep_set_access_flags(pte_t *ptep, pte_t entry) 137 + static inline void radix__ptep_set_access_flags(struct mm_struct *mm, 138 + pte_t *ptep, pte_t entry) 170 139 { 171 - pte_t pte; 172 - unsigned long old_pte, new_pte; 140 + 173 141 unsigned long set = pte_val(entry) & (_PAGE_DIRTY | _PAGE_ACCESSED | 174 142 _PAGE_RW | _PAGE_EXEC); 175 - do { 176 - pte = READ_ONCE(*ptep); 177 - old_pte = pte_val(pte); 143 + 144 + if (cpu_has_feature(CPU_FTR_POWER9_DD1)) { 145 + 146 + unsigned long old_pte, new_pte; 147 + 148 + old_pte = __radix_pte_update(ptep, ~0, 0); 149 + asm volatile("ptesync" : : : "memory"); 150 + /* 151 + * new value of pte 152 + */ 178 153 new_pte = old_pte | set; 179 154 180 - } while (!pte_xchg(ptep, __pte(old_pte), __pte(new_pte))); 155 + /* 156 + * For now let's do heavy pid flush 157 + * radix__flush_tlb_page_psize(mm, addr, mmu_virtual_psize); 158 + */ 159 + radix__flush_tlb_mm(mm); 181 160 182 - /* We already do a sync in cmpxchg, is ptesync needed ?*/ 161 + __radix_pte_update(ptep, 0, new_pte); 162 + } else 163 + __radix_pte_update(ptep, 0, set); 183 164 asm volatile("ptesync" : : : "memory"); 184 165 } 185 166
+2 -1
arch/powerpc/include/asm/nohash/32/pgtable.h
··· 267 267 } 268 268 269 269 270 - static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry) 270 + static inline void __ptep_set_access_flags(struct mm_struct *mm, 271 + pte_t *ptep, pte_t entry) 271 272 { 272 273 unsigned long set = pte_val(entry) & 273 274 (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC);
+2 -1
arch/powerpc/include/asm/nohash/64/pgtable.h
··· 300 300 /* Set the dirty and/or accessed bits atomically in a linux PTE, this 301 301 * function doesn't need to flush the hash entry 302 302 */ 303 - static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry) 303 + static inline void __ptep_set_access_flags(struct mm_struct *mm, 304 + pte_t *ptep, pte_t entry) 304 305 { 305 306 unsigned long bits = pte_val(entry) & 306 307 (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC);
+1 -1
arch/powerpc/mm/pgtable-book3s64.c
··· 35 35 #endif 36 36 changed = !pmd_same(*(pmdp), entry); 37 37 if (changed) { 38 - __ptep_set_access_flags(pmdp_ptep(pmdp), pmd_pte(entry)); 38 + __ptep_set_access_flags(vma->vm_mm, pmdp_ptep(pmdp), pmd_pte(entry)); 39 39 flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE); 40 40 } 41 41 return changed;
+1 -1
arch/powerpc/mm/pgtable.c
··· 224 224 if (changed) { 225 225 if (!is_vm_hugetlb_page(vma)) 226 226 assert_pte_locked(vma->vm_mm, address); 227 - __ptep_set_access_flags(ptep, entry); 227 + __ptep_set_access_flags(vma->vm_mm, ptep, entry); 228 228 flush_tlb_page(vma, address); 229 229 } 230 230 return changed;