diff mbox series

[v2,27/48] xen/sched: Change vcpu_migrate_*() to operate on schedule unit

Message ID 20190809145833.1020-28-jgross@suse.com (mailing list archive)
State Superseded
Headers show
Series xen: add core scheduling support | expand

Commit Message

Jürgen Groß Aug. 9, 2019, 2:58 p.m. UTC
Now that vcpu_migrate_start() and vcpu_migrate_finish() are used only
to ensure a vcpu is running on a suitable processor they can be
switched to operate on schedule units instead of vcpus.

While doing that rename them accordingly and make the _start() variant
static. As it is needed anyway call vcpu_sync_execstate() for each
vcpu of the unit when changing processors.

vcpu_move_locked() is switched to schedule unit, too.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
 xen/common/schedule.c | 106 ++++++++++++++++++++++++++++++--------------------
 1 file changed, 63 insertions(+), 43 deletions(-)

Comments

Jan Beulich Sept. 10, 2019, 3:11 p.m. UTC | #1
On 09.08.2019 16:58, Juergen Gross wrote:
> --- a/xen/common/schedule.c
> +++ b/xen/common/schedule.c
> @@ -733,35 +733,40 @@ void vcpu_unblock(struct vcpu *v)
>  }
>  
>  /*
> - * Do the actual movement of a vcpu from old to new CPU. Locks for *both*
> + * Do the actual movement of an unit from old to new CPU. Locks for *both*
>   * CPUs needs to have been taken already when calling this!
>   */
> -static void vcpu_move_locked(struct vcpu *v, unsigned int new_cpu)
> +static void sched_unit_move_locked(struct sched_unit *unit,
> +                                   unsigned int new_cpu)
>  {
> -    unsigned int old_cpu = v->processor;
> +    unsigned int old_cpu = unit->res->processor;
> +    struct vcpu *v;
>  
>      /*
>       * Transfer urgency status to new CPU before switching CPUs, as
>       * once the switch occurs, v->is_urgent is no longer protected by
>       * the per-CPU scheduler lock we are holding.
>       */
> -    if ( unlikely(v->is_urgent) && (old_cpu != new_cpu) )
> +    for_each_sched_unit_vcpu ( unit, v )
>      {
> -        atomic_inc(&get_sched_res(new_cpu)->urgent_count);
> -        atomic_dec(&get_sched_res(old_cpu)->urgent_count);
> +        if ( unlikely(v->is_urgent) && (old_cpu != new_cpu) )
> +        {
> +            atomic_inc(&get_sched_res(new_cpu)->urgent_count);
> +            atomic_dec(&get_sched_res(old_cpu)->urgent_count);
> +        }
>      }

Shouldn't is_urgent become an attribute of unit rather than a vCPU,
too, eliminating the need for a loop here? I can't see a reason
why not, seeing this collapsing into a single urgent_count.

Then again the question remains whether the non-deep sleeping as
a result of a non-zero urgent_count should indeed be distributed
to all constituents of a unit. I can see arguments both in favor
and against.

> -static void vcpu_migrate_finish(struct vcpu *v)
> +static void sched_unit_migrate_finish(struct sched_unit *unit)
>  {
>      unsigned long flags;
>      unsigned int old_cpu, new_cpu;
>      spinlock_t *old_lock, *new_lock;
>      bool_t pick_called = 0;
> +    struct vcpu *v;
>  
>      /*
> -     * If the vcpu is currently running, this will be handled by
> +     * If the unit is currently running, this will be handled by
>       * context_saved(); and in any case, if the bit is cleared, then
>       * someone else has already done the work so we don't need to.
>       */
> -    if ( v->sched_unit->is_running ||
> -         !test_bit(_VPF_migrating, &v->pause_flags) )
> -        return;
> +    for_each_sched_unit_vcpu ( unit, v )
> +    {
> +        if ( unit->is_running || !test_bit(_VPF_migrating, &v->pause_flags) )
> +            return;
> +    }

Do you really need the loop invariant unit->is_running to be evaluated
once per loop iteration? (Same again further down at least once.)

Furthermore I wonder if VPF_migrating shouldn't become a per-unit
attribute.

> @@ -858,22 +871,30 @@ static void vcpu_migrate_finish(struct vcpu *v)
>       * because they both happen in (different) spinlock regions, and those
>       * regions are strictly serialised.
>       */
> -    if ( v->sched_unit->is_running ||
> -         !test_and_clear_bit(_VPF_migrating, &v->pause_flags) )
> +    for_each_sched_unit_vcpu ( unit, v )
>      {
> -        sched_spin_unlock_double(old_lock, new_lock, flags);
> -        return;
> +        if ( unit->is_running ||
> +             !test_and_clear_bit(_VPF_migrating, &v->pause_flags) )
> +        {
> +            sched_spin_unlock_double(old_lock, new_lock, flags);
> +            return;
> +        }
>      }
>  
> -    vcpu_move_locked(v, new_cpu);
> +    sched_unit_move_locked(unit, new_cpu);
>  
>      sched_spin_unlock_double(old_lock, new_lock, flags);
>  
>      if ( old_cpu != new_cpu )
> -        sched_move_irqs(v->sched_unit);
> +    {
> +        for_each_sched_unit_vcpu ( unit, v )
> +            sync_vcpu_execstate(v);

This is new without being explained anywhere. Or wait, it is mentioned
(with the wrong function name, which is why initially - by searching -
I didn't spot it), but only with a justification of "needed anyway".

> @@ -1794,7 +1814,7 @@ void context_saved(struct vcpu *prev)
>  
>      sched_context_saved(vcpu_scheduler(prev), prev->sched_unit);
>  
> -    vcpu_migrate_finish(prev);
> +    sched_unit_migrate_finish(prev->sched_unit);
>  }

By the end of the series context_saved() still acts on vCPU-s, not
units. What is the meaning/effect of multiple sched_unit_migrate_*()?

Jan
Jürgen Groß Sept. 13, 2019, 12:33 p.m. UTC | #2
On 10.09.19 17:11, Jan Beulich wrote:
> On 09.08.2019 16:58, Juergen Gross wrote:
>> --- a/xen/common/schedule.c
>> +++ b/xen/common/schedule.c
>> @@ -733,35 +733,40 @@ void vcpu_unblock(struct vcpu *v)
>>   }
>>   
>>   /*
>> - * Do the actual movement of a vcpu from old to new CPU. Locks for *both*
>> + * Do the actual movement of an unit from old to new CPU. Locks for *both*
>>    * CPUs needs to have been taken already when calling this!
>>    */
>> -static void vcpu_move_locked(struct vcpu *v, unsigned int new_cpu)
>> +static void sched_unit_move_locked(struct sched_unit *unit,
>> +                                   unsigned int new_cpu)
>>   {
>> -    unsigned int old_cpu = v->processor;
>> +    unsigned int old_cpu = unit->res->processor;
>> +    struct vcpu *v;
>>   
>>       /*
>>        * Transfer urgency status to new CPU before switching CPUs, as
>>        * once the switch occurs, v->is_urgent is no longer protected by
>>        * the per-CPU scheduler lock we are holding.
>>        */
>> -    if ( unlikely(v->is_urgent) && (old_cpu != new_cpu) )
>> +    for_each_sched_unit_vcpu ( unit, v )
>>       {
>> -        atomic_inc(&get_sched_res(new_cpu)->urgent_count);
>> -        atomic_dec(&get_sched_res(old_cpu)->urgent_count);
>> +        if ( unlikely(v->is_urgent) && (old_cpu != new_cpu) )
>> +        {
>> +            atomic_inc(&get_sched_res(new_cpu)->urgent_count);
>> +            atomic_dec(&get_sched_res(old_cpu)->urgent_count);
>> +        }
>>       }
> 
> Shouldn't is_urgent become an attribute of unit rather than a vCPU,
> too, eliminating the need for a loop here? I can't see a reason
> why not, seeing this collapsing into a single urgent_count.

With moving urgent_count to a percpu variable this no longer applies.

> 
> Then again the question remains whether the non-deep sleeping as
> a result of a non-zero urgent_count should indeed be distributed
> to all constituents of a unit. I can see arguments both in favor
> and against.

Against has won. :-)

> 
>> -static void vcpu_migrate_finish(struct vcpu *v)
>> +static void sched_unit_migrate_finish(struct sched_unit *unit)
>>   {
>>       unsigned long flags;
>>       unsigned int old_cpu, new_cpu;
>>       spinlock_t *old_lock, *new_lock;
>>       bool_t pick_called = 0;
>> +    struct vcpu *v;
>>   
>>       /*
>> -     * If the vcpu is currently running, this will be handled by
>> +     * If the unit is currently running, this will be handled by
>>        * context_saved(); and in any case, if the bit is cleared, then
>>        * someone else has already done the work so we don't need to.
>>        */
>> -    if ( v->sched_unit->is_running ||
>> -         !test_bit(_VPF_migrating, &v->pause_flags) )
>> -        return;
>> +    for_each_sched_unit_vcpu ( unit, v )
>> +    {
>> +        if ( unit->is_running || !test_bit(_VPF_migrating, &v->pause_flags) )
>> +            return;
>> +    }
> 
> Do you really need the loop invariant unit->is_running to be evaluated
> once per loop iteration? (Same again further down at least once.)

No, I should test that before entering the loop.

> 
> Furthermore I wonder if VPF_migrating shouldn't become a per-unit
> attribute.

This would make vcpu_runnable() much more complicated. I don't think
that is worth it.

> 
>> @@ -858,22 +871,30 @@ static void vcpu_migrate_finish(struct vcpu *v)
>>        * because they both happen in (different) spinlock regions, and those
>>        * regions are strictly serialised.
>>        */
>> -    if ( v->sched_unit->is_running ||
>> -         !test_and_clear_bit(_VPF_migrating, &v->pause_flags) )
>> +    for_each_sched_unit_vcpu ( unit, v )
>>       {
>> -        sched_spin_unlock_double(old_lock, new_lock, flags);
>> -        return;
>> +        if ( unit->is_running ||
>> +             !test_and_clear_bit(_VPF_migrating, &v->pause_flags) )
>> +        {
>> +            sched_spin_unlock_double(old_lock, new_lock, flags);
>> +            return;
>> +        }
>>       }
>>   
>> -    vcpu_move_locked(v, new_cpu);
>> +    sched_unit_move_locked(unit, new_cpu);
>>   
>>       sched_spin_unlock_double(old_lock, new_lock, flags);
>>   
>>       if ( old_cpu != new_cpu )
>> -        sched_move_irqs(v->sched_unit);
>> +    {
>> +        for_each_sched_unit_vcpu ( unit, v )
>> +            sync_vcpu_execstate(v);
> 
> This is new without being explained anywhere. Or wait, it is mentioned
> (with the wrong function name, which is why initially - by searching -
> I didn't spot it), but only with a justification of "needed anyway".

I'll correct it and make it more verbose.

> 
>> @@ -1794,7 +1814,7 @@ void context_saved(struct vcpu *prev)
>>   
>>       sched_context_saved(vcpu_scheduler(prev), prev->sched_unit);
>>   
>> -    vcpu_migrate_finish(prev);
>> +    sched_unit_migrate_finish(prev->sched_unit);
>>   }
> 
> By the end of the series context_saved() still acts on vCPU-s, not
> units. What is the meaning/effect of multiple sched_unit_migrate_*()?

That's corrected in V3 by having split context_saved() into a vcpu- and
a unit-part.


Juergen
diff mbox series

Patch

diff --git a/xen/common/schedule.c b/xen/common/schedule.c
index 4c488ddde0..e4d0dd4b65 100644
--- a/xen/common/schedule.c
+++ b/xen/common/schedule.c
@@ -733,35 +733,40 @@  void vcpu_unblock(struct vcpu *v)
 }
 
 /*
- * Do the actual movement of a vcpu from old to new CPU. Locks for *both*
+ * Do the actual movement of an unit from old to new CPU. Locks for *both*
  * CPUs needs to have been taken already when calling this!
  */
-static void vcpu_move_locked(struct vcpu *v, unsigned int new_cpu)
+static void sched_unit_move_locked(struct sched_unit *unit,
+                                   unsigned int new_cpu)
 {
-    unsigned int old_cpu = v->processor;
+    unsigned int old_cpu = unit->res->processor;
+    struct vcpu *v;
 
     /*
      * Transfer urgency status to new CPU before switching CPUs, as
      * once the switch occurs, v->is_urgent is no longer protected by
      * the per-CPU scheduler lock we are holding.
      */
-    if ( unlikely(v->is_urgent) && (old_cpu != new_cpu) )
+    for_each_sched_unit_vcpu ( unit, v )
     {
-        atomic_inc(&get_sched_res(new_cpu)->urgent_count);
-        atomic_dec(&get_sched_res(old_cpu)->urgent_count);
+        if ( unlikely(v->is_urgent) && (old_cpu != new_cpu) )
+        {
+            atomic_inc(&get_sched_res(new_cpu)->urgent_count);
+            atomic_dec(&get_sched_res(old_cpu)->urgent_count);
+        }
     }
 
     /*
      * Actual CPU switch to new CPU.  This is safe because the lock
      * pointer can't change while the current lock is held.
      */
-    sched_migrate(vcpu_scheduler(v), v->sched_unit, new_cpu);
+    sched_migrate(unit_scheduler(unit), unit, new_cpu);
 }
 
 /*
  * Initiating migration
  *
- * In order to migrate, we need the vcpu in question to have stopped
+ * In order to migrate, we need the unit in question to have stopped
  * running and had sched_sleep() called (to take it off any
  * runqueues, for instance); and if it is currently running, it needs
  * to be scheduled out.  Finally, we need to hold the scheduling locks
@@ -777,37 +782,45 @@  static void vcpu_move_locked(struct vcpu *v, unsigned int new_cpu)
  * should be called like this:
  *
  *     lock = unit_schedule_lock_irq(unit);
- *     vcpu_migrate_start(v);
+ *     sched_unit_migrate_start(unit);
  *     unit_schedule_unlock_irq(lock, unit)
- *     vcpu_migrate_finish(v);
+ *     sched_unit_migrate_finish(unit);
  *
- * vcpu_migrate_finish() will do the work now if it can, or simply
- * return if it can't (because v is still running); in that case
- * vcpu_migrate_finish() will be called by context_saved().
+ * sched_unit_migrate_finish() will do the work now if it can, or simply
+ * return if it can't (because unit is still running); in that case
+ * sched_unit_migrate_finish() will be called by context_saved().
  */
-static void vcpu_migrate_start(struct vcpu *v)
+static void sched_unit_migrate_start(struct sched_unit *unit)
 {
-    set_bit(_VPF_migrating, &v->pause_flags);
-    vcpu_sleep_nosync_locked(v);
+    struct vcpu *v;
+
+    for_each_sched_unit_vcpu ( unit, v )
+    {
+        set_bit(_VPF_migrating, &v->pause_flags);
+        vcpu_sleep_nosync_locked(v);
+    }
 }
 
-static void vcpu_migrate_finish(struct vcpu *v)
+static void sched_unit_migrate_finish(struct sched_unit *unit)
 {
     unsigned long flags;
     unsigned int old_cpu, new_cpu;
     spinlock_t *old_lock, *new_lock;
     bool_t pick_called = 0;
+    struct vcpu *v;
 
     /*
-     * If the vcpu is currently running, this will be handled by
+     * If the unit is currently running, this will be handled by
      * context_saved(); and in any case, if the bit is cleared, then
      * someone else has already done the work so we don't need to.
      */
-    if ( v->sched_unit->is_running ||
-         !test_bit(_VPF_migrating, &v->pause_flags) )
-        return;
+    for_each_sched_unit_vcpu ( unit, v )
+    {
+        if ( unit->is_running || !test_bit(_VPF_migrating, &v->pause_flags) )
+            return;
+    }
 
-    old_cpu = new_cpu = v->processor;
+    old_cpu = new_cpu = unit->res->processor;
     for ( ; ; )
     {
         /*
@@ -820,7 +833,7 @@  static void vcpu_migrate_finish(struct vcpu *v)
 
         sched_spin_lock_double(old_lock, new_lock, &flags);
 
-        old_cpu = v->processor;
+        old_cpu = unit->res->processor;
         if ( old_lock == get_sched_res(old_cpu)->schedule_lock )
         {
             /*
@@ -829,15 +842,15 @@  static void vcpu_migrate_finish(struct vcpu *v)
              */
             if ( pick_called &&
                  (new_lock == get_sched_res(new_cpu)->schedule_lock) &&
-                 cpumask_test_cpu(new_cpu, v->sched_unit->cpu_hard_affinity) &&
-                 cpumask_test_cpu(new_cpu, v->domain->cpupool->cpu_valid) )
+                 cpumask_test_cpu(new_cpu, unit->cpu_hard_affinity) &&
+                 cpumask_test_cpu(new_cpu, unit->domain->cpupool->cpu_valid) )
                 break;
 
             /* Select a new CPU. */
-            new_cpu = sched_pick_resource(vcpu_scheduler(v),
-                                          v->sched_unit)->processor;
+            new_cpu = sched_pick_resource(unit_scheduler(unit),
+                                          unit)->processor;
             if ( (new_lock == get_sched_res(new_cpu)->schedule_lock) &&
-                 cpumask_test_cpu(new_cpu, v->domain->cpupool->cpu_valid) )
+                 cpumask_test_cpu(new_cpu, unit->domain->cpupool->cpu_valid) )
                 break;
             pick_called = 1;
         }
@@ -858,22 +871,30 @@  static void vcpu_migrate_finish(struct vcpu *v)
      * because they both happen in (different) spinlock regions, and those
      * regions are strictly serialised.
      */
-    if ( v->sched_unit->is_running ||
-         !test_and_clear_bit(_VPF_migrating, &v->pause_flags) )
+    for_each_sched_unit_vcpu ( unit, v )
     {
-        sched_spin_unlock_double(old_lock, new_lock, flags);
-        return;
+        if ( unit->is_running ||
+             !test_and_clear_bit(_VPF_migrating, &v->pause_flags) )
+        {
+            sched_spin_unlock_double(old_lock, new_lock, flags);
+            return;
+        }
     }
 
-    vcpu_move_locked(v, new_cpu);
+    sched_unit_move_locked(unit, new_cpu);
 
     sched_spin_unlock_double(old_lock, new_lock, flags);
 
     if ( old_cpu != new_cpu )
-        sched_move_irqs(v->sched_unit);
+    {
+        for_each_sched_unit_vcpu ( unit, v )
+            sync_vcpu_execstate(v);
+        sched_move_irqs(unit);
+    }
 
     /* Wake on new CPU. */
-    vcpu_wake(v);
+    for_each_sched_unit_vcpu ( unit, v )
+        vcpu_wake(v);
 }
 
 /*
@@ -1041,10 +1062,9 @@  int cpu_disable_scheduler(unsigned int cpu)
              *  * the scheduler will always find a suitable solution, or
              *    things would have failed before getting in here.
              */
-            vcpu_migrate_start(unit->vcpu_list);
+            sched_unit_migrate_start(unit);
             unit_schedule_unlock_irqrestore(lock, flags, unit);
-
-            vcpu_migrate_finish(unit->vcpu_list);
+            sched_unit_migrate_finish(unit);
 
             /*
              * The only caveat, in this case, is that if a vcpu active in
@@ -1128,14 +1148,14 @@  static int vcpu_set_affinity(
             ASSERT(which == unit->cpu_soft_affinity);
             sched_set_affinity(v, NULL, affinity);
         }
-        vcpu_migrate_start(v);
+        sched_unit_migrate_start(unit);
     }
 
     unit_schedule_unlock_irq(lock, unit);
 
     domain_update_node_affinity(v->domain);
 
-    vcpu_migrate_finish(v);
+    sched_unit_migrate_finish(unit);
 
     return ret;
 }
@@ -1396,12 +1416,12 @@  int vcpu_temporary_affinity(struct vcpu *v, unsigned int cpu, uint8_t reason)
 
     migrate = !ret && !cpumask_test_cpu(v->processor, unit->cpu_hard_affinity);
     if ( migrate )
-        vcpu_migrate_start(v);
+        sched_unit_migrate_start(unit);
 
     unit_schedule_unlock_irq(lock, unit);
 
     if ( migrate )
-        vcpu_migrate_finish(v);
+        sched_unit_migrate_finish(unit);
 
     return ret;
 }
@@ -1794,7 +1814,7 @@  void context_saved(struct vcpu *prev)
 
     sched_context_saved(vcpu_scheduler(prev), prev->sched_unit);
 
-    vcpu_migrate_finish(prev);
+    sched_unit_migrate_finish(prev->sched_unit);
 }
 
 /* The scheduler timer: force a run through the scheduler */