mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* softirq
@ 2024-06-26  5:37 Julia Lawall
  2024-06-26 15:17 ` softirq Vincent Guittot
  0 siblings, 1 reply; 6+ messages in thread
From: Julia Lawall @ 2024-06-26  5:37 UTC (permalink / raw)
  To: Vincent Guittot, Peter Zijlstra, Ingo Molnar, Dietmar Eggemann,
	Mel Gorman, K Prateek Nayak, linux-kernel

Hello,

I'm not sure to understand how soft irqs work.  I see the code:

open_softirq(SCHED_SOFTIRQ, sched_balance_softirq);

Intuitively, I would expect that sched_balance_softirq would be run by
ksoftirqd.  That is, I would expect ksoftirqd to be scheduled
(sched_switch event), then the various actions of sched_balance_softirq to
be executed, and the ksoftirqd to be unscheduled (another ksoftirqd)
event.

But in practice, I see the code of sched_balance_softirq being executed
by the idle task, before the ksoftirqd is scheduled (see core 40):

          <idle>-0     [040]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
          <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting nohz
          <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting _nohz_idle_balance
          bt.B.x-12022 [047]  3611.432554: softirq_entry:        vec=1 [action=TIMER]
          <idle>-0     [040]  3611.432554: bputs:                _nohz_idle_balance.isra.0: searching for a cpu
          bt.B.x-12033 [003]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
          <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: ending _nohz_idle_balance
          bt.B.x-12052 [011]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
          <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: nohz returns true ending soft irq
          <idle>-0     [040]  3611.432554: softirq_exit:         vec=7 [action=SCHED]

For example, idle seems to be running the code in _nohz_idle_balance.

I updated the code of _nohz_idle_balance as follows:

trace_printk("searching for a cpu\n");
        for_each_cpu_wrap(balance_cpu,  nohz.idle_cpus_mask, this_cpu+1) {
                if (!idle_cpu(balance_cpu))
                        continue;
trace_printk("found an idle cpu\n");

It prints searching for a cpu, but not found an idle cpu, because the
ksoftirqd on the core's runqueue makes the core not idle.  This makes the
whole softirq seem fairly useless when the only idle core is the one
raising the soft irq.

This is all for the same scenario that I have discussed previously, where
there are two sockets and an overload of on thread on one and an underload
of on thread on the other, and all the thread have been marked by numa
balancing as preferring to be where they are.  Now I am trying Prateek's
patch series.

thanks,
julia

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: softirq
  2024-06-26  5:37 softirq Julia Lawall
@ 2024-06-26 15:17 ` Vincent Guittot
  2024-06-26 21:07   ` softirq Julia Lawall
  0 siblings, 1 reply; 6+ messages in thread
From: Vincent Guittot @ 2024-06-26 15:17 UTC (permalink / raw)
  To: Julia Lawall
  Cc: Peter Zijlstra, Ingo Molnar, Dietmar Eggemann, Mel Gorman,
	K Prateek Nayak, linux-kernel

On Wed, 26 Jun 2024 at 07:37, Julia Lawall <julia.lawall@inria.fr> wrote:
>
> Hello,
>
> I'm not sure to understand how soft irqs work.  I see the code:
>
> open_softirq(SCHED_SOFTIRQ, sched_balance_softirq);
>
> Intuitively, I would expect that sched_balance_softirq would be run by
> ksoftirqd.  That is, I would expect ksoftirqd to be scheduled

By default, sched_softirq and others run in interrupt context.
ksoftirqd is woken up only in some cases like when we spent too much
time processing softirq in interrupt context or the softirq is raised
outside interrupt context

> (sched_switch event), then the various actions of sched_balance_softirq to
> be executed, and the ksoftirqd to be unscheduled (another ksoftirqd)
> event.
>
> But in practice, I see the code of sched_balance_softirq being executed
> by the idle task, before the ksoftirqd is scheduled (see core 40):

What wakes up ksoftirqd ? and which softirq finally runs in ksoftirqd ?

>
>           <idle>-0     [040]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
>           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting nohz
>           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting _nohz_idle_balance
>           bt.B.x-12022 [047]  3611.432554: softirq_entry:        vec=1 [action=TIMER]
>           <idle>-0     [040]  3611.432554: bputs:                _nohz_idle_balance.isra.0: searching for a cpu
>           bt.B.x-12033 [003]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
>           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: ending _nohz_idle_balance
>           bt.B.x-12052 [011]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
>           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: nohz returns true ending soft irq
>           <idle>-0     [040]  3611.432554: softirq_exit:         vec=7 [action=SCHED]
>
> For example, idle seems to be running the code in _nohz_idle_balance.
>
> I updated the code of _nohz_idle_balance as follows:
>
> trace_printk("searching for a cpu\n");
>         for_each_cpu_wrap(balance_cpu,  nohz.idle_cpus_mask, this_cpu+1) {
>                 if (!idle_cpu(balance_cpu))
>                         continue;
> trace_printk("found an idle cpu\n");
>
> It prints searching for a cpu, but not found an idle cpu, because the
> ksoftirqd on the core's runqueue makes the core not idle.  This makes the
> whole softirq seem fairly useless when the only idle core is the one
> raising the soft irq.

The typical behavior is:

CPUA                                   CPUB
                                       do_idle
                                         while (!need_resched()) {
                                         ...

kick_ilb
  smp_call_function_single_async(CPUB)
    send_call_function_single_ipi
      raise_ipi  --------------------->    cpuidle exit event
                                           irq_handler_entry
                                             ipi_handler
                                               raise sched_softirq
                                           irq_handler_exit
                                           sorftirq_entry
                                             sched_balance_softirq
                                               __nohe_idle_balance
                                           softirq_exit
                                           cpuidle_enter event

softirq is done in the interrupt context after the irq handler and
CPUB never leaves the while (!need_resched())  loop

In your case, I suspect that you have a racing with the polling mode
and the fact that you leave the while (!need_resched()) loop and call
flush_smp_call_function_queue()

We don't use polling on arm64 so I can't even try to reproduce your case

>
> This is all for the same scenario that I have discussed previously, where
> there are two sockets and an overload of on thread on one and an underload
> of on thread on the other, and all the thread have been marked by numa
> balancing as preferring to be where they are.  Now I am trying Prateek's
> patch series.
>
> thanks,
> julia

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: softirq
  2024-06-26 15:17 ` softirq Vincent Guittot
