6d3351
From 3b5aa0e6272a4b507034bba3d093734ebd658b3b Mon Sep 17 00:00:00 2001
6d3351
Message-Id: <3b5aa0e6272a4b507034bba3d093734ebd658b3b@dist-git>
6d3351
From: Jiri Denemark <jdenemar@redhat.com>
6d3351
Date: Wed, 31 May 2017 12:34:10 +0200
6d3351
Subject: [PATCH] qemu: Use updated CPU when starting QEMU if possible
6d3351
6d3351
If QEMU is new enough and we have the live updated CPU definition in
6d3351
either save or migration cookie, we can use it to enforce ABI. The
6d3351
original guest CPU from domain XML will be stored in private data.
6d3351
6d3351
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
6d3351
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
6d3351
(cherry picked from commit 8e34f478137c2a6b5e57e382729bd2776b042301)
6d3351
6d3351
https://bugzilla.redhat.com/show_bug.cgi?id=1441662
6d3351
6d3351
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
6d3351
---
6d3351
 src/qemu/qemu_domain.c    | 40 ++++++++++++++++++++++++++++++++++++++++
6d3351
 src/qemu/qemu_domain.h    |  5 +++++
6d3351
 src/qemu/qemu_driver.c    | 30 ++++++++++++++++++++++++------
6d3351
 src/qemu/qemu_migration.c |  2 +-
6d3351
 src/qemu/qemu_process.c   | 24 ++++++++++++++++++++++--
6d3351
 src/qemu/qemu_process.h   |  2 ++
6d3351
 6 files changed, 94 insertions(+), 9 deletions(-)
6d3351
6d3351
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
6d3351
index 7897a44634..82dacb3c9c 100644
6d3351
--- a/src/qemu/qemu_domain.c
6d3351
+++ b/src/qemu/qemu_domain.c
6d3351
@@ -9225,3 +9225,43 @@ virSaveCookieCallbacks virQEMUDriverDomainSaveCookie = {
6d3351
     .parse = qemuDomainSaveCookieParse,
6d3351
     .format = qemuDomainSaveCookieFormat,
6d3351
 };
