aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--xen/arch/x86/hvm/hvm.c32
-rw-r--r--xen/common/domain.c1
-rw-r--r--xen/include/xen/sched.h6
3 files changed, 37 insertions, 2 deletions
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 6f4a92445e..a43ca392b2 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -1661,14 +1661,38 @@ static int hvmop_set_pci_link_route(
static int hvmop_flush_tlb_all(void)
{
+ struct domain *d = current->domain;
struct vcpu *v;
+ /* Avoid deadlock if more than one vcpu tries this at the same time. */
+ if ( !spin_trylock(&d->hypercall_deadlock_mutex) )
+ return -EAGAIN;
+
+ /* Pause all other vcpus. */
+ for_each_vcpu ( d, v )
+ if ( v != current )
+ vcpu_pause_nosync(v);
+
+ /* Now that all VCPUs are signalled to deschedule, we wait... */
+ for_each_vcpu ( d, v )
+ if ( v != current )
+ while ( !vcpu_runnable(v) && v->is_running )
+ cpu_relax();
+
+ /* All other vcpus are paused, safe to unlock now. */
+ spin_unlock(&d->hypercall_deadlock_mutex);
+
/* Flush paging-mode soft state (e.g., va->gfn cache; PAE PDPE cache). */
- for_each_vcpu ( current->domain, v )
+ for_each_vcpu ( d, v )
paging_update_cr3(v);
/* Flush all dirty TLBs. */
- flush_tlb_mask(current->domain->domain_dirty_cpumask);
+ flush_tlb_mask(d->domain_dirty_cpumask);
+
+ /* Done. */
+ for_each_vcpu ( d, v )
+ if ( v != current )
+ vcpu_unpause(v);
return 0;
}
@@ -1780,6 +1804,10 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
}
}
+ if ( rc == -EAGAIN )
+ rc = hypercall_create_continuation(
+ __HYPERVISOR_hvm_op, "lh", op, arg);
+
return rc;
}
diff --git a/xen/common/domain.c b/xen/common/domain.c
index e3b583d021..55152a3953 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -68,6 +68,7 @@ struct domain *alloc_domain(domid_t domid)
spin_lock_init(&d->big_lock);
spin_lock_init(&d->page_alloc_lock);
spin_lock_init(&d->shutdown_lock);
+ spin_lock_init(&d->hypercall_deadlock_mutex);
INIT_LIST_HEAD(&d->page_list);
INIT_LIST_HEAD(&d->xenpage_list);
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index cf5342ccf1..67a203b969 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -227,6 +227,12 @@ struct domain
int32_t time_offset_seconds;
struct rcu_head rcu;
+
+ /*
+ * Hypercall deadlock avoidance lock. Used if a hypercall might
+ * cause a deadlock. Acquirers don't spin waiting; they preempt.
+ */
+ spinlock_t hypercall_deadlock_mutex;
};
struct domain_setup_info