Blame view

mm/mmu_gather.c 8.44 KB
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
1
2
3
4
5
6
7
8
9
10
11
12
  #include <linux/gfp.h>
  #include <linux/highmem.h>
  #include <linux/kernel.h>
  #include <linux/mmdebug.h>
  #include <linux/mm_types.h>
  #include <linux/pagemap.h>
  #include <linux/rcupdate.h>
  #include <linux/smp.h>
  #include <linux/swap.h>
  
  #include <asm/pgalloc.h>
  #include <asm/tlb.h>
580a586c4   Peter Zijlstra   asm-generic/tlb: ...
13
  #ifndef CONFIG_MMU_GATHER_NO_GATHER
952a31c9e   Martin Schwidefsky   asm-generic/tlb: ...
14

196d9d8bb   Peter Zijlstra   mm/memory: Move m...
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  static bool tlb_next_batch(struct mmu_gather *tlb)
  {
  	struct mmu_gather_batch *batch;
  
  	batch = tlb->active;
  	if (batch->next) {
  		tlb->active = batch->next;
  		return true;
  	}
  
  	if (tlb->batch_count == MAX_GATHER_BATCH_COUNT)
  		return false;
  
  	batch = (void *)__get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0);
  	if (!batch)
  		return false;
  
  	tlb->batch_count++;
  	batch->next = NULL;
  	batch->nr   = 0;
  	batch->max  = MAX_GATHER_BATCH;
  
  	tlb->active->next = batch;
  	tlb->active = batch;
  
  	return true;
  }
952a31c9e   Martin Schwidefsky   asm-generic/tlb: ...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  static void tlb_batch_pages_flush(struct mmu_gather *tlb)
  {
  	struct mmu_gather_batch *batch;
  
  	for (batch = &tlb->local; batch && batch->nr; batch = batch->next) {
  		free_pages_and_swap_cache(batch->pages, batch->nr);
  		batch->nr = 0;
  	}
  	tlb->active = &tlb->local;
  }
  
  static void tlb_batch_list_free(struct mmu_gather *tlb)
  {
  	struct mmu_gather_batch *batch, *next;
  
  	for (batch = tlb->local.next; batch; batch = next) {
  		next = batch->next;
  		free_pages((unsigned long)batch, 0);
  	}
  	tlb->local.next = NULL;
  }
  
  bool __tlb_remove_page_size(struct mmu_gather *tlb, struct page *page, int page_size)
  {
  	struct mmu_gather_batch *batch;
  
  	VM_BUG_ON(!tlb->end);
3af4bd033   Peter Zijlstra   asm-generic/tlb: ...
69
  #ifdef CONFIG_MMU_GATHER_PAGE_SIZE
952a31c9e   Martin Schwidefsky   asm-generic/tlb: ...
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  	VM_WARN_ON(tlb->page_size != page_size);
  #endif
  
  	batch = tlb->active;
  	/*
  	 * Add the page and check if we are full. If so
  	 * force a flush.
  	 */
  	batch->pages[batch->nr++] = page;
  	if (batch->nr == batch->max) {
  		if (!tlb_next_batch(tlb))
  			return true;
  		batch = tlb->active;
  	}
  	VM_BUG_ON_PAGE(batch->nr > batch->max, page);
  
  	return false;
  }
580a586c4   Peter Zijlstra   asm-generic/tlb: ...
88
  #endif /* MMU_GATHER_NO_GATHER */
952a31c9e   Martin Schwidefsky   asm-generic/tlb: ...
89

0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
90
  #ifdef CONFIG_MMU_GATHER_TABLE_FREE
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
91

0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
92
93
94
95
96
97
98
99
100
101
102
  static void __tlb_remove_table_free(struct mmu_table_batch *batch)
  {
  	int i;
  
  	for (i = 0; i < batch->nr; i++)
  		__tlb_remove_table(batch->tables[i]);
  
  	free_page((unsigned long)batch);
  }
  
  #ifdef CONFIG_MMU_GATHER_RCU_TABLE_FREE
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
103
104
  
  /*
0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
   * Semi RCU freeing of the page directories.
   *
   * This is needed by some architectures to implement software pagetable walkers.
   *
   * gup_fast() and other software pagetable walkers do a lockless page-table
   * walk and therefore needs some synchronization with the freeing of the page
   * directories. The chosen means to accomplish that is by disabling IRQs over
   * the walk.
   *
   * Architectures that use IPIs to flush TLBs will then automagically DTRT,
   * since we unlink the page, flush TLBs, free the page. Since the disabling of
   * IRQs delays the completion of the TLB flush we can never observe an already
   * freed page.
   *
   * Architectures that do not have this (PPC) need to delay the freeing by some
   * other means, this is that means.
   *
   * What we do is batch the freed directory pages (tables) and RCU free them.
   * We use the sched RCU variant, as that guarantees that IRQ/preempt disabling
   * holds off grace periods.
   *
   * However, in order to batch these pages we need to allocate storage, this
   * allocation is deep inside the MM code and can thus easily fail on memory
   * pressure. To guarantee progress we fall back to single table freeing, see
   * the implementation of tlb_remove_table_one().
   *
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
131
   */
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
132
133
134
135
136
  
  static void tlb_remove_table_smp_sync(void *arg)
  {
  	/* Simply deliver the interrupt */
  }
