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 |
