github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/time/calibrated_clock.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package time provides a calibrated clock synchronized to a system reference 16 // clock. 17 package time 18 19 import ( 20 "time" 21 22 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 23 "github.com/nicocha30/gvisor-ligolo/pkg/log" 24 "github.com/nicocha30/gvisor-ligolo/pkg/metric" 25 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 26 ) 27 28 // CalibratedClock implements a clock that tracks a reference clock. 29 // 30 // Users should call Update at regular intervals of around approxUpdateInterval 31 // to ensure that the clock does not drift significantly from the reference 32 // clock. 33 type CalibratedClock struct { 34 // mu protects the fields below. 35 // TODO(mpratt): consider a sequence counter for read locking. 36 mu sync.RWMutex 37 38 // ref sample the reference clock that this clock is calibrated 39 // against. 40 ref *sampler 41 42 // ready indicates that the fields below are ready for use calculating 43 // time. 44 ready bool 45 46 // params are the current timekeeping parameters. 47 params Parameters 48 49 // errorNS is the estimated clock error in nanoseconds. 50 errorNS ReferenceNS 51 } 52 53 // NewCalibratedClock creates a CalibratedClock that tracks the given ClockID. 54 func NewCalibratedClock(c ClockID) *CalibratedClock { 55 return &CalibratedClock{ 56 ref: newSampler(c), 57 } 58 } 59 60 // Debugf logs at debug level. 61 func (c *CalibratedClock) Debugf(format string, v ...any) { 62 if log.IsLogging(log.Debug) { 63 args := []any{c.ref.clockID} 64 args = append(args, v...) 65 log.Debugf("CalibratedClock(%v): "+format, args...) 66 } 67 } 68 69 // Infof logs at debug level. 70 func (c *CalibratedClock) Infof(format string, v ...any) { 71 if log.IsLogging(log.Info) { 72 args := []any{c.ref.clockID} 73 args = append(args, v...) 74 log.Infof("CalibratedClock(%v): "+format, args...) 75 } 76 } 77 78 // Warningf logs at debug level. 79 func (c *CalibratedClock) Warningf(format string, v ...any) { 80 if log.IsLogging(log.Warning) { 81 args := []any{c.ref.clockID} 82 args = append(args, v...) 83 log.Warningf("CalibratedClock(%v): "+format, args...) 84 } 85 } 86 87 // reset forces the clock to restart the calibration process, logging the 88 // passed message. 89 func (c *CalibratedClock) reset(str string, v ...any) { 90 c.mu.Lock() 91 defer c.mu.Unlock() 92 c.resetLocked(str, v...) 93 } 94 95 // resetLocked is equivalent to reset with c.mu already held for writing. 96 func (c *CalibratedClock) resetLocked(str string, v ...any) { 97 c.Warningf(str+" Resetting clock; time may jump.", v...) 98 c.ready = false 99 c.ref.Reset() 100 metric.WeirdnessMetric.Increment(&metric.WeirdnessTypeTimeFallback) 101 } 102 103 // updateParams updates the timekeeping parameters based on the passed 104 // parameters. 105 // 106 // actual is the actual estimated timekeeping parameters. The stored parameters 107 // may need to be adjusted slightly from these values to compensate for error. 108 // 109 // Preconditions: c.mu must be held for writing. 110 func (c *CalibratedClock) updateParams(actual Parameters) { 111 if !c.ready { 112 // At initial calibration there is nothing to correct. 113 c.params = actual 114 c.ready = true 115 116 c.Infof("ready") 117 118 return 119 } 120 121 // Otherwise, adjust the params to correct for errors. 122 newParams, errorNS, err := errorAdjust(c.params, actual, actual.BaseCycles) 123 if err != nil { 124 // Something is very wrong. Reset and try again from the 125 // beginning. 126 c.resetLocked("Unable to update params: %v.", err) 127 return 128 } 129 logErrorAdjustment(c.ref.clockID, errorNS, c.params, newParams) 130 131 if errorNS.Magnitude() >= MaxClockError { 132 // We should never get such extreme error, something is very 133 // wrong. Reset everything and start again. 134 // 135 // N.B. logErrorAdjustment will have already logged the error 136 // at warning level. 137 // 138 // TODO(mpratt): We could allow Realtime clock jumps here. 139 c.resetLocked("Extreme clock error.") 140 return 141 } 142 143 c.params = newParams 144 c.errorNS = errorNS 145 } 146 147 // Update runs the update step of the clock, updating its synchronization with 148 // the reference clock. 149 // 150 // Update returns timekeeping and true with the new timekeeping parameters if 151 // the clock is calibrated. Update should be called regularly to prevent the 152 // clock from getting significantly out of sync from the reference clock. 153 // 154 // The returned timekeeping parameters are invalidated on the next call to 155 // Update. 156 func (c *CalibratedClock) Update() (Parameters, bool) { 157 c.mu.Lock() 158 defer c.mu.Unlock() 159 160 if err := c.ref.Sample(); err != nil { 161 c.resetLocked("Unable to update calibrated clock: %v.", err) 162 return Parameters{}, false 163 } 164 165 oldest, newest, ok := c.ref.Range() 166 if !ok { 167 // Not ready yet. 168 return Parameters{}, false 169 } 170 171 minCount := uint64(newest.before - oldest.after) 172 maxCount := uint64(newest.after - oldest.before) 173 refInterval := uint64(newest.ref - oldest.ref) 174 175 // freq hz = count / (interval ns) * (nsPerS ns) / (1 s) 176 nsPerS := uint64(time.Second.Nanoseconds()) 177 178 minHz, ok := muldiv64(minCount, nsPerS, refInterval) 179 if !ok { 180 c.resetLocked("Unable to update calibrated clock: (%v - %v) * %v / %v overflows.", newest.before, oldest.after, nsPerS, refInterval) 181 return Parameters{}, false 182 } 183 184 maxHz, ok := muldiv64(maxCount, nsPerS, refInterval) 185 if !ok { 186 c.resetLocked("Unable to update calibrated clock: (%v - %v) * %v / %v overflows.", newest.after, oldest.before, nsPerS, refInterval) 187 return Parameters{}, false 188 } 189 190 c.updateParams(Parameters{ 191 Frequency: (minHz + maxHz) / 2, 192 BaseRef: newest.ref, 193 BaseCycles: newest.after, 194 }) 195 196 return c.params, true 197 } 198 199 // GetTime returns the current time based on the clock calibration. 200 func (c *CalibratedClock) GetTime() (int64, error) { 201 c.mu.RLock() 202 203 if !c.ready { 204 // Fallback to a syscall. 205 now, err := c.ref.Syscall() 206 c.mu.RUnlock() 207 return int64(now), err 208 } 209 210 now := c.ref.Cycles() 211 v, ok := c.params.ComputeTime(now) 212 if !ok { 213 // Something is seriously wrong with the clock. Try 214 // again with syscalls. 215 c.resetLocked("Time computation overflowed. params = %+v, now = %v.", c.params, now) 216 now, err := c.ref.Syscall() 217 c.mu.RUnlock() 218 return int64(now), err 219 } 220 221 c.mu.RUnlock() 222 return v, nil 223 } 224 225 // CalibratedClocks contains calibrated monotonic and realtime clocks. 226 // 227 // TODO(mpratt): We know that Linux runs the monotonic and realtime clocks at 228 // the same rate, so rather than tracking both individually, we could do one 229 // calibration for both clocks. 230 type CalibratedClocks struct { 231 // monotonic is the clock tracking the system monotonic clock. 232 monotonic *CalibratedClock 233 234 // realtime is the realtime equivalent of monotonic. 235 realtime *CalibratedClock 236 } 237 238 // NewCalibratedClocks creates a CalibratedClocks. 239 func NewCalibratedClocks() *CalibratedClocks { 240 return &CalibratedClocks{ 241 monotonic: NewCalibratedClock(Monotonic), 242 realtime: NewCalibratedClock(Realtime), 243 } 244 } 245 246 // Update implements Clocks.Update. 247 func (c *CalibratedClocks) Update() (Parameters, bool, Parameters, bool) { 248 monotonicParams, monotonicOk := c.monotonic.Update() 249 realtimeParams, realtimeOk := c.realtime.Update() 250 251 return monotonicParams, monotonicOk, realtimeParams, realtimeOk 252 } 253 254 // GetTime implements Clocks.GetTime. 255 func (c *CalibratedClocks) GetTime(id ClockID) (int64, error) { 256 switch id { 257 case Monotonic: 258 return c.monotonic.GetTime() 259 case Realtime: 260 return c.realtime.GetTime() 261 default: 262 return 0, linuxerr.EINVAL 263 } 264 }