summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJamie Liu <jamieliu@google.com>2021-10-28 16:52:28 -0700
committergVisor bot <gvisor-bot@google.com>2021-10-28 16:55:58 -0700
commitca55c18a31789b8f2541d5d3b90e2af012d3e6ef (patch)
treee9feee82ceaf8ae00708bf4c9cf6826075ae226f
parentd350c95b04d594abedaad1846f35304b55194e84 (diff)
Use Task blocking timer for nanosleep(2).
kernel/time.Timer allocation is expensive and not sync.Poolable (since time.Timer only supports notification through a channel, requiring a goroutine to receive from the channel, and sync.Pool doesn't invoke any kind of cleanup on discarded items in the pool so it would leak timer goroutines). Using the existing Task.blockingTimer for nanosleep(), and applicable cases in clock_nanosleep(), at least avoids Timer allocation in common cases. PiperOrigin-RevId: 406248394
-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()