0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
137
  static void tlb_remove_table_sync_one(void)
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
138
139
140
141
142
143
  {
  	/*
  	 * This isn't an RCU grace period and hence the page-tables cannot be
  	 * assumed to be actually RCU-freed.
  	 *
  	 * It is however sufficient for software page-table walkers that rely on
0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
144
  	 * IRQ disabling.
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
145
146
  	 */
  	smp_call_function(tlb_remove_table_smp_sync, NULL, 1);
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
147
148
149
150
  }
  
  static void tlb_remove_table_rcu(struct rcu_head *head)
  {
0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
151
152
  	__tlb_remove_table_free(container_of(head, struct mmu_table_batch, rcu));
  }
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
153

0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
154
155
156
157
  static void tlb_remove_table_free(struct mmu_table_batch *batch)
  {
  	call_rcu(&batch->rcu, tlb_remove_table_rcu);
  }
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
158

0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
159
  #else /* !CONFIG_MMU_GATHER_RCU_TABLE_FREE */
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
160

0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  static void tlb_remove_table_sync_one(void) { }
  
  static void tlb_remove_table_free(struct mmu_table_batch *batch)
  {
  	__tlb_remove_table_free(batch);
  }
  
  #endif /* CONFIG_MMU_GATHER_RCU_TABLE_FREE */
  
  /*
   * If we want tlb_remove_table() to imply TLB invalidates.
   */
  static inline void tlb_table_invalidate(struct mmu_gather *tlb)
  {
  	if (tlb_needs_table_invalidate()) {
  		/*
  		 * Invalidate page-table caches used by hardware walkers. Then
  		 * we still need to RCU-sched wait while freeing the pages
  		 * because software walkers can still be in-flight.
  		 */
  		tlb_flush_mmu_tlbonly(tlb);
  	}
  }
  
  static void tlb_remove_table_one(void *table)
  {
  	tlb_remove_table_sync_one();
  	__tlb_remove_table(table);
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
189
  }
0a8caf211   Peter Zijlstra   asm-generic/tlb: ...
190
  static void tlb_table_flush(struct mmu_gather *tlb)
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
191
192
193
194
195
  {
  	struct mmu_table_batch **batch = &tlb->batch;
  
  	if (*batch) {
  		tlb_table_invalidate(tlb);
0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
196
  		tlb_remove_table_free(*batch);
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  		*batch = NULL;
  	}
  }
  
  void tlb_remove_table(struct mmu_gather *tlb, void *table)
  {
  	struct mmu_table_batch **batch = &tlb->batch;
  
  	if (*batch == NULL) {
  		*batch = (struct mmu_table_batch *)__get_free_page(GFP_NOWAIT | __GFP_NOWARN);
  		if (*batch == NULL) {
  			tlb_table_invalidate(tlb);
  			tlb_remove_table_one(table);
  			return;
  		}
  		(*batch)->nr = 0;
  	}
  
  	(*batch)->tables[(*batch)->nr++] = table;
  	if ((*batch)->nr == MAX_TABLE_BATCH)
  		tlb_table_flush(tlb);
  }
0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
219
220
221
222
223
224
225
226
227
228
229
  static inline void tlb_table_init(struct mmu_gather *tlb)
  {
  	tlb->batch = NULL;
  }
  
  #else /* !CONFIG_MMU_GATHER_TABLE_FREE */
  
  static inline void tlb_table_flush(struct mmu_gather *tlb) { }
  static inline void tlb_table_init(struct mmu_gather *tlb) { }
  
  #endif /* CONFIG_MMU_GATHER_TABLE_FREE */
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
230

