On Thu, 2022-06-30 at 12:58 +0100, David Woodhouse wrote: > The TSC of each vCPU#n was at a given value of kvmclock That one isn't even particularly hard to do. At its core, it looks something like this (thanks to Simon for fixing the arithmetic. My first proof-of-concept was only a *few* orders of magnitude out...) Simon then has a follow-on patch to wrap an ioctl around it and actually allow the kvm_ns to be passed in. diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 1910e1e78b15..ddb67c9566e4 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2629,7 +2629,7 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc, kvm_track_tsc_matching(vcpu); } -static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 data) +static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 data, u64 *kvm_ns) { struct kvm *kvm = vcpu->kvm; u64 offset, ns, elapsed; @@ -2638,8 +2638,23 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 data) bool synchronizing = false; raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags); - offset = kvm_compute_l1_tsc_offset(vcpu, data); ns = get_kvmclock_base_ns(); + if (kvm_ns) { + /* + * We have been provided a KVM clock reference time point at + * which this TSC value was correct. Use this time point to + * compensate for any delays that were incurred since that + * TSC value was computed. Note that time is still passing; + * in an ideal world the get_kvmclock_base_ns() above would + * be based on the *same* TSC value that's hidden in the call + * to kvm_compute_l1_tsc_offset() below. + */ + u32 tsc_khz = vcpu->arch.virtual_tsc_khz; + s64 delta_ns = ns + vcpu->kvm->arch.kvmclock_offset - *kvm_ns; + s64 delta_counts = delta_ns * tsc_khz / NSEC_PER_MSEC; + data += delta_counts; + } + offset = kvm_compute_l1_tsc_offset(vcpu, data); elapsed = ns - kvm->arch.last_tsc_nsec; if (vcpu->arch.virtual_tsc_khz) { @@ -3581,7 +3596,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; case MSR_IA32_TSC: if (msr_info->host_initiated) { - kvm_synchronize_tsc(vcpu, data); + kvm_synchronize_tsc(vcpu, data, NULL); } else { u64 adj = kvm_compute_l1_tsc_offset(vcpu, data) - vcpu->arch.l1_tsc_offset; adjust_tsc_offset_guest(vcpu, adj); @@ -11392,7 +11407,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) if (mutex_lock_killable(&vcpu->mutex)) return; vcpu_load(vcpu); - kvm_synchronize_tsc(vcpu, 0); + kvm_synchronize_tsc(vcpu, 0, NULL); vcpu_put(vcpu); /* poll control enabled by default */