6d3351
From b2196db320485b642d6654279ebb6b75eeafd3e9 Mon Sep 17 00:00:00 2001
6d3351
Message-Id: <b2196db320485b642d6654279ebb6b75eeafd3e9@dist-git>
6d3351
From: Jiri Denemark <jdenemar@redhat.com>
6d3351
Date: Tue, 11 Apr 2017 20:46:05 +0200
6d3351
Subject: [PATCH] qemu: Use more data for comparing CPUs
6d3351
6d3351
With QEMU older than 2.9.0 libvirt uses CPUID instruction to determine
6d3351
what CPU features are supported on the host. This was later used when
6d3351
checking compatibility of guest CPUs. Since QEMU 2.9.0 we ask QEMU for
6d3351
the host CPU data. But the two methods we use usually provide disjoint
6d3351
sets of CPU features because QEMU/KVM does not support all features
6d3351
provided by the host CPU and on the other hand it can enable some
6d3351
feature even if the host CPU does not support them.
6d3351
6d3351
So if there is a domain which requires a CPU features disabled by
6d3351
QEMU/KVM, libvirt will refuse to start it with QEMU > 2.9.0 as its guest
6d3351
CPU is incompatible with the host CPU data we got from QEMU. But such
6d3351
domain would happily start on older QEMU (of course, the features would
6d3351
be missing the guest CPU). To fix this regression, we need to combine
6d3351
both CPU feature sets when checking guest CPU compatibility.
6d3351
6d3351
https://bugzilla.redhat.com/show_bug.cgi?id=1439933
6d3351
6d3351
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
6d3351
(cherry picked from commit 5b4a6adb5ca24a6cb91cdc55c31506fb278d3a91)
6d3351
6d3351
https://bugzilla.redhat.com/show_bug.cgi?id=1444421
6d3351
6d3351
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
6d3351
---
6d3351
 src/qemu/qemu_capabilities.c | 35 +++++++++++++++++++++++++++++++++--
6d3351
 src/qemu/qemu_capabilities.h |  4 ++++
6d3351
 src/qemu/qemu_process.c      |  2 +-
6d3351
 3 files changed, 38 insertions(+), 3 deletions(-)
6d3351
6d3351
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
6d3351
index ec773971e..49de506ed 100644
6d3351
--- a/src/qemu/qemu_capabilities.c
6d3351
+++ b/src/qemu/qemu_capabilities.c
6d3351
@@ -387,6 +387,10 @@ struct _virQEMUCapsHostCPUData {
6d3351
     virCPUDefPtr reported;
6d3351
     /* Migratable host CPU definition used for updating guest CPU. */
6d3351
     virCPUDefPtr migratable;
6d3351
+    /* CPU definition with features detected by libvirt using virCPUGetHost
6d3351
+     * combined with features reported by QEMU. This is used for backward
6d3351
+     * compatible comparison between a guest CPU and a host CPU. */
6d3351
+    virCPUDefPtr full;
6d3351
 };
6d3351
 
6d3351
 /*
6d3351
@@ -2112,6 +2116,10 @@ virQEMUCapsHostCPUDataCopy(virQEMUCapsHostCPUDataPtr dst,
6d3351
         !(dst->migratable = virCPUDefCopy(src->migratable)))
6d3351
         return -1;
6d3351
 
6d3351
+    if (src->full &&
6d3351
+        !(dst->full = virCPUDefCopy(src->full)))
6d3351
+        return -1;
6d3351
+
6d3351
     return 0;
6d3351
 }
6d3351
 
6d3351
@@ -2122,6 +2130,7 @@ virQEMUCapsHostCPUDataClear(virQEMUCapsHostCPUDataPtr cpuData)
6d3351
     qemuMonitorCPUModelInfoFree(cpuData->info);
6d3351
     virCPUDefFree(cpuData->reported);
6d3351
     virCPUDefFree(cpuData->migratable);
6d3351
+    virCPUDefFree(cpuData->full);
6d3351
 
6d3351
     memset(cpuData, 0, sizeof(*cpuData));
6d3351
 }
6d3351
@@ -2463,6 +2472,11 @@ virQEMUCapsGetHostModel(virQEMUCapsPtr qemuCaps,
6d3351
 
6d3351
     case VIR_QEMU_CAPS_HOST_CPU_MIGRATABLE:
6d3351
         return cpuData->migratable;
6d3351
+
6d3351
+    case VIR_QEMU_CAPS_HOST_CPU_FULL:
6d3351
+        /* 'full' is non-NULL only if we have data from both QEMU and
6d3351
+         * virCPUGetHost */
6d3351
+        return cpuData->full ? cpuData->full : cpuData->reported;
6d3351
     }
6d3351
 
6d3351
     return NULL;
6d3351
@@ -2473,12 +2487,14 @@ static void
6d3351
 virQEMUCapsSetHostModel(virQEMUCapsPtr qemuCaps,
6d3351
                         virDomainVirtType type,
6d3351
                         virCPUDefPtr reported,
6d3351
-                        virCPUDefPtr migratable)
6d3351
+                        virCPUDefPtr migratable,
6d3351
+                        virCPUDefPtr full)
6d3351
 {
6d3351
     virQEMUCapsHostCPUDataPtr cpuData = virQEMUCapsGetHostCPUData(qemuCaps, type);
6d3351
 
6d3351
     cpuData->reported = reported;
6d3351
     cpuData->migratable = migratable;
6d3351
+    cpuData->full = full;
6d3351
 }
