diff options
Diffstat (limited to 'kernel/context_tracking.c')
-rw-r--r-- | kernel/context_tracking.c | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 65349f07b878..66677003e223 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c | |||
@@ -71,6 +71,46 @@ void user_enter(void) | |||
71 | local_irq_restore(flags); | 71 | local_irq_restore(flags); |
72 | } | 72 | } |
73 | 73 | ||
74 | #ifdef CONFIG_PREEMPT | ||
75 | /** | ||
76 | * preempt_schedule_context - preempt_schedule called by tracing | ||
77 | * | ||
78 | * The tracing infrastructure uses preempt_enable_notrace to prevent | ||
79 | * recursion and tracing preempt enabling caused by the tracing | ||
80 | * infrastructure itself. But as tracing can happen in areas coming | ||
81 | * from userspace or just about to enter userspace, a preempt enable | ||
82 | * can occur before user_exit() is called. This will cause the scheduler | ||
83 | * to be called when the system is still in usermode. | ||
84 | * | ||
85 | * To prevent this, the preempt_enable_notrace will use this function | ||
86 | * instead of preempt_schedule() to exit user context if needed before | ||
87 | * calling the scheduler. | ||
88 | */ | ||
89 | void __sched notrace preempt_schedule_context(void) | ||
90 | { | ||
91 | struct thread_info *ti = current_thread_info(); | ||
92 | enum ctx_state prev_ctx; | ||
93 | |||
94 | if (likely(ti->preempt_count || irqs_disabled())) | ||
95 | return; | ||
96 | |||
97 | /* | ||
98 | * Need to disable preemption in case user_exit() is traced | ||
99 | * and the tracer calls preempt_enable_notrace() causing | ||
100 | * an infinite recursion. | ||
101 | */ | ||
102 | preempt_disable_notrace(); | ||
103 | prev_ctx = exception_enter(); | ||
104 | preempt_enable_no_resched_notrace(); | ||
105 | |||
106 | preempt_schedule(); | ||
107 | |||
108 | preempt_disable_notrace(); | ||
109 | exception_exit(prev_ctx); | ||
110 | preempt_enable_notrace(); | ||
111 | } | ||
112 | EXPORT_SYMBOL_GPL(preempt_schedule_context); | ||
113 | #endif /* CONFIG_PREEMPT */ | ||
74 | 114 | ||
75 | /** | 115 | /** |
76 | * user_exit - Inform the context tracking that the CPU is | 116 | * user_exit - Inform the context tracking that the CPU is |