6d3351
+
6d3351
+
6d3351
+/**
6d3351
+ * qemuDomainUpdateCPU:
6d3351
+ * @vm: domain which is being started
6d3351
+ * @cpu: CPU updated when the domain was running previously (before migration,
6d3351
+ *       snapshot, or save)
6d3351
+ * @origCPU: where to store the original CPU from vm->def in case @cpu was
6d3351
+ *           used instead
6d3351
+ *
6d3351
+ * Replace the CPU definition with the updated one when QEMU is new enough to
6d3351
+ * allow us to check extra features it is about to enable or disable when
6d3351
+ * starting a domain. The original CPU is stored in @origCPU.
6d3351
+ *
6d3351
+ * Returns 0 on success, -1 on error.
6d3351
+ */
6d3351
+int
6d3351
+qemuDomainUpdateCPU(virDomainObjPtr vm,
6d3351
+                    virCPUDefPtr cpu,
6d3351
+                    virCPUDefPtr *origCPU)
6d3351
+{
6d3351
+    qemuDomainObjPrivatePtr priv = vm->privateData;
6d3351
+
6d3351
+    *origCPU = NULL;
6d3351
+
6d3351
+    if (!cpu || !vm->def->cpu ||
6d3351
+        !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION) ||
6d3351
+        virCPUDefIsEqual(vm->def->cpu, cpu, false))
6d3351
+        return 0;
6d3351
+
6d3351
+    if (!(cpu = virCPUDefCopy(cpu)))
6d3351
+        return -1;
6d3351
+
6d3351
+    VIR_DEBUG("Replacing CPU def with the updated one");
6d3351
+
6d3351
+    *origCPU = vm->def->cpu;
6d3351
+    vm->def->cpu = cpu;
6d3351
+
6d3351
+    return 0;
6d3351
+}
6d3351
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
6d3351
index 2eea7924fb..d635d2995f 100644
6d3351
--- a/src/qemu/qemu_domain.h
6d3351
+++ b/src/qemu/qemu_domain.h
6d3351
@@ -921,4 +921,9 @@ char *qemuDomainDiskBackingStoreGetName(virDomainDiskDefPtr disk,
6d3351
 virStorageSourcePtr qemuDomainGetStorageSourceByDevstr(const char *devstr,
6d3351
                                                        virDomainDefPtr def);
6d3351
 
6d3351
+int
6d3351
+qemuDomainUpdateCPU(virDomainObjPtr vm,
6d3351
+                    virCPUDefPtr cpu,
6d3351
+                    virCPUDefPtr *origCPU);
6d3351
+
6d3351
 #endif /* __QEMU_DOMAIN_H__ */
6d3351
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
6d3351
index f5c5c302be..5567103c37 100644
6d3351
--- a/src/qemu/qemu_driver.c
6d3351
+++ b/src/qemu/qemu_driver.c
6d3351
@@ -1782,7 +1782,7 @@ static virDomainPtr qemuDomainCreateXML(virConnectPtr conn,
6d3351
         goto cleanup;
6d3351
     }
6d3351
 
6d3351
-    if (qemuProcessStart(conn, driver, vm, QEMU_ASYNC_JOB_START,
6d3351
+    if (qemuProcessStart(conn, driver, vm, NULL, QEMU_ASYNC_JOB_START,
6d3351
                          NULL, -1, NULL, NULL,
6d3351
                          VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
6d3351
                          start_flags) < 0) {
6d3351
@@ -6500,8 +6500,8 @@ qemuDomainSaveImageStartVM(virConnectPtr conn,
6d3351
         }
6d3351
     }
6d3351
 
6d3351
-    if (qemuProcessStart(conn, driver, vm, asyncJob,
6d3351
-                         "stdio", *fd, path, NULL,
6d3351
+    if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL,
6d3351
+                         asyncJob, "stdio", *fd, path, NULL,
6d3351
                          VIR_NETDEV_VPORT_PROFILE_OP_RESTORE,
6d3351
                          VIR_QEMU_PROCESS_START_PAUSED) == 0)
6d3351
         restored = true;
6d3351
@@ -7118,7 +7118,7 @@ qemuDomainObjStart(virConnectPtr conn,
6d3351
         }
6d3351
     }
6d3351
 
6d3351
-    ret = qemuProcessStart(conn, driver, vm, asyncJob,
6d3351
+    ret = qemuProcessStart(conn, driver, vm, NULL, asyncJob,
6d3351
                            NULL, -1, NULL, NULL,
6d3351
                            VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags);
6d3351
     virDomainAuditStart(vm, "booted", ret >= 0);
6d3351
@@ -15295,6 +15295,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
6d3351
     virCapsPtr caps = NULL;
6d3351
     bool was_running = false;
6d3351
     bool was_stopped = false;
6d3351
+    qemuDomainSaveCookiePtr cookie;
6d3351
+    virCPUDefPtr origCPU = NULL;
6d3351
 
6d3351
     virCheckFlags(VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING |
6d3351
                   VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED |
6d3351
@@ -15400,6 +15402,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
6d3351
             goto endjob;
6d3351
     }
6d3351
 
6d3351
+    cookie = (qemuDomainSaveCookiePtr) snap->def->cookie;
6d3351
+
6d3351
     switch ((virDomainState) snap->def->state) {
6d3351
     case VIR_DOMAIN_RUNNING:
6d3351
     case VIR_DOMAIN_PAUSED:
6d3351
@@ -15411,6 +15415,15 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
6d3351
          * to have finer control.  */
6d3351
         if (virDomainObjIsActive(vm)) {
6d3351
             /* Transitions 5, 6, 8, 9 */
6d3351
+            /* Replace the CPU in config and put the original one in priv
6d3351
+             * once we're done.
6d3351
+             */
6d3351
+            if (cookie && cookie->cpu && config->cpu) {
6d3351
+                origCPU = config->cpu;
6d3351
+                if (!(config->cpu = virCPUDefCopy(cookie->cpu)))
6d3351
+                    goto endjob;
6d3351
+            }
6d3351
+
6d3351
             /* Check for ABI compatibility. We need to do this check against
6d3351
              * the migratable XML or it will always fail otherwise */
6d3351
             if (config &&
6d3351
@@ -15470,8 +15483,11 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
6d3351
                  * failed loadvm attempt? */
6d3351
                 goto endjob;
6d3351
             }
6d3351
-            if (config)
6d3351
+            if (config) {
6d3351
                 virDomainObjAssignDef(vm, config, false, NULL);
6d3351
+                virCPUDefFree(priv->origCPU);
6d3351
+                VIR_STEAL_PTR(priv->origCPU, origCPU);
6d3351
+            }
6d3351
         } else {
6d3351
             /* Transitions 2, 3 */
6d3351
         load:
6d3351
@@ -15480,6 +15496,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
6d3351
                 virDomainObjAssignDef(vm, config, false, NULL);
6d3351
 
6d3351
             rc = qemuProcessStart(snapshot->domain->conn, driver, vm,
6d3351
+                                  cookie ? cookie->cpu : NULL,
6d3351
                                   QEMU_ASYNC_JOB_START, NULL, -1, NULL, snap,
6d3351
                                   VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
6d3351
                                   VIR_QEMU_PROCESS_START_PAUSED);
6d3351
@@ -15573,7 +15590,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
6d3351
             start_flags |= paused ? VIR_QEMU_PROCESS_START_PAUSED : 0;
6d3351
 
6d3351
             qemuDomainEventQueue(driver, event);
6d3351
-            rc = qemuProcessStart(snapshot->domain->conn, driver, vm,
6d3351
+            rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL,
6d3351
                                   QEMU_ASYNC_JOB_START, NULL, -1, NULL, NULL,
6d3351
                                   VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
6d3351
                                   start_flags);
6d3351
@@ -15645,6 +15662,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
6d3351
     virObjectUnref(caps);
6d3351
     virObjectUnref(cfg);
6d3351
     virNWFilterUnlockFilterUpdates();
6d3351
+    virCPUDefFree(origCPU);
6d3351
 
6d3351
     return ret;
6d3351
 }
6d3351
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
6d3351
index c03f1be575..a4540ce3c4 100644
6d3351
--- a/src/qemu/qemu_migration.c
6d3351
+++ b/src/qemu/qemu_migration.c
6d3351
@@ -2650,7 +2650,7 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
6d3351
         goto stopjob;
6d3351
     }
6d3351
 
6d3351
-    if (qemuProcessInit(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN,
6d3351
+    if (qemuProcessInit(driver, vm, mig->cpu, QEMU_ASYNC_JOB_MIGRATION_IN,
6d3351
                         true, VIR_QEMU_PROCESS_START_AUTODESTROY) < 0)
6d3351
         goto stopjob;
6d3351
     stopProcess = true;
6d3351
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
6d3351
index 79f780ed46..a7abfc720e 100644
6d3351
--- a/src/qemu/qemu_process.c
6d3351
+++ b/src/qemu/qemu_process.c
6d3351
@@ -3946,6 +3946,13 @@ qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver,
6d3351
         if (qemuProcessVerifyCPUFeatures(def, cpu) < 0)
6d3351
             goto cleanup;
6d3351
 
6d3351
+        /* Don't update the CPU if we already did so when starting a domain
6d3351
+         * during migration, restore or snapshot revert. */
6d3351
+        if (priv->origCPU) {
6d3351
+            ret = 0;
6d3351
+            goto cleanup;
6d3351
+        }
6d3351
+
6d3351
         if (!(orig = virCPUDefCopy(def->cpu)))
6d3351
             goto cleanup;
6d3351
 
6d3351
@@ -4864,6 +4871,7 @@ qemuProcessStartValidate(virQEMUDriverPtr driver,
6d3351
 int
6d3351
 qemuProcessInit(virQEMUDriverPtr driver,
6d3351
                 virDomainObjPtr vm,
6d3351
+                virCPUDefPtr updatedCPU,
6d3351
                 qemuDomainAsyncJob asyncJob,
6d3351
                 bool migration,
6d3351
                 unsigned int flags)
6d3351
@@ -4872,6 +4880,7 @@ qemuProcessInit(virQEMUDriverPtr driver,
6d3351
     virCapsPtr caps = NULL;
6d3351
     qemuDomainObjPrivatePtr priv = vm->privateData;
6d3351
     int stopFlags;
6d3351
+    virCPUDefPtr origCPU = NULL;
6d3351
     int ret = -1;
6d3351
 
6d3351
     VIR_DEBUG("vm=%p name=%s id=%d migration=%d",
6d3351
@@ -4896,6 +4905,9 @@ qemuProcessInit(virQEMUDriverPtr driver,
6d3351
                                                       vm->def->os.machine)))
6d3351
         goto cleanup;
6d3351
 
6d3351
+    if (qemuDomainUpdateCPU(vm, updatedCPU, &origCPU) < 0)
6d3351
+        goto cleanup;
6d3351
+
6d3351
     if (qemuProcessStartValidate(driver, vm, priv->qemuCaps, caps, flags) < 0)
6d3351
         goto cleanup;
6d3351
 
6d3351
@@ -4928,11 +4940,14 @@ qemuProcessInit(virQEMUDriverPtr driver,
6d3351
 
6d3351
         if (qemuDomainSetPrivatePaths(driver, vm) < 0)
6d3351
             goto stop;
6d3351
+
6d3351
+        VIR_STEAL_PTR(priv->origCPU, origCPU);
6d3351
     }
6d3351
 
6d3351
     ret = 0;
6d3351
 
6d3351
  cleanup:
6d3351
+    virCPUDefFree(origCPU);
6d3351
     virObjectUnref(cfg);
6d3351
     virObjectUnref(caps);
6d3351
     return ret;
6d3351
@@ -5963,6 +5978,7 @@ int
6d3351
 qemuProcessStart(virConnectPtr conn,
6d3351
                  virQEMUDriverPtr driver,
6d3351
                  virDomainObjPtr vm,
6d3351
+                 virCPUDefPtr updatedCPU,
6d3351
                  qemuDomainAsyncJob asyncJob,
6d3351
                  const char *migrateFrom,
6d3351
                  int migrateFd,
6d3351
@@ -5993,7 +6009,8 @@ qemuProcessStart(virConnectPtr conn,
6d3351
     if (!migrateFrom && !snapshot)
6d3351
         flags |= VIR_QEMU_PROCESS_START_NEW;
6d3351
 
6d3351
-    if (qemuProcessInit(driver, vm, asyncJob, !!migrateFrom, flags) < 0)
6d3351
+    if (qemuProcessInit(driver, vm, updatedCPU,
6d3351
+                        asyncJob, !!migrateFrom, flags) < 0)
6d3351
         goto cleanup;
6d3351
 
6d3351
     if (migrateFrom) {
6d3351
@@ -6072,7 +6089,8 @@ qemuProcessCreatePretendCmd(virConnectPtr conn,
6d3351
     flags |= VIR_QEMU_PROCESS_START_PRETEND;
6d3351
     flags |= VIR_QEMU_PROCESS_START_NEW;
6d3351
 
6d3351
-    if (qemuProcessInit(driver, vm, QEMU_ASYNC_JOB_NONE, !!migrateURI, flags) < 0)
6d3351
+    if (qemuProcessInit(driver, vm, NULL, QEMU_ASYNC_JOB_NONE,
6d3351
+                        !!migrateURI, flags) < 0)
6d3351
         goto cleanup;
6d3351
 
6d3351
     if (qemuProcessPrepareDomain(conn, driver, vm, flags) < 0)
6d3351
@@ -6474,6 +6492,8 @@ void qemuProcessStop(virQEMUDriverPtr driver,
6d3351
 
6d3351
     /* clean up migration data */
6d3351
     VIR_FREE(priv->migTLSAlias);
6d3351
+    virCPUDefFree(priv->origCPU);
6d3351
+    priv->origCPU = NULL;
6d3351
 
6d3351
     /* clear previously used namespaces */
6d3351
     virBitmapFree(priv->namespaces);
6d3351
diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
6d3351
index 830d8cef84..c38310b47a 100644
6d3351
--- a/src/qemu/qemu_process.h
6d3351
+++ b/src/qemu/qemu_process.h
6d3351
@@ -75,6 +75,7 @@ typedef enum {
6d3351
 int qemuProcessStart(virConnectPtr conn,
6d3351
                      virQEMUDriverPtr driver,
6d3351
                      virDomainObjPtr vm,
6d3351
+                     virCPUDefPtr updatedCPU,
6d3351
                      qemuDomainAsyncJob asyncJob,
6d3351
                      const char *migrateFrom,
6d3351
                      int stdin_fd,
6d3351
@@ -93,6 +94,7 @@ virCommandPtr qemuProcessCreatePretendCmd(virConnectPtr conn,
6d3351
 
6d3351
 int qemuProcessInit(virQEMUDriverPtr driver,
6d3351
                     virDomainObjPtr vm,
6d3351
+                    virCPUDefPtr updatedCPU,
6d3351
                     qemuDomainAsyncJob asyncJob,
6d3351
                     bool migration,
6d3351
                     unsigned int flags);
6d3351
-- 
6d3351
2.13.1
6d3351