aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2017-05-28 13:00:12 -0400
committerIngo Molnar <mingo@kernel.org>2017-06-05 03:59:43 -0400
commit454bbad9793f59f5656ce5971ee473a8be736ef5 (patch)
tree633d326720b71db25bddc802b5a036c98dbffdb5
parent59f537c1dea04287165bb11407921e095250dc80 (diff)
x86/mm: Refactor flush_tlb_mm_range() to merge local and remote cases
The local flush path is very similar to the remote flush path. Merge them. This is intended to make no difference to behavior whatsoever. It removes some code and will make future changes to the flushing mechanics simpler. This patch does remove one small optimization: flush_tlb_mm_range() now has an unconditional smp_mb() instead of using MOV to CR3 or INVLPG as a full barrier when applicable. I think this is okay for a few reasons. First, smp_mb() is quite cheap compared to the cost of a TLB flush. Second, this rearrangement makes a bigger optimization available: with some work on the SMP function call code, we could do the local and remote flushes in parallel. Third, I'm planning a rework of the TLB flush algorithm that will require an atomic operation at the beginning of each flush, and that operation will replace the smp_mb(). Signed-off-by: Andy Lutomirski <luto@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Arjan van de Ven <arjan@linux.intel.com> Cc: Borislav Petkov <bpetkov@suse.de> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mel Gorman <mgorman@suse.de> Cc: Michal Hocko <mhocko@suse.com> Cc: Nadav Amit <nadav.amit@gmail.com> Cc: Nadav Amit <namit@vmware.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rik van Riel <riel@redhat.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-mm@kvack.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/tlbflush.h1
-rw-r--r--arch/x86/mm/tlb.c113
2 files changed, 48 insertions, 66 deletions
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 6f439ac92026..9934c7c99213 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -225,7 +225,6 @@ static inline void __flush_tlb_one(unsigned long addr)
225 * ..but the i386 has somewhat limited tlb flushing capabilities, 225 * ..but the i386 has somewhat limited tlb flushing capabilities,
226 * and page-granular flushes are available only on i486 and up. 226 * and page-granular flushes are available only on i486 and up.
227 */ 227 */
228
229struct flush_tlb_info { 228struct flush_tlb_info {
230 struct mm_struct *mm; 229 struct mm_struct *mm;
231 unsigned long start; 230 unsigned long start;
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 3143c9a180e5..12b8812e8926 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -216,22 +216,9 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
216 * write/read ordering problems. 216 * write/read ordering problems.
217 */ 217 */
218 218
219/* 219static void flush_tlb_func_common(const struct flush_tlb_info *f,
220 * TLB flush funcation: 220 bool local, enum tlb_flush_reason reason)
221 * 1) Flush the tlb entries if the cpu uses the mm that's being flushed.
222 * 2) Leave the mm if we are in the lazy tlb mode.
223 */
224static void flush_tlb_func(void *info)
225{ 221{
226 const struct flush_tlb_info *f = info;
227
228 inc_irq_stat(irq_tlb_count);
229
230 if (f->mm && f->mm != this_cpu_read(cpu_tlbstate.active_mm))
231 return;
232
233 count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
234
235 if (this_cpu_read(cpu_tlbstate.state) != TLBSTATE_OK) { 222 if (this_cpu_read(cpu_tlbstate.state) != TLBSTATE_OK) {
236 leave_mm(smp_processor_id()); 223 leave_mm(smp_processor_id());
237 return; 224 return;
@@ -239,7 +226,9 @@ static void flush_tlb_func(void *info)
239 226
240 if (f->end == TLB_FLUSH_ALL) { 227 if (f->end == TLB_FLUSH_ALL) {
241 local_flush_tlb(); 228 local_flush_tlb();
242 trace_tlb_flush(TLB_REMOTE_SHOOTDOWN, TLB_FLUSH_ALL); 229 if (local)
230 count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
231 trace_tlb_flush(reason, TLB_FLUSH_ALL);
243 } else { 232 } else {
244 unsigned long addr; 233 unsigned long addr;
245 unsigned long nr_pages = 234 unsigned long nr_pages =
@@ -249,10 +238,32 @@ static void flush_tlb_func(void *info)
249 __flush_tlb_single(addr); 238 __flush_tlb_single(addr);
250 addr += PAGE_SIZE; 239 addr += PAGE_SIZE;
251 } 240 }
252 trace_tlb_flush(TLB_REMOTE_SHOOTDOWN, nr_pages); 241 if (local)
242 count_vm_tlb_events(NR_TLB_LOCAL_FLUSH_ONE, nr_pages);
243 trace_tlb_flush(reason, nr_pages);
253 } 244 }
254} 245}
255 246
247static void flush_tlb_func_local(void *info, enum tlb_flush_reason reason)
248{
249 const struct flush_tlb_info *f = info;
250
251 flush_tlb_func_common(f, true, reason);
252}
253
254static void flush_tlb_func_remote(void *info)
255{
256 const struct flush_tlb_info *f = info;
257
258 inc_irq_stat(irq_tlb_count);
259
260 if (f->mm && f->mm != this_cpu_read(cpu_tlbstate.active_mm))
261 return;
262
263 count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
264 flush_tlb_func_common(f, false, TLB_REMOTE_SHOOTDOWN);
265}
266
256void native_flush_tlb_others(const struct cpumask *cpumask, 267void native_flush_tlb_others(const struct cpumask *cpumask,
257 const struct flush_tlb_info *info) 268 const struct flush_tlb_info *info)
258{ 269{
@@ -269,11 +280,11 @@ void native_flush_tlb_others(const struct cpumask *cpumask,
269 cpu = smp_processor_id(); 280 cpu = smp_processor_id();
270 cpumask = uv_flush_tlb_others(cpumask, info); 281 cpumask = uv_flush_tlb_others(cpumask, info);
271 if (cpumask) 282 if (cpumask)
272 smp_call_function_many(cpumask, flush_tlb_func, 283 smp_call_function_many(cpumask, flush_tlb_func_remote,
273 (void *)info, 1); 284 (void *)info, 1);
274 return; 285 return;
275 } 286 }
276 smp_call_function_many(cpumask, flush_tlb_func, 287 smp_call_function_many(cpumask, flush_tlb_func_remote,
277 (void *)info, 1); 288 (void *)info, 1);
278} 289}
279 290
@@ -292,61 +303,33 @@ static unsigned long tlb_single_page_flush_ceiling __read_mostly = 33;
292void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start, 303void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
293 unsigned long end, unsigned long vmflag) 304 unsigned long end, unsigned long vmflag)
294{ 305{
295 unsigned long addr; 306 int cpu;
296 struct flush_tlb_info info;
297 /* do a global flush by default */
298 unsigned long base_pages_to_flush = TLB_FLUSH_ALL;
299
300 preempt_disable();
301 307
302 if ((end != TLB_FLUSH_ALL) && !(vmflag & VM_HUGETLB)) 308 struct flush_tlb_info info = {
303 base_pages_to_flush = (end - start) >> PAGE_SHIFT; 309 .mm = mm,
304 if (base_pages_to_flush > tlb_single_page_flush_ceiling) 310 };
305 base_pages_to_flush = TLB_FLUSH_ALL;
306
307 if (current->active_mm != mm) {
308 /* Synchronize with switch_mm. */
309 smp_mb();
310
311 goto out;
312 }
313
314 if (this_cpu_read(cpu_tlbstate.state) != TLBSTATE_OK) {
315 leave_mm(smp_processor_id());
316 311
317 /* Synchronize with switch_mm. */ 312 cpu = get_cpu();
318 smp_mb();
319 313
320 goto out; 314 /* Synchronize with switch_mm. */
321 } 315 smp_mb();
322 316
323 /* 317 /* Should we flush just the requested range? */
324 * Both branches below are implicit full barriers (MOV to CR or 318 if ((end != TLB_FLUSH_ALL) &&
325 * INVLPG) that synchronize with switch_mm. 319 !(vmflag & VM_HUGETLB) &&
326 */ 320 ((end - start) >> PAGE_SHIFT) <= tlb_single_page_flush_ceiling) {
327 if (base_pages_to_flush == TLB_FLUSH_ALL) { 321 info.start = start;
328 count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL); 322 info.end = end;
329 local_flush_tlb();
330 } else { 323 } else {
331 /* flush range by one by one 'invlpg' */
332 for (addr = start; addr < end; addr += PAGE_SIZE) {
333 count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ONE);
334 __flush_tlb_single(addr);
335 }
336 }
337 trace_tlb_flush(TLB_LOCAL_MM_SHOOTDOWN, base_pages_to_flush);
338out:
339 info.mm = mm;
340 if (base_pages_to_flush == TLB_FLUSH_ALL) {
341 info.start = 0UL; 324 info.start = 0UL;
342 info.end = TLB_FLUSH_ALL; 325 info.end = TLB_FLUSH_ALL;
343 } else {
344 info.start = start;
345 info.end = end;
346 } 326 }
347 if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) 327
328 if (mm == current->active_mm)
329 flush_tlb_func_local(&info, TLB_LOCAL_MM_SHOOTDOWN);
330 if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids)
348 flush_tlb_others(mm_cpumask(mm), &info); 331 flush_tlb_others(mm_cpumask(mm), &info);
349 preempt_enable(); 332 put_cpu();
350} 333}
351 334
352 335