0a8caf211   Peter Zijlstra   asm-generic/tlb: ...
231
232
  static void tlb_flush_mmu_free(struct mmu_gather *tlb)
  {
0a8caf211   Peter Zijlstra   asm-generic/tlb: ...
233
  	tlb_table_flush(tlb);
580a586c4   Peter Zijlstra   asm-generic/tlb: ...
234
  #ifndef CONFIG_MMU_GATHER_NO_GATHER
0a8caf211   Peter Zijlstra   asm-generic/tlb: ...
235
236
237
238
239
240
241
242
243
  	tlb_batch_pages_flush(tlb);
  #endif
  }
  
  void tlb_flush_mmu(struct mmu_gather *tlb)
  {
  	tlb_flush_mmu_tlbonly(tlb);
  	tlb_flush_mmu_free(tlb);
  }
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  /**
   * tlb_gather_mmu - initialize an mmu_gather structure for page-table tear-down
   * @tlb: the mmu_gather structure to initialize
   * @mm: the mm_struct of the target address space
   * @start: start of the region that will be removed from the page-table
   * @end: end of the region that will be removed from the page-table
   *
   * Called to initialize an (on-stack) mmu_gather structure for page-table
   * tear-down from @mm. The @start and @end are set to 0 and -1
   * respectively when @mm is without users and we're going to destroy
   * the full address space (exit/execve).
   */
  void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm,
  			unsigned long start, unsigned long end)
  {
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
259
260
261
262
  	tlb->mm = mm;
  
  	/* Is it from 0 to ~0? */
  	tlb->fullmm     = !(start | (end+1));
580a586c4   Peter Zijlstra   asm-generic/tlb: ...
263
  #ifndef CONFIG_MMU_GATHER_NO_GATHER
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
264
265
266
267
268
269
270
  	tlb->need_flush_all = 0;
  	tlb->local.next = NULL;
  	tlb->local.nr   = 0;
  	tlb->local.max  = ARRAY_SIZE(tlb->__pages);
  	tlb->active     = &tlb->local;
  	tlb->batch_count = 0;
  #endif
0d6e24d43   Peter Zijlstra   asm-generic/tlb: ...
271
  	tlb_table_init(tlb);
3af4bd033   Peter Zijlstra   asm-generic/tlb: ...
272
  #ifdef CONFIG_MMU_GATHER_PAGE_SIZE
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
273
274
275
276
  	tlb->page_size = 0;
  #endif
  
  	__tlb_reset_range(tlb);
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
277
278
  	inc_tlb_flush_pending(tlb->mm);
  }
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
279
280
281
282
283
284
285
286
287
  /**
   * tlb_finish_mmu - finish an mmu_gather structure
   * @tlb: the mmu_gather structure to finish
   * @start: start of the region that will be removed from the page-table
   * @end: end of the region that will be removed from the page-table
   *
   * Called at the end of the shootdown operation to free up any resources that
   * were required.
   */
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
288
289
290
291
292
  void tlb_finish_mmu(struct mmu_gather *tlb,
  		unsigned long start, unsigned long end)
  {
  	/*
  	 * If there are parallel threads are doing PTE changes on same range
c1e8d7c6a   Michel Lespinasse   mmap locking API:...
293
  	 * under non-exclusive lock (e.g., mmap_lock read-side) but defer TLB
7a30df49f   Yang Shi   mm: mmu_gather: r...
294
295
296
297
298
299
300
301
  	 * flush by batching, one thread may end up seeing inconsistent PTEs
  	 * and result in having stale TLB entries.  So flush TLB forcefully
  	 * if we detect parallel PTE batching threads.
  	 *
  	 * However, some syscalls, e.g. munmap(), may free page tables, this
  	 * needs force flush everything in the given range. Otherwise this
  	 * may result in having stale TLB entries for some architectures,
  	 * e.g. aarch64, that could specify flush what level TLB.
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
302
  	 */
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
303
  	if (mm_tlb_flush_nested(tlb->mm)) {
7a30df49f   Yang Shi   mm: mmu_gather: r...
304
305
306
307
308
309
310
311
312
  		/*
  		 * The aarch64 yields better performance with fullmm by
  		 * avoiding multiple CPUs spamming TLBI messages at the
  		 * same time.
  		 *
  		 * On x86 non-fullmm doesn't yield significant difference
  		 * against fullmm.
  		 */
  		tlb->fullmm = 1;
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
313
  		__tlb_reset_range(tlb);
7a30df49f   Yang Shi   mm: mmu_gather: r...
314
  		tlb->freed_tables = 1;
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
315
  	}
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
316

1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
317
  	tlb_flush_mmu(tlb);
580a586c4   Peter Zijlstra   asm-generic/tlb: ...
318
  #ifndef CONFIG_MMU_GATHER_NO_GATHER
1808d65b5   Peter Zijlstra   asm-generic/tlb: ...
319
320
  	tlb_batch_list_free(tlb);
  #endif
196d9d8bb   Peter Zijlstra   mm/memory: Move m...
321
322
  	dec_tlb_flush_pending(tlb->mm);
  }