// Copyright 2018 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package time provides a calibrated clock synchronized to a system reference // clock. package time import ( "time" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/metric" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/syserror" ) // fallbackMetric tracks failed updates. It is not sync, as it is not critical // that all occurrences are captured and CalibratedClock may fallback many // times. var fallbackMetric = metric.MustCreateNewUint64Metric("/time/fallback", false /* sync */, "Incremented when a clock falls back to system calls due to a failed update") // CalibratedClock implements a clock that tracks a reference clock. // // Users should call Update at regular intervals of around approxUpdateInterval // to ensure that the clock does not drift significantly from the reference // clock. type CalibratedClock struct { // mu protects the fields below. // TODO(mpratt): consider a sequence counter for read locking. mu sync.RWMutex // ref sample the reference clock that this clock is calibrated // against. ref *sampler // ready indicates that the fields below are ready for use calculating // time. ready bool // params are the current timekeeping parameters. params Parameters // errorNS is the estimated clock error in nanoseconds. errorNS ReferenceNS } // NewCalibratedClock creates a CalibratedClock that tracks the given ClockID. func NewCalibratedClock(c ClockID) *CalibratedClock { return &CalibratedClock{ ref: newSampler(c), } } // Debugf logs at debug level. func (c *CalibratedClock) Debugf(format string, v ...interface{}) { if log.IsLogging(log.Debug) { args := []interface{}{c.ref.clockID} args = append(args, v...) log.Debugf("CalibratedClock(%v): "+format, args...) } } // Infof logs at debug level. func (c *CalibratedClock) Infof(format string, v ...interface{}) { if log.IsLogging(log.Info) { args := []interface{}{c.ref.clockID} args = append(args, v...) log.Infof("CalibratedClock(%v): "+format, args...) } } // Warningf logs at debug level. func (c *CalibratedClock) Warningf(format string, v ...interface{}) { if log.IsLogging(log.Warning) { args := []interface{}{c.ref.clockID} args = append(args, v...) log.Warningf("CalibratedClock(%v): "+format, args...) } } // reset forces the clock to restart the calibration process, logging the // passed message. func (c *CalibratedClock) reset(str string, v ...interface{}) { c.mu.Lock() defer c.mu.Unlock() c.resetLocked(str, v...) } // resetLocked is equivalent to reset with c.mu already held for writing. func (c *CalibratedClock) resetLocked(str string, v ...interface{}) { c.Warningf(str+" Resetting clock; time may jump.", v...) c.ready = false c.ref.Reset() fallbackMetric.Increment() } // updateParams updates the timekeeping parameters based on the passed // parameters. // // actual is the actual estimated timekeeping parameters. The stored parameters // may need to be adjusted slightly from these values to compensate for error. // // Preconditions: c.mu must be held for writing. func (c *CalibratedClock) updateParams(actual Parameters) { if !c.ready { // At initial calibration there is nothing to correct. c.params = actual c.ready = true c.Infof("ready") return } // Otherwise, adjust the params to correct for errors. newParams, errorNS, err := errorAdjust(c.params, actual, actual.BaseCycles) if err != nil { // Something is very wrong. Reset and try again from the // beginning. c.resetLocked("Unable to update params: %v.", err) return } logErrorAdjustment(c.ref.clockID, errorNS, c.params, newParams) if errorNS.Magnitude() >= MaxClockError { // We should never get such extreme error, something is very // wrong. Reset everything and start again. // // N.B. logErrorAdjustment will have already logged the error // at warning level. // // TODO(mpratt): We could allow Realtime clock jumps here. c.resetLocked("Extreme clock error.") return } c.params = newParams c.errorNS = errorNS } // Update runs the update step of the clock, updating its synchronization with // the reference clock. // // Update returns timekeeping and true with the new timekeeping parameters if // the clock is calibrated. Update should be called regularly to prevent the // clock from getting significantly out of sync from the reference clock. // // The returned timekeeping parameters are invalidated on the next call to // Update. func (c *CalibratedClock) Update() (Parameters, bool) { c.mu.Lock() defer c.mu.Unlock() if err := c.ref.Sample(); err != nil { c.resetLocked("Unable to update calibrated clock: %v.", err) return Parameters{}, false } oldest, newest, ok := c.ref.Range() if !ok { // Not ready yet. return Parameters{}, false } minCount := uint64(newest.before - oldest.after) maxCount := uint64(newest.after - oldest.before) refInterval := uint64(newest.ref - oldest.ref) // freq hz = count / (interval ns) * (nsPerS ns) / (1 s) nsPerS := uint64(time.Second.Nanoseconds()) minHz, ok := muldiv64(minCount, nsPerS, refInterval) if !ok { c.resetLocked("Unable to update calibrated clock: (%v - %v) * %v / %v overflows.", newest.before, oldest.after, nsPerS, refInterval) return Parameters{}, false } maxHz, ok := muldiv64(maxCount, nsPerS, refInterval) if !ok { c.resetLocked("Unable to update calibrated clock: (%v - %v) * %v / %v overflows.", newest.after, oldest.before, nsPerS, refInterval) return Parameters{}, false } c.updateParams(Parameters{ Frequency: (minHz + maxHz) / 2, BaseRef: newest.ref, BaseCycles: newest.after, }) return c.params, true } // GetTime returns the current time based on the clock calibration. func (c *CalibratedClock) GetTime() (int64, error) { c.mu.RLock() if !c.ready { // Fallback to a syscall. now, err := c.ref.Syscall() c.mu.RUnlock() return int64(now), err } now := c.ref.Cycles() v, ok := c.params.ComputeTime(now) if !ok { // Something is seriously wrong with the clock. Try // again with syscalls. c.resetLocked("Time computation overflowed. params = %+v, now = %v.", c.params, now) now, err := c.ref.Syscall() c.mu.RUnlock() return int64(now), err } c.mu.RUnlock() return v, nil } // CalibratedClocks contains calibrated monotonic and realtime clocks. // // TODO(mpratt): We know that Linux runs the monotonic and realtime clocks at // the same rate, so rather than tracking both individually, we could do one // calibration for both clocks. type CalibratedClocks struct { // monotonic is the clock tracking the system monotonic clock. monotonic *CalibratedClock // realtime is the realtime equivalent of monotonic. realtime *CalibratedClock } // NewCalibratedClocks creates a CalibratedClocks. func NewCalibratedClocks() *CalibratedClocks { return &CalibratedClocks{ monotonic: NewCalibratedClock(Monotonic), realtime: NewCalibratedClock(Realtime), } } // Update implements Clocks.Update. func (c *CalibratedClocks) Update() (Parameters, bool, Parameters, bool) { monotonicParams, monotonicOk := c.monotonic.Update() realtimeParams, realtimeOk := c.realtime.Update() return monotonicParams, monotonicOk, realtimeParams, realtimeOk } // GetTime implements Clocks.GetTime. func (c *CalibratedClocks) GetTime(id ClockID) (int64, error) { switch id { case Monotonic: return c.monotonic.GetTime() case Realtime: return c.realtime.GetTime() default: return 0, syserror.EINVAL } }