Re: [PATCH v8 05/17] KVM: x86: Avoid NTP frequency skew for KVM clock on 32-bit host
Sean Christopherson <[email protected]> Wed, 5 Aug 2026 11:21:27 -0700
| Newsgroups | dev.linux.lists.sashiko-reviews,org.kernel.vger.kvm |
|---|---|
| Message-ID | <[email protected]> |
On Wed, Aug 05, 2026, [email protected] wrote: > > diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c > > index 3107878a6fe5d..8f469fd9863f9 100644 > > --- a/arch/x86/kvm/x86.c > > +++ b/arch/x86/kvm/x86.c > > [ ... ] > > > @@ -915,13 +914,6 @@ static s64 get_kvmclock_base_ns(void) > > /* Count up from boot time, but with the frequency of the raw clock. */ > > return ktime_to_ns(ktime_add(ktime_get_raw(), pvclock_gtod_data.offs_boot)); > > [Severity: Low] > Will reading the 64-bit pvclock_gtod_data.offs_boot without seqcount > protection or data_race() annotations trigger KCSAN data race warnings on > 32-bit systems? > > By removing the ktime_get_boottime_ns() fallback, this read now executes on > 32-bit architectures where it compiles to two non-atomic 32-bit accesses. > If a KVM vCPU thread calls get_kvmclock_base_ns() while a timer interrupt > runs timekeeping_update(), it overwrites offs_boot. > > Even though the value only actually changes during suspend when the freezer > subsystem guarantees vCPU threads are frozen (preventing functional tearing), > overwriting the identical value concurrently with an unprotected read still > introduces a formal C11 data race. Huh. And strictly speaking, 64-bit could tear the store/load. Stealing heavily from ktime_mono_to_any(), this as a prep patch plus fixup (not yet tested)? diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b6e1dfd6db6a..57679d871581 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -921,7 +921,8 @@ static void update_pvclock_gtod(struct timekeeper *tk) vdata->wall_time_sec = tk->xtime_sec; - vdata->offs_boot = tk->offs_boot; + /* Pairs with the READ_ONCE() in get_kvmclock_base_ns(). */ + WRITE_ONCE(vdata->offs_boot, tk->offs_boot); write_seqcount_end(&vdata->seq); } @@ -929,7 +930,26 @@ static void update_pvclock_gtod(struct timekeeper *tk) static s64 get_kvmclock_base_ns(void) { /* Count up from boot time, but with the frequency of the raw clock. */ - return ktime_to_ns(ktime_add(ktime_get_raw(), pvclock_gtod_data.offs_boot)); + struct pvclock_gtod_data *gtod = &pvclock_gtod_data; + ktime_t raw = ktime_get_raw(); + ktime_t now; + + /* + * Synchronization with clock updates isn't required on 64-bit as only + * one field is being consume + * */ +#ifdef CONFIG_X86_64 + now = ktime_add(raw, READ_ONCE(gtod->offs_boot)); +#else + unsigned int seq; + + do { + seq = read_seqcount_begin(>od->seq); + now = ktime_add(raw, *offset); + } while (read_seqcount_retry(gtod->seq, seq)); +#endif + + return ktime_to_ns(now); } static uint32_t div_frac(uint32_t dividend, uint32_t divisor)