|
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 | | -}; |
49 | | - |
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; |
70 | | -#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 | | - |
150 | | -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); |
| 41 | +#include <asm-generic/tlb.h> |
163 | 42 |
|
164 | | -#ifdef CONFIG_HAVE_RCU_TABLE_FREE |
165 | | - tlb->batch = NULL; |
| 43 | +#ifndef CONFIG_HAVE_RCU_TABLE_FREE |
| 44 | +#define tlb_remove_table(tlb, entry) tlb_remove_page(tlb, entry) |
166 | 45 | #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 | 46 |
|
214 | 47 | 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) |
279 | | -{ |
280 | | - tlb_add_flush(tlb, addr); |
281 | | -} |
282 | | - |
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) |
| 65 | +__pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp, unsigned long addr) |
291 | 66 | { |
292 | | -} |
293 | | - |
294 | | -static inline void tlb_flush_remove_tables(struct mm_struct *mm) |
295 | | -{ |
296 | | -} |
| 67 | +#ifdef CONFIG_ARM_LPAE |
| 68 | + struct page *page = virt_to_page(pmdp); |
297 | 69 |
|
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 */ |
|
0 commit comments