|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |