From aa0cecb067ca6077c67cfc13f0ce31af7a3b72bb Mon Sep 17 00:00:00 2001
From: Olaf Hering <olaf@aepfle.de>
Date: Mon, 10 Feb 2014 09:43:40 +0100
Subject: xend: set migration constraints from cmdline

xend part of required libxl change to tweak parameters of xc_domain_save

Add xm migrate --log_progress option. xc_domain_save does print progress
messages. These verbose messages are disabled per default to avoid flood
in xend.log. Sometimes it is helpful to see progress when migrating
large and busy guests. So add a new option to xm migrate to actually
enable the printing of progress messsages.

Print messages from xc_save with xc_report. Make use of xc_report in
xc_save to log also pid if some error occoured.

Signed-off-by: Olaf Hering <olaf@aepfle.de>
---
 tools/libxc/xc_private.h                |  1 +
 tools/libxc/xenguest.h                  |  1 +
 tools/python/xen/xend/XendCheckpoint.py | 22 +++++++++-
 tools/python/xen/xend/XendDomain.py     | 14 ++++++
 tools/python/xen/xend/XendDomainInfo.py | 16 +++++++
 tools/python/xen/xm/migrate.py          | 26 +++++++++++
 tools/xcutils/xc_save.c                 | 76 +++++++++++++++++++++------------
 7 files changed, 127 insertions(+), 29 deletions(-)

Index: xen-4.4.1-testing/tools/libxc/xc_private.h
===================================================================
--- xen-4.4.1-testing.orig/tools/libxc/xc_private.h
+++ xen-4.4.1-testing/tools/libxc/xc_private.h
@@ -119,6 +119,7 @@ void xc_report_progress_step(xc_interfac
 
 /* anamorphic macros:  struct xc_interface *xch  must be in scope */
 
+#define WPRINTF(_f, _a...) xc_report(xch, xch->error_handler, XTL_WARN,0, _f , ## _a)
 #define IPRINTF(_f, _a...) xc_report(xch, xch->error_handler, XTL_INFO,0, _f , ## _a)
 #define DPRINTF(_f, _a...) xc_report(xch, xch->error_handler, XTL_DETAIL,0, _f , ## _a)
 #define DBGPRINTF(_f, _a...) xc_report(xch, xch->error_handler, XTL_DEBUG,0, _f , ## _a)
Index: xen-4.4.1-testing/tools/libxc/xenguest.h
===================================================================
--- xen-4.4.1-testing.orig/tools/libxc/xenguest.h
+++ xen-4.4.1-testing/tools/libxc/xenguest.h
@@ -29,6 +29,7 @@
 #define XCFLAGS_STDVGA    (1 << 3)
 #define XCFLAGS_CHECKPOINT_COMPRESS    (1 << 4)
 #define XCFLAGS_DOMSAVE_ABORT_IF_BUSY  (1 << 5)
+#define XCFLAGS_PROGRESS  (1 << 6)
 
 #define X86_64_B_SIZE   64 
 #define X86_32_B_SIZE   32
Index: xen-4.4.1-testing/tools/python/xen/xend/XendCheckpoint.py
===================================================================
--- xen-4.4.1-testing.orig/tools/python/xen/xend/XendCheckpoint.py
+++ xen-4.4.1-testing/tools/python/xen/xend/XendCheckpoint.py
@@ -118,9 +118,27 @@ def save(fd, dominfo, network, live, dst
         # enabled. Passing "0" simply uses the defaults compiled into
         # libxenguest; see the comments and/or code in xc_linux_save() for
         # more information.
+        max_iters = dominfo.info.get('max_iters', "0")
+        max_factor = dominfo.info.get('max_factor', "0")
+        min_remaining = dominfo.info.get('min_remaining', "0")
+        abort_if_busy = dominfo.info.get('abort_if_busy', "0")
+        log_save_progress = dominfo.info.get('log_save_progress', "0")
+        if max_iters == "None":
+            max_iters = "0"
+        if max_factor == "None":
+            max_factor = "0"
+        if min_remaining == "None":
+            min_remaining = "0"
+        if abort_if_busy == "None":
+            abort_if_busy = "0"
+        if log_save_progress == "None":
+            log_save_progress = "0"
         cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
-               str(dominfo.getDomid()), "0", "0", 
-               str(int(live) | (int(hvm) << 2)) ]
+               str(dominfo.getDomid()),
+               max_iters, max_factor,
+               str( int(live) | (int(hvm) << 2) | (int(abort_if_busy) << 5) | (int(log_save_progress) << 6) ),
+               min_remaining
+               ]
         log.debug("[xc_save]: %s", string.join(cmd))
 
         def saveInputHandler(line, tochild):
