aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcelo Tosatti <mtosatti@redhat.com>2020-06-16 13:58:05 -0300
committerPaolo Bonzini <pbonzini@redhat.com>2020-06-26 09:39:40 -0400
commit74aaddc6283ff789923127f3b95f781796fe3f39 (patch)
tree4c9dc155e4038ff5caae52a30029eaf859329911
parent32a354dc6c07d766e70b51f42a62d8cd479e3f82 (diff)
downloadqemu-74aaddc6283ff789923127f3b95f781796fe3f39.zip
qemu-74aaddc6283ff789923127f3b95f781796fe3f39.tar.gz
qemu-74aaddc6283ff789923127f3b95f781796fe3f39.tar.bz2
kvm: i386: allow TSC to differ by NTP correction bounds without TSC scaling
The Linux TSC calibration procedure is subject to small variations (its common to see +-1 kHz difference between reboots on a given CPU, for example). So migrating a guest between two hosts with identical processor can fail, in case of a small variation in calibrated TSC between them. Allow a conservative 250ppm error between host TSC and VM TSC frequencies, rather than requiring an exact match. NTP daemon in the guest can correct this difference. Also change migration to accept this bound. KVM_SET_TSC_KHZ depends on a kernel interface change. Without this change, the behaviour remains the same: in case of a different frequency between host and VM, KVM_SET_TSC_KHZ will fail and QEMU will exit. Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Message-Id: <20200616165805.GA324612@fuller.cnet> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--target/i386/kvm.c46
1 files changed, 41 insertions, 5 deletions
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index b3c13cb..6adbff3 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -740,26 +740,62 @@ static bool hyperv_enabled(X86CPU *cpu)
cpu->hyperv_features || cpu->hyperv_passthrough);
}
+/*
+ * Check whether target_freq is within conservative
+ * ntp correctable bounds (250ppm) of freq
+ */
+static inline bool freq_within_bounds(int freq, int target_freq)
+{
+ int max_freq = freq + (freq * 250 / 1000000);
+ int min_freq = freq - (freq * 250 / 1000000);
+
+ if (target_freq >= min_freq && target_freq <= max_freq) {
+ return true;
+ }
+
+ return false;
+}
+
static int kvm_arch_set_tsc_khz(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
- int r;
+ int r, cur_freq;
+ bool set_ioctl = false;
if (!env->tsc_khz) {
return 0;
}
- r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL) ?
+ cur_freq = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ?
+ kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) : -ENOTSUP;
+
+ /*
+ * If TSC scaling is supported, attempt to set TSC frequency.
+ */
+ if (kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL)) {
+ set_ioctl = true;
+ }
+
+ /*
+ * If desired TSC frequency is within bounds of NTP correction,
+ * attempt to set TSC frequency.
+ */
+ if (cur_freq != -ENOTSUP && freq_within_bounds(cur_freq, env->tsc_khz)) {
+ set_ioctl = true;
+ }
+
+ r = set_ioctl ?
kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz) :
-ENOTSUP;
+
if (r < 0) {
/* When KVM_SET_TSC_KHZ fails, it's an error only if the current
* TSC frequency doesn't match the one we want.
*/
- int cur_freq = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ?
- kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) :
- -ENOTSUP;
+ cur_freq = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ?
+ kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) :
+ -ENOTSUP;
if (cur_freq <= 0 || cur_freq != env->tsc_khz) {
warn_report("TSC frequency mismatch between "
"VM (%" PRId64 " kHz) and host (%d kHz), "