Commit f821bc97 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman

powerpc/64s: move context tracking exit to interrupt exit path

The interrupt handler wrapper functions are not the ideal place to
maintain context tracking because after they return, the low level exit
code must then determine if there are interrupts to replay, or if the
task should be preempted, etc. Those paths (e.g., schedule_user) include
their own exception_enter/exit pairs to fix this up but it's a bit hacky
(see schedule_user() comments).

Ideally context tracking will go to user mode only when there are no
more interrupts or context switches or other exit processing work to
handle.

64e can not do this because it does not use the C interrupt exit code.
Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210130130852.2952424-36-npiggin@gmail.com
parent 1b1b6a6f
...@@ -7,16 +7,30 @@ ...@@ -7,16 +7,30 @@
#include <asm/ftrace.h> #include <asm/ftrace.h>
struct interrupt_state { struct interrupt_state {
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC_BOOK3E_64
enum ctx_state ctx_state; enum ctx_state ctx_state;
#endif #endif
}; };
static inline void interrupt_enter_prepare(struct pt_regs *regs, struct interrupt_state *state) static inline void interrupt_enter_prepare(struct pt_regs *regs, struct interrupt_state *state)
{ {
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC_BOOK3E_64
state->ctx_state = exception_enter(); state->ctx_state = exception_enter();
#endif #endif
#ifdef CONFIG_PPC_BOOK3S_64
if (user_mode(regs)) {
CT_WARN_ON(ct_state() != CONTEXT_USER);
user_exit_irqoff();
} else {
/*
* CT_WARN_ON comes here via program_check_exception,
* so avoid recursion.
*/
if (TRAP(regs) != 0x700)
CT_WARN_ON(ct_state() != CONTEXT_KERNEL);
}
#endif
} }
/* /*
...@@ -35,9 +49,23 @@ static inline void interrupt_enter_prepare(struct pt_regs *regs, struct interrup ...@@ -35,9 +49,23 @@ static inline void interrupt_enter_prepare(struct pt_regs *regs, struct interrup
*/ */
static inline void interrupt_exit_prepare(struct pt_regs *regs, struct interrupt_state *state) static inline void interrupt_exit_prepare(struct pt_regs *regs, struct interrupt_state *state)
{ {
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC_BOOK3E_64
exception_exit(state->ctx_state); exception_exit(state->ctx_state);
#endif #endif
/*
* Book3S exits to user via interrupt_exit_user_prepare(), which does
* context tracking, which is a cleaner way to handle PREEMPT=y
* and avoid context entry/exit in e.g., preempt_schedule_irq()),
* which is likely to be where the core code wants to end up.
*
* The above comment explains why we can't do the
*
* if (user_mode(regs))
* user_exit_irqoff();
*
* sequence here.
*/
} }
static inline void interrupt_async_enter_prepare(struct pt_regs *regs, struct interrupt_state *state) static inline void interrupt_async_enter_prepare(struct pt_regs *regs, struct interrupt_state *state)
......
...@@ -255,9 +255,9 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, ...@@ -255,9 +255,9 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
ret |= _TIF_RESTOREALL; ret |= _TIF_RESTOREALL;
} }
again:
local_irq_disable(); local_irq_disable();
again:
ti_flags = READ_ONCE(*ti_flagsp); ti_flags = READ_ONCE(*ti_flagsp);
while (unlikely(ti_flags & (_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM))) { while (unlikely(ti_flags & (_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM))) {
local_irq_enable(); local_irq_enable();
...@@ -307,6 +307,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, ...@@ -307,6 +307,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
if (unlikely(!__prep_irq_for_enabled_exit(!scv))) { if (unlikely(!__prep_irq_for_enabled_exit(!scv))) {
user_exit_irqoff(); user_exit_irqoff();
local_irq_enable(); local_irq_enable();
local_irq_disable();
goto again; goto again;
} }
...@@ -341,6 +342,7 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned ...@@ -341,6 +342,7 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned
BUG_ON(!(regs->msr & MSR_PR)); BUG_ON(!(regs->msr & MSR_PR));
BUG_ON(!FULL_REGS(regs)); BUG_ON(!FULL_REGS(regs));
BUG_ON(regs->softe != IRQS_ENABLED); BUG_ON(regs->softe != IRQS_ENABLED);
CT_WARN_ON(ct_state() == CONTEXT_USER);
/* /*
* We don't need to restore AMR on the way back to userspace for KUAP. * We don't need to restore AMR on the way back to userspace for KUAP.
...@@ -383,8 +385,14 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned ...@@ -383,8 +385,14 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned
} }
} }
if (unlikely(!prep_irq_for_enabled_exit(true, !irqs_disabled_flags(flags)))) user_enter_irqoff();
if (unlikely(!__prep_irq_for_enabled_exit(true))) {
user_exit_irqoff();
local_irq_enable();
local_irq_disable();
goto again; goto again;
}
#ifdef CONFIG_PPC_BOOK3E #ifdef CONFIG_PPC_BOOK3E
if (unlikely(ts->debug.dbcr0 & DBCR0_IDM)) { if (unlikely(ts->debug.dbcr0 & DBCR0_IDM)) {
...@@ -425,6 +433,12 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs, unsign ...@@ -425,6 +433,12 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs, unsign
unrecoverable_exception(regs); unrecoverable_exception(regs);
BUG_ON(regs->msr & MSR_PR); BUG_ON(regs->msr & MSR_PR);
BUG_ON(!FULL_REGS(regs)); BUG_ON(!FULL_REGS(regs));
/*
* CT_WARN_ON comes here via program_check_exception,
* so avoid recursion.
*/
if (TRAP(regs) != 0x700)
CT_WARN_ON(ct_state() == CONTEXT_USER);
amr = kuap_get_and_check_amr(); amr = kuap_get_and_check_amr();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment