Skip to content

Commit ca28aa6

Browse files
committed
KVM: x86/xen: Use guest's copy of pvclock when starting timer
Use the guest's copy of its pvclock when starting a Xen timer, as KVM's reference copy may not be up-to-date, i.e. may yield a false positive of sorts. In the unlikely scenario that the guest is starting a Xen timer and has used a Xen pvclock in the past, but has since but turned it "off", then vcpu->arch.hv_clock may be stale, as KVM's reference copy is updated if and only if at least one pvclock is enabled. Furthermore, vcpu->arch.hv_clock is currently used by three different pvclocks: kvmclock, Xen, and Xen compat. While it's extremely unlikely a guest would ever enable multiple pvclocks, effectively sharing KVM's reference clock could yield very weird behavior. Using the guest's active Xen pvclock instead of KVM's reference will allow dropping KVM's reference copy. Fixes: 451a707 ("KVM: x86/xen: improve accuracy of Xen timers") Cc: Paul Durrant <pdurrant@amazon.com> Cc: David Woodhouse <dwmw@amazon.co.uk> Reviewed-by: Paul Durrant <paul@xen.org> Link: https://lore.kernel.org/r/20250201013827.680235-6-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 6c4927a commit ca28aa6

File tree

1 file changed

+60
-5
lines changed

1 file changed

+60
-5
lines changed

arch/x86/kvm/xen.c

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,46 @@ static enum hrtimer_restart xen_timer_callback(struct hrtimer *timer)
150150
return HRTIMER_NORESTART;
151151
}
152152

153+
static int xen_get_guest_pvclock(struct kvm_vcpu *vcpu,
154+
struct pvclock_vcpu_time_info *hv_clock,
155+
struct gfn_to_pfn_cache *gpc,
156+
unsigned int offset)
157+
{
158+
unsigned long flags;
159+
int r;
160+
161+
read_lock_irqsave(&gpc->lock, flags);
162+
while (!kvm_gpc_check(gpc, offset + sizeof(*hv_clock))) {
163+
read_unlock_irqrestore(&gpc->lock, flags);
164+
165+
r = kvm_gpc_refresh(gpc, offset + sizeof(*hv_clock));
166+
if (r)
167+
return r;
168+
169+
read_lock_irqsave(&gpc->lock, flags);
170+
}
171+
172+
memcpy(hv_clock, gpc->khva + offset, sizeof(*hv_clock));
173+
read_unlock_irqrestore(&gpc->lock, flags);
174+
175+
/*
176+
* Sanity check TSC shift+multiplier to verify the guest's view of time
177+
* is more or less consistent.
178+
*/
179+
if (hv_clock->tsc_shift != vcpu->arch.hv_clock.tsc_shift ||
180+
hv_clock->tsc_to_system_mul != vcpu->arch.hv_clock.tsc_to_system_mul)
181+
return -EINVAL;
182+
183+
return 0;
184+
}
185+
153186
static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs,
154187
bool linux_wa)
155188
{
189+
struct kvm_vcpu_xen *xen = &vcpu->arch.xen;
156190
int64_t kernel_now, delta;
157191
uint64_t guest_now;
192+
int r = -EOPNOTSUPP;
158193

159194
/*
160195
* The guest provides the requested timeout in absolute nanoseconds
@@ -173,10 +208,29 @@ static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs,
173208
* the absolute CLOCK_MONOTONIC time at which the timer should
174209
* fire.
175210
*/
176-
if (vcpu->arch.hv_clock.version && vcpu->kvm->arch.use_master_clock &&
177-
static_cpu_has(X86_FEATURE_CONSTANT_TSC)) {
211+
do {
212+
struct pvclock_vcpu_time_info hv_clock;
178213
uint64_t host_tsc, guest_tsc;
179214

215+
if (!static_cpu_has(X86_FEATURE_CONSTANT_TSC) ||
216+
!vcpu->kvm->arch.use_master_clock)
217+
break;
218+
219+
/*
220+
* If both Xen PV clocks are active, arbitrarily try to use the
221+
* compat clock first, but also try to use the non-compat clock
222+
* if the compat clock is unusable. The two PV clocks hold the
223+
* same information, but it's possible one (or both) is stale
224+
* and/or currently unreachable.
225+
*/
226+
if (xen->vcpu_info_cache.active)
227+
r = xen_get_guest_pvclock(vcpu, &hv_clock, &xen->vcpu_info_cache,
228+
offsetof(struct compat_vcpu_info, time));
229+
if (r && xen->vcpu_time_info_cache.active)
230+
r = xen_get_guest_pvclock(vcpu, &hv_clock, &xen->vcpu_time_info_cache, 0);
231+
if (r)
232+
break;
233+
180234
if (!IS_ENABLED(CONFIG_64BIT) ||
181235
!kvm_get_monotonic_and_clockread(&kernel_now, &host_tsc)) {
182236
/*
@@ -197,9 +251,10 @@ static void kvm_xen_start_timer(struct kvm_vcpu *vcpu, u64 guest_abs,
197251

198252
/* Calculate the guest kvmclock as the guest would do it. */
199253
guest_tsc = kvm_read_l1_tsc(vcpu, host_tsc);
200-
guest_now = __pvclock_read_cycles(&vcpu->arch.hv_clock,
201-
guest_tsc);
202-
} else {
254+
guest_now = __pvclock_read_cycles(&hv_clock, guest_tsc);
255+
} while (0);
256+
257+
if (r) {
203258
/*
204259
* Without CONSTANT_TSC, get_kvmclock_ns() is the only option.
205260
*

0 commit comments

Comments
 (0)