@ 2024-06-26 21:07   ` Julia Lawall
  2024-06-27 14:13     ` softirq K Prateek Nayak
  0 siblings, 1 reply; 6+ messages in thread
From: Julia Lawall @ 2024-06-26 21:07 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: Julia Lawall, Peter Zijlstra, Ingo Molnar, Dietmar Eggemann,
	Mel Gorman, K Prateek Nayak, linux-kernel



On Wed, 26 Jun 2024, Vincent Guittot wrote:

> On Wed, 26 Jun 2024 at 07:37, Julia Lawall <julia.lawall@inria.fr> wrote:
> >
> > Hello,
> >
> > I'm not sure to understand how soft irqs work.  I see the code:
> >
> > open_softirq(SCHED_SOFTIRQ, sched_balance_softirq);
> >
> > Intuitively, I would expect that sched_balance_softirq would be run by
> > ksoftirqd.  That is, I would expect ksoftirqd to be scheduled
>
> By default, sched_softirq and others run in interrupt context.
> ksoftirqd is woken up only in some cases like when we spent too much
> time processing softirq in interrupt context or the softirq is raised
> outside interrupt context

nohz_csd_func calls raise_softirq_irqoff, which does:

inline void raise_softirq_irqoff(unsigned int nr)
{
        __raise_softirq_irqoff(nr);

        /*
         * If we're in an interrupt or softirq, we're done
         * (this also catches softirq-disabled code). We will
         * actually run the softirq once we return from
         * the irq or softirq.
         *
         * Otherwise we wake up ksoftirqd to make sure we
         * schedule the softirq soon.
         */
        if (!in_interrupt() && should_wake_ksoftirqd())
		wakeup_softirqd();
}

My impression was that wakeup_softirqd was getting called.

But it is true that if the code is being executed by idle, then
in_interrupt() should be true.  So perhaps it is someone else who is
waking up ksoftirqd.  When I switched to __raise_softirq_irqoff, the
behavior seemed to change, but I may not have fully understood why that
happened.

