diff options
author | Andy Lutomirski <luto@kernel.org> | 2016-03-09 16:24:33 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-03-10 04:53:26 -0500 |
commit | 9999c8c01f34c918a57d6e5ba2f5d8b79aa04801 (patch) | |
tree | fc47bd645a0dd3d20f8a1fd78812d9e6f09b5ad7 /arch/x86/entry/common.c | |
parent | a798f091113ef4999277dbe0483d37d04fa35b2e (diff) |
x86/entry: Call enter_from_user_mode() with IRQs off
Now that slow-path syscalls always enter C before enabling
interrupts, it's straightforward to call enter_from_user_mode() before
enabling interrupts rather than doing it as part of entry tracing.
With this change, we should finally be able to retire exception_enter().
This will also enable optimizations based on knowing that we never
change context tracking state with interrupts on.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rik van Riel <riel@redhat.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/bc376ecf87921a495e874ff98139b1ca2f5c5dd7.1457558566.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/entry/common.c')
-rw-r--r-- | arch/x86/entry/common.c | 33 |
1 files changed, 11 insertions, 22 deletions
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index d69d1b6e6c31..e79d93d44ecd 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c | |||
@@ -45,6 +45,8 @@ __visible void enter_from_user_mode(void) | |||
45 | CT_WARN_ON(ct_state() != CONTEXT_USER); | 45 | CT_WARN_ON(ct_state() != CONTEXT_USER); |
46 | user_exit(); | 46 | user_exit(); |
47 | } | 47 | } |
48 | #else | ||
49 | static inline void enter_from_user_mode(void) {} | ||
48 | #endif | 50 | #endif |
49 | 51 | ||
50 | static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch) | 52 | static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch) |
@@ -85,17 +87,6 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch) | |||
85 | 87 | ||
86 | work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY; | 88 | work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY; |
87 | 89 | ||
88 | #ifdef CONFIG_CONTEXT_TRACKING | ||
89 | /* | ||
90 | * If TIF_NOHZ is set, we are required to call user_exit() before | ||
91 | * doing anything that could touch RCU. | ||
92 | */ | ||
93 | if (work & _TIF_NOHZ) { | ||
94 | enter_from_user_mode(); | ||
95 | work &= ~_TIF_NOHZ; | ||
96 | } | ||
97 | #endif | ||
98 | |||
99 | #ifdef CONFIG_SECCOMP | 90 | #ifdef CONFIG_SECCOMP |
100 | /* | 91 | /* |
101 | * Do seccomp first -- it should minimize exposure of other | 92 | * Do seccomp first -- it should minimize exposure of other |
@@ -344,6 +335,7 @@ __visible void do_syscall_64(struct pt_regs *regs) | |||
344 | struct thread_info *ti = pt_regs_to_thread_info(regs); | 335 | struct thread_info *ti = pt_regs_to_thread_info(regs); |
345 | unsigned long nr = regs->orig_ax; | 336 | unsigned long nr = regs->orig_ax; |
346 | 337 | ||
338 | enter_from_user_mode(); | ||
347 | local_irq_enable(); | 339 | local_irq_enable(); |
348 | 340 | ||
349 | if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY) | 341 | if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY) |
@@ -366,9 +358,9 @@ __visible void do_syscall_64(struct pt_regs *regs) | |||
366 | 358 | ||
367 | #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) | 359 | #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) |
368 | /* | 360 | /* |
369 | * Does a 32-bit syscall. Called with IRQs on and does all entry and | 361 | * Does a 32-bit syscall. Called with IRQs on in CONTEXT_KERNEL. Does |
370 | * exit work and returns with IRQs off. This function is extremely hot | 362 | * all entry and exit work and returns with IRQs off. This function is |
371 | * in workloads that use it, and it's usually called from | 363 | * extremely hot in workloads that use it, and it's usually called from |
372 | * do_fast_syscall_32, so forcibly inline it to improve performance. | 364 | * do_fast_syscall_32, so forcibly inline it to improve performance. |
373 | */ | 365 | */ |
374 | static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs) | 366 | static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs) |
@@ -409,6 +401,7 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs) | |||
409 | /* Handles int $0x80 */ | 401 | /* Handles int $0x80 */ |
410 | __visible void do_int80_syscall_32(struct pt_regs *regs) | 402 | __visible void do_int80_syscall_32(struct pt_regs *regs) |
411 | { | 403 | { |
404 | enter_from_user_mode(); | ||
412 | local_irq_enable(); | 405 | local_irq_enable(); |
413 | do_syscall_32_irqs_on(regs); | 406 | do_syscall_32_irqs_on(regs); |
414 | } | 407 | } |
@@ -431,12 +424,11 @@ __visible long do_fast_syscall_32(struct pt_regs *regs) | |||
431 | */ | 424 | */ |
432 | regs->ip = landing_pad; | 425 | regs->ip = landing_pad; |
433 | 426 | ||
434 | /* | 427 | enter_from_user_mode(); |
435 | * Fetch EBP from where the vDSO stashed it. | 428 | |
436 | * | ||
437 | * WARNING: We are in CONTEXT_USER and RCU isn't paying attention! | ||
438 | */ | ||
439 | local_irq_enable(); | 429 | local_irq_enable(); |
430 | |||
431 | /* Fetch EBP from where the vDSO stashed it. */ | ||
440 | if ( | 432 | if ( |
441 | #ifdef CONFIG_X86_64 | 433 | #ifdef CONFIG_X86_64 |
442 | /* | 434 | /* |
@@ -454,9 +446,6 @@ __visible long do_fast_syscall_32(struct pt_regs *regs) | |||
454 | /* User code screwed up. */ | 446 | /* User code screwed up. */ |
455 | local_irq_disable(); | 447 | local_irq_disable(); |
456 | regs->ax = -EFAULT; | 448 | regs->ax = -EFAULT; |
457 | #ifdef CONFIG_CONTEXT_TRACKING | ||
458 | enter_from_user_mode(); | ||
459 | #endif | ||
460 | prepare_exit_to_usermode(regs); | 449 | prepare_exit_to_usermode(regs); |
461 | return 0; /* Keep it simple: use IRET. */ | 450 | return 0; /* Keep it simple: use IRET. */ |
462 | } | 451 | } |