When the guest and host use different TSC frequencies - specifically when
"tsc-frequency" is configured - the guest TSC becomes unsynchronized after
additional vCPUs are added.

Suppose the host TSC frequency is 2596.102 MHz. Here are steps to
reproduce:

Create the QEMU instance with a different TSC frequency 2596.100 MHz.

-cpu host,tsc-frequency=2596100000 \
-smp 2,maxcpus=8 \

After the guest VM has booted, add two additional vCPUs.

(qemu) device_add host-x86_64-cpu,id=core2,socket-id=0,core-id=2,thread-id=0
(qemu) device_add host-x86_64-cpu,id=core3,socket-id=0,core-id=3,thread-id=0

The guest TSC becomes unsynchronized. The vCPUs end up with different TSC
offsets.

host# cat /sys/kernel/debug/kvm/167789-11/vcpu0/tsc-offset
-345550695701016
host# cat /sys/kernel/debug/kvm/167789-11/vcpu1/tsc-offset
-345550695701016
host# cat /sys/kernel/debug/kvm/167789-11/vcpu2/tsc-offset
-345700146347894
host# cat /sys/kernel/debug/kvm/167789-11/vcpu3/tsc-offset
-345700945993728

This issue occurs because KVM synchronizes the guest TSC twice for each
newly added vCPU. In other words, kvm_synchronize_tsc() is invoked twice
per vCPU by KVM.

During the first synchronization, kvm->arch.default_tsc_khz is used. During
the second synchronization, the TSC frequency configured by QEMU is used.
Because different TSC frequencies are used during these two synchronization
steps, the guest TSC becomes unsynchronized in KVM.

Linux kernel commit ffbb61d09fc5 ("KVM: x86: Accept KVM_[GS]ET_TSC_KHZ as
a VM ioctl.") introduced support in KVM to help mitigate this issue. QEMU
should add corresponding support to address the problem on its side.

Always issue the VM ioctl KVM_SET_TSC_KHZ before creating any vCPUs, so
that the default TSC frequency is set correctly for all subsequently
created vCPUs.

Signed-off-by: Dongli Zhang <[email protected]>
---
This is based on the latest QEMU commit, along with the patchset below,
which has already been queued by Paolo.
https://lore.kernel.org/qemu-devel/[email protected]

 target/i386/kvm/kvm.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 9f1a4d4cbb..56bba9460d 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -2065,6 +2065,9 @@ int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
     int ret;
 
     if (first) {
+        X86CPU *x86cpu = X86_CPU(cpu);
+        CPUX86State *env = &x86cpu->env;
+
         first = false;
 
         /*
@@ -2086,6 +2089,17 @@ int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
                 return ret;
             }
         }
+
+        if (!is_tdx_vm() && env->tsc_khz &&
+            kvm_check_extension(kvm_state, KVM_CAP_VM_TSC_CONTROL)) {
+            ret = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, env->tsc_khz);
+            if (ret < 0) {
+                error_setg_errno(errp, -ret,
+                           "Unable to set TSC frequency to %"PRId64" kHz",
+                           env->tsc_khz);
+                return ret;
+            }
+        }
     }
 
     if (is_tdx_vm()) {
-- 
2.39.3


Reply via email to