>
> > (sched_switch event), then the various actions of sched_balance_softirq to
> > be executed, and the ksoftirqd to be unscheduled (another ksoftirqd)
> > event.
> >
> > But in practice, I see the code of sched_balance_softirq being executed
> > by the idle task, before the ksoftirqd is scheduled (see core 40):
>
> What wakes up ksoftirqd ? and which softirq finally runs in ksoftirqd ?
>
> >
> >           <idle>-0     [040]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> >           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting nohz
> >           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting _nohz_idle_balance
> >           bt.B.x-12022 [047]  3611.432554: softirq_entry:        vec=1 [action=TIMER]
> >           <idle>-0     [040]  3611.432554: bputs:                _nohz_idle_balance.isra.0: searching for a cpu
> >           bt.B.x-12033 [003]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> >           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: ending _nohz_idle_balance
> >           bt.B.x-12052 [011]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> >           <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: nohz returns true ending soft irq
> >           <idle>-0     [040]  3611.432554: softirq_exit:         vec=7 [action=SCHED]
> >
> > For example, idle seems to be running the code in _nohz_idle_balance.
> >
> > I updated the code of _nohz_idle_balance as follows:
> >
> > trace_printk("searching for a cpu\n");
> >         for_each_cpu_wrap(balance_cpu,  nohz.idle_cpus_mask, this_cpu+1) {
> >                 if (!idle_cpu(balance_cpu))
> >                         continue;
> > trace_printk("found an idle cpu\n");
> >
> > It prints searching for a cpu, but not found an idle cpu, because the
> > ksoftirqd on the core's runqueue makes the core not idle.  This makes the
> > whole softirq seem fairly useless when the only idle core is the one
> > raising the soft irq.
>
> The typical behavior is:
>
> CPUA                                   CPUB
>                                        do_idle
>                                          while (!need_resched()) {
>                                          ...
>
> kick_ilb
>   smp_call_function_single_async(CPUB)
>     send_call_function_single_ipi
>       raise_ipi  --------------------->    cpuidle exit event
>                                            irq_handler_entry
>                                              ipi_handler
>                                                raise sched_softirq
>                                            irq_handler_exit
>                                            sorftirq_entry
>                                              sched_balance_softirq
>                                                __nohe_idle_balance
>                                            softirq_exit
>                                            cpuidle_enter event
>
> softirq is done in the interrupt context after the irq handler and
> CPUB never leaves the while (!need_resched())  loop
>
> In your case, I suspect that you have a racing with the polling mode
> and the fact that you leave the while (!need_resched()) loop and call
> flush_smp_call_function_queue()
>
> We don't use polling on arm64 so I can't even try to reproduce your case

This is with Prateek's patch.  So need_resched is not true any more.

thanks,
julia

> >
> > This is all for the same scenario that I have discussed previously, where
> > there are two sockets and an overload of on thread on one and an underload
> > of on thread on the other, and all the thread have been marked by numa
> > balancing as preferring to be where they are.  Now I am trying Prateek's
> > patch series.
> >
> > thanks,
> > julia
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: softirq
  2024-06-26 21:07   ` softirq Julia Lawall
@ 2024-06-27 14:13     ` K Prateek Nayak
  2024-06-27 15:25       ` softirq Vincent Guittot
  0 siblings, 1 reply; 6+ messages in thread
From: K Prateek Nayak @ 2024-06-27 14:13 UTC (permalink / raw)
  To: Julia Lawall, Vincent Guittot
  Cc: Peter Zijlstra, Ingo Molnar, Dietmar Eggemann, Mel Gorman, linux-kernel

Hello Julia,

Some data from my 3rd Generation EPYC machine.

On 6/27/2024 2:37 AM, Julia Lawall wrote:
> 
> 
> On Wed, 26 Jun 2024, Vincent Guittot wrote:
> 
>> On Wed, 26 Jun 2024 at 07:37, Julia Lawall <julia.lawall@inria.fr> wrote:
>>>
>>> Hello,
>>>
>>> I'm not sure to understand how soft irqs work.  I see the code:
>>>
>>> open_softirq(SCHED_SOFTIRQ, sched_balance_softirq);
>>>
>>> Intuitively, I would expect that sched_balance_softirq would be run by
>>> ksoftirqd.  That is, I would expect ksoftirqd to be scheduled
>>
>> By default, sched_softirq and others run in interrupt context.
>> ksoftirqd is woken up only in some cases like when we spent too much
>> time processing softirq in interrupt context or the softirq is raised
>> outside interrupt context
> 
> nohz_csd_func calls raise_softirq_irqoff, which does:
> 
> inline void raise_softirq_irqoff(unsigned int nr)
> {
>          __raise_softirq_irqoff(nr);
> 
>          /*
>           * If we're in an interrupt or softirq, we're done
>           * (this also catches softirq-disabled code). We will
>           * actually run the softirq once we return from
>           * the irq or softirq.
>           *
>           * Otherwise we wake up ksoftirqd to make sure we
>           * schedule the softirq soon.
>           */
>          if (!in_interrupt() && should_wake_ksoftirqd())

I think it is the !in_interrupt() check that fails. When I disable C2
(which is I/O Port based C-state on AMD) and only leave C0 (Poll loop)
and C1 (MWAIT based C-state), both of which set TIF_POLLING_NRFLAG while
idling, and I add the following log line:

	trace_printk("raise_softirq_irqoff %d %lu %lu\n",
		     preempt_count(),
		     in_interrupt());

just above the "if" condition on previous line, I see:

#                                _-----=> irqs-off/BH-disabled
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| / _-=> migrate-disable
#                              |||| /     delay
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#              | |         |   |||||     |         |
           <idle>-0       [000] d..1.   364.875516: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
           <idle>-0       [000] d..1.   364.879504: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
           <idle>-0       [000] d..1.   365.299507: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
           <idle>-0       [000] d..1.   365.963524: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
           <idle>-0       [000] d..1.   367.291500: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
           <idle>-0       [000] d..1.   370.339504: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
           <idle>-0       [000] d..1.   371.875481: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
           <idle>-0       [000] d..1.   374.875462: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
	  ...

(Note, this is only for SCHED_SOFTIRQ being raised from nohz_csd_func())

Since for !CONFIG_PREEMPT_RT "should_wake_ksoftirqd()" always returns
true, we end up waking softirqd for idle load balancing. Note that
"hardirq/softirq" column is always a "." since nohz_csd_func() is
executed from "flush_smp_call_function_queue()" on the way out of
do_idle().

With C2 enabled, which is an I/O Port based C-state on AMD and does
not set TIF_POLLING_NRFLAG, I see:

#                                _-----=> irqs-off/BH-disabled
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| / _-=> migrate-disable
#                              |||| /     delay
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#              | |         |   |||||     |         |
           <idle>-0       [000] d.h1.  2880.497140: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
           <idle>-0       [000] d.H2.  2882.193270: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65794 65792
           <idle>-0       [000] d.h1.  2884.857103: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
           <idle>-0       [000] d.h1.  2886.769577: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
           <idle>-0       [000] d.h1.  2886.989832: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
           <idle>-0       [000] d.h1.  2887.281561: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
           <idle>-0       [000] d.h1.  2887.825556: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
           <idle>-0       [000] d.h1.  2888.817564: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536

Raising of softirq here happens in "hardirq" context which, I believe,
will lead to SCHED_SOFTIRQ being serviced on the way out. When enabling
soft_irq_enter and soft_irq_exit tracepoints, I see:

           <idle>-0       [000] d.h1.  3309.994942: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536 65536
           <idle>-0       [000] ..s1.  3309.994943: softirq_entry: vec=7 [action=SCHED]
           <idle>-0       [000] ..s1.  3309.995026: softirq_exit: vec=7 [action=SCHED]

With the former, I do see nr_running > 1 whenever "ksoftirqd" is running
"sched_balance_domains":

      ksoftirqd/0-16      [000] ..s.. 10153.805434: sched_balance_domains: nr_running: 1
    ksoftirqd/168-1038    [168] ..s.. 10163.765221: sched_balance_domains: nr_running: 1
      ksoftirqd/0-16      [000] ..s.. 10166.761349: sched_balance_domains: nr_running: 1
    ksoftirqd/120-747     [120] ..s.. 10166.809204: sched_balance_domains: nr_running: 2
    ksoftirqd/132-820     [132] ..s.. 10166.813203: sched_balance_domains: nr_running: 1
    ksoftirqd/246-1511    [246] ..s.. 10166.845532: sched_balance_domains: nr_running: 1
    ksoftirqd/107-668     [107] ..s.. 10166.853201: sched_balance_domains: nr_running: 2
    ksoftirqd/120-747     [120] ..s.. 10166.865359: sched_balance_domains: nr_running: 1
      ksoftirqd/0-16      [000] ..s.. 10191.273328: sched_balance_domains: nr_running: 1
      ksoftirqd/0-16      [000] ..s.. 10193.137307: sched_balance_domains: nr_running: 1
      ksoftirqd/0-16      [000] ..s.. 10235.057105: sched_balance_domains: nr_running: 1
      ksoftirqd/0-16      [000] ..s.. 10320.172832: sched_balance_domains: nr_running: 1
      ksoftirqd/0-16      [000] ..s.. 10323.708863: sched_balance_domains: nr_running: 1
      ksoftirqd/0-16      [000] ..s.. 10338.912787: sched_balance_domains: nr_running: 1

-- 
Thanks and Regards,
Prateek

> 		wakeup_softirqd();
> }
> 
> My impression was that wakeup_softirqd was getting called.
> 
> But it is true that if the code is being executed by idle, then
> in_interrupt() should be true.  So perhaps it is someone else who is
> waking up ksoftirqd.  When I switched to __raise_softirq_irqoff, the
> behavior seemed to change, but I may not have fully understood why that
> happened.
> 
>>
>>> (sched_switch event), then the various actions of sched_balance_softirq to
>>> be executed, and the ksoftirqd to be unscheduled (another ksoftirqd)
>>> event.
>>>
>>> But in practice, I see the code of sched_balance_softirq being executed
>>> by the idle task, before the ksoftirqd is scheduled (see core 40):
>>
>> What wakes up ksoftirqd ? and which softirq finally runs in ksoftirqd ?
>>
>>>
>>>            <idle>-0     [040]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
>>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting nohz
>>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting _nohz_idle_balance
>>>            bt.B.x-12022 [047]  3611.432554: softirq_entry:        vec=1 [action=TIMER]
>>>            <idle>-0     [040]  3611.432554: bputs:                _nohz_idle_balance.isra.0: searching for a cpu
>>>            bt.B.x-12033 [003]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
>>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: ending _nohz_idle_balance
>>>            bt.B.x-12052 [011]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
>>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: nohz returns true ending soft irq
>>>            <idle>-0     [040]  3611.432554: softirq_exit:         vec=7 [action=SCHED]
>>>
>>> For example, idle seems to be running the code in _nohz_idle_balance.
>>>
>>> I updated the code of _nohz_idle_balance as follows:
>>>
>>> trace_printk("searching for a cpu\n");
>>>          for_each_cpu_wrap(balance_cpu,  nohz.idle_cpus_mask, this_cpu+1) {
>>>                  if (!idle_cpu(balance_cpu))
>>>                          continue;
>>> trace_printk("found an idle cpu\n");
>>>
>>> It prints searching for a cpu, but not found an idle cpu, because the
>>> ksoftirqd on the core's runqueue makes the core not idle.  This makes the
>>> whole softirq seem fairly useless when the only idle core is the one
>>> raising the soft irq.
>>
>> The typical behavior is:
>>
>> CPUA                                   CPUB
>>                                         do_idle
>>                                           while (!need_resched()) {
>>                                           ...
>>
>> kick_ilb
>>    smp_call_function_single_async(CPUB)
>>      send_call_function_single_ipi
>>        raise_ipi  --------------------->    cpuidle exit event
>>                                             irq_handler_entry
>>                                               ipi_handler
>>                                                 raise sched_softirq
>>                                             irq_handler_exit
>>                                             sorftirq_entry
>>                                               sched_balance_softirq
>>                                                 __nohe_idle_balance
>>                                             softirq_exit
>>                                             cpuidle_enter event
>>
>> softirq is done in the interrupt context after the irq handler and
>> CPUB never leaves the while (!need_resched())  loop
>>
>> In your case, I suspect that you have a racing with the polling mode
>> and the fact that you leave the while (!need_resched()) loop and call
>> flush_smp_call_function_queue()
>>
>> We don't use polling on arm64 so I can't even try to reproduce your case
> 
> This is with Prateek's patch.  So need_resched is not true any more.
> 
> thanks,
> julia
> 
>>>
>>> This is all for the same scenario that I have discussed previously, where
>>> there are two sockets and an overload of on thread on one and an underload
>>> of on thread on the other, and all the thread have been marked by numa
>>> balancing as preferring to be where they are.  Now I am trying Prateek's
>>> patch series.
>>>
>>> thanks,
>>> julia
>>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: softirq
  2024-06-27 14:13     ` softirq K Prateek Nayak
@ 2024-06-27 15:25       ` Vincent Guittot
  2024-06-27 22:30         ` softirq Julia Lawall
  0 siblings, 1 reply; 6+ messages in thread
From: Vincent Guittot @ 2024-06-27 15:25 UTC (permalink / raw)
  To: K Prateek Nayak
  Cc: Julia Lawall, Peter Zijlstra, Ingo Molnar, Dietmar Eggemann,
	Mel Gorman, linux-kernel

On Thu, 27 Jun 2024 at 16:13, K Prateek Nayak <kprateek.nayak@amd.com> wrote:
>
> Hello Julia,
>
> Some data from my 3rd Generation EPYC machine.
>
> On 6/27/2024 2:37 AM, Julia Lawall wrote:
> >
> >
> > On Wed, 26 Jun 2024, Vincent Guittot wrote:
> >
> >> On Wed, 26 Jun 2024 at 07:37, Julia Lawall <julia.lawall@inria.fr> wrote:
> >>>
> >>> Hello,
> >>>
> >>> I'm not sure to understand how soft irqs work.  I see the code:
> >>>
> >>> open_softirq(SCHED_SOFTIRQ, sched_balance_softirq);
> >>>
> >>> Intuitively, I would expect that sched_balance_softirq would be run by
> >>> ksoftirqd.  That is, I would expect ksoftirqd to be scheduled
> >>
> >> By default, sched_softirq and others run in interrupt context.
> >> ksoftirqd is woken up only in some cases like when we spent too much
> >> time processing softirq in interrupt context or the softirq is raised
> >> outside interrupt context
> >
> > nohz_csd_func calls raise_softirq_irqoff, which does:
> >
> > inline void raise_softirq_irqoff(unsigned int nr)
> > {
> >          __raise_softirq_irqoff(nr);
> >
> >          /*
> >           * If we're in an interrupt or softirq, we're done
> >           * (this also catches softirq-disabled code). We will
> >           * actually run the softirq once we return from
> >           * the irq or softirq.
> >           *
> >           * Otherwise we wake up ksoftirqd to make sure we
> >           * schedule the softirq soon.
> >           */
> >          if (!in_interrupt() && should_wake_ksoftirqd())
>
> I think it is the !in_interrupt() check that fails. When I disable C2
> (which is I/O Port based C-state on AMD) and only leave C0 (Poll loop)
> and C1 (MWAIT based C-state), both of which set TIF_POLLING_NRFLAG while
> idling, and I add the following log line:
>
>         trace_printk("raise_softirq_irqoff %d %lu %lu\n",
>                      preempt_count(),
>                      in_interrupt());
>
> just above the "if" condition on previous line, I see:
>
> #                                _-----=> irqs-off/BH-disabled
> #                               / _----=> need-resched
> #                              | / _---=> hardirq/softirq
> #                              || / _--=> preempt-depth
> #                              ||| / _-=> migrate-disable
> #                              |||| /     delay
> #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
> #              | |         |   |||||     |         |
>            <idle>-0       [000] d..1.   364.875516: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>            <idle>-0       [000] d..1.   364.879504: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>            <idle>-0       [000] d..1.   365.299507: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>            <idle>-0       [000] d..1.   365.963524: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>            <idle>-0       [000] d..1.   367.291500: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>            <idle>-0       [000] d..1.   370.339504: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>            <idle>-0       [000] d..1.   371.875481: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>            <idle>-0       [000] d..1.   374.875462: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
>           ...
>
> (Note, this is only for SCHED_SOFTIRQ being raised from nohz_csd_func())
>
> Since for !CONFIG_PREEMPT_RT "should_wake_ksoftirqd()" always returns
> true, we end up waking softirqd for idle load balancing. Note that
> "hardirq/softirq" column is always a "." since nohz_csd_func() is
> executed from "flush_smp_call_function_queue()" on the way out of
> do_idle().
>
> With C2 enabled, which is an I/O Port based C-state on AMD and does
> not set TIF_POLLING_NRFLAG, I see:
>
> #                                _-----=> irqs-off/BH-disabled
> #                               / _----=> need-resched
> #                              | / _---=> hardirq/softirq
> #                              || / _--=> preempt-depth
> #                              ||| / _-=> migrate-disable
> #                              |||| /     delay
> #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
> #              | |         |   |||||     |         |
>            <idle>-0       [000] d.h1.  2880.497140: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
>            <idle>-0       [000] d.H2.  2882.193270: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65794 65792
>            <idle>-0       [000] d.h1.  2884.857103: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
>            <idle>-0       [000] d.h1.  2886.769577: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
>            <idle>-0       [000] d.h1.  2886.989832: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
>            <idle>-0       [000] d.h1.  2887.281561: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
>            <idle>-0       [000] d.h1.  2887.825556: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
>            <idle>-0       [000] d.h1.  2888.817564: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
>
> Raising of softirq here happens in "hardirq" context which, I believe,
> will lead to SCHED_SOFTIRQ being serviced on the way out. When enabling
> soft_irq_enter and soft_irq_exit tracepoints, I see:
>
>            <idle>-0       [000] d.h1.  3309.994942: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536 65536
>            <idle>-0       [000] ..s1.  3309.994943: softirq_entry: vec=7 [action=SCHED]
>            <idle>-0       [000] ..s1.  3309.995026: softirq_exit: vec=7 [action=SCHED]
>
> With the former, I do see nr_running > 1 whenever "ksoftirqd" is running
> "sched_balance_domains":
>
>       ksoftirqd/0-16      [000] ..s.. 10153.805434: sched_balance_domains: nr_running: 1
>     ksoftirqd/168-1038    [168] ..s.. 10163.765221: sched_balance_domains: nr_running: 1
>       ksoftirqd/0-16      [000] ..s.. 10166.761349: sched_balance_domains: nr_running: 1
>     ksoftirqd/120-747     [120] ..s.. 10166.809204: sched_balance_domains: nr_running: 2
>     ksoftirqd/132-820     [132] ..s.. 10166.813203: sched_balance_domains: nr_running: 1
>     ksoftirqd/246-1511    [246] ..s.. 10166.845532: sched_balance_domains: nr_running: 1
>     ksoftirqd/107-668     [107] ..s.. 10166.853201: sched_balance_domains: nr_running: 2
>     ksoftirqd/120-747     [120] ..s.. 10166.865359: sched_balance_domains: nr_running: 1
>       ksoftirqd/0-16      [000] ..s.. 10191.273328: sched_balance_domains: nr_running: 1
>       ksoftirqd/0-16      [000] ..s.. 10193.137307: sched_balance_domains: nr_running: 1
>       ksoftirqd/0-16      [000] ..s.. 10235.057105: sched_balance_domains: nr_running: 1
>       ksoftirqd/0-16      [000] ..s.. 10320.172832: sched_balance_domains: nr_running: 1
>       ksoftirqd/0-16      [000] ..s.. 10323.708863: sched_balance_domains: nr_running: 1
>       ksoftirqd/0-16      [000] ..s.. 10338.912787: sched_balance_domains: nr_running: 1
>

This is probably linked to flush_smp_call_function_queue()

flush_smp_call_function_queue();
  local_irq_save(flags);
  __flush_smp_call_function_queue(true);
    nohz_csd_func
      wakeup ksoftirqd because not in interrupt context
  if (local_softirq_pending())
    do_softirq_post_smp_call_flush(was_pending);
      do_softirq()
        sched_balance_softirq
  local_irq_restore(flags);



> --
> Thanks and Regards,
> Prateek
>
> >               wakeup_softirqd();
> > }
> >
> > My impression was that wakeup_softirqd was getting called.
> >
> > But it is true that if the code is being executed by idle, then
> > in_interrupt() should be true.  So perhaps it is someone else who is
> > waking up ksoftirqd.  When I switched to __raise_softirq_irqoff, the
> > behavior seemed to change, but I may not have fully understood why that
> > happened.
> >
> >>
> >>> (sched_switch event), then the various actions of sched_balance_softirq to
> >>> be executed, and the ksoftirqd to be unscheduled (another ksoftirqd)
> >>> event.
> >>>
> >>> But in practice, I see the code of sched_balance_softirq being executed
> >>> by the idle task, before the ksoftirqd is scheduled (see core 40):
> >>
> >> What wakes up ksoftirqd ? and which softirq finally runs in ksoftirqd ?
> >>
> >>>
> >>>            <idle>-0     [040]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting nohz
> >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting _nohz_idle_balance
> >>>            bt.B.x-12022 [047]  3611.432554: softirq_entry:        vec=1 [action=TIMER]
> >>>            <idle>-0     [040]  3611.432554: bputs:                _nohz_idle_balance.isra.0: searching for a cpu
> >>>            bt.B.x-12033 [003]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: ending _nohz_idle_balance
> >>>            bt.B.x-12052 [011]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: nohz returns true ending soft irq
> >>>            <idle>-0     [040]  3611.432554: softirq_exit:         vec=7 [action=SCHED]
> >>>
> >>> For example, idle seems to be running the code in _nohz_idle_balance.
> >>>
> >>> I updated the code of _nohz_idle_balance as follows:
> >>>
> >>> trace_printk("searching for a cpu\n");
> >>>          for_each_cpu_wrap(balance_cpu,  nohz.idle_cpus_mask, this_cpu+1) {
> >>>                  if (!idle_cpu(balance_cpu))
> >>>                          continue;
> >>> trace_printk("found an idle cpu\n");
> >>>
> >>> It prints searching for a cpu, but not found an idle cpu, because the
> >>> ksoftirqd on the core's runqueue makes the core not idle.  This makes the
> >>> whole softirq seem fairly useless when the only idle core is the one
> >>> raising the soft irq.
> >>
> >> The typical behavior is:
> >>
> >> CPUA                                   CPUB
> >>                                         do_idle
> >>                                           while (!need_resched()) {
> >>                                           ...
> >>
> >> kick_ilb
> >>    smp_call_function_single_async(CPUB)
> >>      send_call_function_single_ipi
> >>        raise_ipi  --------------------->    cpuidle exit event
> >>                                             irq_handler_entry
> >>                                               ipi_handler
> >>                                                 raise sched_softirq
> >>                                             irq_handler_exit
> >>                                             sorftirq_entry
> >>                                               sched_balance_softirq
> >>                                                 __nohe_idle_balance
> >>                                             softirq_exit
> >>                                             cpuidle_enter event
> >>
> >> softirq is done in the interrupt context after the irq handler and
> >> CPUB never leaves the while (!need_resched())  loop
> >>
> >> In your case, I suspect that you have a racing with the polling mode
> >> and the fact that you leave the while (!need_resched()) loop and call
> >> flush_smp_call_function_queue()
> >>
> >> We don't use polling on arm64 so I can't even try to reproduce your case
> >
> > This is with Prateek's patch.  So need_resched is not true any more.
> >
> > thanks,
> > julia
> >
> >>>
> >>> This is all for the same scenario that I have discussed previously, where
> >>> there are two sockets and an overload of on thread on one and an underload
> >>> of on thread on the other, and all the thread have been marked by numa
> >>> balancing as preferring to be where they are.  Now I am trying Prateek's
> >>> patch series.
> >>>
> >>> thanks,
> >>> julia
> >>
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: softirq
  2024-06-27 15:25       ` softirq Vincent Guittot
@ 2024-06-27 22:30         ` Julia Lawall
  0 siblings, 0 replies; 6+ messages in thread
From: Julia Lawall @ 2024-06-27 22:30 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: K Prateek Nayak, Peter Zijlstra, Ingo Molnar, Dietmar Eggemann,
	Mel Gorman, linux-kernel



On Thu, 27 Jun 2024, Vincent Guittot wrote:

> On Thu, 27 Jun 2024 at 16:13, K Prateek Nayak <kprateek.nayak@amd.com> wrote:
> >
> > Hello Julia,
> >
> > Some data from my 3rd Generation EPYC machine.
> >
> > On 6/27/2024 2:37 AM, Julia Lawall wrote:
> > >
> > >
> > > On Wed, 26 Jun 2024, Vincent Guittot wrote:
> > >
> > >> On Wed, 26 Jun 2024 at 07:37, Julia Lawall <julia.lawall@inria.fr> wrote:
> > >>>
> > >>> Hello,
> > >>>
> > >>> I'm not sure to understand how soft irqs work.  I see the code:
> > >>>
> > >>> open_softirq(SCHED_SOFTIRQ, sched_balance_softirq);
> > >>>
> > >>> Intuitively, I would expect that sched_balance_softirq would be run by
> > >>> ksoftirqd.  That is, I would expect ksoftirqd to be scheduled
> > >>
> > >> By default, sched_softirq and others run in interrupt context.
> > >> ksoftirqd is woken up only in some cases like when we spent too much
> > >> time processing softirq in interrupt context or the softirq is raised
> > >> outside interrupt context
> > >
> > > nohz_csd_func calls raise_softirq_irqoff, which does:
> > >
> > > inline void raise_softirq_irqoff(unsigned int nr)
> > > {
> > >          __raise_softirq_irqoff(nr);
> > >
> > >          /*
> > >           * If we're in an interrupt or softirq, we're done
> > >           * (this also catches softirq-disabled code). We will
> > >           * actually run the softirq once we return from
> > >           * the irq or softirq.
> > >           *
> > >           * Otherwise we wake up ksoftirqd to make sure we
> > >           * schedule the softirq soon.
> > >           */
> > >          if (!in_interrupt() && should_wake_ksoftirqd())
> >
> > I think it is the !in_interrupt() check that fails. When I disable C2
> > (which is I/O Port based C-state on AMD) and only leave C0 (Poll loop)
> > and C1 (MWAIT based C-state), both of which set TIF_POLLING_NRFLAG while
> > idling, and I add the following log line:
> >
> >         trace_printk("raise_softirq_irqoff %d %lu %lu\n",
> >                      preempt_count(),
> >                      in_interrupt());
> >
> > just above the "if" condition on previous line, I see:
> >
> > #                                _-----=> irqs-off/BH-disabled
> > #                               / _----=> need-resched
> > #                              | / _---=> hardirq/softirq
> > #                              || / _--=> preempt-depth
> > #                              ||| / _-=> migrate-disable
> > #                              |||| /     delay
> > #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
> > #              | |         |   |||||     |         |
> >            <idle>-0       [000] d..1.   364.875516: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >            <idle>-0       [000] d..1.   364.879504: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >            <idle>-0       [000] d..1.   365.299507: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >            <idle>-0       [000] d..1.   365.963524: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >            <idle>-0       [000] d..1.   367.291500: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >            <idle>-0       [000] d..1.   370.339504: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >            <idle>-0       [000] d..1.   371.875481: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >            <idle>-0       [000] d..1.   374.875462: raise_softirq_irqoff_nohz: raise_softirq_irqoff 1 0
> >           ...
> >
> > (Note, this is only for SCHED_SOFTIRQ being raised from nohz_csd_func())
> >
> > Since for !CONFIG_PREEMPT_RT "should_wake_ksoftirqd()" always returns
> > true, we end up waking softirqd for idle load balancing. Note that
> > "hardirq/softirq" column is always a "." since nohz_csd_func() is
> > executed from "flush_smp_call_function_queue()" on the way out of
> > do_idle().
> >
> > With C2 enabled, which is an I/O Port based C-state on AMD and does
> > not set TIF_POLLING_NRFLAG, I see:
> >
> > #                                _-----=> irqs-off/BH-disabled
> > #                               / _----=> need-resched
> > #                              | / _---=> hardirq/softirq
> > #                              || / _--=> preempt-depth
> > #                              ||| / _-=> migrate-disable
> > #                              |||| /     delay
> > #           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
> > #              | |         |   |||||     |         |
> >            <idle>-0       [000] d.h1.  2880.497140: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
> >            <idle>-0       [000] d.H2.  2882.193270: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65794 65792
> >            <idle>-0       [000] d.h1.  2884.857103: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
> >            <idle>-0       [000] d.h1.  2886.769577: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
> >            <idle>-0       [000] d.h1.  2886.989832: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
> >            <idle>-0       [000] d.h1.  2887.281561: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
> >            <idle>-0       [000] d.h1.  2887.825556: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
> >            <idle>-0       [000] d.h1.  2888.817564: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536
> >
> > Raising of softirq here happens in "hardirq" context which, I believe,
> > will lead to SCHED_SOFTIRQ being serviced on the way out. When enabling
> > soft_irq_enter and soft_irq_exit tracepoints, I see:
> >
> >            <idle>-0       [000] d.h1.  3309.994942: raise_softirq_irqoff_nohz: raise_softirq_irqoff 65537 65536 65536
> >            <idle>-0       [000] ..s1.  3309.994943: softirq_entry: vec=7 [action=SCHED]
> >            <idle>-0       [000] ..s1.  3309.995026: softirq_exit: vec=7 [action=SCHED]
> >
> > With the former, I do see nr_running > 1 whenever "ksoftirqd" is running
> > "sched_balance_domains":
> >
> >       ksoftirqd/0-16      [000] ..s.. 10153.805434: sched_balance_domains: nr_running: 1
> >     ksoftirqd/168-1038    [168] ..s.. 10163.765221: sched_balance_domains: nr_running: 1
> >       ksoftirqd/0-16      [000] ..s.. 10166.761349: sched_balance_domains: nr_running: 1
> >     ksoftirqd/120-747     [120] ..s.. 10166.809204: sched_balance_domains: nr_running: 2
> >     ksoftirqd/132-820     [132] ..s.. 10166.813203: sched_balance_domains: nr_running: 1
> >     ksoftirqd/246-1511    [246] ..s.. 10166.845532: sched_balance_domains: nr_running: 1
> >     ksoftirqd/107-668     [107] ..s.. 10166.853201: sched_balance_domains: nr_running: 2
> >     ksoftirqd/120-747     [120] ..s.. 10166.865359: sched_balance_domains: nr_running: 1
> >       ksoftirqd/0-16      [000] ..s.. 10191.273328: sched_balance_domains: nr_running: 1
> >       ksoftirqd/0-16      [000] ..s.. 10193.137307: sched_balance_domains: nr_running: 1
> >       ksoftirqd/0-16      [000] ..s.. 10235.057105: sched_balance_domains: nr_running: 1
> >       ksoftirqd/0-16      [000] ..s.. 10320.172832: sched_balance_domains: nr_running: 1
> >       ksoftirqd/0-16      [000] ..s.. 10323.708863: sched_balance_domains: nr_running: 1
> >       ksoftirqd/0-16      [000] ..s.. 10338.912787: sched_balance_domains: nr_running: 1
> >
>
> This is probably linked to flush_smp_call_function_queue()
>
> flush_smp_call_function_queue();
>   local_irq_save(flags);
>   __flush_smp_call_function_queue(true);
>     nohz_csd_func
>       wakeup ksoftirqd because not in interrupt context
>   if (local_softirq_pending())
>     do_softirq_post_smp_call_flush(was_pending);
>       do_softirq()
>         sched_balance_softirq
>   local_irq_restore(flags);

Thanks for the experiment Prateek.

I agree that the issue is that when polling, we are not in interrupt
context, and thus there is a wakeup ksoftirqd.  The problem is that when
there is only one idle core, waking up the ksoftirqd is counterproductive,
because it prevents load balancing, because that core is then seen as not
being idle.

julia

>
>
>
> > --
> > Thanks and Regards,
> > Prateek
> >
> > >               wakeup_softirqd();
> > > }
> > >
> > > My impression was that wakeup_softirqd was getting called.
> > >
> > > But it is true that if the code is being executed by idle, then
> > > in_interrupt() should be true.  So perhaps it is someone else who is
> > > waking up ksoftirqd.  When I switched to __raise_softirq_irqoff, the
> > > behavior seemed to change, but I may not have fully understood why that
> > > happened.
> > >
> > >>
> > >>> (sched_switch event), then the various actions of sched_balance_softirq to
> > >>> be executed, and the ksoftirqd to be unscheduled (another ksoftirqd)
> > >>> event.
> > >>>
> > >>> But in practice, I see the code of sched_balance_softirq being executed
> > >>> by the idle task, before the ksoftirqd is scheduled (see core 40):
> > >>
> > >> What wakes up ksoftirqd ? and which softirq finally runs in ksoftirqd ?
> > >>
> > >>>
> > >>>            <idle>-0     [040]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> > >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting nohz
> > >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: starting _nohz_idle_balance
> > >>>            bt.B.x-12022 [047]  3611.432554: softirq_entry:        vec=1 [action=TIMER]
> > >>>            <idle>-0     [040]  3611.432554: bputs:                _nohz_idle_balance.isra.0: searching for a cpu
> > >>>            bt.B.x-12033 [003]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> > >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: ending _nohz_idle_balance
> > >>>            bt.B.x-12052 [011]  3611.432554: softirq_entry:        vec=7 [action=SCHED]
> > >>>            <idle>-0     [040]  3611.432554: bputs:                sched_balance_softirq: nohz returns true ending soft irq
> > >>>            <idle>-0     [040]  3611.432554: softirq_exit:         vec=7 [action=SCHED]
> > >>>
> > >>> For example, idle seems to be running the code in _nohz_idle_balance.
> > >>>
> > >>> I updated the code of _nohz_idle_balance as follows:
> > >>>
> > >>> trace_printk("searching for a cpu\n");
> > >>>          for_each_cpu_wrap(balance_cpu,  nohz.idle_cpus_mask, this_cpu+1) {
> > >>>                  if (!idle_cpu(balance_cpu))
> > >>>                          continue;
> > >>> trace_printk("found an idle cpu\n");
> > >>>
> > >>> It prints searching for a cpu, but not found an idle cpu, because the
> > >>> ksoftirqd on the core's runqueue makes the core not idle.  This makes the
> > >>> whole softirq seem fairly useless when the only idle core is the one
> > >>> raising the soft irq.
> > >>
> > >> The typical behavior is:
> > >>
> > >> CPUA                                   CPUB
> > >>                                         do_idle
> > >>                                           while (!need_resched()) {
> > >>                                           ...
> > >>
> > >> kick_ilb
> > >>    smp_call_function_single_async(CPUB)
> > >>      send_call_function_single_ipi
> > >>        raise_ipi  --------------------->    cpuidle exit event
> > >>                                             irq_handler_entry
> > >>                                               ipi_handler
> > >>                                                 raise sched_softirq
> > >>                                             irq_handler_exit
> > >>                                             sorftirq_entry
> > >>                                               sched_balance_softirq
> > >>                                                 __nohe_idle_balance
> > >>                                             softirq_exit
> > >>                                             cpuidle_enter event
> > >>
> > >> softirq is done in the interrupt context after the irq handler and
> > >> CPUB never leaves the while (!need_resched())  loop
> > >>
> > >> In your case, I suspect that you have a racing with the polling mode
> > >> and the fact that you leave the while (!need_resched()) loop and call
> > >> flush_smp_call_function_queue()
> > >>
> > >> We don't use polling on arm64 so I can't even try to reproduce your case
> > >
> > > This is with Prateek's patch.  So need_resched is not true any more.
> > >
> > > thanks,
> > > julia
> > >
> > >>>
> > >>> This is all for the same scenario that I have discussed previously, where
> > >>> there are two sockets and an overload of on thread on one and an underload
> > >>> of on thread on the other, and all the thread have been marked by numa
> > >>> balancing as preferring to be where they are.  Now I am trying Prateek's
> > >>> patch series.
> > >>>
> > >>> thanks,
> > >>> julia
> > >>
> >
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-06-27 22:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-26  5:37 softirq Julia Lawall
2024-06-26 15:17 ` softirq Vincent Guittot
2024-06-26 21:07   ` softirq Julia Lawall
2024-06-27 14:13     ` softirq K Prateek Nayak
2024-06-27 15:25       ` softirq Vincent Guittot
2024-06-27 22:30         ` softirq Julia Lawall

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®