[libvirt] [PATCH 5/5] qemu: Check TSC frequency before starting QEMU

Jiri Denemark jdenemar at redhat.com
Fri May 31 12:00:11 UTC 2019


When migrating a domain with invtsc CPU feature enabled, the TSC
frequency of the destination host must match the frequency used when the
domain was started on the source host or the destination host has to
support TSC scaling.

If the frequencies do not match and the destination host does not
support TSC scaling, QEMU will fail to set the right TSC frequency when
starting vCPUs on the destination and thus migration will fail. However,
this is quite late since both host might have spent significant time
transferring memory and perhaps even storage data.

By adding the check to libvirt we can let migration fail before any data
starts to be sent over. If for some reason libvirt is unable to detect
the host's TSC frequency or scaling support, we'll just let QEMU try and
the migration will either succeed or fail later.

Luckily, we mandate TSC frequency to be explicitly set in the domain XML
to even allow migration of domains with invtsc. We can just check
whether the requested frequency is compatible with the current host
before starting QEMU.

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

Signed-off-by: Jiri Denemark <jdenemar at redhat.com>
---
 src/qemu/qemu_process.c | 53 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index fa5909e9be..481daa937e 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -5282,6 +5282,56 @@ qemuProcessStartValidateDisks(virDomainObjPtr vm,
 }
 
 
+static int
+qemuProcessStartValidateTSC(virDomainObjPtr vm,
+                            virCapsPtr caps)
+{
+    size_t i;
+    unsigned long long freq = 0;
+    virHostCPUTscInfoPtr tsc;
+
+    for (i = 0; i < vm->def->clock.ntimers; i++) {
+        virDomainTimerDefPtr timer = vm->def->clock.timers[i];
+
+        if (timer->name == VIR_DOMAIN_TIMER_NAME_TSC &&
+            timer->frequency > 0) {
+            freq = timer->frequency;
+            break;
+        }
+    }
+
+    if (freq == 0)
+        return 0;
+
+    VIR_DEBUG("Requested TSC frequency %llu Hz", freq);
+
+    if (!caps->host.cpu || !caps->host.cpu->tsc) {
+        VIR_DEBUG("Host TSC frequency could not be probed");
+        return 0;
+    }
+
+    tsc = caps->host.cpu->tsc;
+    VIR_DEBUG("Host TSC frequency %llu Hz, scaling %s",
+              tsc->frequency, virTristateBoolTypeToString(tsc->scaling));
+
+    if (freq == tsc->frequency || tsc->scaling == VIR_TRISTATE_BOOL_YES)
+        return 0;
+
+    if (tsc->scaling == VIR_TRISTATE_BOOL_ABSENT) {
+        VIR_DEBUG("TSC frequencies do not match and scaling support is "
+                  "unknown, QEMU will try and possibly fail later");
+        return 0;
+    }
+
+    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                   _("Requested TSC frequency %llu Hz does not match "
+                     "host (%llu Hz) and TSC scaling is not supported "
+                     "by the host CPU"),
+                   freq, tsc->frequency);
+    return -1;
+}
+
+
 /**
  * qemuProcessStartValidate:
  * @vm: domain object
@@ -5346,6 +5396,9 @@ qemuProcessStartValidate(virQEMUDriverPtr driver,
     if (qemuProcessStartValidateDisks(vm, qemuCaps) < 0)
         return -1;
 
+    if (qemuProcessStartValidateTSC(vm, caps) < 0)
+        return -1;
+
     VIR_DEBUG("Checking for any possible (non-fatal) issues");
 
     qemuProcessStartWarnShmem(vm);
-- 
2.21.0




More information about the libvir-list mailing list