0f4c8a
From e7f6fc0ea19e73e1ecd9a54f12ac9f2df7cb30cf Mon Sep 17 00:00:00 2001
0f4c8a
Message-Id: <e7f6fc0ea19e73e1ecd9a54f12ac9f2df7cb30cf@dist-git>
0f4c8a
From: Jiri Denemark <jdenemar@redhat.com>
0f4c8a
Date: Fri, 6 Oct 2017 14:49:07 +0200
0f4c8a
Subject: [PATCH] qemu: Fix CPU model broken by older libvirt
0f4c8a
0f4c8a
When libvirt older than 3.9.0 reconnected to a running domain started by
0f4c8a
old libvirt it could have messed up the expansion of host-model by
0f4c8a
adding features QEMU does not support (such as cmt). Thus whenever we
0f4c8a
reconnect to a running domain, revert to an active snapshot, or restore
0f4c8a
a saved domain we need to check the guest CPU model and remove the
0f4c8a
CPU features unknown to QEMU. We can do this because we know the domain
0f4c8a
was successfully started, which means the CPU did not contain the
0f4c8a
features when libvirt started the domain.
0f4c8a
0f4c8a
https://bugzilla.redhat.com/show_bug.cgi?id=1495171
0f4c8a
0f4c8a
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
0f4c8a
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
0f4c8a
(cherry picked from commit 6a6f6b91e0e76480ea961f83135efcb4faf3284a)
0f4c8a
0f4c8a
Conflicts:
0f4c8a
	src/qemu/qemu_domain.c,
0f4c8a
	src/qemu/qemu_domain.h -- context, one more function new
0f4c8a
            function upstream
0f4c8a
0f4c8a
https://bugzilla.redhat.com/show_bug.cgi?id=1508549
0f4c8a
0f4c8a
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
0f4c8a
---
0f4c8a
 src/qemu/qemu_domain.c  | 75 +++++++++++++++++++++++++++++++++++++++++++++++++
0f4c8a
 src/qemu/qemu_domain.h  |  4 +++
0f4c8a
 src/qemu/qemu_driver.c  | 14 +++++++++
0f4c8a
 src/qemu/qemu_process.c |  9 ++++++
0f4c8a
 4 files changed, 102 insertions(+)
0f4c8a
0f4c8a
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
0f4c8a
index a41657099f..68c1f3b7c5 100644
0f4c8a
--- a/src/qemu/qemu_domain.c
0f4c8a
+++ b/src/qemu/qemu_domain.c
0f4c8a
@@ -9324,3 +9324,78 @@ qemuDomainUpdateCPU(virDomainObjPtr vm,
0f4c8a
 
0f4c8a
     return 0;
0f4c8a
 }
0f4c8a
+
0f4c8a
+
0f4c8a
+/**
0f4c8a
+ * qemuDomainFixupCPUS:
0f4c8a
+ * @vm: domain object
0f4c8a
+ * @origCPU: original CPU used when the domain was started
0f4c8a
+ *
0f4c8a
+ * Libvirt older than 3.9.0 could have messed up the expansion of host-model
0f4c8a
+ * CPU when reconnecting to a running domain by adding features QEMU does not
0f4c8a
+ * support (such as cmt). This API fixes both the actual CPU provided by QEMU
0f4c8a
+ * (stored in the domain object) and the @origCPU used when starting the
0f4c8a
+ * domain.
0f4c8a
+ *
0f4c8a
+ * This is safe even if the original CPU definition used mode='custom' (rather
0f4c8a
+ * than host-model) since we know QEMU was able to start the domain and thus
0f4c8a
+ * the CPU definitions do not contain any features unknown to QEMU.
0f4c8a
+ *
0f4c8a
+ * This function can only be used on an active domain or when restoring a
0f4c8a
+ * domain which was running.
0f4c8a
+ *
0f4c8a
+ * Returns 0 on success, -1 on error.
0f4c8a
+ */
0f4c8a
+int
0f4c8a
+qemuDomainFixupCPUs(virDomainObjPtr vm,
0f4c8a
+                    virCPUDefPtr *origCPU)
0f4c8a
+{
0f4c8a
+    virCPUDefPtr fixedCPU = NULL;
0f4c8a
+    virCPUDefPtr fixedOrig = NULL;
0f4c8a
+    virArch arch = vm->def->os.arch;
0f4c8a
+    int ret = 0;
0f4c8a
+
0f4c8a
+    if (!ARCH_IS_X86(arch))
0f4c8a
+        return 0;
0f4c8a
+
0f4c8a
+    if (!vm->def->cpu ||
0f4c8a
+        vm->def->cpu->mode != VIR_CPU_MODE_CUSTOM ||
0f4c8a
+        !vm->def->cpu->model)
0f4c8a
+        return 0;
0f4c8a
+
0f4c8a
+    /* Missing origCPU means QEMU created exactly the same virtual CPU which
0f4c8a
+     * we asked for or libvirt was too old to mess up the translation from
0f4c8a
+     * host-model.
0f4c8a
+     */
0f4c8a
+    if (!*origCPU)
0f4c8a
+        return 0;
0f4c8a
+
0f4c8a
+    if (virCPUDefFindFeature(vm->def->cpu, "cmt") &&
0f4c8a
+        (!(fixedCPU = virCPUDefCopyWithoutModel(vm->def->cpu)) ||
0f4c8a
+         virCPUDefCopyModelFilter(fixedCPU, vm->def->cpu, false,
0f4c8a
+                                  virQEMUCapsCPUFilterFeatures, &arch) < 0))
0f4c8a
+        goto cleanup;
0f4c8a
+
0f4c8a
+    if (virCPUDefFindFeature(*origCPU, "cmt") &&
0f4c8a
+        (!(fixedOrig = virCPUDefCopyWithoutModel(*origCPU)) ||
0f4c8a
+         virCPUDefCopyModelFilter(fixedOrig, *origCPU, false,
0f4c8a
+                                  virQEMUCapsCPUFilterFeatures, &arch) < 0))
0f4c8a
+        goto cleanup;
0f4c8a
+
0f4c8a
+    if (fixedCPU) {
0f4c8a
+        virCPUDefFree(vm->def->cpu);
0f4c8a
+        VIR_STEAL_PTR(vm->def->cpu, fixedCPU);
0f4c8a
+    }
0f4c8a
+
0f4c8a
+    if (fixedOrig) {
0f4c8a
+        virCPUDefFree(*origCPU);
0f4c8a
+        VIR_STEAL_PTR(*origCPU, fixedOrig);
0f4c8a
+    }
0f4c8a
+
0f4c8a
+    ret = 0;
0f4c8a
+
0f4c8a
+ cleanup:
0f4c8a
+    virCPUDefFree(fixedCPU);
0f4c8a
+    virCPUDefFree(fixedOrig);
0f4c8a
+    return ret;
0f4c8a
+}
0f4c8a
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
0f4c8a
index 7ad34e563e..1a658bcf7e 100644
0f4c8a
--- a/src/qemu/qemu_domain.h
0f4c8a
+++ b/src/qemu/qemu_domain.h
0f4c8a
@@ -930,4 +930,8 @@ qemuDomainUpdateCPU(virDomainObjPtr vm,
0f4c8a
                     virCPUDefPtr cpu,
0f4c8a
                     virCPUDefPtr *origCPU);
