[libvirt] [PATCH 4/4] qemu: Fix CPU model broken by older libvirt

Jiri Denemark posted 4 patches 7 years, 7 months ago
[libvirt] [PATCH 4/4] qemu: Fix CPU model broken by older libvirt
Posted by Jiri Denemark 7 years, 7 months ago
When libvirt older than 3.9.0 reconnected to a running domain started by
old libvirt it could have messed up the expansion of host-model by
adding features QEMU does not support (such as cmt). Thus whenever we
reconnect to a running domain, revert to an active snapshot, or restore
a saved domain we need to check the guest CPU model and remove the
CPU features unknown to QEMU. We can do this because we know the domain
was successfully started, which means the CPU did not contain the
features when libvirt started the domain.

https://bugzilla.redhat.com/show_bug.cgi?id=1495171

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
 src/qemu/qemu_domain.c  | 76 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_domain.h  |  4 +++
 src/qemu/qemu_driver.c  | 14 +++++++++
 src/qemu/qemu_process.c |  9 ++++++
 4 files changed, 103 insertions(+)

diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index f621cf7afc..29b6618a56 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -10021,6 +10021,82 @@ qemuDomainUpdateCPU(virDomainObjPtr vm,
     return 0;
 }
 
+
+/**
+ * qemuDomainFixupCPUS:
+ * @vm: domain object
+ * @origCPU: original CPU used when the domain was started
+ *
+ * Libvirt older than 3.9.0 could have messed up the expansion of host-model
+ * CPU when reconnecting to a running domain by adding features QEMU does not
+ * support (such as cmt). This API fixes both the actual CPU provided by QEMU
+ * (stored in the domain object) and the @origCPU used when starting the
+ * domain.
+ *
+ * This is safe even if the original CPU definition used mode='custom' (rather
+ * than host-model) since we know QEMU was able to start the domain and thus
+ * the CPU definitions do not contain any features unknown to QEMU.
+ *
+ * This function can only be used on an active domain or when restoring a
+ * domain which was running.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int
+qemuDomainFixupCPUs(virDomainObjPtr vm,
+                    virCPUDefPtr *origCPU)
+{
+    virCPUDefPtr fixedCPU = NULL;
+    virCPUDefPtr fixedOrig = NULL;
+    virArch arch = vm->def->os.arch;
+    int ret = 0;
+
+    if (!ARCH_IS_X86(arch))
+        return 0;
+
+    if (!vm->def->cpu ||
+        vm->def->cpu->mode != VIR_CPU_MODE_CUSTOM ||
+        !vm->def->cpu->model)
+        return 0;
+
+    /* Missing origCPU means QEMU created exactly the same virtual CPU which
+     * we asked for or libvirt was too old to mess up the translation from
+     * host-model.
+     */
+    if (!*origCPU)
+        return 0;
+
+    if (virCPUDefFindFeature(vm->def->cpu, "cmt") &&
+        (!(fixedCPU = virCPUDefCopyWithoutModel(vm->def->cpu)) ||
+         virCPUDefCopyModelFilter(fixedCPU, vm->def->cpu, false,
+                                  virQEMUCapsCPUFilterFeatures, &arch) < 0))
+        goto cleanup;
+
+    if (virCPUDefFindFeature(*origCPU, "cmt") &&
+        (!(fixedOrig = virCPUDefCopyWithoutModel(*origCPU)) ||
+         virCPUDefCopyModelFilter(fixedOrig, *origCPU, false,
+                                  virQEMUCapsCPUFilterFeatures, &arch) < 0))
+        goto cleanup;
+
+    if (fixedCPU) {
+        virCPUDefFree(vm->def->cpu);
+        VIR_STEAL_PTR(vm->def->cpu, fixedCPU);
+    }
+
+    if (fixedOrig) {
+        virCPUDefFree(*origCPU);
+        VIR_STEAL_PTR(*origCPU, fixedOrig);
+    }
+
+    ret = 0;
+
+ cleanup:
+    virCPUDefFree(fixedCPU);
+    virCPUDefFree(fixedOrig);
+    return ret;
+}
+
+
 char *
 qemuDomainGetMachineName(virDomainObjPtr vm)
 {
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index c34cd37fc4..e430302519 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -971,6 +971,10 @@ qemuDomainUpdateCPU(virDomainObjPtr vm,
                     virCPUDefPtr cpu,
                     virCPUDefPtr *origCPU);
 
+int
+qemuDomainFixupCPUs(virDomainObjPtr vm,
+                    virCPUDefPtr *origCPU);
+
 char *
 qemuDomainGetMachineName(virDomainObjPtr vm);
 
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 842e088519..2715d86615 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -6551,6 +6551,13 @@ qemuDomainSaveImageStartVM(virConnectPtr conn,
         }
     }
 
