diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2020-03-26 16:28:52 +0100 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2020-05-05 11:27:55 +0200 |
commit | ac3638d40bc69c4f52a44af326f7d7ce6099830b (patch) | |
tree | 7f5e0234bbd488bb6bdebd8c742dc6c104d8c732 | |
parent | a03ea071011c4e09adc9e200889300d005277840 (diff) |
x86/entry/common: Provide idtentry_enter/exit()
Provide functions which handle the low level entry and exit similiar to
enter/exit from user mode.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/entry/common.c | 89 | ||||
-rw-r--r-- | arch/x86/include/asm/idtentry.h | 3 |
2 files changed, 92 insertions, 0 deletions
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 8451a15b08fc..188effed3436 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -510,3 +510,92 @@ SYSCALL_DEFINE0(ni_syscall) { return -ENOSYS; } + +/** + * idtentry_enter - Handle state tracking on idtentry + * @regs: Pointer to pt_regs of interrupted context + * + * Invokes: + * - lockdep irqflag state tracking as low level ASM entry disabled + * interrupts. + * + * - Context tracking if the exception hit user mode. + * + * - RCU notification if the exception hit kernel mode + * + * - The hardirq tracer to keep the state consistent as low level ASM + * entry disabled interrupts. + */ +void noinstr idtentry_enter(struct pt_regs *regs) +{ + if (user_mode(regs)) { + enter_from_user_mode(); + } else { + lockdep_hardirqs_off(CALLER_ADDR0); + rcu_irq_enter(); + instr_begin(); + trace_hardirqs_off_prepare(); + instr_end(); + } +} + +/** + * idtentry_exit - Common code to handle return from exceptions + * @regs: Pointer to pt_regs (exception entry regs) + * + * Depending on the return target (kernel/user) this runs the necessary + * preemption and work checks if possible and reguired and returns to + * the caller with interrupts disabled and no further work pending. + * + * This is the last action before returning to the low level ASM code which + * just needs to return to the appropriate context. + * + * Invoked by all exception/interrupt IDTENTRY handlers which are not + * returning through the paranoid exit path (all except NMI, #DF and the IST + * variants of #MC and #DB). + */ +void noinstr idtentry_exit(struct pt_regs *regs) +{ + lockdep_assert_irqs_disabled(); + + /* Check whether this returns to user mode */ + if (user_mode(regs)) { + prepare_exit_to_usermode(regs); + } else if (regs->flags & X86_EFLAGS_IF) { + /* Check kernel preemption, if enabled */ + if (IS_ENABLED(CONFIG_PREEMPTION)) { + /* + * This needs to be done very carefully. + * idtentry_enter() invoked rcu_irq_enter(). This + * needs to undone before scheduling. + * + * Preemption is disabled inside of RCU idle + * sections. When the task returns from + * preempt_schedule_irq(), RCU is still watching. + * + * rcu_irq_exit_preempt() has additional state + * checking if CONFIG_PROVE_RCU=y + */ + if (!preempt_count()) { + instr_begin(); + rcu_irq_exit_preempt(); + if (need_resched()) + preempt_schedule_irq(); + /* Covers both tracing and lockdep */ + trace_hardirqs_on(); + instr_end(); + return; + } + } + instr_begin(); + /* Tell the tracer that IRET will enable interrupts */ + trace_hardirqs_on_prepare(); + lockdep_hardirqs_on_prepare(CALLER_ADDR0); + instr_end(); + rcu_irq_exit(); + lockdep_hardirqs_on(CALLER_ADDR0); + } else { + /* IRQ flags state is correct already. Just tell RCU */ + rcu_irq_exit(); + } +} diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h index 5bced430eaf5..a2ba2b6c981d 100644 --- a/arch/x86/include/asm/idtentry.h +++ b/arch/x86/include/asm/idtentry.h @@ -7,6 +7,9 @@ #ifndef __ASSEMBLY__ +void idtentry_enter(struct pt_regs *regs); +void idtentry_exit(struct pt_regs *regs); + /** * DECLARE_IDTENTRY - Declare functions for simple IDT entry points * No error code pushed by hardware |