0f4c8a
 
0f4c8a
+int
0f4c8a
+qemuDomainFixupCPUs(virDomainObjPtr vm,
0f4c8a
+                    virCPUDefPtr *origCPU);
0f4c8a
+
0f4c8a
 #endif /* __QEMU_DOMAIN_H__ */
0f4c8a
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
0f4c8a
index b6d72303ca..bfd7ff6c09 100644
0f4c8a
--- a/src/qemu/qemu_driver.c
0f4c8a
+++ b/src/qemu/qemu_driver.c
0f4c8a
@@ -6500,6 +6500,13 @@ qemuDomainSaveImageStartVM(virConnectPtr conn,
0f4c8a
         }
0f4c8a
     }
0f4c8a
 
0f4c8a
+    /* No cookie means libvirt which saved the domain was too old to mess up
0f4c8a
+     * the CPU definitions.
0f4c8a
+     */
0f4c8a
+    if (cookie &&
0f4c8a
+        qemuDomainFixupCPUs(vm, &cookie->cpu) < 0)
0f4c8a
+        goto cleanup;
0f4c8a
+
0f4c8a
     if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL,
0f4c8a
                          asyncJob, "stdio", *fd, path, NULL,
0f4c8a
                          VIR_NETDEV_VPORT_PROFILE_OP_RESTORE,
0f4c8a
@@ -15520,6 +15527,13 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
0f4c8a
             if (config)
0f4c8a
                 virDomainObjAssignDef(vm, config, false, NULL);
0f4c8a
 
0f4c8a
+            /* No cookie means libvirt which saved the domain was too old to
0f4c8a
+             * mess up the CPU definitions.
0f4c8a
+             */
0f4c8a
+            if (cookie &&
0f4c8a
+                qemuDomainFixupCPUs(vm, &cookie->cpu) < 0)
0f4c8a
+                goto cleanup;
0f4c8a
+
0f4c8a
             rc = qemuProcessStart(snapshot->domain->conn, driver, vm,
0f4c8a
                                   cookie ? cookie->cpu : NULL,
0f4c8a
                                   QEMU_ASYNC_JOB_START, NULL, -1, NULL, snap,
0f4c8a
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
0f4c8a
index 0bed084381..5802a553cf 100644
0f4c8a
--- a/src/qemu/qemu_process.c
0f4c8a
+++ b/src/qemu/qemu_process.c
0f4c8a
@@ -6667,6 +6667,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver,
0f4c8a
                       virDomainObjPtr vm)
0f4c8a
 {
0f4c8a
     virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false);
0f4c8a
+    qemuDomainObjPrivatePtr priv = vm->privateData;
0f4c8a
     virCPUDefPtr host = NULL;
0f4c8a
     virCPUDefPtr cpu = NULL;
0f4c8a
     int ret = -1;
0f4c8a
@@ -6701,6 +6702,14 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver,
0f4c8a
 
0f4c8a
         if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
0f4c8a
             goto cleanup;
0f4c8a
+    } else if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) {
0f4c8a
+        /* We only try to fix CPUs when the libvirt/QEMU combo used to start
0f4c8a
+         * the domain did not know about query-cpu-model-expansion in which
0f4c8a
+         * case the host-model is known to not contain features which QEMU
0f4c8a
+         * doesn't know about.
0f4c8a
+         */
0f4c8a
+        if (qemuDomainFixupCPUs(vm, &priv->origCPU) < 0)
0f4c8a
+            goto cleanup;
0f4c8a
     }
0f4c8a
 
0f4c8a
     ret = 0;
0f4c8a
-- 
0f4c8a
2.15.0
0f4c8a