+    /* No cookie means libvirt which saved the domain was too old to mess up
+     * the CPU definitions.
+     */
+    if (cookie &&
+        qemuDomainFixupCPUs(vm, &cookie->cpu) < 0)
+        goto cleanup;
+
     if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL,
                          asyncJob, "stdio", *fd, path, NULL,
                          VIR_NETDEV_VPORT_PROFILE_OP_RESTORE,
@@ -15754,6 +15761,13 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
             if (config)
                 virDomainObjAssignDef(vm, config, false, NULL);
 
+            /* No cookie means libvirt which saved the domain was too old to
+             * mess up the CPU definitions.
+             */
+            if (cookie &&
+                qemuDomainFixupCPUs(vm, &cookie->cpu) < 0)
+                goto cleanup;
+
             rc = qemuProcessStart(snapshot->domain->conn, driver, vm,
                                   cookie ? cookie->cpu : NULL,
                                   QEMU_ASYNC_JOB_START, NULL, -1, NULL, snap,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 8553c5126f..780af5bd78 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -6881,6 +6881,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver,
                       virDomainObjPtr vm)
 {
     virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false);
+    qemuDomainObjPrivatePtr priv = vm->privateData;
     virCPUDefPtr host = NULL;
     virCPUDefPtr cpu = NULL;
     int ret = -1;
@@ -6913,6 +6914,14 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver,
 
         if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
             goto cleanup;
+    } else if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) {
+        /* We only try to fix CPUs when the libvirt/QEMU combo used to start
+         * the domain did not know about query-cpu-model-expansion in which
+         * case the host-model is known to not contain features which QEMU
+         * doesn't know about.
+         */
+        if (qemuDomainFixupCPUs(vm, &priv->origCPU) < 0)
+            goto cleanup;
     }
 
     ret = 0;
-- 
2.14.2

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 4/4] qemu: Fix CPU model broken by older libvirt
Posted by Pavel Hrdina 7 years, 6 months ago
On Wed, Oct 11, 2017 at 12:11:17PM +0200, Jiri Denemark wrote:
> When libvirt older than 3.9.0 reconnected to a running domain started by
> old libvirt it could have messed up the expansion of host-model by
> adding features QEMU does not support (such as cmt). Thus whenever we
> reconnect to a running domain, revert to an active snapshot, or restore
> a saved domain we need to check the guest CPU model and remove the
> CPU features unknown to QEMU. We can do this because we know the domain
> was successfully started, which means the CPU did not contain the
> features when libvirt started the domain.
> 
> https://bugzilla.redhat.com/show_bug.cgi?id=1495171
> 
> Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
> ---
>  src/qemu/qemu_domain.c  | 76 +++++++++++++++++++++++++++++++++++++++++++++++++
>  src/qemu/qemu_domain.h  |  4 +++
>  src/qemu/qemu_driver.c  | 14 +++++++++
>  src/qemu/qemu_process.c |  9 ++++++
>  4 files changed, 103 insertions(+)

Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH 4/4] qemu: Fix CPU model broken by older libvirt
Posted by Jiri Denemark 7 years, 6 months ago
On Tue, Oct 17, 2017 at 14:34:25 +0200, Pavel Hrdina wrote:
> On Wed, Oct 11, 2017 at 12:11:17PM +0200, Jiri Denemark wrote:
> > When libvirt older than 3.9.0 reconnected to a running domain started by
> > old libvirt it could have messed up the expansion of host-model by
> > adding features QEMU does not support (such as cmt). Thus whenever we
> > reconnect to a running domain, revert to an active snapshot, or restore
> > a saved domain we need to check the guest CPU model and remove the
> > CPU features unknown to QEMU. We can do this because we know the domain
> > was successfully started, which means the CPU did not contain the
> > features when libvirt started the domain.
> > 
> > https://bugzilla.redhat.com/show_bug.cgi?id=1495171
> > 
> > Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
> > ---
> >  src/qemu/qemu_domain.c  | 76 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  src/qemu/qemu_domain.h  |  4 +++
> >  src/qemu/qemu_driver.c  | 14 +++++++++
> >  src/qemu/qemu_process.c |  9 ++++++
> >  4 files changed, 103 insertions(+)
> 
> Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

Thanks for the review. I fixed the issues and pushed this series.

Jirka

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list