6d3351
 
6d3351
 
6d3351
@@ -3350,6 +3366,8 @@ virQEMUCapsInitHostCPUModel(virQEMUCapsPtr qemuCaps,
6d3351
     virCPUDefPtr cpu = NULL;
6d3351
     virCPUDefPtr migCPU = NULL;
6d3351
     virCPUDefPtr hostCPU = NULL;
6d3351
+    virCPUDefPtr fullCPU = NULL;
6d3351
+    size_t i;
6d3351
     int rc;
6d3351
 
6d3351
     if (!caps || !virQEMUCapsGuestIsNative(caps->host.arch, qemuCaps->arch))
6d3351
@@ -3369,6 +3387,18 @@ virQEMUCapsInitHostCPUModel(virQEMUCapsPtr qemuCaps,
6d3351
                                      virQEMUCapsCPUFilterFeatures,
6d3351
                                      qemuCaps) < 0)
6d3351
             goto error;
6d3351
+    } else if (type == VIR_DOMAIN_VIRT_KVM &&
6d3351
+               virCPUGetHostIsSupported(qemuCaps->arch)) {
6d3351
+        if (!(fullCPU = virCPUGetHost(qemuCaps->arch, VIR_CPU_TYPE_GUEST,
6d3351
+                                      NULL, NULL, 0)))
6d3351
+            goto error;
6d3351
+
6d3351
+        for (i = 0; i < cpu->nfeatures; i++) {
6d3351
+            if (cpu->features[i].policy == VIR_CPU_FEATURE_REQUIRE &&
6d3351
+                virCPUDefUpdateFeature(fullCPU, cpu->features[i].name,
6d3351
+                                       VIR_CPU_FEATURE_REQUIRE) < 0)
6d3351
+                goto error;
6d3351
+        }
6d3351
     }
6d3351
 
6d3351
     if (!(migCPU = virQEMUCapsNewHostCPUModel()))
6d3351
@@ -3384,7 +3414,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCapsPtr qemuCaps,
6d3351
             goto error;
6d3351
     }
6d3351
 
6d3351
-    virQEMUCapsSetHostModel(qemuCaps, type, cpu, migCPU);
6d3351
+    virQEMUCapsSetHostModel(qemuCaps, type, cpu, migCPU, fullCPU);
6d3351
 
6d3351
  cleanup:
6d3351
     virCPUDefFree(hostCPU);
6d3351
@@ -3393,6 +3423,7 @@ virQEMUCapsInitHostCPUModel(virQEMUCapsPtr qemuCaps,
6d3351
  error:
6d3351
     virCPUDefFree(cpu);
6d3351
     virCPUDefFree(migCPU);
6d3351
+    virCPUDefFree(fullCPU);
6d3351
     virResetLastError();
6d3351
     goto cleanup;
6d3351
 }
6d3351
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
6d3351
index 16fe044cc..644b8e27b 100644
6d3351
--- a/src/qemu/qemu_capabilities.h
6d3351
+++ b/src/qemu/qemu_capabilities.h
6d3351
@@ -456,6 +456,10 @@ typedef enum {
6d3351
     VIR_QEMU_CAPS_HOST_CPU_REPORTED,
6d3351
     /* Migratable host CPU definition used for updating guest CPU. */
6d3351
     VIR_QEMU_CAPS_HOST_CPU_MIGRATABLE,
6d3351
+    /* CPU definition with features detected by libvirt using virCPUGetHost
6d3351
+     * combined with features reported by QEMU. This is used for backward
6d3351
+     * compatible comparison between a guest CPU and a host CPU. */
6d3351
+    VIR_QEMU_CAPS_HOST_CPU_FULL,
6d3351
 } virQEMUCapsHostCPUType;
6d3351
 
6d3351
 virCPUDefPtr virQEMUCapsGetHostModel(virQEMUCapsPtr qemuCaps,
6d3351
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
6d3351
index c81389ef2..a33ec87e6 100644
6d3351
--- a/src/qemu/qemu_process.c
6d3351
+++ b/src/qemu/qemu_process.c
6d3351
@@ -5307,7 +5307,7 @@ qemuProcessUpdateGuestCPU(virDomainDefPtr def,
6d3351
     if (def->cpu->check == VIR_CPU_CHECK_PARTIAL &&
6d3351
         virCPUCompare(caps->host.arch,
6d3351
                       virQEMUCapsGetHostModel(qemuCaps, def->virtType,
6d3351
-                                              VIR_QEMU_CAPS_HOST_CPU_REPORTED),
6d3351
+                                              VIR_QEMU_CAPS_HOST_CPU_FULL),
6d3351
                       def->cpu, true) < 0)
6d3351
         return -1;
6d3351
 
6d3351
-- 
6d3351
2.12.2
6d3351