From: "Gautham R. Shenoy" <gautham.shenoy@xxxxxxx> Currently TIF_NEED_RESCHED is being overloaded, to wakeup an idle CPU in TIF_POLLING mode to service an IPI even if there are no new tasks being woken up on the said CPU. In preparation of a proper fix, introduce a new helper "need_resched_or_ipi()" which is intended to return true if either the TIF_NEED_RESCHED flag or if TIF_NOTIFY_IPI flag is set. Use this helper function in place of need_resched() in idle loops where TIF_POLLING_NRFLAG is set. To preserve bisectibility and avoid unbreakable idle loops, all the need_resched() checks within TIF_POLLING_NRFLAGS sections, have been replaced tree-wide with the need_resched_or_ipi() check. [ prateek: Replaced some of the missed out occurrences of need_resched() within a TIF_POLLING sections with need_resched_or_ipi() ] Cc: Richard Henderson <richard.henderson@xxxxxxxxxx> Cc: Ivan Kokshaysky <ink@xxxxxxxxxxxxxxxxxxxx> Cc: Matt Turner <mattst88@xxxxxxxxx> Cc: Russell King <linux@xxxxxxxxxxxxxxx> Cc: Guo Ren <guoren@xxxxxxxxxx> Cc: Michal Simek <monstr@xxxxxxxxx> Cc: Dinh Nguyen <dinguyen@xxxxxxxxxx> Cc: Jonas Bonn <jonas@xxxxxxxxxxxx> Cc: Stefan Kristiansson <stefan.kristiansson@xxxxxxxxxxxxx> Cc: Stafford Horne <shorne@xxxxxxxxx> Cc: "James E.J. Bottomley" <James.Bottomley@xxxxxxxxxxxxxxxxxxxxx> Cc: Helge Deller <deller@xxxxxx> Cc: Michael Ellerman <mpe@xxxxxxxxxxxxxx> Cc: Nicholas Piggin <npiggin@xxxxxxxxx> Cc: Christophe Leroy <christophe.leroy@xxxxxxxxxx> Cc: "Naveen N. Rao" <naveen.n.rao@xxxxxxxxxxxxx> Cc: Yoshinori Sato <ysato@xxxxxxxxxxxxxxxxxxxx> Cc: Rich Felker <dalias@xxxxxxxx> Cc: John Paul Adrian Glaubitz <glaubitz@xxxxxxxxxxxxxxxxxxx> Cc: "David S. Miller" <davem@xxxxxxxxxxxxx> Cc: Andreas Larsson <andreas@xxxxxxxxxxx> Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: Borislav Petkov <bp@xxxxxxxxx> Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx> Cc: "H. Peter Anvin" <hpa@xxxxxxxxx> Cc: "Rafael J. Wysocki" <rafael@xxxxxxxxxx> Cc: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx> Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx> Cc: Juri Lelli <juri.lelli@xxxxxxxxxx> Cc: Vincent Guittot <vincent.guittot@xxxxxxxxxx> Cc: Dietmar Eggemann <dietmar.eggemann@xxxxxxx> Cc: Steven Rostedt <rostedt@xxxxxxxxxxx> Cc: Ben Segall <bsegall@xxxxxxxxxx> Cc: Mel Gorman <mgorman@xxxxxxx> Cc: Daniel Bristot de Oliveira <bristot@xxxxxxxxxx> Cc: Valentin Schneider <vschneid@xxxxxxxxxx> Cc: Andrew Donnellan <ajd@xxxxxxxxxxxxx> Cc: Benjamin Gray <bgray@xxxxxxxxxxxxx> Cc: Frederic Weisbecker <frederic@xxxxxxxxxx> Cc: Xin Li <xin3.li@xxxxxxxxx> Cc: Kees Cook <keescook@xxxxxxxxxxxx> Cc: Rick Edgecombe <rick.p.edgecombe@xxxxxxxxx> Cc: Tony Battersby <tonyb@xxxxxxxxxxxxxxx> Cc: Bjorn Helgaas <bhelgaas@xxxxxxxxxx> Cc: Brian Gerst <brgerst@xxxxxxxxx> Cc: Leonardo Bras <leobras@xxxxxxxxxx> Cc: Imran Khan <imran.f.khan@xxxxxxxxxx> Cc: "Paul E. McKenney" <paulmck@xxxxxxxxxx> Cc: Rik van Riel <riel@xxxxxxxxxxx> Cc: Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx> Cc: David Vernet <void@xxxxxxxxxxxxx> Cc: Julia Lawall <julia.lawall@xxxxxxxx> Cc: linux-alpha@xxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx Cc: linux-csky@xxxxxxxxxxxxxxx Cc: linux-openrisc@xxxxxxxxxxxxxxx Cc: linux-parisc@xxxxxxxxxxxxxxx Cc: linuxppc-dev@xxxxxxxxxxxxxxxx Cc: linux-sh@xxxxxxxxxxxxxxx Cc: sparclinux@xxxxxxxxxxxxxxx Cc: linux-pm@xxxxxxxxxxxxxxx Cc: x86@xxxxxxxxxx Signed-off-by: Gautham R. Shenoy <gautham.shenoy@xxxxxxx> Co-developed-by: K Prateek Nayak <kprateek.nayak@xxxxxxx> Signed-off-by: K Prateek Nayak <kprateek.nayak@xxxxxxx> --- v1..v2: o Fixed a conflict with commit edc8fc01f608 ("x86: Fix CPUIDLE_FLAG_IRQ_ENABLE leaking timer reprogram") that touched mwait_idle_with_hints() in arch/x86/include/asm/mwait.h --- arch/x86/include/asm/mwait.h | 2 +- arch/x86/kernel/process.c | 2 +- drivers/cpuidle/cpuidle-powernv.c | 2 +- drivers/cpuidle/cpuidle-pseries.c | 2 +- drivers/cpuidle/poll_state.c | 2 +- include/linux/sched.h | 5 +++++ include/linux/sched/idle.h | 4 ++-- kernel/sched/idle.c | 7 ++++--- 8 files changed, 16 insertions(+), 10 deletions(-) diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h index 920426d691ce..3fa6f0bbd74f 100644 --- a/arch/x86/include/asm/mwait.h +++ b/arch/x86/include/asm/mwait.h @@ -125,7 +125,7 @@ static __always_inline void mwait_idle_with_hints(unsigned long eax, unsigned lo __monitor((void *)¤t_thread_info()->flags, 0, 0); - if (!need_resched()) { + if (!need_resched_or_ipi()) { if (ecx & 1) { __mwait(eax, ecx); } else { diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index b8441147eb5e..dd73cd6f735c 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -901,7 +901,7 @@ static __cpuidle void mwait_idle(void) } __monitor((void *)¤t_thread_info()->flags, 0, 0); - if (!need_resched()) { + if (!need_resched_or_ipi()) { __sti_mwait(0, 0); raw_local_irq_disable(); } diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 9ebedd972df0..77c3bb371f56 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -79,7 +79,7 @@ static int snooze_loop(struct cpuidle_device *dev, dev->poll_time_limit = false; ppc64_runlatch_off(); HMT_very_low(); - while (!need_resched()) { + while (!need_resched_or_ipi()) { if (likely(snooze_timeout_en) && get_tb() > snooze_exit_time) { /* * Task has not woken up but we are exiting the polling diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c index 14db9b7d985d..4f2b490f8b73 100644 --- a/drivers/cpuidle/cpuidle-pseries.c +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -46,7 +46,7 @@ int snooze_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, snooze_exit_time = get_tb() + snooze_timeout; dev->poll_time_limit = false; - while (!need_resched()) { + while (!need_resched_or_ipi()) { HMT_low(); HMT_very_low(); if (likely(snooze_timeout_en) && get_tb() > snooze_exit_time) { diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c index 9b6d90a72601..225f37897e45 100644 --- a/drivers/cpuidle/poll_state.c +++ b/drivers/cpuidle/poll_state.c @@ -26,7 +26,7 @@ static int __cpuidle poll_idle(struct cpuidle_device *dev, limit = cpuidle_poll_time(drv, dev); - while (!need_resched()) { + while (!need_resched_or_ipi()) { cpu_relax(); if (loop_count++ < POLL_IDLE_RELAX_COUNT) continue; diff --git a/include/linux/sched.h b/include/linux/sched.h index 90691d99027e..e52cdd1298bf 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2069,6 +2069,11 @@ static __always_inline bool need_resched(void) return unlikely(tif_need_resched()); } +static __always_inline bool need_resched_or_ipi(void) +{ + return unlikely(tif_need_resched() || tif_notify_ipi()); +} + /* * Wrappers for p->thread_info->cpu access. No-op on UP. */ diff --git a/include/linux/sched/idle.h b/include/linux/sched/idle.h index e670ac282333..497518b84e8d 100644 --- a/include/linux/sched/idle.h +++ b/include/linux/sched/idle.h @@ -63,7 +63,7 @@ static __always_inline bool __must_check current_set_polling_and_test(void) */ smp_mb__after_atomic(); - return unlikely(tif_need_resched()); + return unlikely(need_resched_or_ipi()); } static __always_inline bool __must_check current_clr_polling_and_test(void) @@ -76,7 +76,7 @@ static __always_inline bool __must_check current_clr_polling_and_test(void) */ smp_mb__after_atomic(); - return unlikely(tif_need_resched()); + return unlikely(need_resched_or_ipi()); } #else diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 6e78d071beb5..7de94df5d477 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -57,7 +57,7 @@ static noinline int __cpuidle cpu_idle_poll(void) ct_cpuidle_enter(); raw_local_irq_enable(); - while (!tif_need_resched() && + while (!need_resched_or_ipi() && (cpu_idle_force_poll || tick_check_broadcast_expired())) cpu_relax(); raw_local_irq_disable(); @@ -174,7 +174,7 @@ static void cpuidle_idle_call(void) * Check if the idle task must be rescheduled. If it is the * case, exit the function after re-enabling the local IRQ. */ - if (need_resched()) { + if (need_resched_or_ipi()) { local_irq_enable(); return; } @@ -270,7 +270,7 @@ static void do_idle(void) __current_set_polling(); tick_nohz_idle_enter(); - while (!need_resched()) { + while (!need_resched_or_ipi()) { rmb(); /* @@ -350,6 +350,7 @@ static void do_idle(void) * RCU relies on this call to be done outside of an RCU read-side * critical section. */ + current_clr_notify_ipi(); flush_smp_call_function_queue(); schedule_idle(); -- 2.34.1