[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH 5/5] xen: RCU: avoid busy waiting until the end of grace period.



On Thu, 27 Jul 2017, Dario Faggioli wrote:
> Instead of having the CPU where a callback is queued, busy
> looping on rcu_pending(), use a timer.
> 
> In fact, we let the CPU go idla,e but we program a timer
                              ^ idle,


> that will periodically wake it up, for checking whether the
> grace period has actually ended.
> 
> It is kind of similar to introducing a periodic tick, but
> with a much more limited scope, and a lot less overhead. In
> fact, this timer is:
> - only active for the CPU(s) that have callbacks queued,
>   waiting for the end of a grace period;
> - only active when those CPU(s) are idle (and stopped as
>   soon as they resume execution).
> 
> Signed-off-by: Dario Faggioli <dario.faggioli@xxxxxxxxxx>
> ---
> Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx>
> Cc: Julien Grall <julien.grall@xxxxxxx>
> Cc: Jan Beulich <jbeulich@xxxxxxxx>
> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
> ---
>  xen/arch/arm/domain.c         |    4 ++-
>  xen/arch/x86/acpi/cpu_idle.c  |    6 +++--
>  xen/arch/x86/cpu/mwait-idle.c |    6 +++--
>  xen/common/rcupdate.c         |   52 
> ++++++++++++++++++++++++++++++++++++++++-
>  xen/include/xen/rcupdate.h    |    3 ++
>  5 files changed, 65 insertions(+), 6 deletions(-)
> 
> diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
> index 666b7ef..01da96e 100644
> --- a/xen/arch/arm/domain.c
> +++ b/xen/arch/arm/domain.c
> @@ -43,8 +43,9 @@ static void do_idle(void)
>  {
>      unsigned int cpu = smp_processor_id();
>  
> +    rcu_idle_timer_start();
>      sched_tick_suspend();
> -    /* sched_tick_suspend() can raise TIMER_SOFTIRQ. Process it now. */
> +    /* Timer related operations can raise TIMER_SOFTIRQ. Process it now. */
>      process_pending_softirqs();
>  
>      local_irq_disable();
> @@ -58,6 +59,7 @@ static void do_idle(void)
>      local_irq_enable();
>  
>      sched_tick_resume();
> +    rcu_idle_timer_stop();
>  }
>  
>  void idle_loop(void)
> diff --git a/xen/arch/x86/acpi/cpu_idle.c b/xen/arch/x86/acpi/cpu_idle.c
> index 04c52e8..b97986f 100644
> --- a/xen/arch/x86/acpi/cpu_idle.c
> +++ b/xen/arch/x86/acpi/cpu_idle.c
> @@ -576,10 +576,10 @@ static void acpi_processor_idle(void)
>          return;
>      }
>  
> +    rcu_idle_timer_start();
>      cpufreq_dbs_timer_suspend();
> -
>      sched_tick_suspend();
> -    /* sched_tick_suspend() can raise TIMER_SOFTIRQ. Process it now. */
> +    /* Timer related operations can raise TIMER_SOFTIRQ. Process it now. */
>      process_pending_softirqs();
>  
>      /*
> @@ -593,6 +593,7 @@ static void acpi_processor_idle(void)
>          local_irq_enable();
>          sched_tick_resume();
>          cpufreq_dbs_timer_resume();
> +        rcu_idle_timer_stop();
>          return;
>      }
>  
> @@ -726,6 +727,7 @@ static void acpi_processor_idle(void)
>  
>      sched_tick_resume();
>      cpufreq_dbs_timer_resume();
> +    rcu_idle_timer_stop();
>  
>      if ( cpuidle_current_governor->reflect )
>          cpuidle_current_governor->reflect(power);
> diff --git a/xen/arch/x86/cpu/mwait-idle.c b/xen/arch/x86/cpu/mwait-idle.c
> index ae9e92b..c426e41 100644
> --- a/xen/arch/x86/cpu/mwait-idle.c
> +++ b/xen/arch/x86/cpu/mwait-idle.c
> @@ -743,10 +743,10 @@ static void mwait_idle(void)
>               return;
>       }
>  
> +     rcu_idle_timer_start();
>       cpufreq_dbs_timer_suspend();
> -
>       sched_tick_suspend();
> -     /* sched_tick_suspend() can raise TIMER_SOFTIRQ. Process it now. */
> +     /* Timer related operations can raise TIMER_SOFTIRQ. Process it now. */
>       process_pending_softirqs();
>  
>       /* Interrupts must be disabled for C2 and higher transitions. */
> @@ -756,6 +756,7 @@ static void mwait_idle(void)
>               local_irq_enable();
>               sched_tick_resume();
>               cpufreq_dbs_timer_resume();
> +                rcu_idle_timer_stop();
>               return;
>       }
>  
> @@ -802,6 +803,7 @@ static void mwait_idle(void)
>  
>       sched_tick_resume();
>       cpufreq_dbs_timer_resume();
> +     rcu_idle_timer_stop();
>  
>       if ( cpuidle_current_governor->reflect )
>               cpuidle_current_governor->reflect(power);
> diff --git a/xen/common/rcupdate.c b/xen/common/rcupdate.c
> index f0fdc87..4586f2a 100644
> --- a/xen/common/rcupdate.c
> +++ b/xen/common/rcupdate.c
> @@ -84,8 +84,14 @@ struct rcu_data {
>      int cpu;
>      struct rcu_head barrier;
>      long            last_rs_qlen;     /* qlen during the last resched */
> +
> +    /* 3) idle CPUs handling */
> +    struct timer idle_timer;
> +    bool idle_timer_active;
>  };
>  
> +#define RCU_IDLE_TIMER_PERIOD MILLISECS(10)

