]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/xen.git/commitdiff
Avoid possible live-lock in vcpu_migrate
authorJuergen Gross <juergen.gross@ts.fujitsu.com>
Mon, 28 Feb 2011 15:09:33 +0000 (15:09 +0000)
committerJuergen Gross <juergen.gross@ts.fujitsu.com>
Mon, 28 Feb 2011 15:09:33 +0000 (15:09 +0000)
If vcpu_migrate is called for two vcpus active on different cpus
resulting in swapping the cpus, a live-lock could occur as both
instances try to take the scheduling lock of the physical cpus in
opposite order.

To avoid this problem the locks are always taken in the same order
(sorted by the address of the lock).

Signed-off-by: Jueregn Gross <juergen.gross@ts.fujitsu.com>
xen/common/schedule.c

index b6ef4769d187ce561c05cd1f53e8ac94b20de021..e89da7a34b65db770e69eea231a2eb135e15fc21 100644 (file)
@@ -393,32 +393,52 @@ void vcpu_unblock(struct vcpu *v)
 static void vcpu_migrate(struct vcpu *v)
 {
     unsigned long flags;
-    int old_cpu, new_cpu;
-    int same_lock;
+    unsigned int old_cpu, new_cpu;
+    spinlock_t *old_lock, *new_lock;
 
-    for (;;)
+    old_cpu = new_cpu = v->processor;
+    for ( ; ; )
     {
-        vcpu_schedule_lock_irqsave(v, flags);
+        /*
+         * If per-cpu locks for old and new cpu are different, take the one
+         * with the lower lock address first. This avoids dead- or live-locks
+         * when this code is running on both cpus at the same time.
+         * We need another iteration if the pre-calculated lock addresses
+         * are not correct any longer after evaluating old and new cpu holding
+         * the locks.
+         */
 
-        /* Select new CPU. */
-        old_cpu = v->processor;
-        new_cpu = SCHED_OP(VCPU2OP(v), pick_cpu, v);
-        same_lock = (per_cpu(schedule_data, new_cpu).schedule_lock ==
-                     per_cpu(schedule_data, old_cpu).schedule_lock);
+        old_lock = per_cpu(schedule_data, old_cpu).schedule_lock;
+        new_lock = per_cpu(schedule_data, new_cpu).schedule_lock;
 
-        if ( same_lock )
-            break;
+        if ( old_lock == new_lock )
+        {
+            spin_lock_irqsave(old_lock, flags);
+        }
+        else if ( old_lock < new_lock )
+        {
+            spin_lock_irqsave(old_lock, flags);
+            spin_lock(new_lock);
+        }
+        else
+        {
+            spin_lock_irqsave(new_lock, flags);
+            spin_lock(old_lock);
+        }
 
-        if ( !pcpu_schedule_trylock(new_cpu) )
+        /* Select new CPU. */
+        old_cpu = v->processor;
+        if ( old_lock == per_cpu(schedule_data, old_cpu).schedule_lock )
         {
-            vcpu_schedule_unlock_irqrestore(v, flags);
-            continue;
+            new_cpu = SCHED_OP(VCPU2OP(v), pick_cpu, v);
+            if ( (new_lock == per_cpu(schedule_data, new_cpu).schedule_lock) &&
+                 cpu_isset(new_cpu, v->domain->cpupool->cpu_valid) )
+                break;
         }
-        if ( cpu_isset(new_cpu, v->domain->cpupool->cpu_valid) )
-            break;
 
-        pcpu_schedule_unlock(new_cpu);
-        vcpu_schedule_unlock_irqrestore(v, flags);
+        if ( old_lock != new_lock )
+            spin_unlock(new_lock);
+        spin_unlock_irqrestore(old_lock, flags);
     }
 
     /*
@@ -429,10 +449,9 @@ static void vcpu_migrate(struct vcpu *v)
     if ( v->is_running ||
          !test_and_clear_bit(_VPF_migrating, &v->pause_flags) )
     {
-        if ( !same_lock )
-            pcpu_schedule_unlock(new_cpu);
-
-        vcpu_schedule_unlock_irqrestore(v, flags);
+        if ( old_lock != new_lock )
+            spin_unlock(new_lock);
+        spin_unlock_irqrestore(old_lock, flags);
         return;
     }
 
@@ -453,11 +472,9 @@ static void vcpu_migrate(struct vcpu *v)
      */
     v->processor = new_cpu;
 
-    if ( !same_lock )
-        pcpu_schedule_unlock(new_cpu);
-
-    spin_unlock_irqrestore(
-        per_cpu(schedule_data, old_cpu).schedule_lock, flags);
+    if ( old_lock != new_lock )
+        spin_unlock(new_lock);
+    spin_unlock_irqrestore(old_lock, flags);
 
     if ( old_cpu != new_cpu )
         evtchn_move_pirqs(v);