From: Olaf Hering <olaf@aepfle.de>
Date: Thu, 17 May 2018 17:41:31 +0200
Subject: libxl: always call qemus xen-save-devices-state in suspend/resume
References: bsc#1079730, bsc#1098403

If a domU has a qemu-xen instance attached, it is required to call qemus
"xen-save-devices-state" method. Without it, the receiving side of a PV
migration may be unable to lock the image:

xen be: qdisk-51712: xen be: qdisk-51712: error: Failed to get "write" lock
error: Failed to get "write" lock
xen be: qdisk-51712: xen be: qdisk-51712: initialise() failed
initialise() failed

The proper way to decide if a PV or PVH domU has a qemu-xen running is
to use the libxl__need_xenpv_qemu API. But since there is no copy of a
libxl_domain_config available in these places, it should be enough to
check if xenstore contains the relevant info.

PS:
Unfortunately, libxl sets 'qemu_xen' unconditionally even for PV.
As a quick fix, ignore QMP errors in case of PV and ENOENT. This fixes
domUs without qcow2 and without vfb=.

Signed-off-by: Olaf Hering <olaf@aepfle.de>
---
 tools/libxl/libxl_dom_suspend.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

--- a/tools/libxl/libxl_dom_suspend.c
+++ b/tools/libxl/libxl_dom_suspend.c
@@ -377,7 +377,9 @@ static void domain_suspend_common_guest_
     libxl__ev_xswatch_deregister(gc, &dsps->guest_watch);
     libxl__ev_time_deregister(gc, &dsps->guest_timeout);
 
-    if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) {
+    if (dsps->type == LIBXL_DOMAIN_TYPE_HVM ||
+        libxl__device_model_version_running(gc, dsps->domid) ==
+        LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
         rc = libxl__domain_suspend_device_model(gc, dsps);
         if (rc) {
             LOGD(ERROR, dsps->domid,
@@ -460,7 +462,9 @@ int libxl__domain_resume(libxl__gc *gc,
         goto out;
     }
 
-    if (type == LIBXL_DOMAIN_TYPE_HVM) {
+    if (type == LIBXL_DOMAIN_TYPE_HVM ||
+        libxl__device_model_version_running(gc, domid) ==
+        LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
         rc = libxl__domain_resume_device_model(gc, domid);
         if (rc) {
             LOGD(ERROR, domid, "failed to resume device model:%d", rc);
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1803,6 +1803,7 @@ typedef struct libxl__qmp_handler libxl_
  *   Return an handler or NULL if there is an error
  */
 _hidden libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc,
+                                                  libxl_domain_type type,
                                                   uint32_t domid);
 _hidden int libxl__qmp_run_command_flexarray(libxl__gc *gc, int domid,
                                              const char *cmd,
--- a/tools/libxl/libxl_qmp.c
+++ b/tools/libxl/libxl_qmp.c
@@ -386,17 +386,22 @@ static int qmp_open(libxl__qmp_handler *
 {
     int ret = -1;
     int i = 0;
+    int o_errno = 0;
 
+    errno = 0;
     qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    o_errno = errno;
     if (qmp->qmp_fd < 0) {
         goto out;
     }
     ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1);
+    o_errno = errno;
     if (ret) {
         ret = -1;
         goto out;
     }
     ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1);
+    o_errno = errno;
     if (ret) {
         ret = -1;
         goto out;
@@ -414,6 +419,7 @@ static int qmp_open(libxl__qmp_handler *
     do {
         ret = connect(qmp->qmp_fd, (struct sockaddr *) &qmp->addr,
                       sizeof (qmp->addr));
+        o_errno = errno;
         if (ret == 0)
             break;
         if (errno == ENOENT || errno == ECONNREFUSED) {
@@ -428,6 +434,7 @@ static int qmp_open(libxl__qmp_handler *
 out:
     if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd);
 
+    errno = o_errno;
     return ret;
 }
 
@@ -715,19 +722,27 @@ static void qmp_parameters_add_integer(l
  * API
  */
 
-libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid)
+libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, libxl_domain_type type, uint32_t domid)
 {
     int ret = 0;
+    int o_errno;
     libxl__qmp_handler *qmp = NULL;
     char *qmp_socket;
 
+    LOGD(DEBUG, domid, "%s", __func__);
     qmp = qmp_init_handler(gc, domid);
     if (!qmp) return NULL;
 
     qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
     if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
+        o_errno = errno;
+        if (errno == ENOENT && type == LIBXL_DOMAIN_TYPE_PV) {
+            /* nothing to do, PV may not have a qemu attached */;
+            LOGED(DEBUG, domid, "Connection error");
+        } else
         LOGED(ERROR, domid, "Connection error");
         qmp_free_handler(qmp);
+        errno = o_errno;
         return NULL;
     }
 
@@ -843,10 +858,12 @@ static int qmp_run_command(libxl__gc *gc
 {
     libxl__qmp_handler *qmp = NULL;
     int rc = 0;
+    libxl_domain_type type = libxl__domain_type(gc, domid);
 
-    qmp = libxl__qmp_initialize(gc, domid);
+    LOGD(DEBUG, domid, "%s: %s", __func__, cmd);
+    qmp = libxl__qmp_initialize(gc, type, domid);
     if (!qmp)
-        return ERROR_FAIL;
+        return (errno == ENOENT && type == LIBXL_DOMAIN_TYPE_PV) ? 0 : ERROR_FAIL;
 
     rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout);
 
@@ -876,8 +893,10 @@ int libxl__qmp_pci_add(libxl__gc *gc, in
     libxl__json_object *args = NULL;
     char *hostaddr = NULL;
     int rc = 0;
+    libxl_domain_type type = libxl__domain_type(gc, domid);
 
-    qmp = libxl__qmp_initialize(gc, domid);
+    LOGD(DEBUG, domid, "%s", __func__);
+    qmp = libxl__qmp_initialize(gc, type, domid);
     if (!qmp)
         return -1;
 
@@ -945,10 +964,12 @@ int libxl__qmp_save(libxl__gc *gc, int d
     libxl__json_object *args = NULL;
     libxl__qmp_handler *qmp = NULL;
     int rc;
+    libxl_domain_type type = libxl__domain_type(gc, domid);
 
-    qmp = libxl__qmp_initialize(gc, domid);
+    LOGD(DEBUG, domid, "%s: %s", __func__, filename);
+    qmp = libxl__qmp_initialize(gc, type, domid);
     if (!qmp)
-        return ERROR_FAIL;
+        return (errno == ENOENT && type == LIBXL_DOMAIN_TYPE_PV) ? 0 : ERROR_FAIL;
 
     qmp_parameters_add_string(gc, &args, "filename", (char *)filename);
 
@@ -1219,10 +1240,12 @@ int libxl__qmp_initializations(libxl__gc
     const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
     libxl__qmp_handler *qmp = NULL;
     int ret = 0;
+    libxl_domain_type type = libxl__domain_type(gc, domid);
 
-    qmp = libxl__qmp_initialize(gc, domid);
+    LOGD(DEBUG, domid, "%s", __func__);
+    qmp = libxl__qmp_initialize(gc, type, domid);
     if (!qmp)
-        return -1;
+        return (errno == ENOENT && type == LIBXL_DOMAIN_TYPE_PV) ? 0 : ERROR_FAIL;
     ret = libxl__qmp_query_serial(qmp);
     if (!ret && vnc && vnc->passwd) {
         ret = qmp_change(gc, qmp, "vnc", "password", vnc->passwd);
