Skip to content

Commit 25172e5

Browse files
committed
Merge pull request #16733 from aaltinaydev:freeze-monotonic
PiperOrigin-RevId: 1011059601
2 parents 6e62763 + 4f5ce20 commit 25172e5

28 files changed

Lines changed: 929 additions & 157 deletions

‎g3doc/user_guide/checkpoint_restore.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,26 @@ With `--network=host`, host sockets cannot be saved, so:
178178
- Network configuration visible inside the sandbox (interface statistics, TCP
179179
buffer sizes) reflects the host the sandbox was restored on.
180180

181+
## Clocks
182+
183+
The clocks behave across checkpoint and restore as they do across a Linux system
184+
suspend and resume:
185+
186+
- `CLOCK_MONOTONIC` does not count the time that the sandbox spends
187+
checkpointed, or restoring. When the restored application resumes, it
188+
continues from its value at checkpoint time. Timers and timeouts based on
189+
it, such as `sleep(3)`, `poll(2)` and `epoll_wait(2)` timeouts, and
190+
`CLOCK_MONOTONIC` timers, keep at least the time that they had left at
191+
checkpoint time. They do not all expire at once on restore.
192+
- `CLOCK_BOOTTIME` counts the time that the sandbox spends checkpointed. After
193+
restore, it jumps forward by that time. `CLOCK_BOOTTIME` timers that became
194+
due during that time expire on restore.
195+
- `CLOCK_REALTIME` is the time of day on the host that the sandbox was
196+
restored on, so it also jumps forward by the time that the sandbox spent
197+
checkpointed. Timers set for an absolute `CLOCK_REALTIME` time that passed
198+
during that time expire on restore. As on Linux, relative `CLOCK_REALTIME`
199+
sleeps and timers measure their duration with `CLOCK_MONOTONIC`.
200+
181201
## Checkpoint & Restore with different CPU features
182202

183203
When restoring a state file, gVisor verifies that the target host machine

