[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 05/11] x86/altp2m: basic data structures and support routines.
On 09/01/15 21:26, Ed White wrote: > 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); Under what circumstances would a vcpu initialisation happen on current? All initialisation should happen during domain creation. > + > + 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 ) This memory should be allocated from some domain-accounted pool, probably the paging pool (d->->arch.paging.alloc_page()). You can use map_domain_page_global() to get a safe pointer to anchor in d->arch.altp2m_eptp for hardware. > + { > + 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 static inline bool_t altp2mhvm_active(const struct domain *d) { return d->arch.altp2m_active; } Type systems are a nice. We should use them where possible. > + > +/* 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); These should be added in the patch which introduces them. > + > #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 */ Stray space. > + uint64_t veinfo; /* #VE information page guest pfn */ > +}; > + > +#define vcpu_altp2mhvm(v) ((v)->arch.hvm_vcpu.avcpu) static inline as well please. ~Andrew > + > 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) _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |