summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/syscalls/linux/sys_time.go139
1 files changed, 71 insertions, 68 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_time.go b/pkg/sentry/syscalls/linux/sys_time.go
index 4adc8b8a4..134b66dac 100644
--- a/pkg/sentry/syscalls/linux/sys_time.go
+++ b/pkg/sentry/syscalls/linux/sys_time.go
@@ -175,73 +175,6 @@ func Time(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
return uintptr(r), nil, nil
}
-// clockNanosleepRestartBlock encapsulates the state required to restart
-// clock_nanosleep(2) via restart_syscall(2).
-//
-// +stateify savable
-type clockNanosleepRestartBlock struct {
- c ktime.Clock
- end ktime.Time
- rem hostarch.Addr
-}
-
-// Restart implements kernel.SyscallRestartBlock.Restart.
-func (n *clockNanosleepRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
- return 0, clockNanosleepUntil(t, n.c, n.end, n.rem, true)
-}
-
-// clockNanosleepUntil blocks until a specified time.
-//
-// If blocking is interrupted, the syscall is restarted with the original
-// arguments.
-func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, end ktime.Time, rem hostarch.Addr, needRestartBlock bool) error {
- notifier, tchan := ktime.NewChannelNotifier()
- timer := ktime.NewTimer(c, notifier)
-
- // Turn on the timer.
- timer.Swap(ktime.Setting{
- Period: 0,
- Enabled: true,
- Next: end,
- })
-
- err := t.BlockWithTimer(nil, tchan)
-
- timer.Destroy()
-
- switch {
- case linuxerr.Equals(linuxerr.ETIMEDOUT, err):
- // Slept for entire timeout.
- return nil
- case err == linuxerr.ErrInterrupted:
- // Interrupted.
- remaining := end.Sub(c.Now())
- if remaining <= 0 {
- return nil
- }
-
- // Copy out remaining time.
- if rem != 0 {
- timeleft := linux.NsecToTimespec(remaining.Nanoseconds())
- if err := copyTimespecOut(t, rem, &timeleft); err != nil {
- return err
- }
- }
- if needRestartBlock {
- // Arrange for a restart with the remaining duration.
- t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
- c: c,
- end: end,
- rem: rem,
- })
- return linuxerr.ERESTART_RESTARTBLOCK
- }
- return linuxerr.ERESTARTNOHAND
- default:
- panic(fmt.Sprintf("Impossible BlockWithTimer error %v", err))
- }
-}
-
// Nanosleep implements linux syscall Nanosleep(2).
func Nanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
addr := args[0].Pointer()
@@ -279,10 +212,12 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
return 0, nil, linuxerr.EINVAL
}
- // Only allow clock constants also allowed by Linux.
+ // Only allow clock constants also allowed by Linux. (CLOCK_TAI is
+ // unimplemented.)
if clockID > 0 {
if clockID != linux.CLOCK_REALTIME &&
clockID != linux.CLOCK_MONOTONIC &&
+ clockID != linux.CLOCK_BOOTTIME &&
clockID != linux.CLOCK_PROCESS_CPUTIME_ID {
return 0, nil, linuxerr.EINVAL
}
@@ -301,6 +236,74 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
return 0, nil, clockNanosleepUntil(t, c, c.Now().Add(dur), rem, true)
}
+// clockNanosleepUntil blocks until a specified time.
+//
+// If blocking is interrupted, the syscall is restarted with the original
+// arguments.
+func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, end ktime.Time, rem hostarch.Addr, needRestartBlock bool) error {
+ var err error
+ if c == t.Kernel().MonotonicClock() {
+ err = t.BlockWithDeadline(nil, true, end)
+ } else {
+ notifier, tchan := ktime.NewChannelNotifier()
+ timer := ktime.NewTimer(c, notifier)
+ timer.Swap(ktime.Setting{
+ Period: 0,
+ Enabled: true,
+ Next: end,
+ })
+ err = t.BlockWithTimer(nil, tchan)
+ timer.Destroy()
+ }
+
+ switch {
+ case linuxerr.Equals(linuxerr.ETIMEDOUT, err):
+ // Slept for entire timeout.
+ return nil
+ case err == linuxerr.ErrInterrupted:
+ // Interrupted.
+ remaining := end.Sub(c.Now())
+ if remaining <= 0 {
+ return nil
+ }
+
+ // Copy out remaining time.
+ if rem != 0 {
+ timeleft := linux.NsecToTimespec(remaining.Nanoseconds())
+ if err := copyTimespecOut(t, rem, &timeleft); err != nil {
+ return err
+ }
+ }
+ if needRestartBlock {
+ // Arrange for a restart with the remaining duration.
+ t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
+ c: c,
+ end: end,
+ rem: rem,
+ })
+ return linuxerr.ERESTART_RESTARTBLOCK
+ }
+ return linuxerr.ERESTARTNOHAND
+ default:
+ panic(fmt.Sprintf("Impossible BlockWithTimer error %v", err))
+ }
+}
+
+// clockNanosleepRestartBlock encapsulates the state required to restart
+// clock_nanosleep(2) via restart_syscall(2).
+//
+// +stateify savable
+type clockNanosleepRestartBlock struct {
+ c ktime.Clock
+ end ktime.Time
+ rem hostarch.Addr
+}
+
+// Restart implements kernel.SyscallRestartBlock.Restart.
+func (n *clockNanosleepRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
+ return 0, clockNanosleepUntil(t, n.c, n.end, n.rem, true)
+}
+
// Gettimeofday implements linux syscall gettimeofday(2).
func Gettimeofday(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
tv := args[0].Pointer()