Index: xen-4.4.1-testing/tools/python/xen/xend/XendDomain.py
===================================================================
--- xen-4.4.1-testing.orig/tools/python/xen/xend/XendDomain.py
+++ xen-4.4.1-testing/tools/python/xen/xend/XendDomain.py
@@ -1832,6 +1832,20 @@ class XendDomain:
             log.exception(ex)
             raise XendError(str(ex))
 
+    def domain_migrate_constraints_set(self, domid, max_iters, max_factor, min_remaining, abort_if_busy, log_save_progress):
+        """Set the Migrate Constraints of this domain.
+        @param domid: Domain ID or Name
+        @param max_iters: Number of iterations before final suspend
+        @param max_factor: Max amount of memory to transfer before final suspend
+        @param min_remaining: Number of dirty pages before final suspend
+        @param abort_if_busy: Abort migration instead of doing final suspend
+        @param log_save_progress: Log progress of migrate to xend.log
+        """
+        dominfo = self.domain_lookup_nr(domid)
+        if not dominfo:
+            raise XendInvalidDomain(str(domid))
+        dominfo.setMigrateConstraints(max_iters, max_factor, min_remaining, abort_if_busy, log_save_progress)
+
     def domain_maxmem_set(self, domid, mem):
         """Set the memory limit for a domain.
 
Index: xen-4.4.1-testing/tools/python/xen/xend/XendDomainInfo.py
===================================================================
--- xen-4.4.1-testing.orig/tools/python/xen/xend/XendDomainInfo.py
+++ xen-4.4.1-testing/tools/python/xen/xend/XendDomainInfo.py
@@ -1461,6 +1461,22 @@ class XendDomainInfo:
         pci_conf = self.info['devices'][dev_uuid][1]
         return map(pci_dict_to_bdf_str, pci_conf['devs'])
 
+    def setMigrateConstraints(self, max_iters, max_factor, min_remaining, abort_if_busy, log_save_progress):
+        """Set the Migrate Constraints of this domain.
+        @param max_iters: Number of iterations before final suspend
+        @param max_factor: Max amount of memory to transfer before final suspend
+        @param min_remaining: Number of dirty pages before final suspend
+        @param abort_if_busy: Abort migration instead of doing final suspend
+        @param log_save_progress: Log progress of migrate to xend.log
+        """
+        log.debug("Setting migration constraints of domain %s (%s) to '%s' '%s' '%s' '%s'.",
+                  self.info['name_label'], str(self.domid), max_iters, max_factor, min_remaining, abort_if_busy)
+        self.info['max_iters'] = str(max_iters)
+        self.info['max_factor'] = str(max_factor)
+        self.info['min_remaining'] = str(min_remaining)
+        self.info['abort_if_busy'] = str(abort_if_busy)
+        self.info['log_save_progress'] = str(log_save_progress)
+
     def setMemoryTarget(self, target):
         """Set the memory target of this domain.
         @param target: In MiB.
Index: xen-4.4.1-testing/tools/python/xen/xm/migrate.py
===================================================================
--- xen-4.4.1-testing.orig/tools/python/xen/xm/migrate.py
+++ xen-4.4.1-testing/tools/python/xen/xm/migrate.py
@@ -55,6 +55,26 @@ gopts.opt('change_home_server', short='c
           fn=set_true, default=0,
           use="Change home server for managed domains.")
 
+gopts.opt('max_iters', val='max_iters',
+          fn=set_int, default=0,
+          use="Number of iterations before final suspend (default: 30).")
+
+gopts.opt('max_factor', val='max_factor',
+          fn=set_int, default=0,
+          use="Max amount of memory to transfer before final suspend (default: 3*RAM).")
+
+gopts.opt('min_remaining', val='min_remaining',
+          fn=set_int, default=0,
+          use="Number of dirty pages before final suspend (default: 50).")
+
+gopts.opt('abort_if_busy',
+          fn=set_true, default=0,
+          use="Abort migration instead of doing final suspend.")
+
+gopts.opt('log_progress',
+          fn=set_true, default=0,
+          use="Log progress of migration to xend.log")
+
 def help():
     return str(gopts)
     
@@ -80,6 +100,12 @@ def main(argv):
         server.xenapi.VM.migrate(vm_ref, dst, bool(opts.vals.live),
                                  other_config)
     else:
