ia64/xen-unstable

changeset 15194:020530a6ff5c

Implement VCPUOP_register_vcpu_info

This change implements the VCPUOP_register_vcpu_info vcpu_op. This
allows a guest to choose where each VCPU's vcpu_info structure is
placed within its address space, allowing it to put it somewhere which
is easily accessible via some per-cpu data access mechanism.

When changing the mapping of the vcpu info, there's no obvious way to
prevent the other vcpus from getting a stale pointer of the vcpu_info,
which could result in them accessing bad memory (stale pointers to the
shared_info page are not a problem, because its always valid). To
avoid this, we prevent guests from changing the vcpu_info location
more than once, since there's no obvious need to allow them to do this
at this point.

(If we really want to allow guests to update the vcpu_info location
more than once, then some sort of RCU wait between updating the
pointer and performing the unmap may be the way to do it.)

Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com>
author Jeremy Fitzhardinge <jeremy@xensource.com>
date Thu May 24 10:47:27 2007 +0100 (2007-05-24)
parents 471478a1b89e
children 03c213fd2360
files xen/arch/x86/domain.c xen/common/domain.c xen/include/public/vcpu.h xen/include/xen/sched.h
line diff
     1.1 --- a/xen/arch/x86/domain.c	Thu May 24 10:45:03 2007 +0100
     1.2 +++ b/xen/arch/x86/domain.c	Thu May 24 10:47:27 2007 +0100
     1.3 @@ -28,6 +28,7 @@
     1.4  #include <xen/event.h>
     1.5  #include <xen/console.h>
     1.6  #include <xen/percpu.h>
     1.7 +#include <xen/compat.h>
     1.8  #include <asm/regs.h>
     1.9  #include <asm/mc146818rtc.h>
    1.10  #include <asm/system.h>
    1.11 @@ -49,6 +50,8 @@
    1.12  DEFINE_PER_CPU(struct vcpu *, curr_vcpu);
    1.13  DEFINE_PER_CPU(__u64, efer);
    1.14  
    1.15 +static void unmap_vcpu_info(struct vcpu *v);
    1.16 +
    1.17  static void paravirt_ctxt_switch_from(struct vcpu *v);
    1.18  static void paravirt_ctxt_switch_to(struct vcpu *v);
    1.19  
    1.20 @@ -728,11 +731,94 @@ int arch_set_info_guest(
    1.21  
    1.22  int arch_vcpu_reset(struct vcpu *v)
    1.23  {
    1.24 +    unmap_vcpu_info(v);
    1.25      destroy_gdt(v);
    1.26      vcpu_destroy_pagetables(v);
    1.27      return 0;
    1.28  }
    1.29  
    1.30 +/* 
    1.31 + * Unmap the vcpu info page if the guest decided to place it somewhere
    1.32 + * else.  This is only used from arch_vcpu_reset, so there's no need
    1.33 + * to do anything clever.
    1.34 + */
    1.35 +static void
    1.36 +unmap_vcpu_info(struct vcpu *v)
    1.37 +{
    1.38 +    struct domain *d = v->domain;
    1.39 +    unsigned long mfn;
    1.40 +
    1.41 +    if ( v->vcpu_info_mfn == INVALID_MFN )
    1.42 +        return;
    1.43 +
    1.44 +    mfn = v->vcpu_info_mfn;
    1.45 +    unmap_domain_page_global( v->vcpu_info );
    1.46 +
    1.47 +    v->vcpu_info = shared_info_addr(d, vcpu_info[v->vcpu_id]);
    1.48 +    v->vcpu_info_mfn = INVALID_MFN;
    1.49 +
    1.50 +    put_page_and_type(mfn_to_page(mfn));
    1.51 +}
    1.52 +
    1.53 +/* 
    1.54 + * Map a guest page in and point the vcpu_info pointer at it.  This
    1.55 + * makes sure that the vcpu_info is always pointing at a valid piece
    1.56 + * of memory, and it sets a pending event to make sure that a pending
    1.57 + * event doesn't get missed.
    1.58 + */
    1.59 +static int
    1.60 +map_vcpu_info(struct vcpu *v, unsigned long mfn, unsigned offset)
    1.61 +{
    1.62 +    struct domain *d = v->domain;
    1.63 +    void *mapping;
    1.64 +    vcpu_info_t *new_info;
    1.65 +    int i;
    1.66 +
    1.67 +    if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) )
    1.68 +        return -EINVAL;
    1.69 +
    1.70 +    if ( mfn == INVALID_MFN ||
    1.71 +         v->vcpu_info_mfn != INVALID_MFN )
    1.72 +        return -EINVAL;
    1.73 +
    1.74 +    mfn = gmfn_to_mfn(d, mfn);
    1.75 +    if ( !mfn_valid(mfn) ||
    1.76 +         !get_page_and_type(mfn_to_page(mfn), d, PGT_writable_page) )
    1.77 +        return -EINVAL;
    1.78 +
    1.79 +    mapping = map_domain_page_global(mfn);
    1.80 +    if ( mapping == NULL )
    1.81 +    {
    1.82 +        put_page_and_type(mfn_to_page(mfn));
    1.83 +        return -ENOMEM;
    1.84 +    }
    1.85 +
    1.86 +    new_info = (vcpu_info_t *)(mapping + offset);
    1.87 +
    1.88 +    memcpy(new_info, v->vcpu_info, sizeof(*new_info));
    1.89 +
    1.90 +    v->vcpu_info = new_info;
    1.91 +    v->vcpu_info_mfn = mfn;
    1.92 +
    1.93 +    /* make sure all the pointers are uptodate before setting pending */
    1.94 +    wmb();
    1.95 +
    1.96 +    /* Mark everything as being pending just to make sure nothing gets
    1.97 +       lost.  The domain will get a spurious event, but it can
    1.98 +       cope. */
    1.99 +    vcpu_info(v, evtchn_upcall_pending) = 1;
   1.100 +    for ( i = 0; i < BITS_PER_GUEST_LONG(d); i++ )
   1.101 +        set_bit(i, vcpu_info_addr(v, evtchn_pending_sel));
   1.102 +
   1.103 +    /* Only bother to update time for the current vcpu.  If we're
   1.104 +     * operating on another vcpu, then it had better not be running at
   1.105 +     * the time. */
   1.106 +    if ( v == current )
   1.107 +         update_vcpu_system_time(v);
   1.108 +
   1.109 +    return 0;
   1.110 +}
   1.111 +
   1.112  long
   1.113  arch_do_vcpu_op(
   1.114      int cmd, struct vcpu *v, XEN_GUEST_HANDLE(void) arg)
   1.115 @@ -769,6 +855,22 @@ arch_do_vcpu_op(
   1.116          break;
   1.117      }
   1.118  
   1.119 +    case VCPUOP_register_vcpu_info:
   1.120 +    {
   1.121 +        struct domain *d = v->domain;
   1.122 +        struct vcpu_register_vcpu_info info;
   1.123 +
   1.124 +        rc = -EFAULT;
   1.125 +        if ( copy_from_guest(&info, arg, 1) )
   1.126 +            break;
   1.127 +
   1.128 +        LOCK_BIGLOCK(d);
   1.129 +        rc = map_vcpu_info(v, info.mfn, info.offset);
   1.130 +        UNLOCK_BIGLOCK(d);
   1.131 +
   1.132 +        break;
   1.133 +    }
   1.134 +
   1.135      default:
   1.136          rc = -ENOSYS;
   1.137          break;
     2.1 --- a/xen/common/domain.c	Thu May 24 10:45:03 2007 +0100
     2.2 +++ b/xen/common/domain.c	Thu May 24 10:47:27 2007 +0100
     2.3 @@ -136,6 +136,7 @@ struct vcpu *alloc_vcpu(
     2.4  
     2.5      v->domain = d;
     2.6      v->vcpu_id = vcpu_id;
     2.7 +    v->vcpu_info_mfn = INVALID_MFN;
     2.8  
     2.9      v->runstate.state = is_idle_vcpu(v) ? RUNSTATE_running : RUNSTATE_offline;
    2.10      v->runstate.state_entry_time = NOW();
     3.1 --- a/xen/include/public/vcpu.h	Thu May 24 10:45:03 2007 +0100
     3.2 +++ b/xen/include/public/vcpu.h	Thu May 24 10:47:27 2007 +0100
     3.3 @@ -168,8 +168,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_set_singles
     3.4   * The pointer need not be page aligned, but the structure must not
     3.5   * cross a page boundary.
     3.6   *
     3.7 - * If the specified mfn is INVALID_MFN, then it reverts to using the
     3.8 - * vcpu_info structure in the shared_info page.
     3.9 + * This may be called only once per vcpu.
    3.10   */
    3.11  #define VCPUOP_register_vcpu_info   10  /* arg == struct vcpu_info */
    3.12  struct vcpu_register_vcpu_info {
     4.1 --- a/xen/include/xen/sched.h	Thu May 24 10:45:03 2007 +0100
     4.2 +++ b/xen/include/xen/sched.h	Thu May 24 10:47:27 2007 +0100
     4.3 @@ -75,6 +75,7 @@ struct vcpu
     4.4      int              processor;
     4.5  
     4.6      vcpu_info_t     *vcpu_info;
     4.7 +    unsigned long    vcpu_info_mfn;
     4.8  
     4.9      struct domain   *domain;
    4.10