]> xenbits.xensource.com Git - people/vhanquez/xen.git/commitdiff
Avoid deadlocks on domctl_lock when pausing domains/vcpus.
authorKeir Fraser <keir.fraser@citrix.com>
Wed, 8 Apr 2009 13:20:53 +0000 (14:20 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Wed, 8 Apr 2009 13:20:53 +0000 (14:20 +0100)
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen-unstable changeset:   19519:3929487cdb82
xen-unstable date:        Wed Apr 08 13:48:35 2009 +0100

xen/arch/x86/hvm/hvm.c
xen/common/domctl.c
xen/include/xen/domain.h
xen/include/xen/hypercall.h

index 3328e9facd39f4f482ddb85aa8f1976534571dc5..8a992dd028f3dc2921426514256668aa316bdfcf 100644 (file)
@@ -2472,20 +2472,23 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
                 if ( !paging_mode_hap(d) )
                     break;
 
-                domain_pause(d);
-
                 /*
                  * Update GUEST_CR3 in each VMCS to point at identity map.
                  * All foreign updates to guest state must synchronise on
                  * the domctl_lock.
                  */
-                spin_lock(&domctl_lock);
+                rc = -EAGAIN;
+                if ( !domctl_lock_acquire() )
+                    break;
+
+                rc = 0;
+                domain_pause(d);
                 d->arch.hvm_domain.params[a.index] = a.value;
                 for_each_vcpu ( d, v )
                     paging_update_cr3(v);
-                spin_unlock(&domctl_lock);
-
                 domain_unpause(d);
+
+                domctl_lock_release();
                 break;
             case HVM_PARAM_DM_DOMAIN:
                 /* Privileged domains only, as we must domain_pause(d). */
index cf8c64aa535b56d2da4da587c82dfbfbe0a5f4ae..fe4ecdcc2297524ad0910e4614c562796e88dc8a 100644 (file)
@@ -25,7 +25,7 @@
 #include <public/domctl.h>
 #include <xsm/xsm.h>
 
-DEFINE_SPINLOCK(domctl_lock);
+static DEFINE_SPINLOCK(domctl_lock);
 
 extern long arch_do_domctl(
     struct xen_domctl *op, XEN_GUEST_HANDLE(xen_domctl_t) u_domctl);
@@ -179,6 +179,33 @@ static unsigned int default_vcpu0_location(void)
     return cpu;
 }
 
+bool_t domctl_lock_acquire(void)
+{
+    /*
+     * Caller may try to pause its own VCPUs. We must prevent deadlock
+     * against other non-domctl routines which try to do the same.
+     */
+    if ( !spin_trylock(&current->domain->hypercall_deadlock_mutex) )
+        return 0;
+
+    /*
+     * Trylock here is paranoia if we have multiple privileged domains. Then
+     * we could have one domain trying to pause another which is spinning
+     * on domctl_lock -- results in deadlock.
+     */
+    if ( spin_trylock(&domctl_lock) )
+        return 1;
+
+    spin_unlock(&current->domain->hypercall_deadlock_mutex);
+    return 0;
+}
+
+void domctl_lock_release(void)
+{
+    spin_unlock(&domctl_lock);
+    spin_unlock(&current->domain->hypercall_deadlock_mutex);
+}
+
 long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl)
 {
     long ret = 0;
@@ -193,7 +220,9 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl)
     if ( op->interface_version != XEN_DOMCTL_INTERFACE_VERSION )
         return -EACCES;
 
-    spin_lock(&domctl_lock);
+    if ( !domctl_lock_acquire() )
+        return hypercall_create_continuation(
+            __HYPERVISOR_domctl, "h", u_domctl);
 
     switch ( op->cmd )
     {
@@ -858,7 +887,7 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl)
         break;
     }
 
-    spin_unlock(&domctl_lock);
+    domctl_lock_release();
 
     return ret;
 }
index cf82d07ea065aeeec40db3eff303273c28e737b6..f0c5ea4f32a470c321872e9717207c13f6821d8a 100644 (file)
@@ -54,6 +54,9 @@ void arch_dump_domain_info(struct domain *d);
 
 void arch_vcpu_reset(struct vcpu *v);
 
+bool_t domctl_lock_acquire(void);
+void domctl_lock_release(void);
+
 extern unsigned int xen_processor_pmbits;
 
 #endif /* __XEN_DOMAIN_H__ */
index 3997b2f96a5b706e1c10832ec2dae911e7ed0c8b..8d133965125c128526ccdd9e7cb182bc7ce99cb8 100644 (file)
@@ -30,7 +30,6 @@ do_sched_op(
     int cmd,
     XEN_GUEST_HANDLE(void) arg);
 
-extern spinlock_t domctl_lock;
 extern long
 do_domctl(
     XEN_GUEST_HANDLE(xen_domctl_t) u_domctl);