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

[Xen-devel] [PATCH RFC v2 9/9] xen/arm: Implement toolstack for xl restore/save and migrate



From: "From: Alexey Sokolov" <sokolov.a@xxxxxxxxxxx>

Implement for xl restore/save (which are also used for migrate)
operation in xc_arm_migrate.c and make it compilable.

Singed-off-by: Alexey Sokolov <sokolov.a@xxxxxxxxxxx>
Signed-off-by: Elena Pyatunina <e.pyatunina@xxxxxxxxxxx>
Singed-off-by: Evgeny Fedotov <e.fedotov@xxxxxxxxxxx>
Singed-off-by: Nikolay Martyanov <n.martyanov@xxxxxxxxxxx>
---
 config/arm32.mk              |   1 +
 tools/libxc/Makefile         |   5 +
 tools/libxc/xc_arm_migrate.c | 586 +++++++++++++++++++++++++++++++++++++++++++
 tools/misc/Makefile          |   4 +
 4 files changed, 596 insertions(+)
 create mode 100644 tools/libxc/xc_arm_migrate.c

diff --git a/config/arm32.mk b/config/arm32.mk
index d8e958b..4e35337 100644
--- a/config/arm32.mk
+++ b/config/arm32.mk
@@ -1,6 +1,7 @@
 CONFIG_ARM := y
 CONFIG_ARM_32 := y
 CONFIG_ARM_$(XEN_OS) := y
+CONFIG_MIGRATE := y
 
 # -march= -mcpu=
 
diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile
index 512a994..05dfef4 100644
--- a/tools/libxc/Makefile
+++ b/tools/libxc/Makefile
@@ -42,8 +42,13 @@ CTRL_SRCS-$(CONFIG_MiniOS) += xc_minios.c
 GUEST_SRCS-y :=
 GUEST_SRCS-y += xg_private.c xc_suspend.c
 ifeq ($(CONFIG_MIGRATE),y)
+ifeq ($(CONFIG_X86),y)
 GUEST_SRCS-y += xc_domain_restore.c xc_domain_save.c
 GUEST_SRCS-y += xc_offline_page.c xc_compression.c
+endif
+ifeq ($(CONFIG_ARM),y)
+GUEST_SRCS-y += xc_arm_migrate.c
+endif
 else
 GUEST_SRCS-y += xc_nomigrate.c
 endif
