diff options
author | Adin Scannell <ascannell@google.com> | 2019-03-11 18:18:41 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2019-03-11 18:19:45 -0700 |
commit | 6e6dbf0e566270ae96a4db81d9d04275d0fffb00 (patch) | |
tree | 15fbd810d753020e8f29baa562dc6e18594081e3 /pkg | |
parent | 4a8062990f33f33b3972feb0a7f2abe55bae16c7 (diff) |
kvm: minimum guest/host timekeeping delta.
PiperOrigin-RevId: 237927368
Change-Id: I359badd1967bb118fe74eab3282c946c18937edc
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/sentry/platform/kvm/machine_amd64_unsafe.go | 47 |
1 files changed, 37 insertions, 10 deletions
diff --git a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go index 8ebd4ab71..69ba67ced 100644 --- a/pkg/sentry/platform/kvm/machine_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/machine_amd64_unsafe.go @@ -87,23 +87,50 @@ func (c *vCPU) setCPUID() error { // setSystemTime sets the TSC for the vCPU. // -// FIXME: This introduces a slight TSC offset between host and -// guest, which may vary per vCPU. +// This has to make the call many times in order to minimize the intrinstic +// error in the offset. Unfortunately KVM does not expose a relative offset via +// the API, so this is an approximation. We do this via an iterative algorithm. +// This has the advantage that it can generally deal with highly variable +// system call times and should converge on the correct offset. func (c *vCPU) setSystemTime() error { - const _MSR_IA32_TSC = 0x00000010 + const ( + _MSR_IA32_TSC = 0x00000010 + calibrateTries = 10 + ) registers := modelControlRegisters{ nmsrs: 1, } registers.entries[0] = modelControlRegister{ index: _MSR_IA32_TSC, - data: uint64(time.Rdtsc()), } - if _, _, errno := syscall.RawSyscall( - syscall.SYS_IOCTL, - uintptr(c.fd), - _KVM_SET_MSRS, - uintptr(unsafe.Pointer(®isters))); errno != 0 { - return fmt.Errorf("error setting system time: %v", errno) + target := uint64(^uint32(0)) + for done := 0; done < calibrateTries; { + start := uint64(time.Rdtsc()) + registers.entries[0].data = start + target + if _, _, errno := syscall.RawSyscall( + syscall.SYS_IOCTL, + uintptr(c.fd), + _KVM_SET_MSRS, + uintptr(unsafe.Pointer(®isters))); errno != 0 { + return fmt.Errorf("error setting system time: %v", errno) + } + // See if this is our new minimum call time. Note that this + // serves two functions: one, we make sure that we are + // accurately predicting the offset we need to set. Second, we + // don't want to do the final set on a slow call, which could + // produce a really bad result. So we only count attempts + // within +/- 6.25% of our minimum as an attempt. + end := uint64(time.Rdtsc()) + if end < start { + continue // Totally bogus. + } + half := (end - start) / 2 + if half < target { + target = half + } + if (half - target) < target/8 { + done++ + } } return nil } |