rcu/sched: Fix ignored rescheduling after rcu_eqs_enter() v4
Subsystems (6)
Frederic Weisbecker
maintainercover rcu/sched: Fix ignored rescheduling after rcu_eqs_enter() v4
So, here is a hopefully improved version with the following changes:
* No more late wake up debugging, objtool should debug that later with noinstr code calling into the scheduler (Peter suggestion)
* Dropped the double rdp fetch patch, just keep the fix part for now
* Properly protect irq work call from rcu_user_enter() inside instrumention_begin()
* Handle CONFIG_KVM_XFER_TO_GUEST_WORK (as per Peter suggestion)
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks.git
sched/idle-v4
HEAD: d3e956d0b693a572bd5f56241816a6390c5b2797
Thanks,
Frederic
---
Frederic Weisbecker (5):
rcu: Pull deferred rcuog wake up to rcu_eqs_enter() callers rcu/nocb: Perform deferred wake up before last idle's need_resched() check rcu/nocb: Trigger self-IPI on late deferred wake up before user resume
entry: Explicitly flush pending rcuog wakeup before last rescheduling point entry/kvm: Explicitly flush pending rcuog wakeup before last rescheduling point
arch/x86/kvm/x86.c | 1 + include/linux/entry-kvm.h | 14 +++++++++++++ include/linux/rcupdate.h | 2 ++ kernel/entry/common.c | 7 +++++++ kernel/rcu/tree.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++- kernel/rcu/tree.h | 2 +- kernel/rcu/tree_plugin.h | 31 +++++++++++++++++++-------- kernel/sched/idle.c | 3 +++ 8 files changed, 102 insertions(+), 11 deletions(-)
Frederic Weisbecker
maintainerPATCH 1/5 rcu: Pull deferred rcuog wake up to rcu_eqs_enter() callers
Deferred wakeup of rcuog kthreads upon RCU idle mode entry is going to be handled differently whether initiated by idle, user or guest. Prepare with pulling that control up to rcu_eqs_enter() callers.
--- kernel/rcu/tree.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
kernel/rcu/tree.c +10 -1
@@ -644,7 +644,6 @@ static noinstr void rcu_eqs_enter(bool user)trace_rcu_dyntick (TPS ("Start" ), rdp->dynticks_nesting,0 ,atomic_read (&rdp->dynticks));WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current)); rdp =this_cpu_ptr (&rcu_data); -do_nocb_deferred_wakeup (rdp);rcu_prepare_for_idle ();rcu_preempt_deferred_qs (current);
@@ -672,7 +671,10 @@ static noinstr void rcu_eqs_enter(bool user)*/ void rcu_idle_enter (void ) { +struct rcu_data *rdp =this_cpu_ptr (&rcu_data); +lockdep_assert_irqs_disabled (); +do_nocb_deferred_wakeup (rdp);rcu_eqs_enter (false); }EXPORT_SYMBOL_GPL (rcu_idle_enter);
@@ -691,7 +693,14 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter);*/ noinstr void rcu_user_enter(void) { +struct rcu_data *rdp =this_cpu_ptr (&rcu_data); +lockdep_assert_irqs_disabled (); + +instrumentation_begin (); +do_nocb_deferred_wakeup (rdp); +instrumentation_end (); +rcu_eqs_enter (true); }#endif /* CONFIG_NO_HZ_FULL */
Signature · 1 line
2.25.1
[tip: sched/core] rcu: Pull deferred rcuog wake up to rcu_eqs_enter() callers
The following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: e4234f21d2ea7674bcc1aeaca9d382b50ca1efec Gitweb: https://git.kernel.org/tip/e4234f21d2ea7674bcc1aeaca9d382b50ca1efec Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:44 +01:00 Committer: Peter Zijlstra <[email protected]> CommitterDate: Wed, 10 Feb 2021 14:44:49 +01:00
rcu: Pull deferred rcuog wake up to rcu_eqs_enter() callers
Deferred wakeup of rcuog kthreads upon RCU idle mode entry is going to be handled differently whether initiated by idle, user or guest. Prepare with pulling that control up to rcu_eqs_enter() callers.
--- kernel/rcu/tree.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
kernel/rcu/tree.c +10 -1
@@ -644,7 +644,6 @@ static noinstr void rcu_eqs_enter(bool user)trace_rcu_dyntick (TPS ("Start" ), rdp->dynticks_nesting,0 ,atomic_read (&rdp->dynticks));WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current)); rdp =this_cpu_ptr (&rcu_data); -do_nocb_deferred_wakeup (rdp);rcu_prepare_for_idle ();rcu_preempt_deferred_qs (current);
@@ -672,7 +671,10 @@ static noinstr void rcu_eqs_enter(bool user)*/ void rcu_idle_enter (void ) { +struct rcu_data *rdp =this_cpu_ptr (&rcu_data); +lockdep_assert_irqs_disabled (); +do_nocb_deferred_wakeup (rdp);rcu_eqs_enter (false); }EXPORT_SYMBOL_GPL (rcu_idle_enter);
@@ -691,7 +693,14 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter);*/ noinstr void rcu_user_enter(void) { +struct rcu_data *rdp =this_cpu_ptr (&rcu_data); +lockdep_assert_irqs_disabled (); + +instrumentation_begin (); +do_nocb_deferred_wakeup (rdp); +instrumentation_end (); +rcu_eqs_enter (true); }#endif /* CONFIG_NO_HZ_FULL */
[tip: sched/core] rcu: Pull deferred rcuog wake up to rcu_eqs_enter() callers
In reply to Frederic WeisbeckerThe following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 54b7429efffc99e845ba9381bee3244f012a06c2 Gitweb: https://git.kernel.org/tip/54b7429efffc99e845ba9381bee3244f012a06c2 Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:44 +01:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Wed, 17 Feb 2021 14:12:42 +01:00
rcu: Pull deferred rcuog wake up to rcu_eqs_enter() callers
Deferred wakeup of rcuog kthreads upon RCU idle mode entry is going to be handled differently whether initiated by idle, user or guest. Prepare with pulling that control up to rcu_eqs_enter() callers.
--- kernel/rcu/tree.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
kernel/rcu/tree.c +10 -1
@@ -644,7 +644,6 @@ static noinstr void rcu_eqs_enter(bool user)trace_rcu_dyntick (TPS ("Start" ), rdp->dynticks_nesting,0 ,atomic_read (&rdp->dynticks));WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current)); rdp =this_cpu_ptr (&rcu_data); -do_nocb_deferred_wakeup (rdp);rcu_prepare_for_idle ();rcu_preempt_deferred_qs (current);
@@ -672,7 +671,10 @@ static noinstr void rcu_eqs_enter(bool user)*/ void rcu_idle_enter (void ) { +struct rcu_data *rdp =this_cpu_ptr (&rcu_data); +lockdep_assert_irqs_disabled (); +do_nocb_deferred_wakeup (rdp);rcu_eqs_enter (false); }EXPORT_SYMBOL_GPL (rcu_idle_enter);
@@ -691,7 +693,14 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter);*/ noinstr void rcu_user_enter(void) { +struct rcu_data *rdp =this_cpu_ptr (&rcu_data); +lockdep_assert_irqs_disabled (); + +instrumentation_begin (); +do_nocb_deferred_wakeup (rdp); +instrumentation_end (); +rcu_eqs_enter (true); }#endif /* CONFIG_NO_HZ_FULL */
Frederic Weisbecker
maintainerPATCH 2/5 rcu/nocb: Perform deferred wake up before last idle's need_resched() check
In reply to Frederic WeisbeckerEntering RCU idle mode may cause a deferred wake up of an RCU NOCB_GP kthread (rcuog) to be serviced.
Usually a local wake up happening while running the idle task is handled in one of the need_resched() checks carefully placed within the idle loop that can break to the scheduler.
Unfortunately the call to rcu_idle_enter() is already beyond the last generic need_resched() check and we may halt the CPU with a resched request unhandled, leaving the task hanging.
Fix this with splitting the rcuog wakeup handling from rcu_idle_enter() and place it before the last generic need_resched() check in the idle loop. It is then assumed that no call to call_rcu() will be performed after that in the idle loop until the CPU is put in low power mode.
--- include/linux/rcupdate.h | 2 ++ kernel/rcu/tree.c | 3 --- kernel/rcu/tree_plugin.h | 5 +++++ kernel/sched/idle.c | 3 +++ 4 files changed, 10 insertions(+), 3 deletions(-)
include/linux/rcupdate.h +2 -0
@@ -110,8 +110,10 @@ static inline void rcu_user_exit(void) { }#ifdef CONFIG_RCU_NOCB_CPU void rcu_init_nohz (void ); +void rcu_nocb_flush_deferred_wakeup (void );#else /* #ifdef CONFIG_RCU_NOCB_CPU */ static inline void rcu_init_nohz (void ) { } +static inline void rcu_nocb_flush_deferred_wakeup (void ) { }#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ /**
kernel/rcu/tree.c +0 -3
@@ -671,10 +671,7 @@ static noinstr void rcu_eqs_enter(bool user)*/ void rcu_idle_enter (void ) { -struct rcu_data *rdp =this_cpu_ptr (&rcu_data); -lockdep_assert_irqs_disabled (); -do_nocb_deferred_wakeup (rdp);rcu_eqs_enter (false); }EXPORT_SYMBOL_GPL (rcu_idle_enter);
kernel/rcu/tree_plugin.h +5 -0
@@ -2187,6 +2187,11 @@ static void do_nocb_deferred_wakeup(struct rcu_data *rdp)do_nocb_deferred_wakeup_common (rdp); } +void rcu_nocb_flush_deferred_wakeup (void ) +{ +do_nocb_deferred_wakeup (this_cpu_ptr (&rcu_data)); +} +void __init rcu_init_nohz (void ) {int cpu;
kernel/sched/idle.c +3 -0
@@ -55,6 +55,7 @@ __setup("hlt", cpu_idle_nopoll_setup); static noinline int __cpuidle cpu_idle_poll(void) {trace_cpu_idle (0 ,smp_processor_id ()); +rcu_nocb_flush_deferred_wakeup ();stop_critical_timings ();rcu_idle_enter ();local_irq_enable ();
@@ -173,6 +174,8 @@ static void cpuidle_idle_call(void)struct cpuidle_driver *drv =cpuidle_get_cpu_driver (dev);int next_state, entered_state; +rcu_nocb_flush_deferred_wakeup (); +/* * Check if the idle task must be rescheduled. If it is the * case, exit the function after re-enabling the local irq.
Signature · 1 line
2.25.1
Peter Zijlstra
maintainerRe: [PATCH 2/5] rcu/nocb: Perform deferred wake up before last idle's need_resched() check
Quoted 13 lines · Frederic Weisbecker
kernel/sched/idle.c +1
-0@@ -55,6 +55,7 @@ __setup("hlt", cpu_idle_nopoll_setup); static noinline int __cpuidle cpu_idle_poll(void) {trace_cpu_idle (0 ,smp_processor_id ()); +rcu_nocb_flush_deferred_wakeup ();stop_critical_timings ();rcu_idle_enter ();local_irq_enable ();
kernel/sched/idle.c static void cpuidle_idle_call(void) · line 181
Quoted 8 lines
kernel/sched/idle.c +2
-0@@ -173,6 +174,8 @@ static void cpuidle_idle_call(void)struct cpuidle_driver *drv =cpuidle_get_cpu_driver (dev);int next_state, entered_state; +rcu_nocb_flush_deferred_wakeup (); +/* * Check if the idle task must be rescheduled. If it is the * case, exit the function after re-enabling the local irq.
Ok if I do this instead?
kernel/sched/idle.c +1 -3
@@ -55,7 +55,6 @@ __setup("hlt", cpu_idle_nopoll_setup); static noinline int __cpuidle cpu_idle_poll(void) {trace_cpu_idle (0 ,smp_processor_id ()); -rcu_nocb_flush_deferred_wakeup ();stop_critical_timings ();rcu_idle_enter ();local_irq_enable ();
@@ -174,8 +173,6 @@ static void cpuidle_idle_call(void)struct cpuidle_driver *drv =cpuidle_get_cpu_driver (dev);int next_state, entered_state; -rcu_nocb_flush_deferred_wakeup (); -/* * Check if the idle task must be rescheduled. If it is the * case, exit the function after re-enabling the local irq.
@@ -288,6 +285,7 @@ static void do_idle(void) }arch_cpu_idle_enter (); +rcu_nocb_flush_deferred_wakeup ();/* * In poll mode we reenable interrupts and spin. Also if we
Frederic Weisbecker
maintainerRe: [PATCH 2/5] rcu/nocb: Perform deferred wake up before last idle's need_resched() check
Quoted 46 lines · Peter Zijlstra
Quoted 13 lines · Frederic Weisbecker
kernel/sched/idle.c +1
-0@@ -55,6 +55,7 @@ __setup("hlt", cpu_idle_nopoll_setup); static noinline int __cpuidle cpu_idle_poll(void) {trace_cpu_idle (0 ,smp_processor_id ()); +rcu_nocb_flush_deferred_wakeup ();stop_critical_timings ();rcu_idle_enter ();local_irq_enable ();kernel/sched/idle.c static void cpuidle_idle_call(void) · line 181
Quoted 8 lines
kernel/sched/idle.c +2
-0@@ -173,6 +174,8 @@ static void cpuidle_idle_call(void)struct cpuidle_driver *drv =cpuidle_get_cpu_driver (dev);int next_state, entered_state; +rcu_nocb_flush_deferred_wakeup (); +/* * Check if the idle task must be rescheduled. If it is the * case, exit the function after re-enabling the local irq. Ok if I do this instead?
kernel/sched/idle.c +0
-3@@ -55,7 +55,6 @@ __setup("hlt", cpu_idle_nopoll_setup); static noinline int __cpuidle cpu_idle_poll(void) {trace_cpu_idle (0 ,smp_processor_id ()); -rcu_nocb_flush_deferred_wakeup ();stop_critical_timings ();rcu_idle_enter ();local_irq_enable ();@@ -174,8 +173,6 @@ static void cpuidle_idle_call(void)struct cpuidle_driver *drv =cpuidle_get_cpu_driver (dev);int next_state, entered_state; -rcu_nocb_flush_deferred_wakeup (); -/* * Check if the idle task must be rescheduled. If it is the * case, exit the function after re-enabling the local irq.
kernel/sched/idle.c static void do_idle(void) · line 291
Quoted 7 lines
kernel/sched/idle.c +1
-0@@ -288,6 +285,7 @@ static void do_idle(void) }arch_cpu_idle_enter (); +rcu_nocb_flush_deferred_wakeup ();/* * In poll mode we reenable interrupts and spin. Also if we
Right, I think that should work. Nothing should call_rcu() before the need_resched() call. And if it does, we still have the nocb_timer to do the deferred wakeup in the worst case.
Thanks.
[tip: sched/core] rcu/nocb: Perform deferred wake up before last idle's need_resched() check
In reply to Frederic WeisbeckerThe following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 3a7b5c87a0b29c8554a9bdbbbd75eeb4176fb5d4 Gitweb: https://git.kernel.org/tip/3a7b5c87a0b29c8554a9bdbbbd75eeb4176fb5d4 Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:45 +01:00 Committer: Peter Zijlstra <[email protected]> CommitterDate: Wed, 10 Feb 2021 14:44:50 +01:00
rcu/nocb: Perform deferred wake up before last idle's need_resched() check
Entering RCU idle mode may cause a deferred wake up of an RCU NOCB_GP kthread (rcuog) to be serviced.
Usually a local wake up happening while running the idle task is handled in one of the need_resched() checks carefully placed within the idle loop that can break to the scheduler.
Unfortunately the call to rcu_idle_enter() is already beyond the last generic need_resched() check and we may halt the CPU with a resched request unhandled, leaving the task hanging.
Fix this with splitting the rcuog wakeup handling from rcu_idle_enter() and place it before the last generic need_resched() check in the idle loop. It is then assumed that no call to call_rcu() will be performed after that in the idle loop until the CPU is put in low power mode.
--- include/linux/rcupdate.h | 2 ++ kernel/rcu/tree.c | 3 --- kernel/rcu/tree_plugin.h | 5 +++++ kernel/sched/idle.c | 1 + 4 files changed, 8 insertions(+), 3 deletions(-)
include/linux/rcupdate.h +2 -0
@@ -110,8 +110,10 @@ static inline void rcu_user_exit(void) { }#ifdef CONFIG_RCU_NOCB_CPU void rcu_init_nohz (void ); +void rcu_nocb_flush_deferred_wakeup (void );#else /* #ifdef CONFIG_RCU_NOCB_CPU */ static inline void rcu_init_nohz (void ) { } +static inline void rcu_nocb_flush_deferred_wakeup (void ) { }#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ /**
kernel/rcu/tree.c +0 -3
@@ -671,10 +671,7 @@ static noinstr void rcu_eqs_enter(bool user)*/ void rcu_idle_enter (void ) { -struct rcu_data *rdp =this_cpu_ptr (&rcu_data); -lockdep_assert_irqs_disabled (); -do_nocb_deferred_wakeup (rdp);rcu_eqs_enter (false); }EXPORT_SYMBOL_GPL (rcu_idle_enter);
kernel/rcu/tree_plugin.h +5 -0
@@ -2187,6 +2187,11 @@ static void do_nocb_deferred_wakeup(struct rcu_data *rdp)do_nocb_deferred_wakeup_common (rdp); } +void rcu_nocb_flush_deferred_wakeup (void ) +{ +do_nocb_deferred_wakeup (this_cpu_ptr (&rcu_data)); +} +void __init rcu_init_nohz (void ) {int cpu;
kernel/sched/idle.c +1 -0
@@ -285,6 +285,7 @@ static void do_idle(void) }arch_cpu_idle_enter (); +rcu_nocb_flush_deferred_wakeup ();/* * In poll mode we reenable interrupts and spin. Also if we
[tip: sched/core] rcu/nocb: Perform deferred wake up before last idle's need_resched() check
In reply to Frederic WeisbeckerThe following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 43789ef3f7d61aa7bed0cb2764e588fc990c30ef Gitweb: https://git.kernel.org/tip/43789ef3f7d61aa7bed0cb2764e588fc990c30ef Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:45 +01:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Wed, 17 Feb 2021 14:12:43 +01:00
rcu/nocb: Perform deferred wake up before last idle's need_resched() check
Entering RCU idle mode may cause a deferred wake up of an RCU NOCB_GP kthread (rcuog) to be serviced.
Usually a local wake up happening while running the idle task is handled in one of the need_resched() checks carefully placed within the idle loop that can break to the scheduler.
Unfortunately the call to rcu_idle_enter() is already beyond the last generic need_resched() check and we may halt the CPU with a resched request unhandled, leaving the task hanging.
Fix this with splitting the rcuog wakeup handling from rcu_idle_enter() and place it before the last generic need_resched() check in the idle loop. It is then assumed that no call to call_rcu() will be performed after that in the idle loop until the CPU is put in low power mode.
--- include/linux/rcupdate.h | 2 ++ kernel/rcu/tree.c | 3 --- kernel/rcu/tree_plugin.h | 5 +++++ kernel/sched/idle.c | 1 + 4 files changed, 8 insertions(+), 3 deletions(-)
include/linux/rcupdate.h +2 -0
@@ -110,8 +110,10 @@ static inline void rcu_user_exit(void) { }#ifdef CONFIG_RCU_NOCB_CPU void rcu_init_nohz (void ); +void rcu_nocb_flush_deferred_wakeup (void );#else /* #ifdef CONFIG_RCU_NOCB_CPU */ static inline void rcu_init_nohz (void ) { } +static inline void rcu_nocb_flush_deferred_wakeup (void ) { }#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ /**
kernel/rcu/tree.c +0 -3
@@ -671,10 +671,7 @@ static noinstr void rcu_eqs_enter(bool user)*/ void rcu_idle_enter (void ) { -struct rcu_data *rdp =this_cpu_ptr (&rcu_data); -lockdep_assert_irqs_disabled (); -do_nocb_deferred_wakeup (rdp);rcu_eqs_enter (false); }EXPORT_SYMBOL_GPL (rcu_idle_enter);
kernel/rcu/tree_plugin.h +5 -0
@@ -2187,6 +2187,11 @@ static void do_nocb_deferred_wakeup(struct rcu_data *rdp)do_nocb_deferred_wakeup_common (rdp); } +void rcu_nocb_flush_deferred_wakeup (void ) +{ +do_nocb_deferred_wakeup (this_cpu_ptr (&rcu_data)); +} +void __init rcu_init_nohz (void ) {int cpu;
kernel/sched/idle.c +1 -0
@@ -285,6 +285,7 @@ static void do_idle(void) }arch_cpu_idle_enter (); +rcu_nocb_flush_deferred_wakeup ();/* * In poll mode we reenable interrupts and spin. Also if we
Frederic Weisbecker
maintainerPATCH 3/5 rcu/nocb: Trigger self-IPI on late deferred wake up before user resume
In reply to Frederic WeisbeckerEntering RCU idle mode may cause a deferred wake up of an RCU NOCB_GP kthread (rcuog) to be serviced.
Unfortunately the call to rcu_user_enter() is already past the last rescheduling opportunity before we resume to userspace or to guest mode. We may escape there with the woken task ignored.
The ultimate resort to fix every callsites is to trigger a self-IPI (nohz_full depends on arch to implement arch_irq_work_raise()) that will trigger a reschedule on IRQ tail or guest exit.
Eventually every site that want a saner treatment will need to carefully place a call to rcu_nocb_flush_deferred_wakeup() before the last explicit need_resched() check upon resume.
--- kernel/rcu/tree.c | 21 ++++++++++++++++++++- kernel/rcu/tree.h | 2 +- kernel/rcu/tree_plugin.h | 25 ++++++++++++++++--------- 3 files changed, 37 insertions(+), 11 deletions(-)
kernel/rcu/tree.c +20 -1
@@ -677,6 +677,18 @@ void rcu_idle_enter(void)EXPORT_SYMBOL_GPL (rcu_idle_enter);#ifdef CONFIG_NO_HZ_FULL + +/* +* An empty function that will trigger a reschedule on +* IRQ tail once IRQs get re-enabled on userspace resume. +*/ +static void late_wakeup_func (struct irq_work *work) +{ +} + +static DEFINE_PER_CPU(struct irq_work, late_wakeup_work) = +IRQ_WORK_INIT (late_wakeup_func); +/** * rcu_user_enter - inform RCU that we are resuming userspace. *
@@ -694,12 +706,19 @@ noinstr void rcu_user_enter(void)lockdep_assert_irqs_disabled (); +/* +* We may be past the last rescheduling opportunity in the entry code. +* Trigger a self IPI that will fire and reschedule once we resume to +* user/guest mode. +*/ instrumentation_begin (); -do_nocb_deferred_wakeup (rdp); +if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) +irq_work_queue (this_cpu_ptr (&late_wakeup_work));instrumentation_end (); rcu_eqs_enter(true); } +#endif /* CONFIG_NO_HZ_FULL */ /**
kernel/rcu/tree.h +1 -1
@@ -433,7 +433,7 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,static void __call_rcu_nocb_wake (struct rcu_data *rdp,bool was_empty,unsigned long flags);static int rcu_nocb_need_deferred_wakeup (struct rcu_data *rdp); -static void do_nocb_deferred_wakeup (struct rcu_data *rdp); +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp);static void rcu_boot_init_nocb_percpu_data (struct rcu_data *rdp);static void rcu_spawn_cpu_nocb_kthread (int cpu);static void __init rcu_spawn_nocb_kthreads (void );
kernel/rcu/tree_plugin.h +16 -9
@@ -1631,8 +1631,8 @@ bool rcu_is_nocb_cpu(int cpu)* Kick the GP kthread for this NOCB group. Caller holds ->nocb_lock * and this function releases it. */ -static void wake_nocb_gp (struct rcu_data *rdp,bool force, -unsigned long flags) +static bool wake_nocb_gp (struct rcu_data *rdp,bool force, +unsigned long flags) __releases(rdp->nocb_lock) {bool needwake = false;
@@ -1643,7 +1643,7 @@ static void wake_nocb_gp(struct rcu_data *rdp, bool force,trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("AlreadyAwake" ));rcu_nocb_unlock_irqrestore (rdp, flags); -return ; +return false; }del_timer (&rdp->nocb_timer);rcu_nocb_unlock_irqrestore (rdp, flags);
@@ -1656,6 +1656,8 @@ static void wake_nocb_gp(struct rcu_data *rdp, bool force,raw_spin_unlock_irqrestore (&rdp_gp->nocb_gp_lock, flags);if (needwake)wake_up_process (rdp_gp->nocb_gp_kthread); + +return needwake; }/*
@@ -2152,20 +2154,23 @@ static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp) }/* Do a deferred wakeup of rcu_nocb_kthread(). */ -static void do_nocb_deferred_wakeup_common (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup_common (struct rcu_data *rdp) {unsigned long flags;int ndw; +int ret;rcu_nocb_lock_irqsave (rdp, flags);if (!rcu_nocb_need_deferred_wakeup (rdp)) {rcu_nocb_unlock_irqrestore (rdp, flags); -return ; +return false; } ndw =READ_ONCE (rdp->nocb_defer_wakeup);WRITE_ONCE (rdp->nocb_defer_wakeup,RCU_NOCB_WAKE_NOT ); -wake_nocb_gp (rdp, ndw ==RCU_NOCB_WAKE_FORCE , flags); + ret =wake_nocb_gp (rdp, ndw ==RCU_NOCB_WAKE_FORCE , flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("DeferredWake" )); + +return ret; }/* Do a deferred wakeup of rcu_nocb_kthread() from a timer handler. */
@@ -2181,10 +2186,11 @@ static void do_nocb_deferred_wakeup_timer(struct timer_list *t)* This means we do an inexact common-case check. Note that if * we miss, ->nocb_timer will eventually clean things up. */ -static void do_nocb_deferred_wakeup (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp) {if (rcu_nocb_need_deferred_wakeup (rdp)) -do_nocb_deferred_wakeup_common (rdp); +return do_nocb_deferred_wakeup_common (rdp); +return false; }void rcu_nocb_flush_deferred_wakeup (void )
@@ -2523,8 +2529,9 @@ static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)return false; } -static void do_nocb_deferred_wakeup (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp) { +return false; }static void rcu_spawn_cpu_nocb_kthread (int cpu)
Signature · 1 line
2.25.1
[tip: sched/core] rcu/nocb: Trigger self-IPI on late deferred wake up before user resume
The following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 0940cbceefbaa40d85efeb968ce9f2707a145e58 Gitweb: https://git.kernel.org/tip/0940cbceefbaa40d85efeb968ce9f2707a145e58 Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:46 +01:00 Committer: Peter Zijlstra <[email protected]> CommitterDate: Wed, 10 Feb 2021 14:44:50 +01:00
rcu/nocb: Trigger self-IPI on late deferred wake up before user resume
Entering RCU idle mode may cause a deferred wake up of an RCU NOCB_GP kthread (rcuog) to be serviced.
Unfortunately the call to rcu_user_enter() is already past the last rescheduling opportunity before we resume to userspace or to guest mode. We may escape there with the woken task ignored.
The ultimate resort to fix every callsites is to trigger a self-IPI (nohz_full depends on arch to implement arch_irq_work_raise()) that will trigger a reschedule on IRQ tail or guest exit.
Eventually every site that want a saner treatment will need to carefully place a call to rcu_nocb_flush_deferred_wakeup() before the last explicit need_resched() check upon resume.
--- kernel/rcu/tree.c | 21 ++++++++++++++++++++- kernel/rcu/tree.h | 2 +- kernel/rcu/tree_plugin.h | 25 ++++++++++++++++--------- 3 files changed, 37 insertions(+), 11 deletions(-)
kernel/rcu/tree.c +20 -1
@@ -677,6 +677,18 @@ void rcu_idle_enter(void)EXPORT_SYMBOL_GPL (rcu_idle_enter);#ifdef CONFIG_NO_HZ_FULL + +/* +* An empty function that will trigger a reschedule on +* IRQ tail once IRQs get re-enabled on userspace resume. +*/ +static void late_wakeup_func (struct irq_work *work) +{ +} + +static DEFINE_PER_CPU(struct irq_work, late_wakeup_work) = +IRQ_WORK_INIT (late_wakeup_func); +/** * rcu_user_enter - inform RCU that we are resuming userspace. *
@@ -694,12 +706,19 @@ noinstr void rcu_user_enter(void)lockdep_assert_irqs_disabled (); +/* +* We may be past the last rescheduling opportunity in the entry code. +* Trigger a self IPI that will fire and reschedule once we resume to +* user/guest mode. +*/ instrumentation_begin (); -do_nocb_deferred_wakeup (rdp); +if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) +irq_work_queue (this_cpu_ptr (&late_wakeup_work));instrumentation_end (); rcu_eqs_enter(true); } +#endif /* CONFIG_NO_HZ_FULL */ /**
kernel/rcu/tree.h +1 -1
@@ -433,7 +433,7 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,static void __call_rcu_nocb_wake (struct rcu_data *rdp,bool was_empty,unsigned long flags);static int rcu_nocb_need_deferred_wakeup (struct rcu_data *rdp); -static void do_nocb_deferred_wakeup (struct rcu_data *rdp); +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp);static void rcu_boot_init_nocb_percpu_data (struct rcu_data *rdp);static void rcu_spawn_cpu_nocb_kthread (int cpu);static void __init rcu_spawn_nocb_kthreads (void );
kernel/rcu/tree_plugin.h +16 -9
@@ -1631,8 +1631,8 @@ bool rcu_is_nocb_cpu(int cpu)* Kick the GP kthread for this NOCB group. Caller holds ->nocb_lock * and this function releases it. */ -static void wake_nocb_gp (struct rcu_data *rdp,bool force, -unsigned long flags) +static bool wake_nocb_gp (struct rcu_data *rdp,bool force, +unsigned long flags) __releases(rdp->nocb_lock) {bool needwake = false;
@@ -1643,7 +1643,7 @@ static void wake_nocb_gp(struct rcu_data *rdp, bool force,trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("AlreadyAwake" ));rcu_nocb_unlock_irqrestore (rdp, flags); -return ; +return false; }del_timer (&rdp->nocb_timer);rcu_nocb_unlock_irqrestore (rdp, flags);
@@ -1656,6 +1656,8 @@ static void wake_nocb_gp(struct rcu_data *rdp, bool force,raw_spin_unlock_irqrestore (&rdp_gp->nocb_gp_lock, flags);if (needwake)wake_up_process (rdp_gp->nocb_gp_kthread); + +return needwake; }/*
@@ -2152,20 +2154,23 @@ static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp) }/* Do a deferred wakeup of rcu_nocb_kthread(). */ -static void do_nocb_deferred_wakeup_common (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup_common (struct rcu_data *rdp) {unsigned long flags;int ndw; +int ret;rcu_nocb_lock_irqsave (rdp, flags);if (!rcu_nocb_need_deferred_wakeup (rdp)) {rcu_nocb_unlock_irqrestore (rdp, flags); -return ; +return false; } ndw =READ_ONCE (rdp->nocb_defer_wakeup);WRITE_ONCE (rdp->nocb_defer_wakeup,RCU_NOCB_WAKE_NOT ); -wake_nocb_gp (rdp, ndw ==RCU_NOCB_WAKE_FORCE , flags); + ret =wake_nocb_gp (rdp, ndw ==RCU_NOCB_WAKE_FORCE , flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("DeferredWake" )); + +return ret; }/* Do a deferred wakeup of rcu_nocb_kthread() from a timer handler. */
@@ -2181,10 +2186,11 @@ static void do_nocb_deferred_wakeup_timer(struct timer_list *t)* This means we do an inexact common-case check. Note that if * we miss, ->nocb_timer will eventually clean things up. */ -static void do_nocb_deferred_wakeup (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp) {if (rcu_nocb_need_deferred_wakeup (rdp)) -do_nocb_deferred_wakeup_common (rdp); +return do_nocb_deferred_wakeup_common (rdp); +return false; }void rcu_nocb_flush_deferred_wakeup (void )
@@ -2523,8 +2529,9 @@ static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)return false; } -static void do_nocb_deferred_wakeup (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp) { +return false; }static void rcu_spawn_cpu_nocb_kthread (int cpu)
[tip: sched/core] rcu/nocb: Trigger self-IPI on late deferred wake up before user resume
In reply to Frederic WeisbeckerThe following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: f8bb5cae9616224a39cbb399de382d36ac41df10 Gitweb: https://git.kernel.org/tip/f8bb5cae9616224a39cbb399de382d36ac41df10 Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:46 +01:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Wed, 17 Feb 2021 14:12:43 +01:00
rcu/nocb: Trigger self-IPI on late deferred wake up before user resume
Entering RCU idle mode may cause a deferred wake up of an RCU NOCB_GP kthread (rcuog) to be serviced.
Unfortunately the call to rcu_user_enter() is already past the last rescheduling opportunity before we resume to userspace or to guest mode. We may escape there with the woken task ignored.
The ultimate resort to fix every callsites is to trigger a self-IPI (nohz_full depends on arch to implement arch_irq_work_raise()) that will trigger a reschedule on IRQ tail or guest exit.
Eventually every site that want a saner treatment will need to carefully place a call to rcu_nocb_flush_deferred_wakeup() before the last explicit need_resched() check upon resume.
--- kernel/rcu/tree.c | 21 ++++++++++++++++++++- kernel/rcu/tree.h | 2 +- kernel/rcu/tree_plugin.h | 25 ++++++++++++++++--------- 3 files changed, 37 insertions(+), 11 deletions(-)
kernel/rcu/tree.c +20 -1
@@ -677,6 +677,18 @@ void rcu_idle_enter(void)EXPORT_SYMBOL_GPL (rcu_idle_enter);#ifdef CONFIG_NO_HZ_FULL + +/* +* An empty function that will trigger a reschedule on +* IRQ tail once IRQs get re-enabled on userspace resume. +*/ +static void late_wakeup_func (struct irq_work *work) +{ +} + +static DEFINE_PER_CPU(struct irq_work, late_wakeup_work) = +IRQ_WORK_INIT (late_wakeup_func); +/** * rcu_user_enter - inform RCU that we are resuming userspace. *
@@ -694,12 +706,19 @@ noinstr void rcu_user_enter(void)lockdep_assert_irqs_disabled (); +/* +* We may be past the last rescheduling opportunity in the entry code. +* Trigger a self IPI that will fire and reschedule once we resume to +* user/guest mode. +*/ instrumentation_begin (); -do_nocb_deferred_wakeup (rdp); +if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) +irq_work_queue (this_cpu_ptr (&late_wakeup_work));instrumentation_end (); rcu_eqs_enter(true); } +#endif /* CONFIG_NO_HZ_FULL */ /**
kernel/rcu/tree.h +1 -1
@@ -433,7 +433,7 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,static void __call_rcu_nocb_wake (struct rcu_data *rdp,bool was_empty,unsigned long flags);static int rcu_nocb_need_deferred_wakeup (struct rcu_data *rdp); -static void do_nocb_deferred_wakeup (struct rcu_data *rdp); +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp);static void rcu_boot_init_nocb_percpu_data (struct rcu_data *rdp);static void rcu_spawn_cpu_nocb_kthread (int cpu);static void __init rcu_spawn_nocb_kthreads (void );
kernel/rcu/tree_plugin.h +16 -9
@@ -1631,8 +1631,8 @@ bool rcu_is_nocb_cpu(int cpu)* Kick the GP kthread for this NOCB group. Caller holds ->nocb_lock * and this function releases it. */ -static void wake_nocb_gp (struct rcu_data *rdp,bool force, -unsigned long flags) +static bool wake_nocb_gp (struct rcu_data *rdp,bool force, +unsigned long flags) __releases(rdp->nocb_lock) {bool needwake = false;
@@ -1643,7 +1643,7 @@ static void wake_nocb_gp(struct rcu_data *rdp, bool force,trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("AlreadyAwake" ));rcu_nocb_unlock_irqrestore (rdp, flags); -return ; +return false; }del_timer (&rdp->nocb_timer);rcu_nocb_unlock_irqrestore (rdp, flags);
@@ -1656,6 +1656,8 @@ static void wake_nocb_gp(struct rcu_data *rdp, bool force,raw_spin_unlock_irqrestore (&rdp_gp->nocb_gp_lock, flags);if (needwake)wake_up_process (rdp_gp->nocb_gp_kthread); + +return needwake; }/*
@@ -2152,20 +2154,23 @@ static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp) }/* Do a deferred wakeup of rcu_nocb_kthread(). */ -static void do_nocb_deferred_wakeup_common (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup_common (struct rcu_data *rdp) {unsigned long flags;int ndw; +int ret;rcu_nocb_lock_irqsave (rdp, flags);if (!rcu_nocb_need_deferred_wakeup (rdp)) {rcu_nocb_unlock_irqrestore (rdp, flags); -return ; +return false; } ndw =READ_ONCE (rdp->nocb_defer_wakeup);WRITE_ONCE (rdp->nocb_defer_wakeup,RCU_NOCB_WAKE_NOT ); -wake_nocb_gp (rdp, ndw ==RCU_NOCB_WAKE_FORCE , flags); + ret =wake_nocb_gp (rdp, ndw ==RCU_NOCB_WAKE_FORCE , flags);trace_rcu_nocb_wake (rcu_state.name, rdp->cpu,TPS ("DeferredWake" )); + +return ret; }/* Do a deferred wakeup of rcu_nocb_kthread() from a timer handler. */
@@ -2181,10 +2186,11 @@ static void do_nocb_deferred_wakeup_timer(struct timer_list *t)* This means we do an inexact common-case check. Note that if * we miss, ->nocb_timer will eventually clean things up. */ -static void do_nocb_deferred_wakeup (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp) {if (rcu_nocb_need_deferred_wakeup (rdp)) -do_nocb_deferred_wakeup_common (rdp); +return do_nocb_deferred_wakeup_common (rdp); +return false; }void rcu_nocb_flush_deferred_wakeup (void )
@@ -2523,8 +2529,9 @@ static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)return false; } -static void do_nocb_deferred_wakeup (struct rcu_data *rdp) +static bool do_nocb_deferred_wakeup (struct rcu_data *rdp) { +return false; }static void rcu_spawn_cpu_nocb_kthread (int cpu)
Frederic Weisbecker
maintainerPATCH 4/5 entry: Explicitly flush pending rcuog wakeup before last rescheduling point
In reply to Frederic WeisbeckerFollowing the idle loop model, cleanly check for pending rcuog wakeup before the last rescheduling point on resuming to user mode. This way we can avoid to do it from rcu_user_enter() with the last resort self-IPI hack that enforces rescheduling.
--- kernel/entry/common.c | 7 +++++++ kernel/rcu/tree.c | 12 +++++++----- 2 files changed, 14 insertions(+), 5 deletions(-)
kernel/entry/common.c +7 -0
@@ -184,6 +184,10 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs,* enabled above. */ local_irq_disable_exit_to_user (); + +/* Check if any of the above work has queued a deferred wakeup */ +rcu_nocb_flush_deferred_wakeup (); + ti_work =READ_ONCE (current_thread_info ()->flags); }
@@ -197,6 +201,9 @@ static void exit_to_user_mode_prepare(struct pt_regs *regs)lockdep_assert_irqs_disabled (); +/* Flush pending rcuog wakeup before the last need_resched() check */ +rcu_nocb_flush_deferred_wakeup (); +if (unlikely (ti_work &EXIT_TO_USER_MODE_WORK )) ti_work =exit_to_user_mode_loop (regs, ti_work);
kernel/rcu/tree.c +7 -5
@@ -707,13 +707,15 @@ noinstr void rcu_user_enter(void)lockdep_assert_irqs_disabled ();/* -* We may be past the last rescheduling opportunity in the entry code. -* Trigger a self IPI that will fire and reschedule once we resume to -* user/guest mode. +* Other than generic entry implementation, we may be past the last +* rescheduling opportunity in the entry code. Trigger a self IPI +* that will fire and reschedule once we resume in user/guest mode. */ instrumentation_begin (); -if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) -irq_work_queue (this_cpu_ptr (&late_wakeup_work)); +if (!IS_ENABLED (CONFIG_GENERIC_ENTRY ) || (current->flags &PF_VCPU )) { +if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) +irq_work_queue (this_cpu_ptr (&late_wakeup_work)); + }instrumentation_end ();rcu_eqs_enter (true);
Signature · 1 line
2.25.1
[tip: sched/core] entry: Explicitly flush pending rcuog wakeup before last rescheduling point
The following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 2c910e0753dc424dfdeb1f8e230ad8f187a744a7 Gitweb: https://git.kernel.org/tip/2c910e0753dc424dfdeb1f8e230ad8f187a744a7 Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:47 +01:00 Committer: Peter Zijlstra <[email protected]> CommitterDate: Wed, 10 Feb 2021 14:44:51 +01:00
entry: Explicitly flush pending rcuog wakeup before last rescheduling point
Following the idle loop model, cleanly check for pending rcuog wakeup before the last rescheduling point on resuming to user mode. This way we can avoid to do it from rcu_user_enter() with the last resort self-IPI hack that enforces rescheduling.
--- kernel/entry/common.c | 7 +++++++ kernel/rcu/tree.c | 12 +++++++----- 2 files changed, 14 insertions(+), 5 deletions(-)
kernel/entry/common.c +7 -0
@@ -184,6 +184,10 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs,* enabled above. */ local_irq_disable_exit_to_user (); + +/* Check if any of the above work has queued a deferred wakeup */ +rcu_nocb_flush_deferred_wakeup (); + ti_work =READ_ONCE (current_thread_info ()->flags); }
@@ -197,6 +201,9 @@ static void exit_to_user_mode_prepare(struct pt_regs *regs)lockdep_assert_irqs_disabled (); +/* Flush pending rcuog wakeup before the last need_resched() check */ +rcu_nocb_flush_deferred_wakeup (); +if (unlikely (ti_work &EXIT_TO_USER_MODE_WORK )) ti_work =exit_to_user_mode_loop (regs, ti_work);
kernel/rcu/tree.c +7 -5
@@ -707,13 +707,15 @@ noinstr void rcu_user_enter(void)lockdep_assert_irqs_disabled ();/* -* We may be past the last rescheduling opportunity in the entry code. -* Trigger a self IPI that will fire and reschedule once we resume to -* user/guest mode. +* Other than generic entry implementation, we may be past the last +* rescheduling opportunity in the entry code. Trigger a self IPI +* that will fire and reschedule once we resume in user/guest mode. */ instrumentation_begin (); -if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) -irq_work_queue (this_cpu_ptr (&late_wakeup_work)); +if (!IS_ENABLED (CONFIG_GENERIC_ENTRY ) || (current->flags &PF_VCPU )) { +if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) +irq_work_queue (this_cpu_ptr (&late_wakeup_work)); + }instrumentation_end ();rcu_eqs_enter (true);
[tip: sched/core] entry: Explicitly flush pending rcuog wakeup before last rescheduling point
In reply to Frederic WeisbeckerThe following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 47b8ff194c1fd73d58dc339b597d466fe48c8958 Gitweb: https://git.kernel.org/tip/47b8ff194c1fd73d58dc339b597d466fe48c8958 Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:47 +01:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Wed, 17 Feb 2021 14:12:43 +01:00
entry: Explicitly flush pending rcuog wakeup before last rescheduling point
Following the idle loop model, cleanly check for pending rcuog wakeup before the last rescheduling point on resuming to user mode. This way we can avoid to do it from rcu_user_enter() with the last resort self-IPI hack that enforces rescheduling.
--- kernel/entry/common.c | 7 +++++++ kernel/rcu/tree.c | 12 +++++++----- 2 files changed, 14 insertions(+), 5 deletions(-)
kernel/entry/common.c +7 -0
@@ -184,6 +184,10 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs,* enabled above. */ local_irq_disable_exit_to_user (); + +/* Check if any of the above work has queued a deferred wakeup */ +rcu_nocb_flush_deferred_wakeup (); + ti_work =READ_ONCE (current_thread_info ()->flags); }
@@ -197,6 +201,9 @@ static void exit_to_user_mode_prepare(struct pt_regs *regs)lockdep_assert_irqs_disabled (); +/* Flush pending rcuog wakeup before the last need_resched() check */ +rcu_nocb_flush_deferred_wakeup (); +if (unlikely (ti_work &EXIT_TO_USER_MODE_WORK )) ti_work =exit_to_user_mode_loop (regs, ti_work);
kernel/rcu/tree.c +7 -5
@@ -707,13 +707,15 @@ noinstr void rcu_user_enter(void)lockdep_assert_irqs_disabled ();/* -* We may be past the last rescheduling opportunity in the entry code. -* Trigger a self IPI that will fire and reschedule once we resume to -* user/guest mode. +* Other than generic entry implementation, we may be past the last +* rescheduling opportunity in the entry code. Trigger a self IPI +* that will fire and reschedule once we resume in user/guest mode. */ instrumentation_begin (); -if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) -irq_work_queue (this_cpu_ptr (&late_wakeup_work)); +if (!IS_ENABLED (CONFIG_GENERIC_ENTRY ) || (current->flags &PF_VCPU )) { +if (do_nocb_deferred_wakeup (rdp) &&need_resched ()) +irq_work_queue (this_cpu_ptr (&late_wakeup_work)); + }instrumentation_end ();rcu_eqs_enter (true);
Frederic Weisbecker
maintainerPATCH 5/5 entry/kvm: Explicitly flush pending rcuog wakeup before last rescheduling point
In reply to Frederic WeisbeckerFollowing the idle loop model, cleanly check for pending rcuog wakeup before the last rescheduling point upon resuming to guest mode. This way we can avoid to do it from rcu_user_enter() with the last resort self-IPI hack that enforces rescheduling.
--- arch/x86/kvm/x86.c | 1 + include/linux/entry-kvm.h | 14 +++++++++++++ kernel/rcu/tree.c | 44 ++++++++++++++++++++++++++++++--------- kernel/rcu/tree_plugin.h | 1 + 4 files changed, 50 insertions(+), 10 deletions(-)
arch/x86/kvm/x86.c +1 -0
@@ -1773,6 +1773,7 @@ EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr);bool kvm_vcpu_exit_request (struct kvm_vcpu *vcpu) { +xfer_to_guest_mode_prepare ();return vcpu->mode ==EXITING_GUEST_MODE ||kvm_request_pending (vcpu) ||xfer_to_guest_mode_work_pending (); }
include/linux/entry-kvm.h +14 -0
@@ -46,6 +46,20 @@ static inline int arch_xfer_to_guest_mode_handle_work(struct kvm_vcpu *vcpu,*/ int xfer_to_guest_mode_handle_work (struct kvm_vcpu *vcpu); +/** +* xfer_to_guest_mode_prepare - Perform last minute preparation work that +* need to be handled while IRQs are disabled +* upon entering to guest. +* +* Has to be invoked with interrupts disabled before the last call +* to xfer_to_guest_mode_work_pending(). +*/ +static inline void xfer_to_guest_mode_prepare (void ) +{ +lockdep_assert_irqs_disabled (); +rcu_nocb_flush_deferred_wakeup (); +} +/** * __xfer_to_guest_mode_work_pending - Check if work is pending *
kernel/rcu/tree.c +34 -10
70 lines not on this page · raw message
kernel/rcu/tree_plugin.h +1 -0
@@ -2197,6 +2197,7 @@ void rcu_nocb_flush_deferred_wakeup(void) {do_nocb_deferred_wakeup (this_cpu_ptr (&rcu_data)); } +EXPORT_SYMBOL_GPL (rcu_nocb_flush_deferred_wakeup);void __init rcu_init_nohz (void ) {
Signature · 1 line
2.25.1
[tip: sched/core] entry/kvm: Explicitly flush pending rcuog wakeup before last rescheduling point
The following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 14bbd41d5109a8049f3f1b77e994e0213f94f4c0 Gitweb: https://git.kernel.org/tip/14bbd41d5109a8049f3f1b77e994e0213f94f4c0 Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:48 +01:00 Committer: Peter Zijlstra <[email protected]> CommitterDate: Wed, 10 Feb 2021 14:44:51 +01:00
entry/kvm: Explicitly flush pending rcuog wakeup before last rescheduling point
Following the idle loop model, cleanly check for pending rcuog wakeup before the last rescheduling point upon resuming to guest mode. This way we can avoid to do it from rcu_user_enter() with the last resort self-IPI hack that enforces rescheduling.
--- arch/x86/kvm/x86.c | 1 +- include/linux/entry-kvm.h | 14 ++++++++++++- kernel/rcu/tree.c | 44 +++++++++++++++++++++++++++++--------- kernel/rcu/tree_plugin.h | 1 +- 4 files changed, 50 insertions(+), 10 deletions(-)
arch/x86/kvm/x86.c +1 -0
@@ -1782,6 +1782,7 @@ EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr);bool kvm_vcpu_exit_request (struct kvm_vcpu *vcpu) { +xfer_to_guest_mode_prepare ();return vcpu->mode ==EXITING_GUEST_MODE ||kvm_request_pending (vcpu) ||xfer_to_guest_mode_work_pending (); }
include/linux/entry-kvm.h +14 -0
@@ -47,6 +47,20 @@ static inline int arch_xfer_to_guest_mode_handle_work(struct kvm_vcpu *vcpu,int xfer_to_guest_mode_handle_work (struct kvm_vcpu *vcpu);/** +* xfer_to_guest_mode_prepare - Perform last minute preparation work that +* need to be handled while IRQs are disabled +* upon entering to guest. +* +* Has to be invoked with interrupts disabled before the last call +* to xfer_to_guest_mode_work_pending(). +*/ +static inline void xfer_to_guest_mode_prepare (void ) +{ +lockdep_assert_irqs_disabled (); +rcu_nocb_flush_deferred_wakeup (); +} + +/** * __xfer_to_guest_mode_work_pending - Check if work is pending * * Returns: True if work pending, False otherwise.
kernel/rcu/tree.c +34 -10
70 lines not on this page · raw message
kernel/rcu/tree_plugin.h +1 -0
@@ -2197,6 +2197,7 @@ void rcu_nocb_flush_deferred_wakeup(void) {do_nocb_deferred_wakeup (this_cpu_ptr (&rcu_data)); } +EXPORT_SYMBOL_GPL (rcu_nocb_flush_deferred_wakeup);void __init rcu_init_nohz (void ) {
[tip: sched/core] entry/kvm: Explicitly flush pending rcuog wakeup before last rescheduling point
In reply to Frederic WeisbeckerThe following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 4ae7dc97f726ea95c58ac58af71cc034ad22d7de Gitweb: https://git.kernel.org/tip/4ae7dc97f726ea95c58ac58af71cc034ad22d7de Author: Frederic Weisbecker <[email protected]> AuthorDate: Mon, 01 Feb 2021 00:05:48 +01:00 Committer: Ingo Molnar <[email protected]> CommitterDate: Wed, 17 Feb 2021 14:12:43 +01:00
entry/kvm: Explicitly flush pending rcuog wakeup before last rescheduling point
Following the idle loop model, cleanly check for pending rcuog wakeup before the last rescheduling point upon resuming to guest mode. This way we can avoid to do it from rcu_user_enter() with the last resort self-IPI hack that enforces rescheduling.
--- arch/x86/kvm/x86.c | 1 +- include/linux/entry-kvm.h | 14 ++++++++++++- kernel/rcu/tree.c | 44 +++++++++++++++++++++++++++++--------- kernel/rcu/tree_plugin.h | 1 +- 4 files changed, 50 insertions(+), 10 deletions(-)
arch/x86/kvm/x86.c +1 -0
@@ -1782,6 +1782,7 @@ EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr);bool kvm_vcpu_exit_request (struct kvm_vcpu *vcpu) { +xfer_to_guest_mode_prepare ();return vcpu->mode ==EXITING_GUEST_MODE ||kvm_request_pending (vcpu) ||xfer_to_guest_mode_work_pending (); }
include/linux/entry-kvm.h +14 -0
@@ -47,6 +47,20 @@ static inline int arch_xfer_to_guest_mode_handle_work(struct kvm_vcpu *vcpu,int xfer_to_guest_mode_handle_work (struct kvm_vcpu *vcpu);/** +* xfer_to_guest_mode_prepare - Perform last minute preparation work that +* need to be handled while IRQs are disabled +* upon entering to guest. +* +* Has to be invoked with interrupts disabled before the last call +* to xfer_to_guest_mode_work_pending(). +*/ +static inline void xfer_to_guest_mode_prepare (void ) +{ +lockdep_assert_irqs_disabled (); +rcu_nocb_flush_deferred_wakeup (); +} + +/** * __xfer_to_guest_mode_work_pending - Check if work is pending * * Returns: True if work pending, False otherwise.
kernel/rcu/tree.c +34 -10
70 lines not on this page · raw message
kernel/rcu/tree_plugin.h +1 -0
@@ -2197,6 +2197,7 @@ void rcu_nocb_flush_deferred_wakeup(void) {do_nocb_deferred_wakeup (this_cpu_ptr (&rcu_data)); } +EXPORT_SYMBOL_GPL (rcu_nocb_flush_deferred_wakeup);void __init rcu_init_nohz (void ) {