diff --git a/tools/libxc/xc_arm_migrate.c b/tools/libxc/xc_arm_migrate.c
new file mode 100644
index 0000000..936dd3d
--- /dev/null
+++ b/tools/libxc/xc_arm_migrate.c
@@ -0,0 +1,586 @@
+/******************************************************************************
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+ *
+ * Copyright (c) 2013, Samsung Electronics
+ */
+
+#include <inttypes.h>
+#include <errno.h>
+#include <xenctrl.h>
+#include <xenguest.h>
+
+#include <unistd.h>
+#include <xc_private.h>
+#include <xc_dom.h>
+#include "xc_bitops.h"
+#include "xg_private.h"
+
+#define DEF_MAX_ITERS   29   /* limit us to 30 times round loop   */
+#define DEF_MAX_FACTOR   3   /* never send more than 3x p2m_size  */
+#define DEF_MIN_DIRTY_PER_ITER 50 /* dirty page count to define last iter */
+#define DEF_PROGRESS_RATE 50 /* progress bar update rate */
+
+static int suspend_and_state(int (*suspend)(void*), void *data,
+                             xc_interface *xch, int dom)
+{
+    xc_dominfo_t info;
+    if ( !(*suspend)(data) )
+    {
+        ERROR("Suspend request failed");
+        return -1;
+    }
+
+    if ( (xc_domain_getinfo(xch, dom, 1, &info) != 1) ||
+            !info.shutdown || (info.shutdown_reason != SHUTDOWN_suspend) )
+    {
+        ERROR("Domain is not in suspended state after suspend attempt");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_exact_handled(xc_interface *xch, int fd, const void *data, 
size_t size)
+{
+    if (write_exact(fd, data, size))
+    {
+        ERROR("Write failed, check space");
+        return -1;
+    }
+    return 0;
+}
+
+/* ============ Memory ============= */
+
+static int save_memory(xc_interface *xch, int io_fd, uint32_t dom,
+                       int live, struct save_callbacks* callbacks,
+                       uint32_t max_iters, uint32_t max_factor, int debug)
+{
+    const xen_pfn_t start = 0x80000; /* Arm-domU RAM starts from 2 gigabytes */
+    const char zero = 0;
+    char reportbuf[80];
+    int iter = 0;
+    int last_iter = !live;
+
+    DECLARE_HYPERCALL_BUFFER(unsigned long, to_send);
+
+    const xen_pfn_t end = xc_domain_maximum_gpfn(xch, dom);
+    const xen_pfn_t mem_size = end - start;
+    xen_pfn_t i;
+    if (write_exact_handled(xch, io_fd, &end, sizeof(xen_pfn_t)))
+    {
+       return -1;
+    }
+
+    if (live)
+    {
+        if (xc_shadow_control(xch, dom, XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
+                    NULL, 0, NULL, 0, NULL) < 0)
+        {
+            ERROR("Couldn't enable log-dirty mode !\n");
+            return -1;
+        }
+        if (debug)
+        {
+            IPRINTF("Log-dirty mode enabled!\n");
+        }
+
+        to_send = xc_hypercall_buffer_alloc_pages(xch, to_send, 
NRPAGES(bitmap_size(mem_size)));
+
+        if (!to_send)
+        {
+            ERROR("Couldn't allocate to_send array!\n");
+            return -1;
+        }
+        max_iters  = max_iters  ? : DEF_MAX_ITERS;
+        max_factor = max_factor ? : DEF_MAX_FACTOR;
+
+        memset(to_send, 0xff, bitmap_size(mem_size));
+    }
+    else
+    {
+        /* This is a non-live suspend. Suspend the domain .*/
+        if (suspend_and_state(callbacks->suspend, callbacks->data, xch, dom))
+        {
+            ERROR("Domain appears not to have suspended");
+            return -1; 
+        }
+    }
+
+    snprintf(reportbuf, sizeof(reportbuf),
+            "Saving memory: iter %d ", iter);
+    xc_report_progress_start(xch, reportbuf, mem_size);
+
+    for (i = start; i < end; ++i)
+    {
+        const char one = 1;
+        char *page = xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ | 
PROT_WRITE, i);
+        if ( !page )
+        {
+           PERROR("xc_map_foreign_range failed, pfn=%llx", i);
+           return -1;
+        }
+        if (write_exact_handled(xch, io_fd, &one, 1) || 
write_exact_handled(xch, io_fd, &i, sizeof(i))
+                       || write_exact_handled(xch, io_fd, page, PAGE_SIZE))
+        {
+               munmap(page, PAGE_SIZE);
+               return -1;
+        }
+        munmap(page, PAGE_SIZE);
+        if (i % DEF_PROGRESS_RATE == 0) xc_report_progress_step(xch, i - 
start, mem_size);
+    }
+    xc_report_progress_step(xch, mem_size, mem_size);
+
+    if (live)
+    {
+        int total_dirty_pages_num = 0;
+        int dirty_pages_on_prev_iter_num = mem_size; 
+        for ( ; ; )
+        {
+            int dirty_pages_on_current_iter_num = 0; 
+            int frc;
+            iter++;
+
+            snprintf(reportbuf, sizeof(reportbuf),
+                 "Saving memory: iter %d (last sent %u)",
+                 iter, dirty_pages_on_prev_iter_num);
+
+            xc_report_progress_start(xch, reportbuf, mem_size);
+
+            if ( (iter > 1 && dirty_pages_on_prev_iter_num < 
DEF_MIN_DIRTY_PER_ITER) ||  
+                 (iter == max_iters) || 
+                 (total_dirty_pages_num >= mem_size*max_factor) )
+            {
+                if (debug) IPRINTF("Last iteration");
+                last_iter = 1;
+            }
+
+            if (last_iter)
+            {
+                if (suspend_and_state(callbacks->suspend, callbacks->data, 
xch, dom))
+                {
+                    ERROR("Domain appears not to have suspended");
+                    return -1; 
+                }
+            }
+
+            frc = xc_shadow_control(xch, dom, XEN_DOMCTL_SHADOW_OP_CLEAN,
+                                    HYPERCALL_BUFFER(to_send), mem_size, NULL, 
0, NULL);
+            if ( frc != mem_size )
+            {
+                ERROR("Error peeking shadow bitmap");
+                xc_hypercall_buffer_free_pages(xch, to_send, 
NRPAGES(bitmap_size(mem_size)));
+                return -1;
+            }
+
+            for (i = start; i < end; ++i)
+            {
+                if (test_bit(i - start, to_send))
+                {
+                    const char one = 1;
+                    char *page = xc_map_foreign_range(xch, dom, PAGE_SIZE, 
PROT_READ | PROT_WRITE, i);
+                    if ( !page )
+                    {
+                        PERROR("xc_map_foreign_range failed, pfn=%llx", i);
+                        return -1;
+                    }
+                    if (write_exact_handled(xch, io_fd, &one, 1) || 
write_exact_handled(xch, io_fd, &i, sizeof(i))
+                                       || write_exact_handled(xch, io_fd, 
page, PAGE_SIZE))
+                    {
+                       munmap(page, PAGE_SIZE);
+                       return -1;
+                    }
+                    munmap(page, PAGE_SIZE);
+                    if (i % DEF_PROGRESS_RATE == 0) 
xc_report_progress_step(xch, i - start, mem_size);
+                    dirty_pages_on_current_iter_num++;
+                }
+            }
+            xc_report_progress_step(xch, mem_size, mem_size);
+
+            dirty_pages_on_prev_iter_num = dirty_pages_on_current_iter_num;
+            total_dirty_pages_num += dirty_pages_on_current_iter_num;
+
+            if (last_iter)
+            {
+                xc_hypercall_buffer_free_pages(xch, to_send, 
NRPAGES(bitmap_size(mem_size)));
+                if (xc_shadow_control(xch, dom, XEN_DOMCTL_SHADOW_OP_OFF, 
NULL, 0, NULL, 0, NULL) < 0)
+                    ERROR("Couldn't disable log-dirty mode");
+                break;
+            }
+        }
+    }
+    return write_exact_handled(xch, io_fd, &zero, 1);
+}
+
+static int restore_memory(xc_interface *xch, int io_fd, uint32_t dom)
+{
+    const xen_pfn_t start = 0x80000; /* Arm-domU RAM starts from 2 gigabytes */
+    xen_pfn_t end;
+    xen_pfn_t gpfn;
+    if (read_exact(io_fd, &end, sizeof(xen_pfn_t)))
+    {
+        PERROR("First read of incoming memory failed");
+        return -1;
+    }
+    /* TODO allocate several pages per call */
+    for (gpfn = start; gpfn < end; ++gpfn)
+    {
+        if (xc_domain_populate_physmap_exact(xch, dom, 1, 0, 0, &gpfn))
+        {
+            PERROR("Memory allocation for a new domain failed");
+            return -1;
+        }
+    }
+    while (1)
+    {
+        char new_page;
+        xen_pfn_t gpfn;
+        char *page;
+        if (read_exact(io_fd, &new_page, 1))
+        {
+            PERROR("End-checking flag read failed during memory transfer");
+            return -1;
+        }
+        if (!new_page)
+        {
+            break;
+        }
+        if (read_exact(io_fd, &gpfn, sizeof(gpfn)))
+        {
+            PERROR("GPFN read failed during memory transfer");
+            return -1;
+        }
+        if (gpfn < start || gpfn >= end) {
+            ERROR("GPFN %llx doesn't belong to RAM address space", gpfn);
+            return -1;
+        }
+        page = xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ | 
PROT_WRITE, gpfn);
+        if ( !page )
+        {
+            PERROR("xc_map_foreign_range failed, pfn=%llx", gpfn);
+            return -1;
+        }
+        if (read_exact(io_fd, page, PAGE_SIZE))
+        {
+            PERROR("Page data read failed during memory transfer");
+            return -1;
+        }
+        munmap(page, PAGE_SIZE);
+    }
+
+    return 0;
+}
+
+
+/* ============ HVM context =========== */
+
+static int save_armhvm(xc_interface *xch, int io_fd, uint32_t dom, int debug)
+{
+    /* HVM: a buffer for holding HVM context */
+    uint32_t hvm_buf_size = 0;
+    uint8_t *hvm_buf = NULL;
+    uint32_t rec_size;
+    int retval = -1;
+    /* Need another buffer for HVM context */
+    hvm_buf_size = xc_domain_hvm_getcontext(xch, dom, 0, 0);
+    if ( hvm_buf_size == -1 )
+    {
+        ERROR("Couldn't get HVM context size from Xen");
+        goto out;
+    }
+    hvm_buf = malloc(hvm_buf_size);
+    if ( !hvm_buf )
+    {
+        ERROR("Couldn't allocate memory for hvm buffer");
+        goto out;
+    }
+    /* Get HVM context from Xen and save it too */
+    if ( (rec_size = xc_domain_hvm_getcontext(xch, dom, hvm_buf,
+                    hvm_buf_size)) == -1 )
+    {
+        ERROR("HVM:Could not get hvm buffer");
+        goto out;
+    }
+    if (debug) IPRINTF("HVM save size %d %d", hvm_buf_size, rec_size);
+
+    if ( write_exact_handled(xch, io_fd, &rec_size, sizeof(uint32_t)) )
+    {
+        goto out;
+    }
+
+    if (  write_exact_handled(xch, io_fd, hvm_buf, rec_size) )
+    {
+        goto out;
+    }
+    retval = 0;
+out:
+    if (hvm_buf) free (hvm_buf);
+    return retval;
+}
+
+static int restore_armhvm(xc_interface *xch, int io_fd, uint32_t dom)
+{
+    uint32_t rec_size;
+    uint32_t hvm_buf_size = 0;
+    uint8_t *hvm_buf = NULL;
+    int frc = 0;
+    int retval = -1;
+    if (read_exact(io_fd, &rec_size, sizeof(uint32_t)))
+    {
+        PERROR("Could not read HVM size");
+        goto out;
+    }
+
+    if ( !rec_size )
+    {
+        ERROR("Zero HVM size");
+        goto out;
+    }
+#ifdef ARM_MIGRATE_VERBOSE
+    IPRINTF("HVM restore size %d %d", hvm_buf_size, rec_size);
+#endif
+
+    hvm_buf_size = xc_domain_hvm_getcontext(xch, dom, 0, 0);
+    if (hvm_buf_size != rec_size)
+    {
+        ERROR("HVM size for this domain is not the same as stored");
+    }
+    hvm_buf = malloc(hvm_buf_size);
+    if ( !hvm_buf )
+    {
+        ERROR("Couldn't allocate memory");
+        goto out;
+    }
+    if (read_exact(io_fd, hvm_buf, hvm_buf_size))
+    {
+        PERROR("Could not read HVM context");
+        goto out;
+    }
+    frc = xc_domain_hvm_setcontext(xch, dom, hvm_buf, hvm_buf_size);
+    if ( frc )
+    {
+        ERROR("error setting the HVM context");
+        goto out;
+    }
+    retval = 0;
+out:
+    if (hvm_buf) free (hvm_buf);
+    return retval;
+}
+
+
+/* ================= Console and Xenstore =========== */
+
+static int save_console_and_xenstore(xc_interface *xch, int io_fd, uint32_t 
dom)
+{
+    unsigned long int console_pfn, store_pfn;
+    if (xc_get_hvm_param(xch, dom, HVM_PARAM_CONSOLE_PFN, &console_pfn))
+    {
+        ERROR("Can't get console gpfn");
+        return -1;
+    }
+    if (xc_get_hvm_param(xch, dom, HVM_PARAM_STORE_PFN, &store_pfn))
+    {
+        ERROR("Can't get store gpfn");
+        return -1;
+    }
+    if ( write_exact_handled(xch, io_fd, &console_pfn, sizeof(console_pfn)) ||
+               write_exact_handled(xch, io_fd, &store_pfn, sizeof(store_pfn)) )
+    {
+       return -1;
+    }
+    return 0;
+}
+
+static int restore_console_and_xenstore(xc_interface *xch, int io_fd, uint32_t 
dom,
+        unsigned int console_evtchn, unsigned long int *console_gpfn, domid_t 
console_domid,
+        unsigned int store_evtchn, unsigned long int *store_gpfn, domid_t 
store_domid)
+{
+    int rc = 0;
+    if (read_exact(io_fd, console_gpfn, sizeof(*console_gpfn)))
+    {
+        PERROR("Can't read console gpfn");
+        return -1;
+    }
+    if (read_exact(io_fd, store_gpfn, sizeof(*store_gpfn)))
+    {
+        PERROR("Can't read xenstore gpfn");
+        return -1;
+    }
+
+    if ((rc = xc_clear_domain_page(xch, dom, *console_gpfn)))
+    {
+        ERROR("Can't clear console page");
+        return rc;
+    }
+    if ((rc = xc_clear_domain_page(xch, dom, *store_gpfn)))
+    {
+        ERROR("Can't clear xenstore page");
+        return rc;
+    }
+    if ((rc = xc_dom_gnttab_hvm_seed(xch, dom, *console_gpfn, *store_gpfn, 
console_domid, store_domid)))
+    {
+        ERROR("Can't grant console and xenstore pages");
+        return rc;
+    }
+    if ((rc = xc_set_hvm_param(xch, dom, HVM_PARAM_CONSOLE_PFN, 
*console_gpfn)))
+    {
+        ERROR("Can't set console gpfn");
+        return rc;
+    }
+    if ((rc = xc_set_hvm_param(xch, dom, HVM_PARAM_STORE_PFN, *store_gpfn)))
+    {
+        ERROR("Can't set xenstore gpfn");
+        return rc;
+    }
+    if ((rc = xc_set_hvm_param(xch, dom, HVM_PARAM_CONSOLE_EVTCHN, 
console_evtchn)))
+    {
+        ERROR("Can't set console event channel");
+        return rc;
+    }
+    if ((rc = xc_set_hvm_param(xch, dom, HVM_PARAM_STORE_EVTCHN, 
store_evtchn)))
+    {
+        ERROR("Can't set xenstore event channel");
+        return rc;
+    }
+
+    return 0;
+}
+
+
+/* ====================== VCPU ============== */
+
+static int save_vcpu(xc_interface *xch, int io_fd, uint32_t dom)
+{
+    vcpu_guest_context_any_t ctxt;
+    xc_vcpu_getcontext(xch, dom, 0, &ctxt);
+    return write_exact_handled(xch, io_fd, &ctxt, sizeof(ctxt));
+}
+
+static int restore_vcpu(xc_interface *xch, int io_fd, uint32_t dom)
+{
+    int rc = -1;
+    DECLARE_DOMCTL;
+    DECLARE_HYPERCALL_BUFFER(vcpu_guest_context_any_t, ctxt);
+    ctxt = xc_hypercall_buffer_alloc(xch, ctxt, sizeof(*ctxt));
+    memset(ctxt, 0, sizeof(*ctxt));
+    if (read_exact(io_fd, ctxt, sizeof(*ctxt)))
+    {
+        PERROR("VCPU context read failed");
+        goto out;
+    }
+    memset(&domctl, 0, sizeof(domctl));
+    domctl.cmd = XEN_DOMCTL_setvcpucontext;
+    domctl.domain = dom;
+    domctl.u.vcpucontext.vcpu = 0;
+    set_xen_guest_handle(domctl.u.vcpucontext.ctxt, ctxt);
+    rc = do_domctl(xch, &domctl);
+    if (rc)
+    {
+        ERROR("VCPU context set failed (error %d)", rc);
+    }
+out:
+    xc_hypercall_buffer_free(xch, ctxt);
+    return rc;
+}
+
+
+/* ================== Main ============== */
+
+int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t 
max_iters,
+                   uint32_t max_factor, uint32_t flags,
+                   struct save_callbacks *callbacks, int hvm,
+                   unsigned long vm_generationid_addr)
+{
+    int live = !!(flags & XCFLAGS_LIVE);
+    int debug = !! (flags & XCFLAGS_DEBUG);
+
+    if (save_memory(xch, io_fd, dom, live, callbacks, max_iters, max_factor, 
debug))
+    {
+        ERROR("Memory not saved");
+        return -1;
+    }
+
+    if (save_console_and_xenstore(xch, io_fd, dom))
+    {
+        ERROR("Can't save console and xenstore");
+        return -1;
+    }
+
+    if (save_armhvm(xch, io_fd, dom, debug))
+    {
+        ERROR("HVM not saved");
+        return -1;
+    }
+
+    if (save_vcpu(xch, io_fd, dom))
+    {
+        ERROR("VCPU not saved");
+        return -1;
+    }
+    return 0;
+}
+
+int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom,
+                      unsigned int store_evtchn, unsigned long *store_gpfn,
+                      domid_t store_domid, unsigned int console_evtchn,
+                      unsigned long *console_gpfn, domid_t console_domid,
+                      unsigned int hvm, unsigned int pae, int superpages,
+                      int no_incr_generationid,
+                      unsigned long *vm_generationid_addr,
+                      struct restore_callbacks *callbacks)
+{
+#ifdef ARM_MIGRATE_VERBOSE
+       IPRINTF("Hello restoration");
+#endif
+    if (restore_memory(xch, io_fd, dom))
+    {
+        ERROR("Can't restore memory");
+        return -1;
+    }
+
+    if (restore_console_and_xenstore(xch, io_fd, dom,
+                console_evtchn, console_gpfn, console_domid,
+                store_evtchn, store_gpfn, store_domid))
+    {
+        ERROR("Can't setup console and xenstore");
+        return -1;
+    }
+
+    if (restore_armhvm(xch, io_fd, dom))
+    {
+        ERROR("HVM not restored");
+        return -1;
+    }
+
+    if (restore_vcpu(xch, io_fd, dom))
+    {
+        ERROR("Can't restore VCPU");
+        return -1;
+    }
+
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/misc/Makefile b/tools/misc/Makefile
index 520ef80..5338f87 100644
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -11,7 +11,9 @@ HDRS     = $(wildcard *.h)
 
 TARGETS-y := xenperf xenpm xen-tmem-list-parse gtraceview gtracestat 
xenlockprof xenwatchdogd xencov
 TARGETS-$(CONFIG_X86) += xen-detect xen-hvmctx xen-hvmcrash xen-lowmemd
+ifeq ($(CONFIG_X86),y)
 TARGETS-$(CONFIG_MIGRATE) += xen-hptool
+endif
 TARGETS := $(TARGETS-y)
 
 SUBDIRS-$(CONFIG_LOMOUNT) += lomount
@@ -25,7 +27,9 @@ INSTALL_BIN := $(INSTALL_BIN-y)
 INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm 
xen-tmem-list-parse gtraceview \
        gtracestat xenlockprof xenwatchdogd xen-ringwatch xencov
 INSTALL_SBIN-$(CONFIG_X86) += xen-hvmctx xen-hvmcrash xen-lowmemd
+ifeq ($(CONFIG_X86),y)
 INSTALL_SBIN-$(CONFIG_MIGRATE) += xen-hptool
+endif
 INSTALL_SBIN := $(INSTALL_SBIN-y)
 
 INSTALL_PRIVBIN-y := xenpvnetboot
-- 
1.8.1.2


_______________________________________________
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®.