diff options
author | Adin Scannell <ascannell@google.com> | 2020-05-04 10:39:36 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2020-05-04 10:40:51 -0700 |
commit | 2c986870e35f967c88ebc1b7df7b576aad2c46d4 (patch) | |
tree | 1a7814479ac99bc6ee53d10357697162ea68dbad /pkg | |
parent | 56c64e4bb9dc75659799f3e5df9daddf10d810e1 (diff) |
Fix flaky monotonic time.
This change ensures that even platforms with some TSC issues (e.g. KVM),
can get reliable monotonic time by applied a lower bound on each read.
PiperOrigin-RevId: 309773801
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/sentry/kernel/timekeeper.go | 19 |
1 files changed, 19 insertions, 0 deletions
diff --git a/pkg/sentry/kernel/timekeeper.go b/pkg/sentry/kernel/timekeeper.go index dc99301de..da0ea7bb5 100644 --- a/pkg/sentry/kernel/timekeeper.go +++ b/pkg/sentry/kernel/timekeeper.go @@ -16,6 +16,7 @@ package kernel import ( "fmt" + "sync/atomic" "time" "gvisor.dev/gvisor/pkg/log" @@ -48,6 +49,9 @@ type Timekeeper struct { // It is set only once, by SetClocks. monotonicOffset int64 `state:"nosave"` + // monotonicLowerBound is the lowerBound for monotonic time. + monotonicLowerBound int64 `state:"nosave"` + // restored, if non-nil, indicates that this Timekeeper was restored // from a state file. The clocks are not set until restored is closed. restored chan struct{} `state:"nosave"` @@ -271,6 +275,21 @@ func (t *Timekeeper) GetTime(c sentrytime.ClockID) (int64, error) { now, err := t.clocks.GetTime(c) if err == nil && c == sentrytime.Monotonic { now += t.monotonicOffset + for { + // It's possible that the clock is shaky. This may be due to + // platform issues, e.g. the KVM platform relies on the guest + // TSC and host TSC, which may not be perfectly in sync. To + // work around this issue, ensure that the monotonic time is + // always bounded by the last time read. + oldLowerBound := atomic.LoadInt64(&t.monotonicLowerBound) + if now < oldLowerBound { + now = oldLowerBound + break + } + if atomic.CompareAndSwapInt64(&t.monotonicLowerBound, oldLowerBound, now) { + break + } + } } return now, err } |