[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v2] xen: idle_loop: either deal with tasklets or go idle
On Tue, 20 Jun 2017, Dario Faggioli wrote: > In fact, there are two kinds of tasklets: vCPU and > softirq context. When we want to do vCPU context tasklet > work, we force the idle vCPU (of a particular pCPU) into > execution, and run it from there. > > This means there are two possible reasons for choosing > to run the idle vCPU: > 1) we want a pCPU to go idle, > 2) we want to run some vCPU context tasklet work. > > If we're in case 2), it does not make sense to even > try to go idle (as the check will _always_ fail). > > This patch rearranges the code of the body of idle > vCPUs, so that we actually check whether we are in > case 1) or 2), and act accordingly. > > As a matter of fact, this also means that we do not > check if there's any tasklet work to do after waking > up from idle. This is not a problem, because: > a) for softirq context tasklets, if any is queued > "during" wakeup from idle, TASKLET_SOFTIRQ is > raised, and the call to do_softirq() (which is still > happening *after* the wakeup) will take care of it; > b) for vCPU context tasklets, if any is queued "during" > wakeup from idle, SCHEDULE_SOFTIRQ is raised and > do_softirq() (happening after the wakeup) calls > the scheduler. The scheduler sees that there is > tasklet work pending and confirms the idle vCPU > in execution, which then will get to execute > do_tasklet(). > > Signed-off-by: Dario Faggioli <dario.faggioli@xxxxxxxxxx> Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx> > --- > Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> > Cc: Julien Grall <julien.grall@xxxxxxx> > Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx> > Cc: Boris Ostrovsky <boris.ostrovsky@xxxxxxxxxx> > Cc: Jan Beulich <jbeulich@xxxxxxxx> > --- > Changes from v1: > * drop the pointless parentheses and indirection of pm_idle (in x86's idle > loop); > * remove the 'cpu' input parameter to do_tasklet(), as suggested during > review; > * in do_tasklet(), convert the check that there is tasklet work to do into an > ASSERT() to the same effect, as suggested during review; > * add a comment in cpu_is_haltable() on why we check the per-cpu > tasklet_work_to_do variable directly, rather than calling the new > tasklet_work_to_do() helper. > --- > xen/arch/arm/domain.c | 21 ++++++++++++++------- > xen/arch/x86/domain.c | 12 +++++++++--- > xen/common/tasklet.c | 12 ++++++++---- > xen/include/xen/sched.h | 5 +++++ > xen/include/xen/tasklet.h | 10 ++++++++++ > 5 files changed, 46 insertions(+), 14 deletions(-) > > diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c > index 76310ed..2dc8b0a 100644 > --- a/xen/arch/arm/domain.c > +++ b/xen/arch/arm/domain.c > @@ -41,20 +41,27 @@ DEFINE_PER_CPU(struct vcpu *, curr_vcpu); > > void idle_loop(void) > { > + unsigned int cpu = smp_processor_id(); > + > for ( ; ; ) > { > - if ( cpu_is_offline(smp_processor_id()) ) > + if ( cpu_is_offline(cpu) ) > stop_cpu(); > > - local_irq_disable(); > - if ( cpu_is_haltable(smp_processor_id()) ) > + /* Are we here for running vcpu context tasklets, or for idling? */ > + if ( unlikely(tasklet_work_to_do(cpu)) ) > + do_tasklet(); > + else > { > - dsb(sy); > - wfi(); > + local_irq_disable(); > + if ( cpu_is_haltable(cpu) ) > + { > + dsb(sy); > + wfi(); > + } > + local_irq_enable(); > } > - local_irq_enable(); > > - do_tasklet(); > do_softirq(); > /* > * We MUST be last (or before dsb, wfi). Otherwise after we get the > diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c > index 49388f4..3a061a9 100644 > --- a/xen/arch/x86/domain.c > +++ b/xen/arch/x86/domain.c > @@ -112,12 +112,18 @@ static void play_dead(void) > > static void idle_loop(void) > { > + unsigned int cpu = smp_processor_id(); > + > for ( ; ; ) > { > - if ( cpu_is_offline(smp_processor_id()) ) > + if ( cpu_is_offline(cpu) ) > play_dead(); > - (*pm_idle)(); > - do_tasklet(); > + > + /* Are we here for running vcpu context tasklets, or for idling? */ > + if ( unlikely(tasklet_work_to_do(cpu)) ) > + do_tasklet(); > + else > + pm_idle(); > do_softirq(); > /* > * We MUST be last (or before pm_idle). Otherwise after we get the > diff --git a/xen/common/tasklet.c b/xen/common/tasklet.c > index 365a777..0f0a6f8 100644 > --- a/xen/common/tasklet.c > +++ b/xen/common/tasklet.c > @@ -111,11 +111,15 @@ void do_tasklet(void) > struct list_head *list = &per_cpu(tasklet_list, cpu); > > /* > - * Work must be enqueued *and* scheduled. Otherwise there is no work to > - * do, and/or scheduler needs to run to update idle vcpu priority. > + * We want to be sure any caller has checked that a tasklet is both > + * enqueued and scheduled, before calling this. And, if the caller has > + * actually checked, it's not an issue that we are outside of the > + * critical region, in fact: > + * - TASKLET_enqueued is cleared only here, > + * - TASKLET_scheduled is only cleared when schedule() find it set, > + * without TASKLET_enqueued being set as well. > */ > - if ( likely(*work_to_do != (TASKLET_enqueued|TASKLET_scheduled)) ) > - return; > + ASSERT(tasklet_work_to_do(cpu)); > > spin_lock_irq(&tasklet_lock); > > diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h > index 1127ca9..6673b27 100644 > --- a/xen/include/xen/sched.h > +++ b/xen/include/xen/sched.h > @@ -843,6 +843,11 @@ uint64_t get_cpu_idle_time(unsigned int cpu); > /* > * Used by idle loop to decide whether there is work to do: > * (1) Run softirqs; or (2) Play dead; or (3) Run tasklets. > + * > + * About (3), if a tasklet is enqueued, it will be scheduled > + * really really soon, and hence it's pointless to try to > + * sleep between these two events (that's why we don't call > + * the tasklet_work_to_do() helper). > */ > #define cpu_is_haltable(cpu) \ > (!softirq_pending(cpu) && \ > diff --git a/xen/include/xen/tasklet.h b/xen/include/xen/tasklet.h > index 8c3de7e..23d69c7 100644 > --- a/xen/include/xen/tasklet.h > +++ b/xen/include/xen/tasklet.h > @@ -40,6 +40,16 @@ DECLARE_PER_CPU(unsigned long, tasklet_work_to_do); > #define TASKLET_enqueued (1ul << _TASKLET_enqueued) > #define TASKLET_scheduled (1ul << _TASKLET_scheduled) > > +static inline bool tasklet_work_to_do(unsigned int cpu) > +{ > + /* > + * Work must be enqueued *and* scheduled. Otherwise there is no work to > + * do, and/or scheduler needs to run to update idle vcpu priority. > + */ > + return per_cpu(tasklet_work_to_do, cpu) == (TASKLET_enqueued| > + TASKLET_scheduled); > +} > + > void tasklet_schedule_on_cpu(struct tasklet *t, unsigned int cpu); > void tasklet_schedule(struct tasklet *t); > void do_tasklet(void); > _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |