From: Glauber Costa <glommer@redhat.com> Date: Fri, 18 Sep 2009 17:27:22 -0400 Subject: [x86] kvm: fix vsyscall going backwards Message-id: 1253309243-13769-2-git-send-email-glommer@redhat.com O-Subject: [PATCH 1/2] RHEL5 BZ524076 fix vsyscall going backwards Bugzilla: 524076 RH-Acked-by: Rik van Riel <riel@redhat.com> When using vsyscall infrastructure, clock can go backwards. This is because kvmclock relies on tsc, but not entirely, so the checks in VXTIME_TSC are not enough. This patch provides VXTIME_KVM, and uses the value stored in vxtime.last_kvm as a lower bound. We won't allow clock to go lower than this value. This mechanism is also implemented for the monotonic clock, that theorectically suffered from the same problem RH-Upstream-status: Specific to RHEL5 port. Signed-off-by: Glauber Costa <glommer@redhat.com> diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index 13e002f..90bc1e7 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -313,7 +313,16 @@ unsigned long long monotonic_clock(void) u32 last_offset, this_offset, offset; unsigned long long base; - if (vxtime.mode == VXTIME_HPET) { + if (vxtime.mode == VXTIME_KVM) { + do { + seq = read_seqbegin(&xtime_lock); + + last_offset = vxtime.last_kvm; + base = monotonic_base; + this_offset = kvm_clock_read(); + } while (read_seqretry(&xtime_lock, seq)); + offset = (this_offset - last_offset); + } else if (vxtime.mode == VXTIME_HPET) { do { seq = read_seqbegin(&xtime_lock); @@ -1199,7 +1208,7 @@ void time_init_gtod(void) if (use_kvm_time) { timetype = "KVM"; vxtime.last_kvm = kvm_clock_read(); - vxtime.mode = VXTIME_TSC; + vxtime.mode = VXTIME_KVM; do_gettimeoffset = do_gettimeoffset_kvm; } else if (timekeeping_use_tsc > 0) { timetype = "TSC Timekeeping"; diff --git a/arch/x86_64/kernel/vsyscall.c b/arch/x86_64/kernel/vsyscall.c index 69719d6..b511d73 100644 --- a/arch/x86_64/kernel/vsyscall.c +++ b/arch/x86_64/kernel/vsyscall.c @@ -76,8 +76,16 @@ static __always_inline void do_vgettimeofday(struct timeval * tv) sec = __xtime.tv_sec; nsec = __xtime.tv_nsec + (__jiffies - __wall_jiffies) * (NSEC_PER_SEC / HZ); + if (__vxtime.mode == VXTIME_KVM) { + long delta; + t = get_cycles_sync(); + if (t < __vxtime.last_tsc) + t = __vxtime.last_tsc; - if (__vxtime.mode != VXTIME_HPET) { + delta = ((t * __vxtime.tsc_quot) >> NS_SCALE) - __vxtime.last_kvm; + if (delta > 0) + nsec += delta; + } else if (__vxtime.mode != VXTIME_HPET) { t = get_cycles_sync(); if (t < __vxtime.last_tsc) t = __vxtime.last_tsc; diff --git a/include/asm-x86_64/vsyscall.h b/include/asm-x86_64/vsyscall.h index 53c1fe9..dd90792 100644 --- a/include/asm-x86_64/vsyscall.h +++ b/include/asm-x86_64/vsyscall.h @@ -27,6 +27,7 @@ enum vsyscall_num { #define VXTIME_TSC 1 #define VXTIME_HPET 2 #define VXTIME_PMTMR 3 +#define VXTIME_KVM 4 #define VGETCPU_RDTSCP 1 #define VGETCPU_LSL 2