* [PATCH v4 1/3] softirq: Allow raising SCHED_SOFTIRQ from SMP-call-function on RT kernel
2024-10-30 7:15 [PATCH v4 0/3] [PATCH v3 0/5] Idle Load Balance fixes and softirq enhancements K Prateek Nayak
@ 2024-10-30 7:15 ` K Prateek Nayak
2024-11-08 12:25 ` Sebastian Andrzej Siewior
2024-10-30 7:15 ` [PATCH v4 2/3] sched/core: Remove the unnecessary need_resched() check in nohz_csd_func() K Prateek Nayak
2024-10-30 7:15 ` [PATCH v4 3/3] sched/core: Prevent wakeup of ksoftirqd during idle load balance K Prateek Nayak
2 siblings, 1 reply; 7+ messages in thread
From: K Prateek Nayak @ 2024-10-30 7:15 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-kernel, linux-rt-devel
Cc: Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
Thomas Gleixner, Tejun Heo, Jens Axboe, NeilBrown, Zqiang,
Caleb Sander Mateos, Gautham R . Shenoy, Chen Yu, Julia Lawall,
K Prateek Nayak
do_softirq_post_smp_call_flush() on PREEMPT_RT kernels carries a
WARN_ON_ONCE() for any SOFTIRQ being raised from an SMP-call-function.
Since do_softirq_post_smp_call_flush() is called with preempt disabled,
raising a SOFTIRQ during flush_smp_call_function_queue() can lead to
longer preempt disabled sections.
Since commit b2a02fc43a1f ("smp: Optimize
send_call_function_single_ipi()") IPIs to an idle CPU in
TIF_POLLING_NRFLAG mode can be optimized out by instead setting
TIF_NEED_RESCHED bit in idle task's thread_info and relying on the
flush_smp_call_function_queue() in the idle-exit path to run the
SMP-call-function.
To trigger an idle load balancing, the scheduler queues
nohz_csd_function() responsible for triggering an idle load balancing on
a target nohz idle CPU and sends an IPI. Only now, this IPI is optimized
out and the SMP-call-function is executed from
flush_smp_call_function_queue() in do_idle() which can raise a
SCHED_SOFTIRQ to trigger the balancing.
So far, this went undetected since, the need_resched() check in
nohz_csd_function() would make it bail out of idle load balancing early
as the idle thread does not clear TIF_POLLING_NRFLAG before calling
flush_smp_call_function_queue(). The need_resched() check was added with
the intent to catch a new task wakeup, however, it has recently
discovered to be unnecessary and will be removed soon. As such,
nohz_csd_function() will raise a SCHED_SOFTIRQ from
flush_smp_call_function_queue() to trigger an idle load balance on an
idle target.
nohz_csd_function() bails out early if "idle_cpu()" check for the
target CPU returns false and should not delay a newly woken up task from
running. Account for this and prevent a WARN_ON_ONCE() when
SCHED_SOFTIRQ is raised from flush_smp_call_function_queue().
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
v3..v4:
o No changes.
---
kernel/softirq.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/kernel/softirq.c b/kernel/softirq.c
index b756d6b3fd09..d89be0affe46 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -280,17 +280,24 @@ static inline void invoke_softirq(void)
wakeup_softirqd();
}
+#define SCHED_SOFTIRQ_MASK BIT(SCHED_SOFTIRQ)
+
/*
* flush_smp_call_function_queue() can raise a soft interrupt in a function
- * call. On RT kernels this is undesired and the only known functionality
- * in the block layer which does this is disabled on RT. If soft interrupts
- * get raised which haven't been raised before the flush, warn so it can be
+ * call. On RT kernels this is undesired and the only known functionalities
+ * are in the block layer which is disabled on RT, and in the scheduler for
+ * idle load balancing. If soft interrupts get raised which haven't been
+ * raised before the flush, warn if it is not a SCHED_SOFTIRQ so it can be
* investigated.
*/
void do_softirq_post_smp_call_flush(unsigned int was_pending)
{
- if (WARN_ON_ONCE(was_pending != local_softirq_pending()))
+ unsigned int is_pending = local_softirq_pending();
+
+ if (unlikely(was_pending != is_pending)) {
+ WARN_ON_ONCE(was_pending != (is_pending & ~SCHED_SOFTIRQ_MASK));
invoke_softirq();
+ }
}
#else /* CONFIG_PREEMPT_RT */
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v4 1/3] softirq: Allow raising SCHED_SOFTIRQ from SMP-call-function on RT kernel
2024-10-30 7:15 ` [PATCH v4 1/3] softirq: Allow raising SCHED_SOFTIRQ from SMP-call-function on RT kernel K Prateek Nayak
@ 2024-11-08 12:25 ` Sebastian Andrzej Siewior
0 siblings, 0 replies; 7+ messages in thread
From: Sebastian Andrzej Siewior @ 2024-11-08 12:25 UTC (permalink / raw)
To: K Prateek Nayak
Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Clark Williams, Steven Rostedt, linux-kernel, linux-rt-devel,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
Thomas Gleixner, Tejun Heo, Jens Axboe, NeilBrown, Zqiang,
Caleb Sander Mateos, Gautham R . Shenoy, Chen Yu, Julia Lawall
On 2024-10-30 07:15:55 [+0000], K Prateek Nayak wrote:
…
> --- a/kernel/softirq.c
> +++ b/kernel/softirq.c
> @@ -280,17 +280,24 @@ static inline void invoke_softirq(void)
> wakeup_softirqd();
> }
>
> +#define SCHED_SOFTIRQ_MASK BIT(SCHED_SOFTIRQ)
> +
> /*
> * flush_smp_call_function_queue() can raise a soft interrupt in a function
> - * call. On RT kernels this is undesired and the only known functionality
> - * in the block layer which does this is disabled on RT. If soft interrupts
> - * get raised which haven't been raised before the flush, warn so it can be
> + * call. On RT kernels this is undesired and the only known functionalities
> + * are in the block layer which is disabled on RT, and in the scheduler for
> + * idle load balancing. If soft interrupts get raised which haven't been
> + * raised before the flush, warn if it is not a SCHED_SOFTIRQ so it can be
> * investigated.
> */
> void do_softirq_post_smp_call_flush(unsigned int was_pending)
> {
> - if (WARN_ON_ONCE(was_pending != local_softirq_pending()))
> + unsigned int is_pending = local_softirq_pending();
> +
> + if (unlikely(was_pending != is_pending)) {
> + WARN_ON_ONCE(was_pending != (is_pending & ~SCHED_SOFTIRQ_MASK));
> invoke_softirq();
This behaviour also happens with threadirqs on !PREEMPT_RT but without
the warning. I haven't checked it but I expect invoke_softirq() to wake
ksoftirqd here, too.
This only happens because of 2/3 in the series as far as I can tell.
Now I am curious to hear from the sched/ NOHZ folks if it makes sense to
invoke SCHED_SOFTIRQ from within ksoftirqd because unlike on an idle CPU
the CPU is now not seen as idle due to ksoftirqd running on the CPU.
There is code that checks rq->nr_running and/ or idle_cpu().
> + }
> }
Sebastian
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 2/3] sched/core: Remove the unnecessary need_resched() check in nohz_csd_func()
2024-10-30 7:15 [PATCH v4 0/3] [PATCH v3 0/5] Idle Load Balance fixes and softirq enhancements K Prateek Nayak
2024-10-30 7:15 ` [PATCH v4 1/3] softirq: Allow raising SCHED_SOFTIRQ from SMP-call-function on RT kernel K Prateek Nayak
@ 2024-10-30 7:15 ` K Prateek Nayak
2024-10-30 7:15 ` [PATCH v4 3/3] sched/core: Prevent wakeup of ksoftirqd during idle load balance K Prateek Nayak
2 siblings, 0 replies; 7+ messages in thread
From: K Prateek Nayak @ 2024-10-30 7:15 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-kernel, linux-rt-devel
Cc: Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
Thomas Gleixner, Tejun Heo, Jens Axboe, NeilBrown, Zqiang,
Caleb Sander Mateos, Gautham R . Shenoy, Chen Yu, Julia Lawall,
K Prateek Nayak
The need_resched() check currently in nohz_csd_func() can be tracked
to have been added in scheduler_ipi() back in 2011 via commit
ca38062e57e9 ("sched: Use resched IPI to kick off the nohz idle balance")
Since then, it has travelled quite a bit but it seems like an idle_cpu()
check currently is sufficient to detect the need to bail out from an
idle load balancing. To justify this removal, consider all the following
case where an idle load balancing could race with a task wakeup:
o Since commit f3dd3f674555b ("sched: Remove the limitation of WF_ON_CPU
on wakelist if wakee cpu is idle") a target perceived to be idle
(target_rq->nr_running == 0) will return true for
ttwu_queue_cond(target) which will offload the task wakeup to the idle
target via an IPI.
In all such cases target_rq->ttwu_pending will be set to 1 before
queuing the wake function.
If an idle load balance races here, following scenarios are possible:
- The CPU is not in TIF_POLLING_NRFLAG mode in which case an actual
IPI is sent to the CPU to wake it out of idle. If the
nohz_csd_func() queues before sched_ttwu_pending(), the idle load
balance will bail out since idle_cpu(target) returns 0 since
target_rq->ttwu_pending is 1. If the nohz_csd_func() is queued after
sched_ttwu_pending() it should see rq->nr_running to be non-zero and
bail out of idle load balancing.
- The CPU is in TIF_POLLING_NRFLAG mode and instead of an actual IPI,
the sender will simply set TIF_NEED_RESCHED for the target to put it
out of idle and flush_smp_call_function_queue() in do_idle() will
execute the call function. Depending on the ordering of the queuing
of nohz_csd_func() and sched_ttwu_pending(), the idle_cpu() check in
nohz_csd_func() should either see target_rq->ttwu_pending = 1 or
target_rq->nr_running to be non-zero if there is a genuine task
wakeup racing with the idle load balance kick.
o The waker CPU perceives the target CPU to be busy
(targer_rq->nr_running != 0) but the CPU is in fact going idle and due
to a series of unfortunate events, the system reaches a case where the
waker CPU decides to perform the wakeup by itself in ttwu_queue() on
the target CPU but target is concurrently selected for idle load
balance (XXX: Can this happen? I'm not sure, but we'll consider the
mother of all coincidences to estimate the worst case scenario).
ttwu_do_activate() calls enqueue_task() which would increment
"rq->nr_running" post which it calls wakeup_preempt() which is
responsible for setting TIF_NEED_RESCHED (via a resched IPI or by
setting TIF_NEED_RESCHED on a TIF_POLLING_NRFLAG idle CPU) The key
thing to note in this case is that rq->nr_running is already non-zero
in case of a wakeup before TIF_NEED_RESCHED is set which would
lead to idle_cpu() check returning false.
In all cases, it seems that need_resched() check is unnecessary when
checking for idle_cpu() first since an impending wakeup racing with idle
load balancer will either set the "rq->ttwu_pending" or indicate a newly
woken task via "rq->nr_running".
Chasing the reason why this check might have existed in the first place,
I came across Peter's suggestion on the fist iteration of Suresh's
patch from 2011 [1] where the condition to raise the SCHED_SOFTIRQ was:
sched_ttwu_do_pending(list);
if (unlikely((rq->idle == current) &&
rq->nohz_balance_kick &&
!need_resched()))
raise_softirq_irqoff(SCHED_SOFTIRQ);
Since the condition to raise the SCHED_SOFIRQ was preceded by
sched_ttwu_do_pending() (which is equivalent of sched_ttwu_pending()) in
the current upstream kernel, the need_resched() check was necessary to
catch a newly queued task. Peter suggested modifying it to:
if (idle_cpu() && rq->nohz_balance_kick && !need_resched())
raise_softirq_irqoff(SCHED_SOFTIRQ);
where idle_cpu() seems to have replaced "rq->idle == current" check.
Even back then, the idle_cpu() check would have been sufficient to catch
a new task being enqueued. Since commit b2a02fc43a1f ("smp: Optimize
send_call_function_single_ipi()") overloads the interpretation of
TIF_NEED_RESCHED for TIF_POLLING_NRFLAG idling, remove the
need_resched() check in nohz_csd_func() to raise SCHED_SOFTIRQ based
on Peter's suggestion.
Link: https://lore.kernel.org/all/1317670590.20367.38.camel@twins/ [1]
Link: https://lore.kernel.org/lkml/20240615014521.GR8774@noisy.programming.kicks-ass.net/
Fixes: b2a02fc43a1f ("smp: Optimize send_call_function_single_ipi()")
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
v3..v4:
o No changes.
---
kernel/sched/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index c57a79e34911..aaf99c0bcb49 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1242,7 +1242,7 @@ static void nohz_csd_func(void *info)
WARN_ON(!(flags & NOHZ_KICK_MASK));
rq->idle_balance = idle_cpu(cpu);
- if (rq->idle_balance && !need_resched()) {
+ if (rq->idle_balance) {
rq->nohz_idle_balance = flags;
raise_softirq_irqoff(SCHED_SOFTIRQ);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v4 3/3] sched/core: Prevent wakeup of ksoftirqd during idle load balance
2024-10-30 7:15 [PATCH v4 0/3] [PATCH v3 0/5] Idle Load Balance fixes and softirq enhancements K Prateek Nayak
2024-10-30 7:15 ` [PATCH v4 1/3] softirq: Allow raising SCHED_SOFTIRQ from SMP-call-function on RT kernel K Prateek Nayak
2024-10-30 7:15 ` [PATCH v4 2/3] sched/core: Remove the unnecessary need_resched() check in nohz_csd_func() K Prateek Nayak
@ 2024-10-30 7:15 ` K Prateek Nayak
2024-11-08 12:17 ` Sebastian Andrzej Siewior
2 siblings, 1 reply; 7+ messages in thread
From: K Prateek Nayak @ 2024-10-30 7:15 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-kernel, linux-rt-devel
Cc: Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
Thomas Gleixner, Tejun Heo, Jens Axboe, NeilBrown, Zqiang,
Caleb Sander Mateos, Gautham R . Shenoy, Chen Yu, Julia Lawall,
K Prateek Nayak, Julia Lawall
Scheduler raises a SCHED_SOFTIRQ to trigger a load balancing event on
from the IPI handler on the idle CPU. Since the softirq can be raised
from flush_smp_call_function_queue(), it can end up waking up ksoftirqd,
which can give an illusion of the idle CPU being busy when doing an idle
load balancing.
Adding a trace_printk() in nohz_csd_func() at the spot of raising
SCHED_SOFTIRQ and enabling trace events for sched_switch, sched_wakeup,
and softirq_entry (for SCHED_SOFTIRQ vector alone) helps observing the
current behavior:
<idle>-0 [000] dN.1.: nohz_csd_func: Raising SCHED_SOFTIRQ from nohz_csd_func
<idle>-0 [000] dN.4.: sched_wakeup: comm=ksoftirqd/0 pid=16 prio=120 target_cpu=000
<idle>-0 [000] .Ns1.: softirq_entry: vec=7 [action=SCHED]
<idle>-0 [000] .Ns1.: softirq_exit: vec=7 [action=SCHED]
<idle>-0 [000] d..2.: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=16 next_prio=120
ksoftirqd/0-16 [000] d..2.: sched_switch: prev_comm=ksoftirqd/0 prev_pid=16 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
...
ksoftirqd is woken up before the idle thread calls
do_softirq_post_smp_call_flush() which can make the runqueue appear
busy and prevent the idle load balancer from pulling task from an
overloaded runqueue towards itself[1].
Since the softirq raised is guranteed to be serviced in irq_exit() or
via do_softirq_post_smp_call_flush(), set SCHED_SOFTIRQ without checking
the need to wakeup ksoftirq for idle load balancing.
Following are the observations with the changes when enabling the same
set of events:
<idle>-0 [000] dN.1.: nohz_csd_func: Raising SCHED_SOFTIRQ for nohz_idle_balance
<idle>-0 [000] dN.1.: softirq_raise: vec=7 [action=SCHED]
<idle>-0 [000] .Ns1.: softirq_entry: vec=7 [action=SCHED]
No unnecessary ksoftirqd wakeups are seen from idle task's context to
service the softirq.
Fixes: b2a02fc43a1f ("smp: Optimize send_call_function_single_ipi()")
Reported-by: Julia Lawall <julia.lawall@inria.fr>
Closes: https://lore.kernel.org/lkml/fcf823f-195e-6c9a-eac3-25f870cb35ac@inria.fr/ [1]
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
v3..v4:
o New patch based on Sebastian's suggestion.
---
kernel/sched/core.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index aaf99c0bcb49..2ee3621d6e7e 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1244,7 +1244,18 @@ static void nohz_csd_func(void *info)
rq->idle_balance = idle_cpu(cpu);
if (rq->idle_balance) {
rq->nohz_idle_balance = flags;
- raise_softirq_irqoff(SCHED_SOFTIRQ);
+
+ /*
+ * Don't wakeup ksoftirqd when raising SCHED_SOFTIRQ
+ * since the idle load balancer may mistake wakeup of
+ * ksoftirqd as a genuine task wakeup and bail out from
+ * load balancing early. Since it is guaranteed that
+ * pending softirqs will be handled soon, either on
+ * irq_exit() or via do_softirq_post_smp_call_flush(),
+ * raise SCHED_SOFTIRQ without checking the need to
+ * wakeup ksoftirqd.
+ */
+ __raise_softirq_irqoff(SCHED_SOFTIRQ);
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v4 3/3] sched/core: Prevent wakeup of ksoftirqd during idle load balance
2024-10-30 7:15 ` [PATCH v4 3/3] sched/core: Prevent wakeup of ksoftirqd during idle load balance K Prateek Nayak
@ 2024-11-08 12:17 ` Sebastian Andrzej Siewior
2024-11-11 4:42 ` K Prateek Nayak
0 siblings, 1 reply; 7+ messages in thread
From: Sebastian Andrzej Siewior @ 2024-11-08 12:17 UTC (permalink / raw)
To: K Prateek Nayak
Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Clark Williams, Steven Rostedt, linux-kernel, linux-rt-devel,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
Thomas Gleixner, Tejun Heo, Jens Axboe, NeilBrown, Zqiang,
Caleb Sander Mateos, Gautham R . Shenoy, Chen Yu, Julia Lawall
On 2024-10-30 07:15:57 [+0000], K Prateek Nayak wrote:
> Scheduler raises a SCHED_SOFTIRQ to trigger a load balancing event on
> from the IPI handler on the idle CPU. Since the softirq can be raised
> from flush_smp_call_function_queue(), it can end up waking up ksoftirqd,
> which can give an illusion of the idle CPU being busy when doing an idle
> load balancing.
>
> Adding a trace_printk() in nohz_csd_func() at the spot of raising
> SCHED_SOFTIRQ and enabling trace events for sched_switch, sched_wakeup,
> and softirq_entry (for SCHED_SOFTIRQ vector alone) helps observing the
> current behavior:
>
> <idle>-0 [000] dN.1.: nohz_csd_func: Raising SCHED_SOFTIRQ from nohz_csd_func
> <idle>-0 [000] dN.4.: sched_wakeup: comm=ksoftirqd/0 pid=16 prio=120 target_cpu=000
> <idle>-0 [000] .Ns1.: softirq_entry: vec=7 [action=SCHED]
> <idle>-0 [000] .Ns1.: softirq_exit: vec=7 [action=SCHED]
> <idle>-0 [000] d..2.: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=16 next_prio=120
> ksoftirqd/0-16 [000] d..2.: sched_switch: prev_comm=ksoftirqd/0 prev_pid=16 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
> ...
>
> ksoftirqd is woken up before the idle thread calls
> do_softirq_post_smp_call_flush() which can make the runqueue appear
> busy and prevent the idle load balancer from pulling task from an
> overloaded runqueue towards itself[1].
>
> Since the softirq raised is guranteed to be serviced in irq_exit() or
> via do_softirq_post_smp_call_flush(), set SCHED_SOFTIRQ without checking
> the need to wakeup ksoftirq for idle load balancing.
>
> Following are the observations with the changes when enabling the same
> set of events:
>
> <idle>-0 [000] dN.1.: nohz_csd_func: Raising SCHED_SOFTIRQ for nohz_idle_balance
> <idle>-0 [000] dN.1.: softirq_raise: vec=7 [action=SCHED]
> <idle>-0 [000] .Ns1.: softirq_entry: vec=7 [action=SCHED]
>
> No unnecessary ksoftirqd wakeups are seen from idle task's context to
> service the softirq.
| Use __raise_softirq_irqoff() to raise the softirq. The SMP function call
| is always invoked on the requested CPU in an interrupt handler. It is
| guaranteed that soft interrupts are handled at the end.
You could extend it
| If the SMP function is invoked from an idle CPU via
| flush_smp_call_function_queue() then the HARD-IRQ flag is not set and
| raise_softirq_irqoff() wakes needlessly ksoftirqd because soft
| interrupts are handled before ksoftirqd get on the CPU.
This on its own is a reasonable optimisation. A different question would
be if flush_smp_call_function_queue() should pretend to be in-IRQ like a
regular IPI but…
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Fixes: b2a02fc43a1f ("smp: Optimize send_call_function_single_ipi()")
> Reported-by: Julia Lawall <julia.lawall@inria.fr>
> Closes: https://lore.kernel.org/lkml/fcf823f-195e-6c9a-eac3-25f870cb35ac@inria.fr/ [1]
> Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
> ---
> v3..v4:
>
> o New patch based on Sebastian's suggestion.
> ---
> kernel/sched/core.c | 13 ++++++++++++-
> 1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index aaf99c0bcb49..2ee3621d6e7e 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -1244,7 +1244,18 @@ static void nohz_csd_func(void *info)
> rq->idle_balance = idle_cpu(cpu);
> if (rq->idle_balance) {
> rq->nohz_idle_balance = flags;
> - raise_softirq_irqoff(SCHED_SOFTIRQ);
> +
> + /*
> + * Don't wakeup ksoftirqd when raising SCHED_SOFTIRQ
> + * since the idle load balancer may mistake wakeup of
> + * ksoftirqd as a genuine task wakeup and bail out from
> + * load balancing early. Since it is guaranteed that
> + * pending softirqs will be handled soon, either on
> + * irq_exit() or via do_softirq_post_smp_call_flush(),
> + * raise SCHED_SOFTIRQ without checking the need to
> + * wakeup ksoftirqd.
> + */
/*
* This is always invoked from an interrupt handler, simply raise the
* softirq.
*/
should be enough IMHO. But *I* would even skip that, since it is
obvious.
> + __raise_softirq_irqoff(SCHED_SOFTIRQ);
> }
> }
Sebastian
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v4 3/3] sched/core: Prevent wakeup of ksoftirqd during idle load balance
2024-11-08 12:17 ` Sebastian Andrzej Siewior
@ 2024-11-11 4:42 ` K Prateek Nayak
0 siblings, 0 replies; 7+ messages in thread
From: K Prateek Nayak @ 2024-11-11 4:42 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Clark Williams, Steven Rostedt, linux-kernel, linux-rt-devel,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
Thomas Gleixner, Tejun Heo, Jens Axboe, NeilBrown, Zqiang,
Caleb Sander Mateos, Gautham R . Shenoy, Chen Yu, Julia Lawall
Hello Sebastian,
On 11/8/2024 5:47 PM, Sebastian Andrzej Siewior wrote:
> On 2024-10-30 07:15:57 [+0000], K Prateek Nayak wrote:
>> Scheduler raises a SCHED_SOFTIRQ to trigger a load balancing event on
>> from the IPI handler on the idle CPU. Since the softirq can be raised
>> from flush_smp_call_function_queue(), it can end up waking up ksoftirqd,
>> which can give an illusion of the idle CPU being busy when doing an idle
>> load balancing.
>>
>> Adding a trace_printk() in nohz_csd_func() at the spot of raising
>> SCHED_SOFTIRQ and enabling trace events for sched_switch, sched_wakeup,
>> and softirq_entry (for SCHED_SOFTIRQ vector alone) helps observing the
>> current behavior:
>>
>> <idle>-0 [000] dN.1.: nohz_csd_func: Raising SCHED_SOFTIRQ from nohz_csd_func
>> <idle>-0 [000] dN.4.: sched_wakeup: comm=ksoftirqd/0 pid=16 prio=120 target_cpu=000
>> <idle>-0 [000] .Ns1.: softirq_entry: vec=7 [action=SCHED]
>> <idle>-0 [000] .Ns1.: softirq_exit: vec=7 [action=SCHED]
>> <idle>-0 [000] d..2.: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/0 next_pid=16 next_prio=120
>> ksoftirqd/0-16 [000] d..2.: sched_switch: prev_comm=ksoftirqd/0 prev_pid=16 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
>> ...
>>
>> ksoftirqd is woken up before the idle thread calls
>> do_softirq_post_smp_call_flush() which can make the runqueue appear
>> busy and prevent the idle load balancer from pulling task from an
>> overloaded runqueue towards itself[1].
>>
>> Since the softirq raised is guranteed to be serviced in irq_exit() or
>> via do_softirq_post_smp_call_flush(), set SCHED_SOFTIRQ without checking
>> the need to wakeup ksoftirq for idle load balancing.
>>
>> Following are the observations with the changes when enabling the same
>> set of events:
>>
>> <idle>-0 [000] dN.1.: nohz_csd_func: Raising SCHED_SOFTIRQ for nohz_idle_balance
>> <idle>-0 [000] dN.1.: softirq_raise: vec=7 [action=SCHED]
>> <idle>-0 [000] .Ns1.: softirq_entry: vec=7 [action=SCHED]
>>
>> No unnecessary ksoftirqd wakeups are seen from idle task's context to
>> service the softirq.
>
> | Use __raise_softirq_irqoff() to raise the softirq. The SMP function call
> | is always invoked on the requested CPU in an interrupt handler. It is
> | guaranteed that soft interrupts are handled at the end.
>
> You could extend it
>
> | If the SMP function is invoked from an idle CPU via
> | flush_smp_call_function_queue() then the HARD-IRQ flag is not set and
> | raise_softirq_irqoff() wakes needlessly ksoftirqd because soft
> | interrupts are handled before ksoftirqd get on the CPU.
I'll reword the log as suggested in the next version.
>
> This on its own is a reasonable optimisation. A different question would
> be if flush_smp_call_function_queue() should pretend to be in-IRQ like a
> regular IPI but…
I thought about it initially but seeing optimizations and checks around
"hardirq_stack" and checks to reuse it in certain context led be to
believe that there may be more nuances that I do not have a full picture
of, and I went ahead with this simpler solution.
>
> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Thank you for the review!
>
>> Fixes: b2a02fc43a1f ("smp: Optimize send_call_function_single_ipi()")
>> Reported-by: Julia Lawall <julia.lawall@inria.fr>
>> Closes: https://lore.kernel.org/lkml/fcf823f-195e-6c9a-eac3-25f870cb35ac@inria.fr/ [1]
>> Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>> Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
>> ---
>> v3..v4:
>>
>> o New patch based on Sebastian's suggestion.
>> ---
>> kernel/sched/core.c | 13 ++++++++++++-
>> 1 file changed, 12 insertions(+), 1 deletion(-)
>>
>> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
>> index aaf99c0bcb49..2ee3621d6e7e 100644
>> --- a/kernel/sched/core.c
>> +++ b/kernel/sched/core.c
>> @@ -1244,7 +1244,18 @@ static void nohz_csd_func(void *info)
>> rq->idle_balance = idle_cpu(cpu);
>> if (rq->idle_balance) {
>> rq->nohz_idle_balance = flags;
>> - raise_softirq_irqoff(SCHED_SOFTIRQ);
>> +
>> + /*
>> + * Don't wakeup ksoftirqd when raising SCHED_SOFTIRQ
>> + * since the idle load balancer may mistake wakeup of
>> + * ksoftirqd as a genuine task wakeup and bail out from
>> + * load balancing early. Since it is guaranteed that
>> + * pending softirqs will be handled soon, either on
>> + * irq_exit() or via do_softirq_post_smp_call_flush(),
>> + * raise SCHED_SOFTIRQ without checking the need to
>> + * wakeup ksoftirqd.
>> + */
>
> /*
> * This is always invoked from an interrupt handler, simply raise the
> * softirq.
> */
>
> should be enough IMHO. But *I* would even skip that, since it is
> obvious.
I'll remove it in the subsequent version. I'll wait a bit before sending
it to see if folks have any suggestion on the parallel thread regarding
handling SCHED_SOFTIRQ from ksoftirqd.
>
>> + __raise_softirq_irqoff(SCHED_SOFTIRQ);
>> }
>> }
>
> Sebastian
--
Thanks and Regards,
Prateek
^ permalink raw reply [flat|nested] 7+ messages in thread