aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/preempt.h18
-rw-r--r--kernel/context_tracking.c40
2 files changed, 57 insertions, 1 deletions
diff --git a/include/linux/preempt.h b/include/linux/preempt.h
index 87a03c746f17..f5d4723cdb3d 100644
--- a/include/linux/preempt.h
+++ b/include/linux/preempt.h
@@ -33,9 +33,25 @@ do { \
33 preempt_schedule(); \ 33 preempt_schedule(); \
34} while (0) 34} while (0)
35 35
36#ifdef CONFIG_CONTEXT_TRACKING
37
38void preempt_schedule_context(void);
39
40#define preempt_check_resched_context() \
41do { \
42 if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \
43 preempt_schedule_context(); \
44} while (0)
45#else
46
47#define preempt_check_resched_context() preempt_check_resched()
48
49#endif /* CONFIG_CONTEXT_TRACKING */
50
36#else /* !CONFIG_PREEMPT */ 51#else /* !CONFIG_PREEMPT */
37 52
38#define preempt_check_resched() do { } while (0) 53#define preempt_check_resched() do { } while (0)
54#define preempt_check_resched_context() do { } while (0)
39 55
40#endif /* CONFIG_PREEMPT */ 56#endif /* CONFIG_PREEMPT */
41 57
@@ -88,7 +104,7 @@ do { \
88do { \ 104do { \
89 preempt_enable_no_resched_notrace(); \ 105 preempt_enable_no_resched_notrace(); \
90 barrier(); \ 106 barrier(); \
91 preempt_check_resched(); \ 107 preempt_check_resched_context(); \
92} while (0) 108} while (0)
93 109
94#else /* !CONFIG_PREEMPT_COUNT */ 110#else /* !CONFIG_PREEMPT_COUNT */
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 */
89void __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}
112EXPORT_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