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
This commit is contained in:
Adin Scannell
2020-05-04 10:40:51 -07:00
committed by gVisor bot
parent 56c64e4bb9
commit 2c986870e3
+19
View File
@@ -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
}