[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v8 3/8] libxl: support mapping static shared memory areas during domain creation
Hi, Stefano On Tue, Oct 9, 2018 at 2:39 AM Stefano Stabellini <sstabellini@xxxxxxxxxx> wrote: > > From: Zhongze Liu <blackskygg@xxxxxxxxx> > > Author: Zhongze Liu <blackskygg@xxxxxxxxx> > > Add libxl__sshm_add to map shared pages from one DomU to another, The mapping > process involves the following steps: > > * Set defaults and check for further errors in the static_shm configs: > overlapping areas, invalid ranges, duplicated master domain, > not page aligned, no master domain etc. > * Use xc_domain_add_to_physmap_batch to map the shared pages to slaves > * When some of the pages can't be successfully mapped, roll back any > successfully mapped pages so that the system stays in a consistent state. > * Write information about static shared memory areas into the appropriate > xenstore paths and set the refcount of the shared region accordingly. > > Temporarily mark this as unsupported on x86 because calling p2m_add_foreign on > two domU's is currently not allowd on x86 (see the comments in > x86/mm/p2m.c:p2m_add_foreign for more details). > > This is for the proposal "Allow setting up shared memory areas between VMs > from xl config file" (see [1]). > > [1] https://lists.xen.org/archives/html/xen-devel/2017-08/msg03242.html > > Signed-off-by: Zhongze Liu <blackskygg@xxxxxxxxx> > Signed-off-by: Stefano Stabellini <stefanos@xxxxxxxxxx> > > Cc: Wei Liu <wei.liu2@xxxxxxxxxx> > Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> > Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx> > Cc: Julien Grall <julien.grall@xxxxxxx> > Cc: xen-devel@xxxxxxxxxxxxx > --- > Changes in v5: > - fix typos > - add comments > - add value checks (including alignment checks) in sshm_setdefaults > --- > tools/libxl/Makefile | 3 +- > tools/libxl/libxl_arch.h | 6 + > tools/libxl/libxl_arm.c | 15 ++ > tools/libxl/libxl_create.c | 27 +++ > tools/libxl/libxl_internal.h | 14 ++ > tools/libxl/libxl_sshm.c | 405 > +++++++++++++++++++++++++++++++++++++++++++ > tools/libxl/libxl_x86.c | 19 ++ > 7 files changed, 488 insertions(+), 1 deletion(-) > create mode 100644 tools/libxl/libxl_sshm.c > > diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile > index 6da342e..53af186 100644 > --- a/tools/libxl/Makefile > +++ b/tools/libxl/Makefile > @@ -140,7 +140,8 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o > libxl_dm.o libxl_pci.o \ > libxl_vtpm.o libxl_nic.o libxl_disk.o libxl_console.o > \ > libxl_cpupool.o libxl_mem.o libxl_sched.o > libxl_tmem.o \ > libxl_9pfs.o libxl_domain.o libxl_vdispl.o \ > - libxl_pvcalls.o libxl_vsnd.o libxl_vkb.o > $(LIBXL_OBJS-y) > + libxl_pvcalls.o libxl_vsnd.o libxl_vkb.o libxl_sshm.o > \ > + $(LIBXL_OBJS-y) > LIBXL_OBJS += libxl_genid.o > LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o > > diff --git a/tools/libxl/libxl_arch.h b/tools/libxl/libxl_arch.h > index 930570e..63c26cc 100644 > --- a/tools/libxl/libxl_arch.h > +++ b/tools/libxl/libxl_arch.h > @@ -73,6 +73,12 @@ int libxl__arch_extra_memory(libxl__gc *gc, > const libxl_domain_build_info *info, > uint64_t *out); > > +_hidden > +bool libxl__arch_domain_support_sshm(const libxl_domain_build_info *b_info); > + > +_hidden > +int libxl__arch_domain_sshm_cachepolicy_setdefault(libxl_static_shm *sshm); > + > #if defined(__i386__) || defined(__x86_64__) > > #define LAPIC_BASE_ADDRESS 0xfee00000 > diff --git a/tools/libxl/libxl_arm.c b/tools/libxl/libxl_arm.c > index 25dc3de..054ad58 100644 > --- a/tools/libxl/libxl_arm.c > +++ b/tools/libxl/libxl_arm.c > @@ -1138,6 +1138,21 @@ void > libxl__arch_domain_build_info_setdefault(libxl__gc *gc, > libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_PVH); > } > > +bool libxl__arch_domain_support_sshm(const libxl_domain_build_info *b_info) > +{ > + return true; > +} > + > +int libxl__arch_domain_sshm_cachepolicy_setdefault(libxl_static_shm *sshm) > +{ > + if (sshm->cache_policy == LIBXL_SSHM_CACHEPOLICY_UNKNOWN) > + sshm->cache_policy = LIBXL_SSHM_CACHEPOLICY_ARM_NORMAL; > + if (sshm->cache_policy >= LIBXL_SSHM_CACHEPOLICY_X86_NORMAL) > + return ERROR_INVAL; > + > + return 0; > +} > + > /* > * Local variables: > * mode: C > diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c > index 320dbed..45ae9e4 100644 > --- a/tools/libxl/libxl_create.c > +++ b/tools/libxl/libxl_create.c > @@ -513,6 +513,14 @@ int libxl__domain_build(libxl__gc *gc, > ret = ERROR_INVAL; > goto out; > } > + > + /* The p2m has been setup, we could map the static shared memory now. */ > + ret = libxl__sshm_add(gc, domid, d_config->sshms, d_config->num_sshms); > + if (ret != 0) { > + LOG(ERROR, "failed to map static shared memory"); > + goto out; > + } > + > ret = libxl__build_post(gc, domid, info, state, vments, localents); > out: > return ret; > @@ -949,6 +957,25 @@ static void initiate_domain_create(libxl__egc *egc, > goto error_out; > } > > + if (d_config->num_sshms != 0 && > + !libxl__arch_domain_support_sshm(&d_config->b_info)) { > + LOGD(ERROR, domid, "static_shm is not supported by this domain > type."); > + ret = ERROR_INVAL; > + goto error_out; > + } > + > + for (i = 0; i < d_config->num_sshms; ++i) { > + ret = libxl__sshm_setdefault(gc, domid, &d_config->sshms[i]); > + if (ret) { > + LOGD(ERROR, domid, "Unable to set defaults for static shm"); > + goto error_out; > + } > + } > + > + ret = libxl__sshm_check_overlap(gc, domid, > + d_config->sshms, d_config->num_sshms); > + if (ret) goto error_out; > + > ret = libxl__domain_make(gc, d_config, &dcs->build_state, &domid); > if (ret) { > LOGD(ERROR, domid, "cannot make domain: %d", ret); > diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h > index 43947b1..6f31a3d 100644 > --- a/tools/libxl/libxl_internal.h > +++ b/tools/libxl/libxl_internal.h > @@ -4434,6 +4434,20 @@ static inline const char > *libxl__qemu_qmp_path(libxl__gc *gc, int domid) > #endif > > /* > + * Set up static shared ram pages for HVM domains to communicate > + * > + * This function should only be called after the memory map is constructed > + * and before any further memory access. > + */ > +_hidden int libxl__sshm_add(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshm, int len); > + > +_hidden int libxl__sshm_check_overlap(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshms, int len); > +_hidden int libxl__sshm_setdefault(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshm); > + > +/* > * Local variables: > * mode: C > * c-basic-offset: 4 > diff --git a/tools/libxl/libxl_sshm.c b/tools/libxl/libxl_sshm.c > new file mode 100644 > index 0000000..f61b80c > --- /dev/null > +++ b/tools/libxl/libxl_sshm.c > @@ -0,0 +1,405 @@ > +#include "libxl_osdeps.h" > +#include "libxl_internal.h" > +#include "libxl_arch.h" > + > +#define SSHM_PATH(id) GCSPRINTF("/libxl/static_shm/%s", id) > + > +#define SSHM_ERROR(domid, sshmid, f, ...) \ > + LOGD(ERROR, domid, "static_shm id = %s: " f, sshmid, ##__VA_ARGS__) > + > + > +/* Set default values for libxl_static_shm */ > +int libxl__sshm_setdefault(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshm) > +{ > + int rc; > + > + if (sshm->role != LIBXL_SSHM_ROLE_SLAVE && > + sshm->role != LIBXL_SSHM_ROLE_MASTER) > + return ERROR_INVAL; > + if (sshm->begin & ~XC_PAGE_MASK || > + sshm->size & ~XC_PAGE_MASK || > + (sshm->offset != LIBXL_SSHM_RANGE_UNKNOWN && > + sshm->offset & ~XC_PAGE_MASK)) { > + SSHM_ERROR(domid, sshm->id, > + "begin/size/offset is not a multiple of 4K"); > + return ERROR_INVAL; > + } > + > + /* role-specific checks */ > + if (sshm->role == LIBXL_SSHM_ROLE_SLAVE) { > + if (sshm->offset == LIBXL_SSHM_RANGE_UNKNOWN) > + sshm->offset = 0; > + if (sshm->cache_policy != LIBXL_SSHM_CACHEPOLICY_UNKNOWN) { > + SSHM_ERROR(domid, sshm->id, > + "cache_policy is only applicable to master domains"); > + rc = ERROR_INVAL; > + goto out; > + } > + } else { > + if (sshm->offset != LIBXL_SSHM_RANGE_UNKNOWN) { > + SSHM_ERROR(domid, sshm->id, > + "offset is only applicable to slave domains"); > + rc = ERROR_INVAL; > + goto out; > + } > + > + rc = libxl__arch_domain_sshm_cachepolicy_setdefault(sshm); > + if (rc) { > + SSHM_ERROR(domid, sshm->id, > + "cache policy not supported on this platform"); > + goto out; > + } > + } > + > + rc = 0; > +out: > + return rc; > +} > + > +/* Comparator for sorting sshm ranges by sshm->begin */ > +static int sshm_range_cmp(const void *a, const void *b) > +{ > + libxl_static_shm *const *sshma = a, *const *sshmb = b; > + return (*sshma)->begin > (*sshmb)->begin ? 1 : -1; > +} > + > +/* Check if the sshm slave configs in @sshm overlap */ > +int libxl__sshm_check_overlap(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshms, int len) > +{ > + > + const libxl_static_shm **slave_sshms = NULL; > + int num_slaves; > + int i; > + > + if (!len) return 0; > + > + slave_sshms = libxl__calloc(gc, len, sizeof(slave_sshms[0])); > + num_slaves = 0; > + for (i = 0; i < len; ++i) { > + if (sshms[i].role == LIBXL_SSHM_ROLE_SLAVE) > + slave_sshms[num_slaves++] = sshms + i; > + } > + qsort(slave_sshms, num_slaves, sizeof(slave_sshms[0]), sshm_range_cmp); > + > + for (i = 0; i < num_slaves - 1; ++i) { > + if (slave_sshms[i+1]->begin < > + slave_sshms[i]->begin + slave_sshms[i]->size) { > + SSHM_ERROR(domid, slave_sshms[i+1]->id, "slave ranges overlap."); > + return ERROR_INVAL; > + } > + } > + > + return 0; > +} > + > +/* libxl__sshm_do_map -- map pages into slave's physmap > + * > + * This functions maps > + * master gfn: [@msshm->begin + @sshm->offset, > + * @msshm->begin + @msshm->size + @sshm->offset) > + * into > + * slave gfn: [@sshm->begin, @sshm->begin + @sshm->size) > + * > + * The gfns of the pages that are successfully mapped will be stored > + * in @mapped, and the number of the gfns will be stored in @nmapped. > + * > + * The caller has to guarantee that all the values are page-aligned. > + */ > +static int libxl__sshm_do_map(libxl__gc *gc, uint32_t mid, uint32_t sid, > + libxl_static_shm *sshm, libxl_static_shm > *msshm, > + xen_pfn_t *mapped, unsigned int *nmapped) > +{ > + int rc; > + int i; > + xen_pfn_t num_mpages, num_spages, num_success, offset; > + int *errs; > + xen_ulong_t *idxs; > + xen_pfn_t *gpfns; > + > + num_mpages = (msshm->size) >> XC_PAGE_SHIFT; > + num_spages = (sshm->size) >> XC_PAGE_SHIFT; > + offset = sshm->offset >> XC_PAGE_SHIFT; > + > + /* Check range. Test offset < mpages first to avoid overflow */ > + if ((offset >= num_mpages) || (num_mpages - offset < num_spages)) { > + SSHM_ERROR(sid, sshm->id, "exceeds master's address space."); > + rc = ERROR_INVAL; > + goto out; > + } > + > + /* fill out the gfn's and do the mapping */ > + errs = libxl__calloc(gc, num_spages, sizeof(int)); > + idxs = libxl__calloc(gc, num_spages, sizeof(xen_ulong_t)); > + gpfns = libxl__calloc(gc, num_spages, sizeof(xen_pfn_t)); > + for (i = 0; i < num_spages; i++) { > + idxs[i] = (msshm->begin >> XC_PAGE_SHIFT) + offset + i; > + gpfns[i]= (sshm->begin >> XC_PAGE_SHIFT) + i; > + } > + rc = xc_domain_add_to_physmap_batch(CTX->xch, > + sid, mid, > + XENMAPSPACE_gmfn_share, > + num_spages, > + idxs, gpfns, errs); > + > + num_success = 0; > + for (i = 0; i < num_spages; i++) { > + if (errs[i]) { > + SSHM_ERROR(sid, sshm->id, > + "can't map at address 0x%"PRIx64".", > + gpfns[i] << XC_PAGE_SHIFT); > + rc = ERROR_FAIL; > + } else { > + mapped[num_success++] = gpfns[i]; > + } > + } > + *nmapped = num_success; > + if (rc) goto out; > + > + rc = 0; > +out: > + return rc; > +} > + > +/* Xenstore ops are protected by a transaction */ > +static int libxl__sshm_incref(libxl__gc *gc, xs_transaction_t xt, > + const char *sshm_path) > +{ > + int rc, count; > + const char *count_path, *count_string; > + > + count_path = GCSPRINTF("%s/usercnt", sshm_path); > + rc = libxl__xs_read_checked(gc, xt, count_path, &count_string); > + if (rc) goto out; > + count = atoi(count_string); > + > + count_string = GCSPRINTF("%d", count+1); > + rc = libxl__xs_write_checked(gc, xt, count_path, count_string); > + if (rc) goto out; > + > + rc = 0; > +out: > + return rc; > +} > + > +static int libxl__sshm_add_slave(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshm) > +{ > + int rc, i; > + const char *sshm_path, *slave_path; > + const char *dom_path, *dom_sshm_path, *dom_role_path; > + const char *xs_value; > + char *ents[9]; > + libxl_static_shm master_sshm; > + uint32_t master_domid; > + xen_pfn_t *mapped; > + unsigned int nmapped = 0; > + xs_transaction_t xt = XBT_NULL; > + bool isretry; > + > + sshm_path = SSHM_PATH(sshm->id); > + slave_path = GCSPRINTF("%s/slaves/%"PRIu32, sshm_path, domid); > + dom_path = libxl__xs_get_dompath(gc, domid); > + /* the domain should be in xenstore by now */ > + assert(dom_path); > + dom_sshm_path = GCSPRINTF("%s/static_shm/%s", dom_path, sshm->id); > + dom_role_path = GCSPRINTF("%s/role", dom_sshm_path); > + > + /* prepare the slave xenstore entries */ > + ents[0] = "begin"; > + ents[1] = GCSPRINTF("0x%"PRIx64, sshm->begin); > + ents[2] = "size"; > + ents[3] = GCSPRINTF("0x%"PRIx64, sshm->size); > + ents[4] = "offset"; > + ents[5] = GCSPRINTF("0x%"PRIx64, sshm->offset); > + ents[6] = "prot"; > + ents[7] = libxl__strdup(gc, libxl_sshm_prot_to_string(sshm->prot)); > + ents[8] = NULL; > + > + mapped = libxl__calloc(gc, sshm->size >> XC_PAGE_SHIFT, > sizeof(xen_pfn_t)); > + > + isretry = false; > + for (;;) { > + rc = libxl__xs_transaction_start(gc, &xt); > + if (rc) goto out; > + > + if (!libxl__xs_read(gc, xt, sshm_path)) { > + SSHM_ERROR(domid, sshm->id, "no master found."); > + rc = ERROR_FAIL; > + goto out; > + } > + > + /* every ID can appear in each domain at most once */ > + if (libxl__xs_read(gc, xt, dom_sshm_path)) { > + SSHM_ERROR(domid, sshm->id, > + "domain tried to map the same ID twice."); > + rc = ERROR_FAIL; > + goto out; > + } > + > + /* look at the master info and see if we could do the mapping */ > + rc = libxl__xs_read_checked(gc, xt, > + GCSPRINTF("%s/prot", sshm_path), > + &xs_value); > + if (rc) goto out; > + libxl_sshm_prot_from_string(xs_value, &master_sshm.prot); > + > + rc = libxl__xs_read_checked(gc, xt, > + GCSPRINTF("%s/begin", sshm_path), > + &xs_value); > + if (rc) goto out; > + master_sshm.begin = strtoull(xs_value, NULL, 16); > + > + rc = libxl__xs_read_checked(gc, xt, > + GCSPRINTF("%s/size", sshm_path), > + &xs_value); > + if (rc) goto out; > + master_sshm.size = strtoull(xs_value, NULL, 16); > + > + rc = libxl__xs_read_checked(gc, xt, > + GCSPRINTF("%s/master", sshm_path), > + &xs_value); > + if (rc) goto out; > + master_domid = strtoull(xs_value, NULL, 16); > + > + if (sshm->prot == LIBXL_SSHM_PROT_UNKNOWN) > + sshm->prot = master_sshm.prot; > + > + /* check if the slave is asking too much permission */ > + if (master_sshm.prot < sshm->prot) { > + SSHM_ERROR(domid, sshm->id, "slave is asking too much > permission."); > + rc = ERROR_INVAL; > + goto out; > + } > + > + /* all checks passed, do the job */ > + if (!isretry) { > + rc = libxl__sshm_do_map(gc, master_domid, domid, > + sshm, &master_sshm, > + mapped, &nmapped); > + if (rc) goto out; > + } > + > + /* write the result to xenstore and commit */ > + rc = libxl__xs_write_checked(gc, xt, dom_role_path, "slave"); > + if (rc) goto out; > + rc = libxl__xs_writev(gc, xt, slave_path, ents); > + if (rc) goto out; > + rc = libxl__sshm_incref(gc, xt, sshm_path); > + if (rc) goto out; > + > + rc = libxl__xs_transaction_commit(gc, &xt); > + if (!rc) break; > + if (rc < 0) goto out; > + isretry = true; Is it possible to loop here forever waiting for the transaction to be committed? > + } > + > + rc = 0; > +out: > + if (rc) { I would extend check: if (rc && nmapped) { > + /* roll back successfully mapped pages */ > + SSHM_ERROR(domid, sshm->id, "failed to map some pages, cancelling."); > + for (i = 0; i < nmapped; i++) { > + xc_domain_remove_from_physmap(CTX->xch, domid, mapped[i]); > + } > + } > + > + libxl__xs_transaction_abort(gc, &xt); > + > + return rc; > +} > + > +static int libxl__sshm_add_master(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshm) > +{ > + int rc; > + const char *sshm_path, *dom_path, *dom_role_path; > + char *ents[13]; > + struct xs_permissions noperm; > + xs_transaction_t xt = XBT_NULL; > + > + sshm_path = SSHM_PATH(sshm->id); > + dom_path = libxl__xs_get_dompath(gc, domid); > + /* the domain should be in xenstore by now */ > + assert(dom_path); > + dom_role_path = GCSPRINTF("%s/static_shm/%s/role", dom_path, sshm->id); > + > + /* prepare the xenstore entries */ > + ents[0] = "master"; > + ents[1] = GCSPRINTF("%"PRIu32, domid); > + ents[2] = "begin"; > + ents[3] = GCSPRINTF("0x%"PRIx64, sshm->begin); > + ents[4] = "size"; > + ents[5] = GCSPRINTF("0x%"PRIx64, sshm->size); > + ents[6] = "prot"; > + ents[7] = libxl__strdup(gc, libxl_sshm_prot_to_string(sshm->prot)); > + ents[8] = "cache_policy"; > + ents[9] = libxl__strdup(gc, > + > libxl_sshm_cachepolicy_to_string(sshm->cache_policy)); > + ents[10] = "usercnt"; > + ents[11] = "1"; > + ents[12] = NULL; > + > + /* could only be accessed by Dom0 */ > + noperm.id = 0; > + noperm.perms = XS_PERM_NONE; > + > + for (;;) { > + rc = libxl__xs_transaction_start(gc, &xt); > + if (rc) goto out; > + > + if (!libxl__xs_read(gc, xt, sshm_path)) { > + /* every ID can appear in each domain at most once */ > + if (libxl__xs_read(gc, xt, dom_role_path)) { > + SSHM_ERROR(domid, sshm->id, > + "domain tried to map the same ID twice."); > + rc = ERROR_FAIL; > + goto out; > + } > + rc = libxl__xs_write_checked(gc, xt, dom_role_path, "master"); > + if (rc) goto out;; > + > + libxl__xs_mknod(gc, xt, sshm_path, &noperm, 1); > + libxl__xs_writev(gc, xt, sshm_path, ents); > + } else { > + SSHM_ERROR(domid, sshm->id, "can only have one master."); > + rc = ERROR_FAIL; > + goto out; > + } > + > + rc = libxl__xs_transaction_commit(gc, &xt); > + if (!rc) break; > + if (rc < 0) goto out; > + } > + > + rc = 0; > +out: > + libxl__xs_transaction_abort(gc, &xt); > + return rc; > +} > + > +int libxl__sshm_add(libxl__gc *gc, uint32_t domid, > + libxl_static_shm *sshms, int len) > +{ > + int rc, i; > + > + for (i = 0; i < len; ++i) { > + if (sshms[i].role == LIBXL_SSHM_ROLE_SLAVE) { > + rc = libxl__sshm_add_slave(gc, domid, sshms+i); > + } else { > + rc = libxl__sshm_add_master(gc, domid, sshms+i); > + } > + if (rc) return rc; > + } > + > + return 0; > +} > + > +/* > + * Local variables: > + * mode: C > + * c-basic-offset: 4 > + * indent-tabs-mode: nil > + * End: > + */ > diff --git a/tools/libxl/libxl_x86.c b/tools/libxl/libxl_x86.c > index 8b6759c..3e70a69 100644 > --- a/tools/libxl/libxl_x86.c > +++ b/tools/libxl/libxl_x86.c > @@ -619,6 +619,25 @@ void libxl__arch_domain_build_info_setdefault(libxl__gc > *gc, > libxl_defbool_setdefault(&b_info->acpi, true); > } > > +bool libxl__arch_domain_support_sshm(const libxl_domain_build_info *b_info) > +{ > + /* FIXME: Mark this as unsupported for calling p2m_add_foreign on two > + * DomU's is currently not allowd on x86, see the comments in > + * x86/mm/p2m.c: p2m_add_foreign. > + */ > + return false; > +} > + > +int libxl__arch_domain_sshm_cachepolicy_setdefault(libxl_static_shm *sshm) > +{ > + if (sshm->cache_policy == LIBXL_SSHM_CACHEPOLICY_UNKNOWN) > + sshm->cache_policy = LIBXL_SSHM_CACHEPOLICY_X86_NORMAL; > + if (sshm->cache_policy < LIBXL_SSHM_CACHEPOLICY_X86_NORMAL) > + return ERROR_INVAL; > + > + return 0; > +} > + > /* > * Local variables: > * mode: C > -- > 1.9.1 > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@xxxxxxxxxxxxxxxxxxxx > https://lists.xenproject.org/mailman/listinfo/xen-devel -- Regards, Oleksandr Tyshchenko _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |