From: Gerd Hoffmann <kraxel@redhat.com>
To: seabios@seabios.org
Cc: kvm@vger.kernel.org, Gerd Hoffmann <kraxel@redhat.com>
Subject: [PATCH] tsc: use kvmclock for calibration
Date: Thu, 9 Aug 2012 13:57:43 +0200 [thread overview]
Message-ID: <1344513463-7329-1-git-send-email-kraxel@redhat.com> (raw)
Use kvmclock for tsc calibration when running on kvm. Without this the
tsc frequency calibrated by seabios can be *way* off in case the virtual
machine is booted on a loaded host. I've seen seabios calibrating 27
instead of ca. 2800 MHz, resulting in timeouts being to short by factor
100. Which in turn leads to disk I/O errors due to timeouts, especially
as I/O requests tend to take a bit longer than usual on a loaded box ...
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
src/clock.c | 9 +++++
src/paravirt.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/paravirt.h | 1 +
3 files changed, 100 insertions(+), 0 deletions(-)
diff --git a/src/clock.c b/src/clock.c
index 69e9f17..5883b1a 100644
--- a/src/clock.c
+++ b/src/clock.c
@@ -13,6 +13,7 @@
#include "bregs.h" // struct bregs
#include "biosvar.h" // GET_GLOBAL
#include "usb-hid.h" // usb_check_event
+#include "paravirt.h" // kvm clock
// RTC register flags
#define RTC_A_UIP 0x80
@@ -80,6 +81,14 @@ calibrate_tsc(void)
return;
}
+ if (kvm_para_available()) {
+ u32 khz = kvm_tsc_khz();
+ if (khz != 0) {
+ SET_GLOBAL(cpu_khz, khz);
+ return;
+ }
+ }
+
// Setup "timer2"
u8 orig = inb(PORT_PS2_CTRLB);
outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB);
diff --git a/src/paravirt.c b/src/paravirt.c
index 2a98d53..942ce11 100644
--- a/src/paravirt.c
+++ b/src/paravirt.c
@@ -12,6 +12,7 @@
#include "ioport.h" // outw
#include "paravirt.h" // qemu_cfg_port_probe
#include "smbios.h" // struct smbios_structure_header
+#include "biosvar.h" // GET_GLOBAL
int qemu_cfg_present;
@@ -346,3 +347,92 @@ void qemu_cfg_romfile_setup(void)
dprintf(3, "Found fw_cfg file: %s (size=%d)\n", file->name, file->size);
}
}
+
+#define KVM_CPUID_SIGNATURE 0x40000000
+#define KVM_CPUID_FEATURES 0x40000001
+#define KVM_FEATURE_CLOCKSOURCE 0
+#define KVM_FEATURE_CLOCKSOURCE2 3
+#define MSR_KVM_SYSTEM_TIME 0x12
+#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01
+
+struct pvclock_vcpu_time_info {
+ u32 version;
+ u32 pad0;
+ u64 tsc_timestamp;
+ u64 system_time;
+ u32 tsc_to_system_mul;
+ s8 tsc_shift;
+ u8 flags;
+ u8 pad[2];
+} PACKED;
+
+/*
+ * do_div() is NOT a C function. It wants to return
+ * two values (the quotient and the remainder), but
+ * since that doesn't work very well in C, what it
+ * does is:
+ *
+ * - modifies the 64-bit dividend _in_place_
+ * - returns the 32-bit remainder
+ *
+ * This ends up being the most efficient "calling
+ * convention" on x86.
+ */
+#define do_div(n, base) \
+ ({ \
+ unsigned long __upper, __low, __high, __mod, __base; \
+ __base = (base); \
+ asm("" : "=a" (__low), "=d" (__high) : "A" (n)); \
+ __upper = __high; \
+ if (__high) { \
+ __upper = __high % (__base); \
+ __high = __high / (__base); \
+ } \
+ asm("divl %2" : "=a" (__low), "=d" (__mod) \
+ : "rm" (__base), "0" (__low), "1" (__upper)); \
+ asm("" : "=A" (n) : "a" (__low), "d" (__high)); \
+ __mod; \
+ })
+
+static u64 pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)
+{
+ u64 pv_tsc_khz = 1000000ULL << 32;
+
+ do_div(pv_tsc_khz, src->tsc_to_system_mul);
+ if (src->tsc_shift < 0)
+ pv_tsc_khz <<= -src->tsc_shift;
+ else
+ pv_tsc_khz >>= src->tsc_shift;
+ return pv_tsc_khz;
+}
+
+u64 kvm_tsc_khz(void)
+{
+ u32 eax, ebx, ecx, edx, msr;
+ struct pvclock_vcpu_time_info time;
+ u32 addr = (u32)(&time);
+ u64 khz;
+
+ /* check presence and figure msr number */
+ cpuid(KVM_CPUID_FEATURES, &eax, &ebx, &ecx, &edx);
+ if (eax & KVM_FEATURE_CLOCKSOURCE2) {
+ msr = MSR_KVM_SYSTEM_TIME_NEW;
+ } else if (eax & KVM_FEATURE_CLOCKSOURCE) {
+ msr = MSR_KVM_SYSTEM_TIME;
+ } else {
+ return 0;
+ }
+
+ /* ask kvm hypervisor to fill struct */
+ memset(&time, 0, sizeof(time));
+ wrmsr(msr, addr | 1);
+ wrmsr(msr, 0);
+ if (time.version < 2 || time.tsc_to_system_mul == 0)
+ return 0;
+
+ /* go figure tsc frequency */
+ khz = pvclock_tsc_khz(&time);
+ dprintf(1, "Using kvmclock, msr 0x%x, tsc %d MHz\n",
+ msr, (u32)khz / 1000);
+ return khz;
+}
diff --git a/src/paravirt.h b/src/paravirt.h
index a284c41..eedfcc3 100644
--- a/src/paravirt.h
+++ b/src/paravirt.h
@@ -27,6 +27,7 @@ static inline int kvm_para_available(void)
return 0;
}
+extern u64 kvm_tsc_khz(void);
#define QEMU_CFG_SIGNATURE 0x00
#define QEMU_CFG_ID 0x01
--
1.7.1
next reply other threads:[~2012-08-09 11:57 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-08-09 11:57 Gerd Hoffmann [this message]
2012-08-09 12:53 ` [PATCH] tsc: use kvmclock for calibration Avi Kivity
2012-08-09 13:25 ` [SeaBIOS] " Fred .
2012-08-09 13:57 ` Gerd Hoffmann
2012-08-09 14:01 ` Avi Kivity
2012-08-09 14:05 ` Avi Kivity
2012-08-09 14:12 ` Gerd Hoffmann
2012-08-09 14:17 ` Avi Kivity
2012-08-09 14:18 ` Gerd Hoffmann
2012-08-09 14:20 ` Avi Kivity
2012-08-09 19:02 ` Marcelo Tosatti
2012-08-12 10:56 ` Avi Kivity
2012-08-09 19:09 ` Marcelo Tosatti
2012-08-10 7:18 ` Gleb Natapov
2012-08-10 7:30 ` Gleb Natapov
2012-08-10 8:10 ` Gerd Hoffmann
2012-08-10 21:26 ` Marcelo Tosatti
2012-08-13 10:37 ` Gerd Hoffmann
2012-08-13 10:46 ` Gleb Natapov
2012-08-13 12:55 ` [SeaBIOS] " Fred .
2012-08-12 9:00 ` Avi Kivity
2012-08-09 18:59 ` Marcelo Tosatti
2012-08-12 9:01 ` Avi Kivity
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1344513463-7329-1-git-send-email-kraxel@redhat.com \
--to=kraxel@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=seabios@seabios.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).