ia64/xen-unstable

changeset 14705:a1b17c48fb40

xen: Allow vcpus to defer a shutdown request across critical
asynchronous operations (in particular, hvm ioreq requests).
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Tue Apr 03 11:44:10 2007 +0100 (2007-04-03)
parents 45e9f6d7e422
children cd01741aaa93
files xen/arch/x86/hvm/hvm.c xen/arch/x86/hvm/io.c xen/arch/x86/mm.c xen/arch/x86/mm/shadow/multi.c xen/common/domain.c xen/common/domctl.c xen/include/xen/sched.h
line diff
     1.1 --- a/xen/arch/x86/hvm/hvm.c	Tue Apr 03 10:36:31 2007 +0100
     1.2 +++ b/xen/arch/x86/hvm/hvm.c	Tue Apr 03 11:44:10 2007 +0100
     1.3 @@ -373,6 +373,9 @@ void hvm_send_assist_req(struct vcpu *v)
     1.4  {
     1.5      ioreq_t *p;
     1.6  
     1.7 +    if ( unlikely(!vcpu_start_shutdown_deferral(v)) )
     1.8 +        return; /* implicitly bins the i/o operation */
     1.9 +
    1.10      p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
    1.11      if ( unlikely(p->state != STATE_IOREQ_NONE) )
    1.12      {
     2.1 --- a/xen/arch/x86/hvm/io.c	Tue Apr 03 10:36:31 2007 +0100
     2.2 +++ b/xen/arch/x86/hvm/io.c	Tue Apr 03 11:44:10 2007 +0100
     2.3 @@ -771,10 +771,11 @@ void hvm_io_assist(struct vcpu *v)
     2.4      struct cpu_user_regs *regs;
     2.5      struct hvm_io_op *io_opp;
     2.6      unsigned long gmfn;
     2.7 +    struct domain *d = v->domain;
     2.8  
     2.9      io_opp = &v->arch.hvm_vcpu.io_op;
    2.10      regs   = &io_opp->io_context;
    2.11 -    vio    = get_vio(v->domain, v->vcpu_id);
    2.12 +    vio    = get_vio(d, v->vcpu_id);
    2.13  
    2.14      p = &vio->vp_ioreq;
    2.15      if ( p->state != STATE_IORESP_READY )
    2.16 @@ -797,11 +798,13 @@ void hvm_io_assist(struct vcpu *v)
    2.17      memcpy(guest_cpu_user_regs(), regs, HVM_CONTEXT_STACK_BYTES);
    2.18  
    2.19      /* Has memory been dirtied? */
    2.20 -    if ( p->dir == IOREQ_READ && p->data_is_ptr )
    2.21 +    if ( (p->dir == IOREQ_READ) && p->data_is_ptr )
    2.22      {
    2.23          gmfn = get_mfn_from_gpfn(paging_gva_to_gfn(v, p->data));
    2.24 -        mark_dirty(v->domain, gmfn);
    2.25 +        mark_dirty(d, gmfn);
    2.26      }
    2.27 +
    2.28 +    vcpu_end_shutdown_deferral(v);
    2.29  }
    2.30  
    2.31  /*
     3.1 --- a/xen/arch/x86/mm.c	Tue Apr 03 10:36:31 2007 +0100
     3.2 +++ b/xen/arch/x86/mm.c	Tue Apr 03 11:44:10 2007 +0100
     3.3 @@ -806,7 +806,8 @@ void put_page_from_l1e(l1_pgentry_t l1e,
     3.4       * (Note that the undestroyable active grants are not a security hole in
     3.5       * Xen. All active grants can safely be cleaned up when the domain dies.)
     3.6       */
     3.7 -    if ( (l1e_get_flags(l1e) & _PAGE_GNTTAB) && !d->is_shutdown && !d->is_dying )
     3.8 +    if ( (l1e_get_flags(l1e) & _PAGE_GNTTAB) &&
     3.9 +         !d->is_shutting_down && !d->is_dying )
    3.10      {
    3.11          MEM_LOG("Attempt to implicitly unmap a granted PTE %" PRIpte,
    3.12                  l1e_get_intpte(l1e));
     4.1 --- a/xen/arch/x86/mm/shadow/multi.c	Tue Apr 03 10:36:31 2007 +0100
     4.2 +++ b/xen/arch/x86/mm/shadow/multi.c	Tue Apr 03 11:44:10 2007 +0100
     4.3 @@ -2823,8 +2823,8 @@ static int sh_page_fault(struct vcpu *v,
     4.4           * are OK, this can only have been caused by a failed
     4.5           * shadow_set_l*e(), which will have crashed the guest.
     4.6           * Get out of the fault handler immediately. */
     4.7 -        ASSERT(d->is_shutdown);
     4.8 -        unmap_walk(v, &gw); 
     4.9 +        ASSERT(d->is_shutting_down);
    4.10 +        unmap_walk(v, &gw);
    4.11          shadow_unlock(d);
    4.12          return 0;
    4.13      }
     5.1 --- a/xen/common/domain.c	Tue Apr 03 10:36:31 2007 +0100
     5.2 +++ b/xen/common/domain.c	Tue Apr 03 11:44:10 2007 +0100
     5.3 @@ -59,6 +59,7 @@ struct domain *alloc_domain(domid_t domi
     5.4      atomic_set(&d->refcnt, 1);
     5.5      spin_lock_init(&d->big_lock);
     5.6      spin_lock_init(&d->page_alloc_lock);
     5.7 +    spin_lock_init(&d->shutdown_lock);
     5.8      INIT_LIST_HEAD(&d->page_list);
     5.9      INIT_LIST_HEAD(&d->xenpage_list);
    5.10  
    5.11 @@ -83,6 +84,45 @@ void free_domain(struct domain *d)
    5.12      xfree(d);
    5.13  }
    5.14  
    5.15 +static void __domain_finalise_shutdown(struct domain *d)
    5.16 +{
    5.17 +    struct vcpu *v;
    5.18 +
    5.19 +    BUG_ON(!spin_is_locked(&d->shutdown_lock));
    5.20 +
    5.21 +    if ( d->is_shut_down )
    5.22 +        return;
    5.23 +
    5.24 +    for_each_vcpu ( d, v )
    5.25 +        if ( !v->paused_for_shutdown )
    5.26 +            return;
    5.27 +
    5.28 +    d->is_shut_down = 1;
    5.29 +
    5.30 +    for_each_vcpu ( d, v )
    5.31 +        vcpu_sleep_nosync(v);
    5.32 +
    5.33 +    send_guest_global_virq(dom0, VIRQ_DOM_EXC);
    5.34 +}
    5.35 +
    5.36 +static void vcpu_check_shutdown(struct vcpu *v)
    5.37 +{
    5.38 +    struct domain *d = v->domain;
    5.39 +
    5.40 +    spin_lock(&d->shutdown_lock);
    5.41 +
    5.42 +    if ( d->is_shutting_down )
    5.43 +    {
    5.44 +        if ( !v->paused_for_shutdown )
    5.45 +            atomic_inc(&v->pause_count);
    5.46 +        v->paused_for_shutdown = 1;
    5.47 +        v->defer_shutdown = 0;
    5.48 +        __domain_finalise_shutdown(d);
    5.49 +    }
    5.50 +
    5.51 +    spin_unlock(&d->shutdown_lock);
    5.52 +}
    5.53 +
    5.54  struct vcpu *alloc_vcpu(
    5.55      struct domain *d, unsigned int vcpu_id, unsigned int cpu_id)
    5.56  {
    5.57 @@ -122,6 +162,9 @@ struct vcpu *alloc_vcpu(
    5.58      if ( vcpu_id != 0 )
    5.59          d->vcpu[v->vcpu_id-1]->next_in_list = v;
    5.60  
    5.61 +    /* Must be called after making new vcpu visible to for_each_vcpu(). */
    5.62 +    vcpu_check_shutdown(v);
    5.63 +
    5.64      return v;
    5.65  }
    5.66  
    5.67 @@ -286,7 +329,7 @@ void domain_kill(struct domain *d)
    5.68  
    5.69  void __domain_crash(struct domain *d)
    5.70  {
    5.71 -    if ( d->is_shutdown )
    5.72 +    if ( d->is_shutting_down )
    5.73      {
    5.74          /* Print nothing: the domain is already shutting down. */
    5.75      }
    5.76 @@ -335,16 +378,73 @@ void domain_shutdown(struct domain *d, u
    5.77      if ( d->domain_id == 0 )
    5.78          dom0_shutdown(reason);
    5.79  
    5.80 -    atomic_inc(&d->pause_count);
    5.81 -    if ( !xchg(&d->is_shutdown, 1) )
    5.82 -        d->shutdown_code = reason;
    5.83 -    else
    5.84 -        domain_unpause(d);
    5.85 +    spin_lock(&d->shutdown_lock);
    5.86 +
    5.87 +    if ( d->is_shutting_down )
    5.88 +    {
    5.89 +        spin_unlock(&d->shutdown_lock);
    5.90 +        return;
    5.91 +    }
    5.92 +
    5.93 +    d->is_shutting_down = 1;
    5.94 +    d->shutdown_code = reason;
    5.95 +
    5.96 +    smp_mb(); /* set shutdown status /then/ check for per-cpu deferrals */
    5.97  
    5.98      for_each_vcpu ( d, v )
    5.99 -        vcpu_sleep_nosync(v);
   5.100 +    {
   5.101 +        if ( v->defer_shutdown )
   5.102 +            continue;
   5.103 +        atomic_inc(&v->pause_count);
   5.104 +        v->paused_for_shutdown = 1;
   5.105 +    }
   5.106 +
   5.107 +    __domain_finalise_shutdown(d);
   5.108 +
   5.109 +    spin_unlock(&d->shutdown_lock);
   5.110 +}
   5.111 +
   5.112 +void domain_resume(struct domain *d)
   5.113 +{
   5.114 +    struct vcpu *v;
   5.115 +
   5.116 +    /*
   5.117 +     * Some code paths assume that shutdown status does not get reset under
   5.118 +     * their feet (e.g., some assertions make this assumption).
   5.119 +     */
   5.120 +    domain_pause(d);
   5.121 +
   5.122 +    spin_lock(&d->shutdown_lock);
   5.123 +
   5.124 +    d->is_shutting_down = d->is_shut_down = 0;
   5.125  
   5.126 -    send_guest_global_virq(dom0, VIRQ_DOM_EXC);
   5.127 +    for_each_vcpu ( d, v )
   5.128 +    {
   5.129 +        if ( v->paused_for_shutdown )
   5.130 +            vcpu_unpause(v);
   5.131 +        v->paused_for_shutdown = 0;
   5.132 +    }
   5.133 +
   5.134 +    spin_unlock(&d->shutdown_lock);
   5.135 +
   5.136 +    domain_unpause(d);
   5.137 +}
   5.138 +
   5.139 +int vcpu_start_shutdown_deferral(struct vcpu *v)
   5.140 +{
   5.141 +    v->defer_shutdown = 1;
   5.142 +    smp_mb(); /* set deferral status /then/ check for shutdown */
   5.143 +    if ( unlikely(v->domain->is_shutting_down) )
   5.144 +        vcpu_check_shutdown(v);
   5.145 +    return v->defer_shutdown;
   5.146 +}
   5.147 +
   5.148 +void vcpu_end_shutdown_deferral(struct vcpu *v)
   5.149 +{
   5.150 +    v->defer_shutdown = 0;
   5.151 +    smp_mb(); /* clear deferral status /then/ check for shutdown */
   5.152 +    if ( unlikely(v->domain->is_shutting_down) )
   5.153 +        vcpu_check_shutdown(v);
   5.154  }
   5.155  
   5.156  void domain_pause_for_debugger(void)
     6.1 --- a/xen/common/domctl.c	Tue Apr 03 10:36:31 2007 +0100
     6.2 +++ b/xen/common/domctl.c	Tue Apr 03 11:44:10 2007 +0100
     6.3 @@ -115,7 +115,7 @@ void getdomaininfo(struct domain *d, str
     6.4  
     6.5      info->flags = flags |
     6.6          (d->is_dying                ? XEN_DOMINF_dying    : 0) |
     6.7 -        (d->is_shutdown             ? XEN_DOMINF_shutdown : 0) |
     6.8 +        (d->is_shut_down            ? XEN_DOMINF_shutdown : 0) |
     6.9          (d->is_paused_by_controller ? XEN_DOMINF_paused   : 0) |
    6.10          d->shutdown_code << XEN_DOMINF_shutdownshift;
    6.11  
    6.12 @@ -287,8 +287,7 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domc
    6.13          if ( d == NULL )
    6.14              break;
    6.15  
    6.16 -        if ( xchg(&d->is_shutdown, 0) )
    6.17 -            domain_unpause(d);
    6.18 +        domain_resume(d);
    6.19          rcu_unlock_domain(d);
    6.20          ret = 0;
    6.21      }
     7.1 --- a/xen/include/xen/sched.h	Tue Apr 03 10:36:31 2007 +0100
     7.2 +++ b/xen/include/xen/sched.h	Tue Apr 03 11:44:10 2007 +0100
     7.3 @@ -114,6 +114,10 @@ struct vcpu
     7.4      bool_t           nmi_pending;
     7.5      /* Avoid NMI reentry by allowing NMIs to be masked for short periods. */
     7.6      bool_t           nmi_masked;
     7.7 +    /* Require shutdown to be deferred for some asynchronous operation? */
     7.8 +    bool_t           defer_shutdown;
     7.9 +    /* VCPU is paused following shutdown request (d->is_shutting_down)? */
    7.10 +    bool_t           paused_for_shutdown;
    7.11  
    7.12      unsigned long    pause_flags;
    7.13      atomic_t         pause_count;
    7.14 @@ -193,7 +197,9 @@ struct domain
    7.15      bool_t           is_paused_by_controller;
    7.16  
    7.17      /* Guest has shut down (inc. reason code)? */
    7.18 -    bool_t           is_shutdown;
    7.19 +    spinlock_t       shutdown_lock;
    7.20 +    bool_t           is_shutting_down; /* in process of shutting down? */
    7.21 +    bool_t           is_shut_down;     /* fully shut down? */
    7.22      int              shutdown_code;
    7.23  
    7.24      atomic_t         pause_count;
    7.25 @@ -331,8 +337,12 @@ struct domain *get_domain_by_id(domid_t 
    7.26  void domain_destroy(struct domain *d);
    7.27  void domain_kill(struct domain *d);
    7.28  void domain_shutdown(struct domain *d, u8 reason);
    7.29 +void domain_resume(struct domain *d);
    7.30  void domain_pause_for_debugger(void);
    7.31  
    7.32 +int vcpu_start_shutdown_deferral(struct vcpu *v);
    7.33 +void vcpu_end_shutdown_deferral(struct vcpu *v);
    7.34 +
    7.35  /*
    7.36   * Mark specified domain as crashed. This function always returns, even if the
    7.37   * caller is the specified domain. The domain is not synchronously descheduled