[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 05/11] x86/altp2m: basic data structures and support routines.



Add the basic data structures needed to support alternate p2m's and
the functions to initialise them and tear them down.

Although Intel hardware can handle 512 EPTP's per hardware thread
concurrently, only 10 per domain are supported in this patch for
performance reasons.

The iterator in hap_enable() does need to handle 512, so that is now
uint16_t.

Signed-off-by: Ed White <edmund.h.white@xxxxxxxxx>
---
 xen/arch/x86/hvm/Makefile           |   3 +-
 xen/arch/x86/hvm/altp2mhvm.c        |  77 +++++++++++++++++++++++++++
 xen/arch/x86/hvm/hvm.c              |  21 ++++++++
 xen/arch/x86/mm/hap/Makefile        |   1 +
 xen/arch/x86/mm/hap/altp2m_hap.c    |  66 +++++++++++++++++++++++
 xen/arch/x86/mm/hap/hap.c           |  30 ++++++++++-
 xen/arch/x86/mm/mm-locks.h          |   4 ++
 xen/arch/x86/mm/p2m.c               | 102 ++++++++++++++++++++++++++++++++++++
 xen/include/asm-x86/domain.h        |   7 +++
 xen/include/asm-x86/hvm/altp2mhvm.h |  36 +++++++++++++
 xen/include/asm-x86/hvm/hvm.h       |  17 ++++++
 xen/include/asm-x86/hvm/vcpu.h      |   9 ++++
 xen/include/asm-x86/p2m.h           |  22 ++++++++
 13 files changed, 393 insertions(+), 2 deletions(-)
 create mode 100644 xen/arch/x86/hvm/altp2mhvm.c
 create mode 100644 xen/arch/x86/mm/hap/altp2m_hap.c
 create mode 100644 xen/include/asm-x86/hvm/altp2mhvm.h

diff --git a/xen/arch/x86/hvm/Makefile b/xen/arch/x86/hvm/Makefile
index eea5555..5bf8b4f 100644
--- a/xen/arch/x86/hvm/Makefile
+++ b/xen/arch/x86/hvm/Makefile
@@ -22,4 +22,5 @@ obj-y += vlapic.o
 obj-y += vmsi.o
 obj-y += vpic.o
 obj-y += vpt.o
