]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: Update CPU definition according to QEMU
authorJiri Denemark <jdenemar@redhat.com>
Mon, 13 Mar 2017 11:32:02 +0000 (12:32 +0100)
committerJiri Denemark <jdenemar@redhat.com>
Fri, 17 Mar 2017 10:50:48 +0000 (11:50 +0100)
When starting a domain with custom guest CPU specification QEMU may add
or remove some CPU features. There are several reasons for this, e.g.,
QEMU/KVM does not support some requested features or the definition of
the requested CPU model in libvirt's cpu_map.xml differs from the one
QEMU is using. We can't really avoid this because CPU models are allowed
to change with machine types and libvirt doesn't know (and probably
doesn't even want to know) about such changes.

Thus when we want to make sure guest ABI doesn't change when a domain
gets migrated to another host, we need to update our live CPU definition
according to the CPU QEMU created. Once updated, we will change CPU
checking to VIR_CPU_CHECK_FULL to make sure the virtual CPU created
after migration exactly matches the one on the source.

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

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
src/cpu/cpu.c
src/cpu/cpu.h
src/cpu/cpu_x86.c
src/libvirt_private.syms
src/qemu/qemu_process.c

index 5b1940b47f9d4aaf29dc1dca5115c339b67d04be..992a0339cc7f6a9e085651b3bc5c1253ae0c0d55 100644 (file)
@@ -712,6 +712,50 @@ virCPUUpdate(virArch arch,
 }
 
 
