This reverts commit e3ff7c609f39671d1aaff4fb4a8594e14f3e03f8. Note that removing this commit reintroduces "live patches failing to complete within a reasonable amount of time due to CPU-bound kthreads." Unfortunately this fix depends quite critically on PREEMPT_DYNAMIC and existence of cond_resched() so this will need an alternate fix. Signed-off-by: Ankur Arora <ankur.a.arora@xxxxxxxxxx> --- include/linux/livepatch.h | 1 - include/linux/livepatch_sched.h | 29 --------- include/linux/sched.h | 20 ++---- kernel/livepatch/core.c | 1 - kernel/livepatch/transition.c | 107 +++++--------------------------- kernel/sched/core.c | 64 +++---------------- 6 files changed, 28 insertions(+), 194 deletions(-) delete mode 100644 include/linux/livepatch_sched.h diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 9b9b38e89563..293e29960c6e 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -13,7 +13,6 @@ #include <linux/ftrace.h> #include <linux/completion.h> #include <linux/list.h> -#include <linux/livepatch_sched.h> #if IS_ENABLED(CONFIG_LIVEPATCH) diff --git a/include/linux/livepatch_sched.h b/include/linux/livepatch_sched.h deleted file mode 100644 index 013794fb5da0..000000000000 --- a/include/linux/livepatch_sched.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef _LINUX_LIVEPATCH_SCHED_H_ -#define _LINUX_LIVEPATCH_SCHED_H_ - -#include <linux/jump_label.h> -#include <linux/static_call_types.h> - -#ifdef CONFIG_LIVEPATCH - -void __klp_sched_try_switch(void); - -#if !defined(CONFIG_PREEMPT_DYNAMIC) || !defined(CONFIG_HAVE_PREEMPT_DYNAMIC_CALL) - -DECLARE_STATIC_KEY_FALSE(klp_sched_try_switch_key); - -static __always_inline void klp_sched_try_switch(void) -{ - if (static_branch_unlikely(&klp_sched_try_switch_key)) - __klp_sched_try_switch(); -} - -#endif /* !CONFIG_PREEMPT_DYNAMIC || !CONFIG_HAVE_PREEMPT_DYNAMIC_CALL */ - -#else /* !CONFIG_LIVEPATCH */ -static inline void klp_sched_try_switch(void) {} -static inline void __klp_sched_try_switch(void) {} -#endif /* CONFIG_LIVEPATCH */ - -#endif /* _LINUX_LIVEPATCH_SCHED_H_ */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 5bdf80136e42..c5b0ef1ecfe4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -36,7 +36,6 @@ #include <linux/seqlock.h> #include <linux/kcsan.h> #include <linux/rv.h> -#include <linux/livepatch_sched.h> #include <asm/kmap_size.h> /* task_struct member predeclarations (sorted alphabetically): */ @@ -2087,9 +2086,6 @@ extern int __cond_resched(void); #if defined(CONFIG_PREEMPT_DYNAMIC) && defined(CONFIG_HAVE_PREEMPT_DYNAMIC_CALL) -void sched_dynamic_klp_enable(void); -void sched_dynamic_klp_disable(void); - DECLARE_STATIC_CALL(cond_resched, __cond_resched); static __always_inline int _cond_resched(void) @@ -2098,7 +2094,6 @@ static __always_inline int _cond_resched(void) } #elif defined(CONFIG_PREEMPT_DYNAMIC) && defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY) - extern int dynamic_cond_resched(void); static __always_inline int _cond_resched(void) @@ -2106,25 +2101,20 @@ static __always_inline int _cond_resched(void) return dynamic_cond_resched(); } -#else /* !CONFIG_PREEMPTION */ +#else static inline int _cond_resched(void) { - klp_sched_try_switch(); return __cond_resched(); } -#endif /* PREEMPT_DYNAMIC && CONFIG_HAVE_PREEMPT_DYNAMIC_CALL */ +#endif /* CONFIG_PREEMPT_DYNAMIC */ -#else /* CONFIG_PREEMPTION && !CONFIG_PREEMPT_DYNAMIC */ +#else -static inline int _cond_resched(void) -{ - klp_sched_try_switch(); - return 0; -} +static inline int _cond_resched(void) { return 0; } -#endif /* !CONFIG_PREEMPTION || CONFIG_PREEMPT_DYNAMIC */ +#endif /* !defined(CONFIG_PREEMPTION) || defined(CONFIG_PREEMPT_DYNAMIC) */ #define cond_resched() ({ \ __might_resched(__FILE__, __LINE__, 0); \ diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 61328328c474..fc851455740c 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -33,7 +33,6 @@ * * - klp_ftrace_handler() * - klp_update_patch_state() - * - __klp_sched_try_switch() */ DEFINE_MUTEX(klp_mutex); diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c index e54c3d60a904..70bc38f27af7 100644 --- a/kernel/livepatch/transition.c +++ b/kernel/livepatch/transition.c @@ -9,7 +9,6 @@ #include <linux/cpu.h> #include <linux/stacktrace.h> -#include <linux/static_call.h> #include "core.h" #include "patch.h" #include "transition.h" @@ -27,25 +26,6 @@ static int klp_target_state = KLP_UNDEFINED; static unsigned int klp_signals_cnt; -/* - * When a livepatch is in progress, enable klp stack checking in - * cond_resched(). This helps CPU-bound kthreads get patched. - */ -#if defined(CONFIG_PREEMPT_DYNAMIC) && defined(CONFIG_HAVE_PREEMPT_DYNAMIC_CALL) - -#define klp_cond_resched_enable() sched_dynamic_klp_enable() -#define klp_cond_resched_disable() sched_dynamic_klp_disable() - -#else /* !CONFIG_PREEMPT_DYNAMIC || !CONFIG_HAVE_PREEMPT_DYNAMIC_CALL */ - -DEFINE_STATIC_KEY_FALSE(klp_sched_try_switch_key); -EXPORT_SYMBOL(klp_sched_try_switch_key); - -#define klp_cond_resched_enable() static_branch_enable(&klp_sched_try_switch_key) -#define klp_cond_resched_disable() static_branch_disable(&klp_sched_try_switch_key) - -#endif /* CONFIG_PREEMPT_DYNAMIC && CONFIG_HAVE_PREEMPT_DYNAMIC_CALL */ - /* * This work can be performed periodically to finish patching or unpatching any * "straggler" tasks which failed to transition in the first attempt. @@ -194,8 +174,8 @@ void klp_update_patch_state(struct task_struct *task) * barrier (smp_rmb) for two cases: * * 1) Enforce the order of the TIF_PATCH_PENDING read and the - * klp_target_state read. The corresponding write barriers are in - * klp_init_transition() and klp_reverse_transition(). + * klp_target_state read. The corresponding write barrier is in + * klp_init_transition(). * * 2) Enforce the order of the TIF_PATCH_PENDING read and a future read * of func->transition, if klp_ftrace_handler() is called later on @@ -363,44 +343,6 @@ static bool klp_try_switch_task(struct task_struct *task) return !ret; } -void __klp_sched_try_switch(void) -{ - if (likely(!klp_patch_pending(current))) - return; - - /* - * This function is called from cond_resched() which is called in many - * places throughout the kernel. Using the klp_mutex here might - * deadlock. - * - * Instead, disable preemption to prevent racing with other callers of - * klp_try_switch_task(). Thanks to task_call_func() they won't be - * able to switch this task while it's running. - */ - preempt_disable(); - - /* - * Make sure current didn't get patched between the above check and - * preempt_disable(). - */ - if (unlikely(!klp_patch_pending(current))) - goto out; - - /* - * Enforce the order of the TIF_PATCH_PENDING read above and the - * klp_target_state read in klp_try_switch_task(). The corresponding - * write barriers are in klp_init_transition() and - * klp_reverse_transition(). - */ - smp_rmb(); - - klp_try_switch_task(current); - -out: - preempt_enable(); -} -EXPORT_SYMBOL(__klp_sched_try_switch); - /* * Sends a fake signal to all non-kthread tasks with TIF_PATCH_PENDING set. * Kthreads with TIF_PATCH_PENDING set are woken up. @@ -507,8 +449,7 @@ void klp_try_complete_transition(void) return; } - /* Done! Now cleanup the data structures. */ - klp_cond_resched_disable(); + /* we're done, now cleanup the data structures */ patch = klp_transition_patch; klp_complete_transition(); @@ -560,8 +501,6 @@ void klp_start_transition(void) set_tsk_thread_flag(task, TIF_PATCH_PENDING); } - klp_cond_resched_enable(); - klp_signals_cnt = 0; } @@ -617,9 +556,8 @@ void klp_init_transition(struct klp_patch *patch, int state) * see a func in transition with a task->patch_state of KLP_UNDEFINED. * * Also enforce the order of the klp_target_state write and future - * TIF_PATCH_PENDING writes to ensure klp_update_patch_state() and - * __klp_sched_try_switch() don't set a task->patch_state to - * KLP_UNDEFINED. + * TIF_PATCH_PENDING writes to ensure klp_update_patch_state() doesn't + * set a task->patch_state to KLP_UNDEFINED. */ smp_wmb(); @@ -655,10 +593,14 @@ void klp_reverse_transition(void) klp_target_state == KLP_PATCHED ? "patching to unpatching" : "unpatching to patching"); + klp_transition_patch->enabled = !klp_transition_patch->enabled; + + klp_target_state = !klp_target_state; + /* * Clear all TIF_PATCH_PENDING flags to prevent races caused by - * klp_update_patch_state() or __klp_sched_try_switch() running in - * parallel with the reverse transition. + * klp_update_patch_state() running in parallel with + * klp_start_transition(). */ read_lock(&tasklist_lock); for_each_process_thread(g, task) @@ -668,28 +610,9 @@ void klp_reverse_transition(void) for_each_possible_cpu(cpu) clear_tsk_thread_flag(idle_task(cpu), TIF_PATCH_PENDING); - /* - * Make sure all existing invocations of klp_update_patch_state() and - * __klp_sched_try_switch() see the cleared TIF_PATCH_PENDING before - * starting the reverse transition. - */ + /* Let any remaining calls to klp_update_patch_state() complete */ klp_synchronize_transition(); - /* - * All patching has stopped, now re-initialize the global variables to - * prepare for the reverse transition. - */ - klp_transition_patch->enabled = !klp_transition_patch->enabled; - klp_target_state = !klp_target_state; - - /* - * Enforce the order of the klp_target_state write and the - * TIF_PATCH_PENDING writes in klp_start_transition() to ensure - * klp_update_patch_state() and __klp_sched_try_switch() don't set - * task->patch_state to the wrong value. - */ - smp_wmb(); - klp_start_transition(); } @@ -703,9 +626,9 @@ void klp_copy_process(struct task_struct *child) * the task flag up to date with the parent here. * * The operation is serialized against all klp_*_transition() - * operations by the tasklist_lock. The only exceptions are - * klp_update_patch_state(current) and __klp_sched_try_switch(), but we - * cannot race with them because we are current. + * operations by the tasklist_lock. The only exception is + * klp_update_patch_state(current), but we cannot race with + * that because we are current. */ if (test_tsk_thread_flag(current, TIF_PATCH_PENDING)) set_tsk_thread_flag(child, TIF_PATCH_PENDING); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 0e8764d63041..b43fda3c5733 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -8597,7 +8597,6 @@ EXPORT_STATIC_CALL_TRAMP(might_resched); static DEFINE_STATIC_KEY_FALSE(sk_dynamic_cond_resched); int __sched dynamic_cond_resched(void) { - klp_sched_try_switch(); if (!static_branch_unlikely(&sk_dynamic_cond_resched)) return 0; return __cond_resched(); @@ -8746,17 +8745,13 @@ int sched_dynamic_mode(const char *str) #error "Unsupported PREEMPT_DYNAMIC mechanism" #endif -DEFINE_MUTEX(sched_dynamic_mutex); -static bool klp_override; - -static void __sched_dynamic_update(int mode) +void sched_dynamic_update(int mode) { /* * Avoid {NONE,VOLUNTARY} -> FULL transitions from ever ending up in * the ZERO state, which is invalid. */ - if (!klp_override) - preempt_dynamic_enable(cond_resched); + preempt_dynamic_enable(cond_resched); preempt_dynamic_enable(might_resched); preempt_dynamic_enable(preempt_schedule); preempt_dynamic_enable(preempt_schedule_notrace); @@ -8764,79 +8759,36 @@ static void __sched_dynamic_update(int mode) switch (mode) { case preempt_dynamic_none: - if (!klp_override) - preempt_dynamic_enable(cond_resched); + preempt_dynamic_enable(cond_resched); preempt_dynamic_disable(might_resched); preempt_dynamic_disable(preempt_schedule); preempt_dynamic_disable(preempt_schedule_notrace); preempt_dynamic_disable(irqentry_exit_cond_resched); - if (mode != preempt_dynamic_mode) - pr_info("Dynamic Preempt: none\n"); + pr_info("Dynamic Preempt: none\n"); break; case preempt_dynamic_voluntary: - if (!klp_override) - preempt_dynamic_enable(cond_resched); + preempt_dynamic_enable(cond_resched); preempt_dynamic_enable(might_resched); preempt_dynamic_disable(preempt_schedule); preempt_dynamic_disable(preempt_schedule_notrace); preempt_dynamic_disable(irqentry_exit_cond_resched); - if (mode != preempt_dynamic_mode) - pr_info("Dynamic Preempt: voluntary\n"); + pr_info("Dynamic Preempt: voluntary\n"); break; case preempt_dynamic_full: - if (!klp_override) - preempt_dynamic_disable(cond_resched); + preempt_dynamic_disable(cond_resched); preempt_dynamic_disable(might_resched); preempt_dynamic_enable(preempt_schedule); preempt_dynamic_enable(preempt_schedule_notrace); preempt_dynamic_enable(irqentry_exit_cond_resched); - if (mode != preempt_dynamic_mode) - pr_info("Dynamic Preempt: full\n"); + pr_info("Dynamic Preempt: full\n"); break; } preempt_dynamic_mode = mode; } -void sched_dynamic_update(int mode) -{ - mutex_lock(&sched_dynamic_mutex); - __sched_dynamic_update(mode); - mutex_unlock(&sched_dynamic_mutex); -} - -#ifdef CONFIG_HAVE_PREEMPT_DYNAMIC_CALL - -static int klp_cond_resched(void) -{ - __klp_sched_try_switch(); - return __cond_resched(); -} - -void sched_dynamic_klp_enable(void) -{ - mutex_lock(&sched_dynamic_mutex); - - klp_override = true; - static_call_update(cond_resched, klp_cond_resched); - - mutex_unlock(&sched_dynamic_mutex); -} - -void sched_dynamic_klp_disable(void) -{ - mutex_lock(&sched_dynamic_mutex); - - klp_override = false; - __sched_dynamic_update(preempt_dynamic_mode); - - mutex_unlock(&sched_dynamic_mutex); -} - -#endif /* CONFIG_HAVE_PREEMPT_DYNAMIC_CALL */ - static int __init setup_preempt_mode(char *str) { int mode = sched_dynamic_mode(str); -- 2.31.1