-obj-y += vpmu.o
\ No newline at end of file
+obj-y += vpmu.o
+obj-y += altp2mhvm.o
diff --git a/xen/arch/x86/hvm/altp2mhvm.c b/xen/arch/x86/hvm/altp2mhvm.c
new file mode 100644
index 0000000..fa0af0c
--- /dev/null
+++ b/xen/arch/x86/hvm/altp2mhvm.c
@@ -0,0 +1,77 @@
+/*
+ * Alternate p2m HVM
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <asm/hvm/support.h>
+#include <asm/hvm/hvm.h>
+#include <asm/p2m.h>
+#include <asm/hvm/altp2mhvm.h>
+
+void
+altp2mhvm_vcpu_reset(struct vcpu *v)
+{
+    struct altp2mvcpu *av = &vcpu_altp2mhvm(v);
+
+    av->p2midx = 0;
+    av->veinfo = 0;
+
+    if ( hvm_funcs.ahvm_vcpu_reset )
+        hvm_funcs.ahvm_vcpu_reset(v);
+}
+
+int
+altp2mhvm_vcpu_initialise(struct vcpu *v)
+{
+    int rc = -EOPNOTSUPP;
+
+    if ( v != current )
+        vcpu_pause(v);
+
+    if ( !hvm_funcs.ahvm_vcpu_initialise ||
+         (hvm_funcs.ahvm_vcpu_initialise(v) == 0) )
+    {
+        rc = 0;
+        altp2mhvm_vcpu_reset(v);
+        cpumask_set_cpu(v->vcpu_id, p2m_get_altp2m(v)->dirty_cpumask);
+
+        ahvm_vcpu_update_eptp(v);
+    }
+
+    if ( v != current )
+        vcpu_unpause(v);
+
+    return rc;
+}
+
+void
+altp2mhvm_vcpu_destroy(struct vcpu *v)
+{
+    if ( v != current )
+        vcpu_pause(v);
+
+    if ( hvm_funcs.ahvm_vcpu_destroy )
+        hvm_funcs.ahvm_vcpu_destroy(v);
+
+    cpumask_clear_cpu(v->processor, p2m_get_altp2m(v)->dirty_cpumask);
+    altp2mhvm_vcpu_reset(v);
+
+    ahvm_vcpu_update_eptp(v);
+    ahvm_vcpu_update_vmfunc_ve(v);
+
+    if ( v != current )
+        vcpu_unpause(v);
+}
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index b89e9d2..e8787cc 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -60,6 +60,7 @@
 #include <asm/hvm/cacheattr.h>
 #include <asm/hvm/trace.h>
 #include <asm/hvm/nestedhvm.h>
+#include <asm/hvm/altp2mhvm.h>
 #include <asm/mtrr.h>
 #include <asm/apic.h>
 #include <public/sched.h>
@@ -2290,6 +2291,7 @@ void hvm_vcpu_destroy(struct vcpu *v)
 
     hvm_all_ioreq_servers_remove_vcpu(d, v);
 
+    altp2mhvm_vcpu_destroy(v);
     nestedhvm_vcpu_destroy(v);
 
     free_compat_arg_xlat(v);
@@ -6377,6 +6379,25 @@ bool_t hvm_altp2m_supported()
     return hvm_funcs.altp2m_supported;
 }
 
+void ahvm_vcpu_update_eptp(struct vcpu *v)
+{
+    if (hvm_funcs.ahvm_vcpu_update_eptp)
+        hvm_funcs.ahvm_vcpu_update_eptp(v);
+}
+
+void ahvm_vcpu_update_vmfunc_ve(struct vcpu *v)
+{
+    if (hvm_funcs.ahvm_vcpu_update_vmfunc_ve)
+        hvm_funcs.ahvm_vcpu_update_vmfunc_ve(v);
+}
+
+bool_t ahvm_vcpu_emulate_ve(struct vcpu *v)
+{
+    if (hvm_funcs.ahvm_vcpu_emulate_ve)
+        return hvm_funcs.ahvm_vcpu_emulate_ve(v);
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/x86/mm/hap/Makefile b/xen/arch/x86/mm/hap/Makefile
index 68f2bb5..216cd90 100644
--- a/xen/arch/x86/mm/hap/Makefile
+++ b/xen/arch/x86/mm/hap/Makefile
@@ -4,6 +4,7 @@ obj-y += guest_walk_3level.o
 obj-$(x86_64) += guest_walk_4level.o
 obj-y += nested_hap.o
 obj-y += nested_ept.o
+obj-y += altp2m_hap.o
 
 guest_walk_%level.o: guest_walk.c Makefile
        $(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
diff --git a/xen/arch/x86/mm/hap/altp2m_hap.c b/xen/arch/x86/mm/hap/altp2m_hap.c
new file mode 100644
index 0000000..c2cdc42
--- /dev/null
+++ b/xen/arch/x86/mm/hap/altp2m_hap.c
@@ -0,0 +1,66 @@
+/******************************************************************************
+ * arch/x86/mm/hap/altp2m_hap.c
+ *
+ * Copyright (c) 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <xen/mem_event.h>
+#include <xen/event.h>
+#include <public/mem_event.h>
+#include <asm/domain.h>
+#include <asm/page.h>
+#include <asm/paging.h>
+#include <asm/p2m.h>
+#include <asm/mem_sharing.h>
+#include <asm/hap.h>
+#include <asm/hvm/support.h>
+
+#include "private.h"
+
+/* Override macros from asm/page.h to make them work with mfn_t */
+#undef mfn_valid
+#define mfn_valid(_mfn) __mfn_valid(mfn_x(_mfn))
+#undef page_to_mfn
+#define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg))
+
+void
+altp2m_write_p2m_entry(struct p2m_domain *p2m, unsigned long gfn,
+    l1_pgentry_t *p, l1_pgentry_t new, unsigned int level)
+{
+    struct domain *d = p2m->domain;
+    uint32_t old_flags;
+
+    paging_lock(d);
+
+    old_flags = l1e_get_flags(*p);
+    safe_write_pte(p, new);
+
+    if (old_flags & _PAGE_PRESENT)
+        flush_tlb_mask(p2m->dirty_cpumask);
+
+    paging_unlock(d);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c
index abf3d7a..8fe0650 100644
--- a/xen/arch/x86/mm/hap/hap.c
+++ b/xen/arch/x86/mm/hap/hap.c
@@ -439,7 +439,7 @@ void hap_domain_init(struct domain *d)
 int hap_enable(struct domain *d, u32 mode)
 {
     unsigned int old_pages;
-    uint8_t i;
+    uint16_t i;
     int rv = 0;
 
     domain_pause(d);
@@ -485,6 +485,23 @@ int hap_enable(struct domain *d, u32 mode)
            goto out;
     }
 
+    /* Init alternate p2m data */
+    if ( (d->arch.altp2m_eptp = alloc_xenheap_page()) == NULL )
+    {
+        rv = -ENOMEM;
+        goto out;
+    }
+    for (i = 0; i < 512; i++)
+        d->arch.altp2m_eptp[i] = ~0ul;
+
+    for (i = 0; i < MAX_ALTP2M; i++) {
+        rv = p2m_alloc_table(d->arch.altp2m_p2m[i]);
+        if ( rv != 0 )
+           goto out;
+    }
+
+    d->arch.altp2m_active = 0;
+
     /* Now let other users see the new mode */
     d->arch.paging.mode = mode | PG_HAP_enable;
 
@@ -497,6 +514,17 @@ void hap_final_teardown(struct domain *d)
 {
     uint8_t i;
 
+    d->arch.altp2m_active = 0;
+
+    if ( d->arch.altp2m_eptp ) {
+        free_xenheap_page(d->arch.altp2m_eptp);
+        d->arch.altp2m_eptp = NULL;
+    }
+
+    for (i = 0; i < MAX_ALTP2M; i++) {
+        p2m_teardown(d->arch.altp2m_p2m[i]);
+    }
+
     /* Destroy nestedp2m's first */
     for (i = 0; i < MAX_NESTEDP2M; i++) {
         p2m_teardown(d->arch.nested_p2m[i]);
diff --git a/xen/arch/x86/mm/mm-locks.h b/xen/arch/x86/mm/mm-locks.h
index 769f7bc..a0faca3 100644
--- a/xen/arch/x86/mm/mm-locks.h
+++ b/xen/arch/x86/mm/mm-locks.h
@@ -209,6 +209,10 @@ declare_mm_lock(nestedp2m)
 #define nestedp2m_lock(d)   mm_lock(nestedp2m, &(d)->arch.nested_p2m_lock)
 #define nestedp2m_unlock(d) mm_unlock(&(d)->arch.nested_p2m_lock)
 
+declare_mm_lock(altp2m)
+#define altp2m_lock(d)   mm_lock(altp2m, &(d)->arch.altp2m_lock)
+#define altp2m_unlock(d) mm_unlock(&(d)->arch.altp2m_lock)
+
 /* P2M lock (per-p2m-table)
  *
  * This protects all queries and updates to the p2m table.
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index 49b66fb..3c6049b 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -35,6 +35,7 @@
 #include <asm/hvm/vmx/vmx.h> /* ept_p2m_init() */
 #include <asm/mem_sharing.h>
 #include <asm/hvm/nestedhvm.h>
+#include <asm/hvm/altp2mhvm.h>
 #include <asm/hvm/svm/amd-iommu-proto.h>
 #include <xsm/xsm.h>
 
@@ -182,6 +183,44 @@ static void p2m_teardown_nestedp2m(struct domain *d)
     }
 }
 
+static void p2m_teardown_altp2m(struct domain *d);
+
+static int p2m_init_altp2m(struct domain *d)
+{
+    uint8_t i;
+    struct p2m_domain *p2m;
+
+    mm_lock_init(&d->arch.altp2m_lock);
+    for (i = 0; i < MAX_ALTP2M; i++)
+    {
+        d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
+        if ( p2m == NULL )
+        {
+            p2m_teardown_altp2m(d);
+            return -ENOMEM;
+        }
+        p2m->write_p2m_entry = altp2m_write_p2m_entry;
+        p2m->alternate = 1;
+    }
+
+    return 0;
+}
+
+static void p2m_teardown_altp2m(struct domain *d)
+{
+    uint8_t i;
+    struct p2m_domain *p2m;
+
+    for (i = 0; i < MAX_ALTP2M; i++)
+    {
+        if ( !d->arch.altp2m_p2m[i] )
+            continue;
+        p2m = d->arch.altp2m_p2m[i];
+        p2m_free_one(p2m);
+        d->arch.altp2m_p2m[i] = NULL;
+    }
+}
+
 int p2m_init(struct domain *d)
 {
     int rc;
@@ -195,7 +234,14 @@ int p2m_init(struct domain *d)
      * (p2m_init runs too early for HVM_PARAM_* options) */
     rc = p2m_init_nestedp2m(d);
     if ( rc )
+    {
         p2m_teardown_hostp2m(d);
+        return rc;
+    }
+
+    rc = p2m_init_altp2m(d);
+    if ( rc )
+        p2m_teardown_altp2m(d);
 
     return rc;
 }
@@ -1891,6 +1937,62 @@ int unmap_mmio_regions(struct domain *d,
     return err;
 }
 
+bool_t p2m_find_altp2m_by_eptp(struct domain *d, uint64_t eptp, unsigned long 
*idx)
+{
+    struct p2m_domain *p2m;
+    struct ept_data *ept;
+    bool_t rc = 0;
+    uint16_t i;
+
+    altp2m_lock(d);
+
+    for ( i = 0; i < MAX_ALTP2M; i++ )
+    {
+        if ( d->arch.altp2m_eptp[i] == ~0ul )
+            continue;
+
+        p2m = d->arch.altp2m_p2m[i];
+        ept = &p2m->ept;
+
+        if ( eptp != ept_get_eptp(ept) )
+            continue;
+
+        *idx = i;
+        rc = 1;
+
+        break;
+    }
+
+    altp2m_unlock(d);
+    return rc;
+}
+
+bool_t p2m_switch_vcpu_altp2m_by_id(struct vcpu *v, uint16_t idx)
+{
+    struct domain *d = v->domain;
+    bool_t rc = 0;
+
+    if ( idx > MAX_ALTP2M )
+        return rc;
+
+    altp2m_lock(d);
+
+    if ( d->arch.altp2m_eptp[idx] != ~0ul )
+    {
+        if ( idx != vcpu_altp2mhvm(v).p2midx )
+        {
+            cpumask_clear_cpu(v->vcpu_id, p2m_get_altp2m(v)->dirty_cpumask);
+            vcpu_altp2mhvm(v).p2midx = idx;
+            cpumask_set_cpu(v->vcpu_id, p2m_get_altp2m(v)->dirty_cpumask);
+            ahvm_vcpu_update_eptp(v);
+        }
+        rc = 1;
+    }
+
+    altp2m_unlock(d);
+    return rc;
+}
+
 /*** Audit ***/
 
 #if P2M_AUDIT
diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
index 6a77a93..a0e9e90 100644
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -230,6 +230,7 @@ struct paging_vcpu {
 typedef xen_domctl_cpuid_t cpuid_input_t;
 
 #define MAX_NESTEDP2M 10
+#define MAX_ALTP2M    10
 struct p2m_domain;
 struct time_scale {
     int shift;
@@ -274,6 +275,12 @@ struct arch_domain
     struct p2m_domain *nested_p2m[MAX_NESTEDP2M];
     mm_lock_t nested_p2m_lock;
 
+    /* altp2m: allow multiple copies of host p2m */
+    bool_t altp2m_active;
+    struct p2m_domain *altp2m_p2m[MAX_ALTP2M];
+    mm_lock_t altp2m_lock;
+    uint64_t *altp2m_eptp;
+
     /* NB. protected by d->event_lock and by irq_desc[irq].lock */
     struct radix_tree_root irq_pirq;
 
diff --git a/xen/include/asm-x86/hvm/altp2mhvm.h 
b/xen/include/asm-x86/hvm/altp2mhvm.h
new file mode 100644
index 0000000..919986e
--- /dev/null
+++ b/xen/include/asm-x86/hvm/altp2mhvm.h
@@ -0,0 +1,36 @@
+/*
+ * Alternate p2m HVM
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _HVM_ALTP2M_H
+#define _HVM_ALTP2M_H
+
+#include <xen/types.h>         /* for uintNN_t */
+#include <xen/sched.h>         /* for struct vcpu, struct domain */
+#include <asm/hvm/vcpu.h>      /* for vcpu_altp2mhvm */
+
+/* Alternate p2m HVM on/off per domain */
+#define altp2mhvm_active(d) \
+    d->arch.altp2m_active
+
+/* Alternate p2m VCPU */
+int altp2mhvm_vcpu_initialise(struct vcpu *v);
+void altp2mhvm_vcpu_destroy(struct vcpu *v);
+void altp2mhvm_vcpu_reset(struct vcpu *v);
+
+#endif /* _HVM_ALTP2M_H */
+
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index 7115a68..32d1d02 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -210,6 +210,14 @@ struct hvm_function_table {
                                   uint32_t *ecx, uint32_t *edx);
 
     void (*enable_msr_exit_interception)(struct domain *d);
+
+    /* Alternate p2m */
+    int (*ahvm_vcpu_initialise)(struct vcpu *v);
+    void (*ahvm_vcpu_destroy)(struct vcpu *v);
+    int (*ahvm_vcpu_reset)(struct vcpu *v);
+    void (*ahvm_vcpu_update_eptp)(struct vcpu *v);
+    void (*ahvm_vcpu_update_vmfunc_ve)(struct vcpu *v);
+    bool_t (*ahvm_vcpu_emulate_ve)(struct vcpu *v);
 };
 
 extern struct hvm_function_table hvm_funcs;
@@ -531,6 +539,15 @@ extern bool_t opt_hvm_fep;
 #define opt_hvm_fep 0
 #endif
 
+/* updates the current EPTP in VMCS */
+void ahvm_vcpu_update_eptp(struct vcpu *v);
+
+/* updates VMCS fields related to VMFUNC and #VE */
+void ahvm_vcpu_update_vmfunc_ve(struct vcpu *v);
+
+/* emulates #VE */
+bool_t ahvm_vcpu_emulate_ve(struct vcpu *v);
+
 #endif /* __ASM_X86_HVM_HVM_H__ */
 
 /*
diff --git a/xen/include/asm-x86/hvm/vcpu.h b/xen/include/asm-x86/hvm/vcpu.h
index 01e0665..9302d40 100644
--- a/xen/include/asm-x86/hvm/vcpu.h
+++ b/xen/include/asm-x86/hvm/vcpu.h
@@ -118,6 +118,13 @@ struct nestedvcpu {
 
 #define vcpu_nestedhvm(v) ((v)->arch.hvm_vcpu.nvcpu)
 
+struct altp2mvcpu {
+    uint16_t    p2midx ;        /* alternate p2m index */
+    uint64_t    veinfo;         /* #VE information page guest pfn */
+};
+
+#define vcpu_altp2mhvm(v) ((v)->arch.hvm_vcpu.avcpu)
+
 struct hvm_vcpu {
     /* Guest control-register and EFER values, just as the guest sees them. */
     unsigned long       guest_cr[5];
@@ -163,6 +170,8 @@ struct hvm_vcpu {
 
     struct nestedvcpu   nvcpu;
 
+    struct altp2mvcpu   avcpu;
+
     struct mtrr_state   mtrr;
     u64                 pat_cr;
 
diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h
index 8193901..9fb5ba0 100644
--- a/xen/include/asm-x86/p2m.h
+++ b/xen/include/asm-x86/p2m.h
@@ -688,6 +688,28 @@ void nestedp2m_write_p2m_entry(struct p2m_domain *p2m, 
unsigned long gfn,
     l1_pgentry_t *p, l1_pgentry_t new, unsigned int level);
 
 /*
+ * Alternate p2m: shadow p2m tables used for alternate memory views
+ */
+
+void altp2m_write_p2m_entry(struct p2m_domain *p2m, unsigned long gfn,
+    l1_pgentry_t *p, l1_pgentry_t new, unsigned int level);
+
+/* get current alternate p2m table */
+static inline struct p2m_domain *p2m_get_altp2m(struct vcpu *v)
+{
+    struct domain *d = v->domain;
+    uint16_t index = vcpu_altp2mhvm(v).p2midx;
+
+    return d->arch.altp2m_p2m[index];
+}
+
+/* Locate an alternate p2m by its EPTP */
+bool_t p2m_find_altp2m_by_eptp(struct domain *d, uint64_t eptp, unsigned long 
*idx);
+
+/* Switch alternate p2m for a single vcpu */
+bool_t p2m_switch_vcpu_altp2m_by_id(struct vcpu *v, uint16_t idx);
+
+/*
  * p2m type to IOMMU flags
  */
 static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt)
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.