From patchwork Sun Jul 26 03:22:45 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Wang X-Patchwork-Id: 114179 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o6Q3Mpvp016359 for ; Mon, 26 Jul 2010 03:22:51 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753372Ab0GZDWt (ORCPT ); Sun, 25 Jul 2010 23:22:49 -0400 Received: from mx1.redhat.com ([209.132.183.28]:16104 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753347Ab0GZDWs (ORCPT ); Sun, 25 Jul 2010 23:22:48 -0400 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o6Q3Mm2Q011985 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Sun, 25 Jul 2010 23:22:48 -0400 Received: from [127.0.1.1] (dhcp-65-104.nay.redhat.com [10.66.65.104]) by int-mx03.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o6Q3Mjof014698; Sun, 25 Jul 2010 23:22:46 -0400 Subject: [PATCH 2/3] test: Add kvmclock driver To: mtosatti@redhat.com, avi@redhat.com, kvm@vger.kernel.org From: Jason Wang Date: Sun, 26 Jul 2009 11:22:45 +0800 Message-ID: <20090726032245.8068.10750.stgit@FreeLancer> In-Reply-To: <20090726032237.8068.32553.stgit@FreeLancer> References: <20090726032237.8068.32553.stgit@FreeLancer> User-Agent: StGit/0.15 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.67 on 10.5.11.16 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Mon, 26 Jul 2010 03:22:51 +0000 (UTC) diff --git a/kvm/test/x86/kvmclock.c b/kvm/test/x86/kvmclock.c new file mode 100644 index 0000000..1520ea4 --- /dev/null +++ b/kvm/test/x86/kvmclock.c @@ -0,0 +1,206 @@ +#include "libcflat.h" +#include "smp.h" +#include "kvmclock.h" + +#define barrier() asm volatile("":::"memory") +#define rmb() asm volatile("lfence":::"memory") + +struct pvclock_vcpu_time_info hv_clock[MAX_CPU]; +struct pvclock_wall_clock wall_clock; +static unsigned char valid_flags = 0; +static u64 __attribute__((aligned(8))) last_value = 0; + +#ifdef __i386__ +static inline u64 atomic64_read(const u64 *v) +{ + u64 res; + + asm volatile("mov %%ebx, %%eax;" + "mov %%ecx, %%edx;" + "lock cmpxchg8b %1;" + : "=&A" (res) + : "m" (*v) + ); + + return res; +} + +static u64 atomic64_cmpxchg(u64 *v, u64 old, u64 new) +{ + u32 low = new; + u32 high = new >> 32; + + asm volatile("lock cmpxchg8b %1\n" + : "+A" (old), "+m" (*v) + : "b" (low), "c" (high) + ); + + return old; +} +#elif defined(__x86_64__) +static inline u64 atomic64_read(const u64 *v) +{ + return (*(volatile long *)v); +} + +static u64 atomic64_cmpxchg(u64 *v, u64 old, u64 new) +{ + u64 ret; + u64 _old = old; + u64 _new = new; + + asm volatile("lock cmpxchgq %1,%2" + : "=a" (ret) + : "r" (_new), "m" (*(volatile long *)v), "0" (_old) + : "memory" + ); + return ret; +} +#endif + +static void wrmsr(unsigned index, u64 value) +{ + unsigned a = value, d = value >> 32; + + asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(index)); +} + +static u64 native_read_tsc(void) +{ + unsigned a, d; + + asm volatile("rdtsc" : "=a"(a), "=d"(d)); + return a | (u64)d << 32; +} + +static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift) +{ + u64 product; +#ifdef __i386__ + u32 tmp1, tmp2; +#endif + + if (shift < 0) + delta >>= -shift; + else + delta <<= shift; + +#ifdef __i386__ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "xor %5,%5 ; " + "add %4,%%eax ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) ); +#elif defined(__x86_64__) + __asm__ ( + "mul %%rdx ; shrd $32,%%rdx,%%rax" + : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) ); +#else +#error implement me! +#endif + + return product; +} + +static unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst, + struct pvclock_vcpu_time_info *src) +{ + do { + dst->version = src->version; + rmb(); /* fetch version before data */ + dst->tsc_timestamp = src->tsc_timestamp; + dst->system_timestamp = src->system_time; + dst->tsc_to_nsec_mul = src->tsc_to_system_mul; + dst->tsc_shift = src->tsc_shift; + dst->flags = src->flags; + rmb(); /* test version after fetching data */ + } while ((src->version & 1) || (dst->version != src->version)); + + return dst->version; +} + +static u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow) +{ + u64 delta = native_read_tsc() - shadow->tsc_timestamp; + return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift); +} + +static cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src) +{ + struct pvclock_shadow_time shadow; + unsigned version; + cycle_t ret, offset; + u64 last; + + do { + version = pvclock_get_time_values(&shadow, src); + barrier(); + offset = pvclock_get_nsec_offset(&shadow); + ret = shadow.system_timestamp + offset; + barrier(); + } while (version != src->version); + + if ((valid_flags & PVCLOCK_CYCLE_RAW_TEST_BIT) || + ((valid_flags & PVCLOCK_TSC_STABLE_BIT) && + (shadow.flags & PVCLOCK_TSC_STABLE_BIT))) + return ret; + + last = atomic64_read(&last_value); + do { + if (ret < last) + return last; + last = atomic64_cmpxchg(&last_value, last, ret); + } while (last != ret); + + return ret; +} + +cycle_t kvm_clock_read() +{ + struct pvclock_vcpu_time_info *src; + cycle_t ret; + int index = smp_id(); + + src = &hv_clock[index]; + ret = pvclock_clocksource_read(src); + return ret; +} + +void kvm_clock_init(void *data) +{ + int index = smp_id(); + struct pvclock_vcpu_time_info *hvc = &hv_clock[index]; + + printf("kvm-clock: cpu %d, msr 0x:%lx \n", index, hvc); + wrmsr(MSR_KVM_SYSTEM_TIME, (unsigned long)hvc | 1); +} + +void kvm_clock_clear(void *data) +{ + wrmsr(MSR_KVM_SYSTEM_TIME, 0LL); +} + +void kvm_get_wallclock(struct timespec *ts) +{ + u32 version; + wrmsr(MSR_KVM_WALL_CLOCK, (u64)&wall_clock); + + do { + version = wall_clock.version; + rmb(); /* fetch version before time */ + ts->sec = wall_clock.sec; + ts->nsec = wall_clock.nsec; + rmb(); /* fetch time before checking version */ + } while ((wall_clock.version & 1) || (version != wall_clock.version)); + +} + +void pvclock_set_flags(unsigned char flags) +{ + valid_flags = flags; +} diff --git a/kvm/test/x86/kvmclock.h b/kvm/test/x86/kvmclock.h new file mode 100644 index 0000000..6cc545e --- /dev/null +++ b/kvm/test/x86/kvmclock.h @@ -0,0 +1,51 @@ +#ifndef KVMCLOCK_H +#define KVMCLOCK_H + +#define MSR_KVM_WALL_CLOCK 0x11 +#define MSR_KVM_SYSTEM_TIME 0x12 + +#define MAX_CPU 4 + +#define PVCLOCK_TSC_STABLE_BIT (1 << 0) +#define PVCLOCK_CYCLE_RAW_TEST_BIT (1 << 1) /* Get raw cycle */ + +typedef u64 cycle_t; + +struct pvclock_vcpu_time_info { + u32 version; + u32 pad0; + u64 tsc_timestamp; + u64 system_time; + u32 tsc_to_system_mul; + signed char tsc_shift; + u8 flags; + u8 pad[2]; +} __attribute__((__packed__)); /* 32 bytes */ + +struct pvclock_shadow_time { + u64 tsc_timestamp; /* TSC at last update of time vals. */ + u64 system_timestamp; /* Time, in nanosecs, since boot. */ + u32 tsc_to_nsec_mul; + int tsc_shift; + u32 version; + u8 flags; +}; + +struct pvclock_wall_clock { + u32 version; + u32 sec; + u32 nsec; +} __attribute__((__packed__)); + +struct timespec { + u32 sec; + u32 nsec; +}; + +void pvclock_set_flags(unsigned char flags); +cycle_t kvm_clock_read(); +void kvm_get_wallclock(struct timespec *ts); +void kvm_clock_init(void *data); +void kvm_clock_clear(void *data); + +#endif