# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Date 1175597050 -3600
# Node ID a1b17c48fb403e9b8fd4fe136198eafa126a5cf7
# Parent 45e9f6d7e422156ec36d64dea817af2fb659ec1a
xen: Allow vcpus to defer a shutdown request across critical
asynchronous operations (in particular, hvm ioreq requests).
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
---
xen/arch/x86/hvm/hvm.c | 3 +
xen/arch/x86/hvm/io.c | 11 ++-
xen/arch/x86/mm.c | 3 -
xen/arch/x86/mm/shadow/multi.c | 4 -
xen/common/domain.c | 118 +++++++++++++++++++++++++++++++++++++----
xen/common/domctl.c | 5 -
xen/include/xen/sched.h | 12 +++-
7 files changed, 136 insertions(+), 20 deletions(-)
diff -r 45e9f6d7e422 -r a1b17c48fb40 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c Tue Apr 03 10:36:31 2007 +0100
+++ b/xen/arch/x86/hvm/hvm.c Tue Apr 03 11:44:10 2007 +0100
@@ -372,6 +372,9 @@ void hvm_send_assist_req(struct vcpu *v)
void hvm_send_assist_req(struct vcpu *v)
{
ioreq_t *p;
+
+ if ( unlikely(!vcpu_start_shutdown_deferral(v)) )
+ return; /* implicitly bins the i/o operation */
p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
if ( unlikely(p->state != STATE_IOREQ_NONE) )
diff -r 45e9f6d7e422 -r a1b17c48fb40 xen/arch/x86/hvm/io.c
--- a/xen/arch/x86/hvm/io.c Tue Apr 03 10:36:31 2007 +0100
+++ b/xen/arch/x86/hvm/io.c Tue Apr 03 11:44:10 2007 +0100
@@ -771,10 +771,11 @@ void hvm_io_assist(struct vcpu *v)
struct cpu_user_regs *regs;
struct hvm_io_op *io_opp;
unsigned long gmfn;
+ struct domain *d = v->domain;
io_opp = &v->arch.hvm_vcpu.io_op;
regs = &io_opp->io_context;
- vio = get_vio(v->domain, v->vcpu_id);
+ vio = get_vio(d, v->vcpu_id);
p = &vio->vp_ioreq;
if ( p->state != STATE_IORESP_READY )
@@ -797,11 +798,13 @@ void hvm_io_assist(struct vcpu *v)
memcpy(guest_cpu_user_regs(), regs, HVM_CONTEXT_STACK_BYTES);
/* Has memory been dirtied? */
- if ( p->dir == IOREQ_READ && p->data_is_ptr )
+ if ( (p->dir == IOREQ_READ) && p->data_is_ptr )
{
gmfn = get_mfn_from_gpfn(paging_gva_to_gfn(v, p->data));
- mark_dirty(v->domain, gmfn);
- }
+ mark_dirty(d, gmfn);
+ }
+
+ vcpu_end_shutdown_deferral(v);
}
/*
diff -r 45e9f6d7e422 -r a1b17c48fb40 xen/arch/x86/mm.c
--- a/xen/arch/x86/mm.c Tue Apr 03 10:36:31 2007 +0100
+++ b/xen/arch/x86/mm.c Tue Apr 03 11:44:10 2007 +0100
@@ -806,7 +806,8 @@ void put_page_from_l1e(l1_pgentry_t l1e,
* (Note that the undestroyable active grants are not a security hole in
* Xen. All active grants can safely be cleaned up when the domain dies.)
*/
- if ( (l1e_get_flags(l1e) & _PAGE_GNTTAB) && !d->is_shutdown &&
!d->is_dying )
+ if ( (l1e_get_flags(l1e) & _PAGE_GNTTAB) &&
+ !d->is_shutting_down && !d->is_dying )
{
MEM_LOG("Attempt to implicitly unmap a granted PTE %" PRIpte,
l1e_get_intpte(l1e));
diff -r 45e9f6d7e422 -r a1b17c48fb40 xen/arch/x86/mm/shadow/multi.c
--- a/xen/arch/x86/mm/shadow/multi.c Tue Apr 03 10:36:31 2007 +0100
+++ b/xen/arch/x86/mm/shadow/multi.c Tue Apr 03 11:44:10 2007 +0100
@@ -2823,8 +2823,8 @@ static int sh_page_fault(struct vcpu *v,
* are OK, this can only have been caused by a failed
* shadow_set_l*e(), which will have crashed the guest.
* Get out of the fault handler immediately. */
- ASSERT(d->is_shutdown);
- unmap_walk(v, &gw);
+ ASSERT(d->is_shutting_down);
+ unmap_walk(v, &gw);
shadow_unlock(d);
return 0;
}
diff -r 45e9f6d7e422 -r a1b17c48fb40 xen/common/domain.c
--- a/xen/common/domain.c Tue Apr 03 10:36:31 2007 +0100
+++ b/xen/common/domain.c Tue Apr 03 11:44:10 2007 +0100
@@ -59,6 +59,7 @@ struct domain *alloc_domain(domid_t domi
atomic_set(&d->refcnt, 1);
spin_lock_init(&d->big_lock);
spin_lock_init(&d->page_alloc_lock);
+ spin_lock_init(&d->shutdown_lock);
INIT_LIST_HEAD(&d->page_list);
INIT_LIST_HEAD(&d->xenpage_list);
@@ -83,6 +84,45 @@ void free_domain(struct domain *d)
xfree(d);
}
+static void __domain_finalise_shutdown(struct domain *d)
+{
+ struct vcpu *v;
+
+ BUG_ON(!spin_is_locked(&d->shutdown_lock));
+
+ if ( d->is_shut_down )
+ return;
+
+ for_each_vcpu ( d, v )
+ if ( !v->paused_for_shutdown )
+ return;
+
+ d->is_shut_down = 1;
+
+ for_each_vcpu ( d, v )
+ vcpu_sleep_nosync(v);
+
+ send_guest_global_virq(dom0, VIRQ_DOM_EXC);
+}
+
+static void vcpu_check_shutdown(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+
+ spin_lock(&d->shutdown_lock);
+
+ if ( d->is_shutting_down )
+ {
+ if ( !v->paused_for_shutdown )
+ atomic_inc(&v->pause_count);
+ v->paused_for_shutdown = 1;
+ v->defer_shutdown = 0;
+ __domain_finalise_shutdown(d);
+ }
+
+ spin_unlock(&d->shutdown_lock);
+}
+
struct vcpu *alloc_vcpu(
struct domain *d, unsigned int vcpu_id, unsigned int cpu_id)
{
@@ -121,6 +161,9 @@ struct vcpu *alloc_vcpu(
d->vcpu[vcpu_id] = v;
if ( vcpu_id != 0 )
d->vcpu[v->vcpu_id-1]->next_in_list = v;
+
+ /* Must be called after making new vcpu visible to for_each_vcpu(). */
+ vcpu_check_shutdown(v);
return v;
}
@@ -286,7 +329,7 @@ void domain_kill(struct domain *d)
void __domain_crash(struct domain *d)
{
- if ( d->is_shutdown )
+ if ( d->is_shutting_down )
{
/* Print nothing: the domain is already shutting down. */
}
@@ -335,16 +378,73 @@ void domain_shutdown(struct domain *d, u
if ( d->domain_id == 0 )
dom0_shutdown(reason);
- atomic_inc(&d->pause_count);
- if ( !xchg(&d->is_shutdown, 1) )
- d->shutdown_code = reason;
- else
- domain_unpause(d);
+ spin_lock(&d->shutdown_lock);
+
+ if ( d->is_shutting_down )
+ {
+ spin_unlock(&d->shutdown_lock);
+ return;
+ }
+
+ d->is_shutting_down = 1;
+ d->shutdown_code = reason;
+
+ smp_mb(); /* set shutdown status /then/ check for per-cpu deferrals */
for_each_vcpu ( d, v )
- vcpu_sleep_nosync(v);
-
- send_guest_global_virq(dom0, VIRQ_DOM_EXC);
+ {
+ if ( v->defer_shutdown )
+ continue;
+ atomic_inc(&v->pause_count);
+ v->paused_for_shutdown = 1;
+ }
+
+ __domain_finalise_shutdown(d);
+
+ spin_unlock(&d->shutdown_lock);
+}
+
+void domain_resume(struct domain *d)
+{
+ struct vcpu *v;
+
+ /*
+ * Some code paths assume that shutdown status does not get reset under
+ * their feet (e.g., some assertions make this assumption).
+ */
+ domain_pause(d);
+
+ spin_lock(&d->shutdown_lock);
+
+ d->is_shutting_down = d->is_shut_down = 0;
+
+ for_each_vcpu ( d, v )
+ {
+ if ( v->paused_for_shutdown )
+ vcpu_unpause(v);
+ v->paused_for_shutdown = 0;
+ }
+
+ spin_unlock(&d->shutdown_lock);
+
+ domain_unpause(d);
+}
+
+int vcpu_start_shutdown_deferral(struct vcpu *v)
+{
+ v->defer_shutdown = 1;
+ smp_mb(); /* set deferral status /then/ check for shutdown */
+ if ( unlikely(v->domain->is_shutting_down) )
+ vcpu_check_shutdown(v);
+ return v->defer_shutdown;
+}
+
+void vcpu_end_shutdown_deferral(struct vcpu *v)
+{
+ v->defer_shutdown = 0;
+ smp_mb(); /* clear deferral status /then/ check for shutdown */
+ if ( unlikely(v->domain->is_shutting_down) )
+ vcpu_check_shutdown(v);
}
void domain_pause_for_debugger(void)
diff -r 45e9f6d7e422 -r a1b17c48fb40 xen/common/domctl.c
--- a/xen/common/domctl.c Tue Apr 03 10:36:31 2007 +0100
+++ b/xen/common/domctl.c Tue Apr 03 11:44:10 2007 +0100
@@ -115,7 +115,7 @@ void getdomaininfo(struct domain *d, str
info->flags = flags |
(d->is_dying ? XEN_DOMINF_dying : 0) |
- (d->is_shutdown ? XEN_DOMINF_shutdown : 0) |
+ (d->is_shut_down ? XEN_DOMINF_shutdown : 0) |
(d->is_paused_by_controller ? XEN_DOMINF_paused : 0) |
d->shutdown_code << XEN_DOMINF_shutdownshift;
@@ -287,8 +287,7 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domc
if ( d == NULL )
break;
- if ( xchg(&d->is_shutdown, 0) )
- domain_unpause(d);
+ domain_resume(d);
rcu_unlock_domain(d);
ret = 0;
}
diff -r 45e9f6d7e422 -r a1b17c48fb40 xen/include/xen/sched.h
--- a/xen/include/xen/sched.h Tue Apr 03 10:36:31 2007 +0100
+++ b/xen/include/xen/sched.h Tue Apr 03 11:44:10 2007 +0100
@@ -114,6 +114,10 @@ struct vcpu
bool_t nmi_pending;
/* Avoid NMI reentry by allowing NMIs to be masked for short periods. */
bool_t nmi_masked;
+ /* Require shutdown to be deferred for some asynchronous operation? */
+ bool_t defer_shutdown;
+ /* VCPU is paused following shutdown request (d->is_shutting_down)? */
+ bool_t paused_for_shutdown;
unsigned long pause_flags;
atomic_t pause_count;
@@ -193,7 +197,9 @@ struct domain
bool_t is_paused_by_controller;
/* Guest has shut down (inc. reason code)? */
- bool_t is_shutdown;
+ spinlock_t shutdown_lock;
+ bool_t is_shutting_down; /* in process of shutting down? */
+ bool_t is_shut_down; /* fully shut down? */
int shutdown_code;
atomic_t pause_count;
@@ -331,7 +337,11 @@ void domain_destroy(struct domain *d);
void domain_destroy(struct domain *d);
void domain_kill(struct domain *d);
void domain_shutdown(struct domain *d, u8 reason);
+void domain_resume(struct domain *d);
void domain_pause_for_debugger(void);
+
+int vcpu_start_shutdown_deferral(struct vcpu *v);
+void vcpu_end_shutdown_deferral(struct vcpu *v);
/*
* Mark specified domain as crashed. This function always returns, even if the
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|