‎pkg/sentry/fsimpl/timerfd/timerfd.go‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ type TimerFileDescription struct {
3838
vfs.NoLockFD
3939

4040
events waiter.Queue
41-
timer ktime.Timer
41+
timer *ktime.SampledTimer
42+
43+
// clock is the clock that the timer fd was created with. timer runs on
44+
// clock, except when it is armed with a relative CLOCK_REALTIME time (see
45+
// SetTime). clock is immutable.
46+
clock ktime.SampledClock
4247

4348
// val is the number of timer expirations since the last successful
4449
// call to PRead, or SetTime. val must be accessed using atomic memory
@@ -50,11 +55,11 @@ var _ vfs.FileDescriptionImpl = (*TimerFileDescription)(nil)
5055
var _ ktime.Listener = (*TimerFileDescription)(nil)
5156

5257
// New returns a new timer fd.
53-
func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, clock ktime.Clock, flags uint32) (*vfs.FileDescription, error) {
58+
func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, clock ktime.SampledClock, flags uint32) (*vfs.FileDescription, error) {
5459
vd := vfsObj.NewAnonVirtualDentry("[timerfd]")
5560
defer vd.DecRef(ctx)
56-
tfd := &TimerFileDescription{}
57-
tfd.timer = clock.NewTimer(tfd)
61+
tfd := &TimerFileDescription{clock: clock}
62+
tfd.timer = ktime.NewSampledTimer(clock, tfd)
5863
if err := tfd.vfsfd.Init(tfd, flags, auth.CredentialsFromContext(ctx), vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{
5964
UseDentryMetadata: true,
6065
DenyPRead: true,
@@ -84,9 +89,9 @@ func (tfd *TimerFileDescription) Read(ctx context.Context, dst usermem.IOSequenc
8489
return 0, linuxerr.ErrWouldBlock
8590
}
8691

87-
// Clock returns the timer fd's Clock.
88-
func (tfd *TimerFileDescription) Clock() ktime.Clock {
89-
return tfd.timer.Clock()
92+
// Clock returns the Clock that the timer fd was created with.
93+
func (tfd *TimerFileDescription) Clock() ktime.SampledClock {
94+
return tfd.clock
9095
}
9196

9297
// GetTime returns the associated Timer's setting and the time at which it was
@@ -95,11 +100,11 @@ func (tfd *TimerFileDescription) GetTime() (ktime.Time, ktime.Setting) {
95100
return tfd.timer.Get()
96101
}
97102

98-
// SetTime atomically changes the associated Timer's setting, resets the number
99-
// of expirations to 0, and returns the previous setting and the time at which
100-
// it was observed.
101-
func (tfd *TimerFileDescription) SetTime(s ktime.Setting) (ktime.Time, ktime.Setting) {
102-
return tfd.timer.Set(s, func() { tfd.val.Store(0) })
103+
// SetTime atomically changes the associated Timer's setting, and the clock it
104+
// runs on to c, resets the number of expirations to 0, and returns the
105+
// previous setting and the time at which it was observed.
106+
func (tfd *TimerFileDescription) SetTime(c ktime.SampledClock, s ktime.Setting) (ktime.Time, ktime.Setting) {
107+
return tfd.timer.SetWithClock(c, s, func() { tfd.val.Store(0) })
103108
}
104109

105110
// Readiness implements waiter.Waitable.Readiness.

‎pkg/sentry/kernel/BUILD‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ go_test(
454454
"//pkg/context",
455455
"//pkg/errors/linuxerr",
456456
"//pkg/hostarch",
457+
"//pkg/safemem",
457458
"//pkg/sentry/arch",
458459
"//pkg/sentry/checkpoint",
459460
"//pkg/sentry/contexttest",

‎pkg/sentry/kernel/kernel.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,11 @@ func (k *Kernel) MonotonicClock() ktime.SampledClock {
22092209
return k.timekeeper.monotonicClock
22102210
}
22112211

2212+
// BoottimeClock returns the application CLOCK_BOOTTIME clock.
2213+
func (k *Kernel) BoottimeClock() ktime.SampledClock {
2214+
return k.timekeeper.boottimeClock
2215+
}
2216+
22122217
// MonotonicRawClock returns the system CLOCK_MONOTONIC_RAW clock. When it is
22132218
// not enabled as a distinct clock this is the same as MonotonicClock.
22142219
func (k *Kernel) MonotonicRawClock() ktime.SampledClock {

‎pkg/sentry/kernel/posixtimer.go‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ import (
2929
type IntervalTimer struct {
3030
timer ktime.Timer
3131

32+
// clock is the clock that the timer was created with. timer runs on
33+
// clock, except when it is armed with a relative CLOCK_REALTIME time (see
34+
// Task.IntervalTimerSettime). clock is immutable.
35+
clock ktime.Clock
36+
3237
// If target is not nil, it receives signo from timer expirations. If group
3338
// is true, these signals are thread-group-directed. These fields are
3439
// immutable.
@@ -189,6 +194,7 @@ func (t *Task) IntervalTimerCreate(c ktime.Clock, sigev *linux.Sigevent) (linux.
189194

190195
// Construct the timer.
191196
it := &IntervalTimer{
197+
clock: c,
192198
id: id,
193199
sigval: sigev.Value,
194200
}
@@ -246,11 +252,28 @@ func (t *Task) IntervalTimerSettime(id linux.TimerID, its linux.Itimerspec, abs
246252
return linux.Itimerspec{}, linuxerr.EINVAL
247253
}
248254

249-
newS, err := ktime.SettingFromItimerspec(its, abs, it.timer.Clock())
255+
// As on Linux, relative CLOCK_REALTIME timers run on CLOCK_MONOTONIC, so
256+
// that discontinuous changes to the realtime clock, such as its jump
257+
// forward on restore, do not change their duration.
258+
c := it.clock
259+
if !abs && c == t.k.RealtimeClock() {
260+
c = t.k.MonotonicClock()
261+
}
262+
newS, err := ktime.SettingFromItimerspec(its, abs, c)
250263
if err != nil {
251264
return linux.Itimerspec{}, err
252265
}
253-
tm, oldS := it.timer.Set(newS, it.timerSettingChanged)
266+
var (
267+
tm ktime.Time
268+
oldS ktime.Setting
269+
)
270+
if c == it.timer.Clock() {
271+
tm, oldS = it.timer.Set(newS, it.timerSettingChanged)
272+
} else {
273+
// Only CLOCK_REALTIME timers change clocks, and timers on the
274+
// Timekeeper's clocks are SampledTimers.
275+
tm, oldS = it.timer.(*ktime.SampledTimer).SetWithClock(c.(ktime.SampledClock), newS, it.timerSettingChanged)
276+
}
254277
its = ktime.ItimerspecFromSetting(tm, oldS)
255278
return its, nil
256279
}

0 commit comments

Comments
 (0)