sched: stop the unbound recursion in preempt_schedule_context()
preempt_schedule_context() does preempt_enable_notrace() at the end and this can call the same function again; exception_exit() is heavy and it is quite possible that need-resched is true again. 1. Change this code to dec preempt_count() and check need_resched() by hand. 2. As Linus suggested, we can use the PREEMPT_ACTIVE bit and avoid the enable/disable dance around __schedule(). But in this case we need to move into sched/core.c. 3. Cosmetic, but x86 forgets to declare this function. This doesn't really matter because it is only called by asm helpers, still it make sense to add the declaration into asm/preempt.h to match preempt_schedule(). Reported-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Alexander Graf <agraf@suse.de> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Christoph Lameter <cl@linux.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Peter Anvin <hpa@zytor.com> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: Chuck Ebbert <cebbert.lkml@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Link: http://lkml.kernel.org/r/20141005202322.GB27962@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
6419265899
commit
009f60e276
|
@ -105,6 +105,7 @@ static __always_inline bool should_resched(void)
|
||||||
# ifdef CONFIG_CONTEXT_TRACKING
|
# ifdef CONFIG_CONTEXT_TRACKING
|
||||||
extern asmlinkage void ___preempt_schedule_context(void);
|
extern asmlinkage void ___preempt_schedule_context(void);
|
||||||
# define __preempt_schedule_context() asm ("call ___preempt_schedule_context")
|
# define __preempt_schedule_context() asm ("call ___preempt_schedule_context")
|
||||||
|
extern asmlinkage void preempt_schedule_context(void);
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -107,46 +107,6 @@ void context_tracking_user_enter(void)
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(context_tracking_user_enter);
|
NOKPROBE_SYMBOL(context_tracking_user_enter);
|
||||||
|
|
||||||
#ifdef CONFIG_PREEMPT
|
|
||||||
/**
|
|
||||||
* preempt_schedule_context - preempt_schedule called by tracing
|
|
||||||
*
|
|
||||||
* The tracing infrastructure uses preempt_enable_notrace to prevent
|
|
||||||
* recursion and tracing preempt enabling caused by the tracing
|
|
||||||
* infrastructure itself. But as tracing can happen in areas coming
|
|
||||||
* from userspace or just about to enter userspace, a preempt enable
|
|
||||||
* can occur before user_exit() is called. This will cause the scheduler
|
|
||||||
* to be called when the system is still in usermode.
|
|
||||||
*
|
|
||||||
* To prevent this, the preempt_enable_notrace will use this function
|
|
||||||
* instead of preempt_schedule() to exit user context if needed before
|
|
||||||
* calling the scheduler.
|
|
||||||
*/
|
|
||||||
asmlinkage __visible void __sched notrace preempt_schedule_context(void)
|
|
||||||
{
|
|
||||||
enum ctx_state prev_ctx;
|
|
||||||
|
|
||||||
if (likely(!preemptible()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Need to disable preemption in case user_exit() is traced
|
|
||||||
* and the tracer calls preempt_enable_notrace() causing
|
|
||||||
* an infinite recursion.
|
|
||||||
*/
|
|
||||||
preempt_disable_notrace();
|
|
||||||
prev_ctx = exception_enter();
|
|
||||||
preempt_enable_no_resched_notrace();
|
|
||||||
|
|
||||||
preempt_schedule();
|
|
||||||
|
|
||||||
preempt_disable_notrace();
|
|
||||||
exception_exit(prev_ctx);
|
|
||||||
preempt_enable_notrace();
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(preempt_schedule_context);
|
|
||||||
#endif /* CONFIG_PREEMPT */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* context_tracking_user_exit - Inform the context tracking that the CPU is
|
* context_tracking_user_exit - Inform the context tracking that the CPU is
|
||||||
* exiting userspace mode and entering the kernel.
|
* exiting userspace mode and entering the kernel.
|
||||||
|
|
|
@ -2951,6 +2951,47 @@ asmlinkage __visible void __sched notrace preempt_schedule(void)
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(preempt_schedule);
|
NOKPROBE_SYMBOL(preempt_schedule);
|
||||||
EXPORT_SYMBOL(preempt_schedule);
|
EXPORT_SYMBOL(preempt_schedule);
|
||||||
|
|
||||||
|
#ifdef CONFIG_CONTEXT_TRACKING
|
||||||
|
/**
|
||||||
|
* preempt_schedule_context - preempt_schedule called by tracing
|
||||||
|
*
|
||||||
|
* The tracing infrastructure uses preempt_enable_notrace to prevent
|
||||||
|
* recursion and tracing preempt enabling caused by the tracing
|
||||||
|
* infrastructure itself. But as tracing can happen in areas coming
|
||||||
|
* from userspace or just about to enter userspace, a preempt enable
|
||||||
|
* can occur before user_exit() is called. This will cause the scheduler
|
||||||
|
* to be called when the system is still in usermode.
|
||||||
|
*
|
||||||
|
* To prevent this, the preempt_enable_notrace will use this function
|
||||||
|
* instead of preempt_schedule() to exit user context if needed before
|
||||||
|
* calling the scheduler.
|
||||||
|
*/
|
||||||
|
asmlinkage __visible void __sched notrace preempt_schedule_context(void)
|
||||||
|
{
|
||||||
|
enum ctx_state prev_ctx;
|
||||||
|
|
||||||
|
if (likely(!preemptible()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
__preempt_count_add(PREEMPT_ACTIVE);
|
||||||
|
/*
|
||||||
|
* Needs preempt disabled in case user_exit() is traced
|
||||||
|
* and the tracer calls preempt_enable_notrace() causing
|
||||||
|
* an infinite recursion.
|
||||||
|
*/
|
||||||
|
prev_ctx = exception_enter();
|
||||||
|
__schedule();
|
||||||
|
exception_exit(prev_ctx);
|
||||||
|
|
||||||
|
__preempt_count_sub(PREEMPT_ACTIVE);
|
||||||
|
barrier();
|
||||||
|
} while (need_resched());
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(preempt_schedule_context);
|
||||||
|
#endif /* CONFIG_CONTEXT_TRACKING */
|
||||||
|
|
||||||
#endif /* CONFIG_PREEMPT */
|
#endif /* CONFIG_PREEMPT */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue