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

arm/tlb: Convert to generic mmu_gather

Generic mmu_gather provides everything that ARM needs:

- range tracking
- RCU table free
- VM_EXEC tracking
- VIPT cache flushing

The one notable curiosity is the 'funny' range tracking for classical
ARM in __pte_free_tlb().

No change in behavior intended.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Will Deacon <will.deacon@arm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Nick Piggin <npiggin@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rik van Riel <riel@surriel.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Peter Zijlstra and committed by
Ingo Molnar
b78180b9 96bc9567

+13 -241
+13 -241
arch/arm/include/asm/tlb.h
··· 33 33 #include <asm/pgalloc.h> 34 34 #include <asm/tlbflush.h> 35 35 36 - #define MMU_GATHER_BUNDLE 8 37 - 38 - #ifdef CONFIG_HAVE_RCU_TABLE_FREE 39 36 static inline void __tlb_remove_table(void *_table) 40 37 { 41 38 free_page_and_swap_cache((struct page *)_table); 42 39 } 43 40 44 - struct mmu_table_batch { 45 - struct rcu_head rcu; 46 - unsigned int nr; 47 - void *tables[0]; 48 - }; 41 + #include <asm-generic/tlb.h> 49 42 50 - #define MAX_TABLE_BATCH \ 51 - ((PAGE_SIZE - sizeof(struct mmu_table_batch)) / sizeof(void *)) 52 - 53 - extern void tlb_table_flush(struct mmu_gather *tlb); 54 - extern void tlb_remove_table(struct mmu_gather *tlb, void *table); 55 - 56 - #define tlb_remove_entry(tlb, entry) tlb_remove_table(tlb, entry) 57 - #else 58 - #define tlb_remove_entry(tlb, entry) tlb_remove_page(tlb, entry) 59 - #endif /* CONFIG_HAVE_RCU_TABLE_FREE */ 60 - 61 - /* 62 - * TLB handling. This allows us to remove pages from the page 63 - * tables, and efficiently handle the TLB issues. 64 - */ 65 - struct mmu_gather { 66 - struct mm_struct *mm; 67 - #ifdef CONFIG_HAVE_RCU_TABLE_FREE 68 - struct mmu_table_batch *batch; 69 - unsigned int need_flush; 43 + #ifndef CONFIG_HAVE_RCU_TABLE_FREE 44 + #define tlb_remove_table(tlb, entry) tlb_remove_page(tlb, entry) 70 45 #endif 71 - unsigned int fullmm; 72 - struct vm_area_struct *vma; 73 - unsigned long start, end; 74 - unsigned long range_start; 75 - unsigned long range_end; 76 - unsigned int nr; 77 - unsigned int max; 78 - struct page **pages; 79 - struct page *local[MMU_GATHER_BUNDLE]; 80 - }; 81 - 82 - DECLARE_PER_CPU(struct mmu_gather, mmu_gathers); 83 - 84 - /* 85 - * This is unnecessarily complex. There's three ways the TLB shootdown 86 - * code is used: 87 - * 1. Unmapping a range of vmas. See zap_page_range(), unmap_region(). 88 - * tlb->fullmm = 0, and tlb_start_vma/tlb_end_vma will be called. 89 - * tlb->vma will be non-NULL. 90 - * 2. Unmapping all vmas. See exit_mmap(). 91 - * tlb->fullmm = 1, and tlb_start_vma/tlb_end_vma will be called. 92 - * tlb->vma will be non-NULL. Additionally, page tables will be freed. 93 - * 3. Unmapping argument pages. See shift_arg_pages(). 94 - * tlb->fullmm = 0, but tlb_start_vma/tlb_end_vma will not be called. 95 - * tlb->vma will be NULL. 96 - */ 97 - static inline void tlb_flush(struct mmu_gather *tlb) 98 - { 99 - if (tlb->fullmm || !tlb->vma) 100 - flush_tlb_mm(tlb->mm); 101 - else if (tlb->range_end > 0) { 102 - flush_tlb_range(tlb->vma, tlb->range_start, tlb->range_end); 103 - tlb->range_start = TASK_SIZE; 104 - tlb->range_end = 0; 105 - } 106 - } 107 - 108 - static inline void tlb_add_flush(struct mmu_gather *tlb, unsigned long addr) 109 - { 110 - if (!tlb->fullmm) { 111 - if (addr < tlb->range_start) 112 - tlb->range_start = addr; 113 - if (addr + PAGE_SIZE > tlb->range_end) 114 - tlb->range_end = addr + PAGE_SIZE; 115 - } 116 - } 117 - 118 - static inline void __tlb_alloc_page(struct mmu_gather *tlb) 119 - { 120 - unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0); 121 - 122 - if (addr) { 123 - tlb->pages = (void *)addr; 124 - tlb->max = PAGE_SIZE / sizeof(struct page *); 125 - } 126 - } 127 - 128 - static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) 129 - { 130 - tlb_flush(tlb); 131 - #ifdef CONFIG_HAVE_RCU_TABLE_FREE 132 - tlb_table_flush(tlb); 133 - #endif 134 - } 135 - 136 - static inline void tlb_flush_mmu_free(struct mmu_gather *tlb) 137 - { 138 - free_pages_and_swap_cache(tlb->pages, tlb->nr); 139 - tlb->nr = 0; 140 - if (tlb->pages == tlb->local) 141 - __tlb_alloc_page(tlb); 142 - } 143 - 144 - static inline void tlb_flush_mmu(struct mmu_gather *tlb) 145 - { 146 - tlb_flush_mmu_tlbonly(tlb); 147 - tlb_flush_mmu_free(tlb); 148 - } 149 46 150 47 static inline void 151 - arch_tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, 152 - unsigned long start, unsigned long end) 153 - { 154 - tlb->mm = mm; 155 - tlb->fullmm = !(start | (end+1)); 156 - tlb->start = start; 157 - tlb->end = end; 158 - tlb->vma = NULL; 159 - tlb->max = ARRAY_SIZE(tlb->local); 160 - tlb->pages = tlb->local; 161 - tlb->nr = 0; 162 - __tlb_alloc_page(tlb); 163 - 164 - #ifdef CONFIG_HAVE_RCU_TABLE_FREE 165 - tlb->batch = NULL; 166 - #endif 167 - } 168 - 169 - static inline void 170 - arch_tlb_finish_mmu(struct mmu_gather *tlb, 171 - unsigned long start, unsigned long end, bool force) 172 - { 173 - if (force) { 174 - tlb->range_start = start; 175 - tlb->range_end = end; 176 - } 177 - 178 - tlb_flush_mmu(tlb); 179 - 180 - /* keep the page table cache within bounds */ 181 - check_pgt_cache(); 182 - 183 - if (tlb->pages != tlb->local) 184 - free_pages((unsigned long)tlb->pages, 0); 185 - } 186 - 187 - /* 188 - * Memorize the range for the TLB flush. 189 - */ 190 - static inline void 191 - tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, unsigned long addr) 192 - { 193 - tlb_add_flush(tlb, addr); 194 - } 195 - 196 - #define tlb_remove_huge_tlb_entry(h, tlb, ptep, address) \ 197 - tlb_remove_tlb_entry(tlb, ptep, address) 198 - /* 199 - * In the case of tlb vma handling, we can optimise these away in the 200 - * case where we're doing a full MM flush. When we're doing a munmap, 201 - * the vmas are adjusted to only cover the region to be torn down. 202 - */ 203 - static inline void 204 - tlb_start_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) 205 - { 206 - if (!tlb->fullmm) { 207 - flush_cache_range(vma, vma->vm_start, vma->vm_end); 208 - tlb->vma = vma; 209 - tlb->range_start = TASK_SIZE; 210 - tlb->range_end = 0; 211 - } 212 - } 213 - 214 - static inline void 215 - tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) 216 - { 217 - if (!tlb->fullmm) 218 - tlb_flush(tlb); 219 - } 220 - 221 - static inline bool __tlb_remove_page(struct mmu_gather *tlb, struct page *page) 222 - { 223 - tlb->pages[tlb->nr++] = page; 224 - VM_WARN_ON(tlb->nr > tlb->max); 225 - if (tlb->nr == tlb->max) 226 - return true; 227 - return false; 228 - } 229 - 230 - static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) 231 - { 232 - if (__tlb_remove_page(tlb, page)) 233 - tlb_flush_mmu(tlb); 234 - } 235 - 236 - static inline bool __tlb_remove_page_size(struct mmu_gather *tlb, 237 - struct page *page, int page_size) 238 - { 239 - return __tlb_remove_page(tlb, page); 240 - } 241 - 242 - static inline void tlb_remove_page_size(struct mmu_gather *tlb, 243 - struct page *page, int page_size) 244 - { 245 - return tlb_remove_page(tlb, page); 246 - } 247 - 248 - static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, 249 - unsigned long addr) 48 + __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, unsigned long addr) 250 49 { 251 50 pgtable_page_dtor(pte); 252 51 253 - #ifdef CONFIG_ARM_LPAE 254 - tlb_add_flush(tlb, addr); 255 - #else 52 + #ifndef CONFIG_ARM_LPAE 256 53 /* 257 54 * With the classic ARM MMU, a pte page has two corresponding pmd 258 55 * entries, each covering 1MB. 259 56 */ 260 - addr &= PMD_MASK; 261 - tlb_add_flush(tlb, addr + SZ_1M - PAGE_SIZE); 262 - tlb_add_flush(tlb, addr + SZ_1M); 57 + addr = (addr & PMD_MASK) + SZ_1M; 58 + __tlb_adjust_range(tlb, addr - PAGE_SIZE, 2 * PAGE_SIZE); 263 59 #endif 264 60 265 - tlb_remove_entry(tlb, pte); 266 - } 267 - 268 - static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, 269 - unsigned long addr) 270 - { 271 - #ifdef CONFIG_ARM_LPAE 272 - tlb_add_flush(tlb, addr); 273 - tlb_remove_entry(tlb, virt_to_page(pmdp)); 274 - #endif 61 + tlb_remove_table(tlb, pte); 275 62 } 276 63 277 64 static inline void 278 - tlb_remove_pmd_tlb_entry(struct mmu_gather *tlb, pmd_t *pmdp, unsigned long addr) 65 + __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, unsigned long addr) 279 66 { 280 - tlb_add_flush(tlb, addr); 281 - } 67 + #ifdef CONFIG_ARM_LPAE 68 + struct page *page = virt_to_page(pmdp); 282 69 283 - #define pte_free_tlb(tlb, ptep, addr) __pte_free_tlb(tlb, ptep, addr) 284 - #define pmd_free_tlb(tlb, pmdp, addr) __pmd_free_tlb(tlb, pmdp, addr) 285 - #define pud_free_tlb(tlb, pudp, addr) pud_free((tlb)->mm, pudp) 286 - 287 - #define tlb_migrate_finish(mm) do { } while (0) 288 - 289 - static inline void tlb_change_page_size(struct mmu_gather *tlb, 290 - unsigned int page_size) 291 - { 292 - } 293 - 294 - static inline void tlb_flush_remove_tables(struct mm_struct *mm) 295 - { 296 - } 297 - 298 - static inline void tlb_flush_remove_tables_local(void *arg) 299 - { 70 + tlb_remove_table(tlb, page); 71 + #endif 300 72 } 301 73 302 74 #endif /* CONFIG_MMU */