+        server.xend.domain.migrate_constraints_set(dom,
+                                                   opts.vals.max_iters,
+                                                   opts.vals.max_factor,
+                                                   opts.vals.min_remaining,
+                                                   opts.vals.abort_if_busy,
+                                                   opts.vals.log_progress)
         server.xend.domain.migrate(dom, dst, opts.vals.live,
                                    opts.vals.port,
                                    opts.vals.node,
Index: xen-4.4.1-testing/tools/xcutils/xc_save.c
===================================================================
--- xen-4.4.1-testing.orig/tools/xcutils/xc_save.c
+++ xen-4.4.1-testing/tools/xcutils/xc_save.c
@@ -7,6 +7,7 @@
  *
  */
 
+#include <unistd.h>
 #include <err.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -19,6 +20,7 @@
 #include <fcntl.h>
 #include <err.h>
 
+#include <xc_private.h>
 #include <xenstore.h>
 #include <xenctrl.h>
 #include <xenguest.h>
@@ -51,16 +53,17 @@ static int compat_suspend(void)
  * receive the acknowledgement from the subscribe event channel. */
 static int evtchn_suspend(void)
 {
+    xc_interface *xch = si.xch;
     int rc;
 
     rc = xc_evtchn_notify(si.xce, si.suspend_evtchn);
     if (rc < 0) {
-        warnx("failed to notify suspend request channel: %d", rc);
+        WPRINTF("failed to notify suspend request channel: %d", rc);
         return 0;
     }
 
-    if (xc_await_suspend(si.xch, si.xce, si.suspend_evtchn) < 0) {
-        warnx("suspend failed");
+    if (xc_await_suspend(xch, si.xce, si.suspend_evtchn) < 0) {
+        WPRINTF("suspend failed");
         return 0;
     }
 
@@ -104,20 +107,27 @@ static int suspend(void* data)
 
 static int switch_qemu_logdirty(int domid, unsigned int enable, void *data)
 {
+    xc_interface *xch = si.xch;
     struct xs_handle *xs;
     char *path, *p, *ret_str, *cmd_str, **watch;
     unsigned int len;
     struct timeval tv;
     fd_set fdset;
 
-    if ((xs = xs_daemon_open()) == NULL)
-        errx(1, "Couldn't contact xenstore");
-    if (!(path = strdup("/local/domain/0/device-model/")))
-        errx(1, "can't get domain path in store");
+    if ((xs = xs_daemon_open()) == NULL) {
+        PERROR("Couldn't contact xenstore");
+        exit(1);
+    }
+    if (!(path = strdup("/local/domain/0/device-model/"))) {
+        PERROR("can't get domain path in store");
+        exit(1);
+    }
     if (!(path = realloc(path, strlen(path) 
                          + 10 
-                         + strlen("/logdirty/cmd") + 1)))
-        errx(1, "no memory for constructing xenstore path");
+                         + strlen("/logdirty/cmd") + 1))) {
+        PERROR("no memory for constructing xenstore path");
+        exit(1);
+    }
     snprintf(path + strlen(path), 11, "%i", domid);
     strcat(path, "/logdirty/");
     p = path + strlen(path);
@@ -126,16 +136,22 @@ static int switch_qemu_logdirty(int domi
     /* Watch for qemu's return value */
     strcpy(p, "ret");
     if (!xs_watch(xs, path, "qemu-logdirty-ret"))
-        errx(1, "can't set watch in store (%s)\n", path);
+    {
+        ERROR("can't set watch in store (%s)\n", path);
+        exit(1);
+    }
 
-    if (!(cmd_str = strdup( enable == 0 ? "disable" : "enable")))
-        errx(1, "can't get logdirty cmd path in store");
+    if (!(cmd_str = strdup( enable == 0 ? "disable" : "enable"))) {
+        PERROR("can't get logdirty cmd path in store");
+        exit(1);
+    }
 
     /* Tell qemu that we want it to start logging dirty page to Xen */
     strcpy(p, "cmd");
-    if (!xs_write(xs, XBT_NULL, path, cmd_str, strlen(cmd_str)))
-        errx(1, "can't write  to store path (%s)\n",
-             path);
+    if (!xs_write(xs, XBT_NULL, path, cmd_str, strlen(cmd_str))) {
+        PERROR("can't write  to store path (%s)\n", path);
+        exit(1);
+    }
 
     /* Wait a while for qemu to signal that it has service logdirty command */
  read_again:
@@ -144,8 +160,10 @@ static int switch_qemu_logdirty(int domi
     FD_ZERO(&fdset);
     FD_SET(xs_fileno(xs), &fdset);
 
-    if ((select(xs_fileno(xs) + 1, &fdset, NULL, NULL, &tv)) != 1)
-        errx(1, "timed out waiting for qemu logdirty response.\n");
+    if ((select(xs_fileno(xs) + 1, &fdset, NULL, NULL, &tv)) != 1) {
+        PERROR("timed out waiting for qemu logdirty response.\n");
+        exit(1);
+    }
 
     watch = xs_read_watch(xs, &len);
     free(watch);
@@ -166,53 +184,57 @@ static int switch_qemu_logdirty(int domi
 int
 main(int argc, char **argv)
 {
-    unsigned int maxit, max_f, lflags;
+    xc_interface *xch;
+    unsigned int maxit, max_f, lflags, min_r;
     int io_fd, ret, port;
     struct save_callbacks callbacks;
     xentoollog_level lvl;
     xentoollog_logger *l;
 
-    if (argc != 6)
-        errx(1, "usage: %s iofd domid maxit maxf flags", argv[0]);
+    if (argc != 7)
+        errx(1, "usage: %s iofd domid maxit maxf flags minr", argv[0]);
 
     io_fd = atoi(argv[1]);
     si.domid = atoi(argv[2]);
     maxit = atoi(argv[3]);
     max_f = atoi(argv[4]);
     si.flags = atoi(argv[5]);
+    min_r = atoi(argv[6]);
 
     si.suspend_evtchn = -1;
 
     lvl = si.flags & XCFLAGS_DEBUG ? XTL_DEBUG: XTL_DETAIL;
-    lflags = XTL_STDIOSTREAM_SHOW_PID | XTL_STDIOSTREAM_HIDE_PROGRESS;
+    lflags = XTL_STDIOSTREAM_SHOW_PID;
+    if (!(si.flags & XCFLAGS_PROGRESS))
+        lflags |= XTL_STDIOSTREAM_HIDE_PROGRESS;
     l = (xentoollog_logger *)xtl_createlogger_stdiostream(stderr, lvl, lflags);
-    si.xch = xc_interface_open(l, 0, 0);
+    xch = si.xch = xc_interface_open(l, 0, 0);
     if (!si.xch)
-        errx(1, "failed to open control interface");
+        errx(1, "[%lu] failed to open control interface", (unsigned long)getpid());
 
     si.xce = xc_evtchn_open(NULL, 0);
     if (si.xce == NULL)
-        warnx("failed to open event channel handle");
+        WPRINTF("failed to open event channel handle");
     else
     {
         port = xs_suspend_evtchn_port(si.domid);
 
         if (port < 0)
-            warnx("failed to get the suspend evtchn port\n");
+            WPRINTF("failed to get the suspend evtchn port\n");
         else
         {
             si.suspend_evtchn =
               xc_suspend_evtchn_init(si.xch, si.xce, si.domid, port);
 
             if (si.suspend_evtchn < 0)
-                warnx("suspend event channel initialization failed, "
+                WPRINTF("suspend event channel initialization failed, "
                        "using slow path");
         }
     }
     memset(&callbacks, 0, sizeof(callbacks));
     callbacks.suspend = suspend;
     callbacks.switch_qemu_logdirty = switch_qemu_logdirty;
-    ret = xc_domain_save(si.xch, io_fd, si.domid, maxit, max_f, si.flags, 
+    ret = xc_domain_save_suse(si.xch, io_fd, si.domid, maxit, max_f, si.flags, min_r,
                          &callbacks, !!(si.flags & XCFLAGS_HVM), 0);
 
     if (si.suspend_evtchn > 0)
Index: xen-4.4.1-testing/tools/python/xen/xend/server/SrvDomain.py
===================================================================
--- xen-4.4.1-testing.orig/tools/python/xen/xend/server/SrvDomain.py
+++ xen-4.4.1-testing/tools/python/xen/xend/server/SrvDomain.py
@@ -108,6 +108,17 @@ class SrvDomain(SrvDir):
                      ['reset',       'int']])
         return fn(req.args, {'dom': self.dom.domid})
 
+    def op_migrate_constraints_set(self, _, req):
+        self.acceptCommand(req)
+        fn = FormFn(self.xd.domain_migrate_constraints_set,
+                    [['dom',           'int'],
+                     ['max_iters',     'int'],
+                     ['max_factor',    'int'],
+                     ['min_remaining', 'int'],
+                     ['abort_if_busy', 'int'],
+                     ['log_progress',  'int']])
+        return fn(req.args, {'dom': self.dom.domid})
+
     def op_migrate(self, op, req):
         return req.threadRequest(self.do_migrate, op, req)
     