+/**
+ * virCPUUpdateLive:
+ *
+ * @arch: CPU architecture
+ * @cpu: guest CPU definition to be updated
+ * @dataEnabled: CPU data of the virtual CPU
+ * @dataDisabled: CPU data with features requested by @cpu but disabled by the
+ *                hypervisor
+ *
+ * Update custom mode CPU according to the virtual CPU created by the
+ * hypervisor.
+ *
+ * Returns -1 on error,
+ *          0 when the CPU was successfully updated,
+ *          1 when the operation does not make sense on the CPU or it is not
+ *            supported for the given architecture.
+ */
+int
+virCPUUpdateLive(virArch arch,
+                 virCPUDefPtr cpu,
+                 virCPUDataPtr dataEnabled,
+                 virCPUDataPtr dataDisabled)
+{
+    struct cpuArchDriver *driver;
+
+    VIR_DEBUG("arch=%s, cpu=%p, dataEnabled=%p, dataDisabled=%p",
+              virArchToString(arch), cpu, dataEnabled, dataDisabled);
+
+    if (!(driver = cpuGetSubDriver(arch)))
+        return -1;
+
+    if (!driver->updateLive)
+        return 1;
+
+    if (cpu->mode != VIR_CPU_MODE_CUSTOM)
+        return 1;
+
+    if (driver->updateLive(cpu, dataEnabled, dataDisabled) < 0)
+        return -1;
+
+    return 0;
+}
+
+
 /**
  * virCPUCheckFeature:
  *
index c329eb1349673d543482a6934b38d4f614ca083c..7d6d3e92114c16da319c8c74bb25a9abd8dffed0 100644 (file)
@@ -86,6 +86,11 @@ typedef int
 (*virCPUArchUpdate)(virCPUDefPtr guest,
                     const virCPUDef *host);
 
+typedef int
+(*virCPUArchUpdateLive)(virCPUDefPtr cpu,
+                        virCPUDataPtr dataEnabled,
+                        virCPUDataPtr dataDisabled);
+
 typedef int
 (*virCPUArchCheckFeature)(const virCPUDef *cpu,
                           const char *feature);
@@ -122,6 +127,7 @@ struct cpuArchDriver {
     virCPUArchGetHost   getHost;
     cpuArchBaseline     baseline;
     virCPUArchUpdate    update;
+    virCPUArchUpdateLive updateLive;
     virCPUArchCheckFeature checkFeature;
     virCPUArchDataCheckFeature dataCheckFeature;
     virCPUArchDataFormat dataFormat;
@@ -198,6 +204,12 @@ virCPUUpdate(virArch arch,
              const virCPUDef *host)
     ATTRIBUTE_NONNULL(2);
 
+int
+virCPUUpdateLive(virArch arch,
+                 virCPUDefPtr cpu,
+                 virCPUDataPtr dataEnabled,
+                 virCPUDataPtr dataDisabled)
+    ATTRIBUTE_NONNULL(2);
 
 int
 virCPUCheckFeature(virArch arch,
index 6719acee253ae3b4bfefcc8cc31564ca33fe754d..a43bb2bdf20f22b8015d5e0e2bfe19f03f7d46b5 100644 (file)
@@ -2677,6 +2677,64 @@ virCPUx86Update(virCPUDefPtr guest,
 }
 
 
+static int
+virCPUx86UpdateLive(virCPUDefPtr cpu,
+                    virCPUDataPtr dataEnabled,
+                    virCPUDataPtr dataDisabled)
+{
+    virCPUx86MapPtr map;
+    virCPUx86ModelPtr model = NULL;
+    virCPUx86Data enabled = VIR_CPU_X86_DATA_INIT;
+    virCPUx86Data disabled = VIR_CPU_X86_DATA_INIT;
+    size_t i;
+    int ret = -1;
+
+    if (!(map = virCPUx86GetMap()))
+        return -1;
+
+    if (!(model = x86ModelFromCPU(cpu, map, -1)))
+        goto cleanup;
+
+    if (dataEnabled &&
+        x86DataCopy(&enabled, &dataEnabled->data.x86) < 0)
+        goto cleanup;
+
+    if (dataDisabled &&
+        x86DataCopy(&disabled, &dataDisabled->data.x86) < 0)
+        goto cleanup;
+
+    x86DataSubtract(&enabled, &model->data);
+
+    for (i = 0; i < map->nfeatures; i++) {
+        virCPUx86FeaturePtr feature = map->features[i];
+
+        if (x86DataIsSubset(&enabled, &feature->data)) {
+            VIR_DEBUG("Adding feature '%s' enabled by the hypervisor",
+                      feature->name);
+            if (virCPUDefUpdateFeature(cpu, feature->name,
+                                       VIR_CPU_FEATURE_REQUIRE) < 0)
+                goto cleanup;
+        }
+
+        if (x86DataIsSubset(&disabled, &feature->data)) {
+            VIR_DEBUG("Removing feature '%s' disabled by the hypervisor",
+                      feature->name);
+            if (virCPUDefUpdateFeature(cpu, feature->name,
+                                       VIR_CPU_FEATURE_DISABLE) < 0)
+                goto cleanup;
+        }
+    }
+
+    ret = 0;
+
+ cleanup:
+    x86ModelFree(model);
+    virCPUx86DataClear(&enabled);
+    virCPUx86DataClear(&disabled);
+    return ret;
+}
+
+
 static int
 virCPUx86CheckFeature(const virCPUDef *cpu,
                       const char *name)
@@ -2854,6 +2912,7 @@ struct cpuArchDriver cpuDriverX86 = {
 #endif
     .baseline   = x86Baseline,
     .update     = virCPUx86Update,
+    .updateLive = virCPUx86UpdateLive,
     .checkFeature = virCPUx86CheckFeature,
     .dataCheckFeature = virCPUx86DataCheckFeature,
     .dataFormat = virCPUx86DataFormat,
index 8af5454b40ef6081d66ef465d45c18c6f3f28afe..165d8cb252ab34fb393cdc38416c26e9b4e0b951 100644 (file)
@@ -1016,6 +1016,7 @@ virCPUGetHost;
 virCPUGetModels;
 virCPUTranslate;
 virCPUUpdate;
+virCPUUpdateLive;
 
 
 # cpu/cpu_x86.h
index db98a2f2cae91785ae06c11aa50c084fc3bda3b7..780f9587a8dc455ad8605b3401611978712c66ab 100644 (file)
@@ -3840,12 +3840,13 @@ qemuProcessVerifyCPUFeatures(virDomainDefPtr def,
 
 
 static int
-qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver,
-                          virDomainObjPtr vm,
-                          qemuDomainAsyncJob asyncJob)
+qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver,
+                              virDomainObjPtr vm,
+                              qemuDomainAsyncJob asyncJob)
 {
     virDomainDefPtr def = vm->def;
     virCPUDataPtr cpu = NULL;
+    virCPUDataPtr disabled = NULL;
     qemuDomainObjPrivatePtr priv = vm->privateData;
     int rc;
     int ret = -1;
@@ -3854,7 +3855,7 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver,
         if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
             goto cleanup;
 
-        rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu, NULL);
+        rc = qemuMonitorGetGuestCPU(priv->mon, def->os.arch, &cpu, &disabled);
 
         if (qemuDomainObjExitMonitor(driver, vm) < 0)
             goto cleanup;
@@ -3871,12 +3872,18 @@ qemuProcessVerifyGuestCPU(virQEMUDriverPtr driver,
 
         if (qemuProcessVerifyCPUFeatures(def, cpu) < 0)
             goto cleanup;
+
+        if ((rc = virCPUUpdateLive(def->os.arch, def->cpu, cpu, disabled)) < 0)
+            goto cleanup;
+        else if (rc == 0)
+            def->cpu->check = VIR_CPU_CHECK_FULL;
     }
 
     ret = 0;
 
  cleanup:
     virCPUDataFree(cpu);
+    virCPUDataFree(disabled);
     return ret;
 }
 
@@ -5720,8 +5727,8 @@ qemuProcessLaunch(virConnectPtr conn,
     if (qemuConnectAgent(driver, vm) < 0)
         goto cleanup;
 
-    VIR_DEBUG("Detecting if required emulator features are present");
-    if (qemuProcessVerifyGuestCPU(driver, vm, asyncJob) < 0)
+    VIR_DEBUG("Verifying and updating provided guest CPU");
+    if (qemuProcessUpdateLiveGuestCPU(driver, vm, asyncJob) < 0)
         goto cleanup;
 
     VIR_DEBUG("Setting up post-init cgroup restrictions");