Isn't this a bit too short? How is it chosen?


>  static DEFINE_PER_CPU(struct rcu_data, rcu_data);
>  
>  static int blimit = 10;
> @@ -402,7 +408,48 @@ int rcu_needs_cpu(int cpu)
>  {
>      struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
>  
> -    return (!!rdp->curlist || rcu_pending(cpu));
> +    return (!!rdp->curlist || rcu_pending(cpu)) && !rdp->idle_timer_active;
> +}
> +
> +/*
> + * Timer for making sure the CPU where a callback is queued does
> + * periodically poke rcu_pedning(), so that it will invoke the callback
> + * not too late after the end of the grace period.
> + */
> +void rcu_idle_timer_start()
> +{
> +    struct rcu_data *rdp = &this_cpu(rcu_data);
> +
> +    if (likely(!rdp->curlist))
> +        return;
> +
> +    set_timer(&rdp->idle_timer, NOW() + RCU_IDLE_TIMER_PERIOD);
> +    rdp->idle_timer_active = true;
> +}
> +
> +void rcu_idle_timer_stop()
> +{
> +    struct rcu_data *rdp = &this_cpu(rcu_data);
> +
> +    if (likely(!rdp->idle_timer_active))
> +        return;
> +
> +    rdp->idle_timer_active = false;
> +    stop_timer(&rdp->idle_timer);
> +}
> +
> +static void rcu_idle_timer_handler(void* data)
> +{
> +    /*
> +     * Nothing, really... And in fact, we don't expect to ever get in here,
> +     * as rcu_idle_timer_stop(), called while waking from idle, prevent that
> +     * to happen by stopping the timer before the TIMER_SOFTIRQ handler has
> +     * a chance to run.
> +     *
> +     * But that's fine, because all we want is the CPU that needs to execute
> +     * the callback to be periodically woken up and check rcu_pending().
> +     */
> +    ASSERT_UNREACHABLE();
>  }
>  
>  void rcu_check_callbacks(int cpu)
> @@ -423,6 +470,8 @@ static void rcu_move_batch(struct rcu_data *this_rdp, 
> struct rcu_head *list,
>  static void rcu_offline_cpu(struct rcu_data *this_rdp,
>                              struct rcu_ctrlblk *rcp, struct rcu_data *rdp)
>  {
> +    kill_timer(&rdp->idle_timer);
> +
>      /* If the cpu going offline owns the grace period we can block
>       * indefinitely waiting for it, so flush it here.
>       */
> @@ -451,6 +500,7 @@ static void rcu_init_percpu_data(int cpu, struct 
> rcu_ctrlblk *rcp,
>      rdp->qs_pending = 0;
>      rdp->cpu = cpu;
>      rdp->blimit = blimit;
> +    init_timer(&rdp->idle_timer, rcu_idle_timer_handler, (void*) rdp, cpu);
>  }
>  
>  static int cpu_callback(
> diff --git a/xen/include/xen/rcupdate.h b/xen/include/xen/rcupdate.h
> index 561ac43..3402eb5 100644
> --- a/xen/include/xen/rcupdate.h
> +++ b/xen/include/xen/rcupdate.h
> @@ -149,4 +149,7 @@ int rcu_barrier(void);
>  void rcu_idle_enter(unsigned int cpu);
>  void rcu_idle_exit(unsigned int cpu);
>  
> +void rcu_idle_timer_start(void);
> +void rcu_idle_timer_stop(void);
> +
>  #endif /* __XEN_RCUPDATE_H */
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.