diff options
-rw-r--r-- | include/linux/preempt.h | 18 | ||||
-rw-r--r-- | kernel/context_tracking.c | 40 |
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 | |||
38 | void preempt_schedule_context(void); | ||
39 | |||
40 | #define preempt_check_resched_context() \ | ||
41 | do { \ | ||
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 { \ | |||
88 | do { \ | 104 | do { \ |
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 | */ | ||
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 |