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().
Unified · Split
| @@ -105,6 +105,7 @@ static __always_inline bool should_resched(void) · 104 unmodified lines | |||
| 105 | # ifdef CONFIG_CONTEXT_TRACKING | 105 | # ifdef CONFIG_CONTEXT_TRACKING |
| 106 | extern asmlinkage void ___preempt_schedule_context(void); | 106 | extern asmlinkage void ___preempt_schedule_context(void); |
| 107 | # define __preempt_schedule_context() asm ("call ___preempt_schedule_context") | 107 | # define __preempt_schedule_context() asm ("call ___preempt_schedule_context") |
| 108 | extern asmlinkage void preempt_schedule_context(void); | ||
| 108 | # endif | 109 | # endif |
| 109 | #endif | 110 | #endif |
| 110 | 111 | ||
| @@ -107,46 +107,6 @@ void context_tracking_user_enter(void) · 106 unmodified lines | |||
| 107 | } | 107 | } |
| 108 | NOKPROBE_SYMBOL(context_tracking_user_enter); | 108 | NOKPROBE_SYMBOL(context_tracking_user_enter); |
| 109 | 109 | ||
| 110 | #ifdef CONFIG_PREEMPT | ||
| 111 | /** | ||
| 112 | * preempt_schedule_context - preempt_schedule called by tracing | ||
| 113 | * | ||
| 114 | * The tracing infrastructure uses preempt_enable_notrace to prevent | ||
| 115 | * recursion and tracing preempt enabling caused by the tracing | ||
| 116 | * infrastructure itself. But as tracing can happen in areas coming | ||
| 117 | * from userspace or just about to enter userspace, a preempt enable | ||
| 118 | * can occur before user_exit() is called. This will cause the scheduler | ||
| 119 | * to be called when the system is still in usermode. | ||
| 120 | * | ||
| 121 | * To prevent this, the preempt_enable_notrace will use this function | ||
| 122 | * instead of preempt_schedule() to exit user context if needed before | ||
| 123 | * calling the scheduler. | ||
| 124 | */ | ||
| 125 | asmlinkage __visible void __sched notrace preempt_schedule_context(void) | ||
| 126 | { | ||
| 127 | enum ctx_state prev_ctx; | ||
| 128 | |||
| 129 | if (likely(!preemptible())) | ||
| 130 | return; | ||
| 131 | |||
| 132 | /* | ||
| 133 | * Need to disable preemption in case user_exit() is traced | ||
| 134 | * and the tracer calls preempt_enable_notrace() causing | ||
| 135 | * an infinite recursion. | ||
| 136 | */ | ||
| 137 | preempt_disable_notrace(); | ||
| 138 | prev_ctx = exception_enter(); | ||
| 139 | preempt_enable_no_resched_notrace(); | ||
| 140 | |||
| 141 | preempt_schedule(); | ||
| 142 | |||
| 143 | preempt_disable_notrace(); | ||
| 144 | exception_exit(prev_ctx); | ||
| 145 | preempt_enable_notrace(); | ||
| 146 | } | ||
| 147 | EXPORT_SYMBOL_GPL(preempt_schedule_context); | ||
| 148 | #endif /* CONFIG_PREEMPT */ | ||
| 149 | |||
| 150 | /** | 110 | /** |
| 151 | * context_tracking_user_exit - Inform the context tracking that the CPU is | 111 | * context_tracking_user_exit - Inform the context tracking that the CPU is |
| 152 | * exiting userspace mode and entering the kernel. | 112 | * exiting userspace mode and entering the kernel. |
| @@ -2951,6 +2951,47 @@ asmlinkage __visible void __sched notrace preempt_schedule(void) · 2950 unmodified lines | |||
| 2951 | } | 2951 | } |
| 2952 | NOKPROBE_SYMBOL(preempt_schedule); | 2952 | NOKPROBE_SYMBOL(preempt_schedule); |
| 2953 | EXPORT_SYMBOL(preempt_schedule); | 2953 | EXPORT_SYMBOL(preempt_schedule); |
| 2954 | |||
| 2955 | #ifdef CONFIG_CONTEXT_TRACKING | ||
| 2956 | /** | ||
| 2957 | * preempt_schedule_context - preempt_schedule called by tracing | ||
| 2958 | * | ||
| 2959 | * The tracing infrastructure uses preempt_enable_notrace to prevent | ||
| 2960 | * recursion and tracing preempt enabling caused by the tracing | ||
| 2961 | * infrastructure itself. But as tracing can happen in areas coming | ||
| 2962 | * from userspace or just about to enter userspace, a preempt enable | ||
| 2963 | * can occur before user_exit() is called. This will cause the scheduler | ||
| 2964 | * to be called when the system is still in usermode. | ||
| 2965 | * | ||
| 2966 | * To prevent this, the preempt_enable_notrace will use this function | ||
| 2967 | * instead of preempt_schedule() to exit user context if needed before | ||
| 2968 | * calling the scheduler. | ||
| 2969 | */ | ||
| 2970 | asmlinkage __visible void __sched notrace preempt_schedule_context(void) | ||
| 2971 | { | ||
| 2972 | enum ctx_state prev_ctx; | ||
| 2973 | |||
| 2974 | if (likely(!preemptible())) | ||
| 2975 | return; | ||
| 2976 | |||
| 2977 | do { | ||
| 2978 | __preempt_count_add(PREEMPT_ACTIVE); | ||
| 2979 | /* | ||
| 2980 | * Needs preempt disabled in case user_exit() is traced | ||
| 2981 | * and the tracer calls preempt_enable_notrace() causing | ||
| 2982 | * an infinite recursion. | ||
| 2983 | */ | ||
| 2984 | prev_ctx = exception_enter(); | ||
| 2985 | __schedule(); | ||
| 2986 | exception_exit(prev_ctx); | ||
| 2987 | |||
| 2988 | __preempt_count_sub(PREEMPT_ACTIVE); | ||
| 2989 | barrier(); | ||
| 2990 | } while (need_resched()); | ||
| 2991 | } | ||
| 2992 | EXPORT_SYMBOL_GPL(preempt_schedule_context); | ||
| 2993 | #endif /* CONFIG_CONTEXT_TRACKING */ | ||
| 2994 | |||
| 2954 | #endif /* CONFIG_PREEMPT */ | 2995 | #endif /* CONFIG_PREEMPT */ |
| 2955 | 2996 | ||
| 2956 | /* | 2997 | /* |