From b2a88ff4713325fca736f6a3bf200be02d2d72a7 Mon Sep 17 00:00:00 2001 From: Jamie Liu Date: Wed, 17 Oct 2018 15:48:55 -0700 Subject: Check thread group CPU timers in the CPU clock ticker. This reduces the number of goroutines and runtime timers when ITIMER_VIRTUAL or ITIMER_PROF are enabled, or when RLIMIT_CPU is set. This also ensures that thread group CPU timers only advance if running tasks are observed at the time the CPU clock advances, mostly eliminating the possibility that a CPU timer expiration observes no running tasks and falls back to the group leader. PiperOrigin-RevId: 217603396 Change-Id: Ia24ce934d5574334857d9afb5ad8ca0b6a6e65f4 --- pkg/sentry/syscalls/linux/sys_rlimit.go | 2 +- pkg/sentry/syscalls/linux/sys_timer.go | 61 +++++++-------------------------- 2 files changed, 13 insertions(+), 50 deletions(-) (limited to 'pkg/sentry/syscalls') diff --git a/pkg/sentry/syscalls/linux/sys_rlimit.go b/pkg/sentry/syscalls/linux/sys_rlimit.go index 481e79eaa..d806b58ab 100644 --- a/pkg/sentry/syscalls/linux/sys_rlimit.go +++ b/pkg/sentry/syscalls/linux/sys_rlimit.go @@ -111,7 +111,7 @@ func prlimit64(t *kernel.Task, resource limits.LimitType, newLim *limits.Limit) } if resource == limits.CPU { - t.ThreadGroup().SetCPUTimer(newLim) + t.NotifyRlimitCPUUpdated() } return oldLim, nil } diff --git a/pkg/sentry/syscalls/linux/sys_timer.go b/pkg/sentry/syscalls/linux/sys_timer.go index a12d12d9d..c41074d54 100644 --- a/pkg/sentry/syscalls/linux/sys_timer.go +++ b/pkg/sentry/syscalls/linux/sys_timer.go @@ -21,7 +21,6 @@ import ( "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" - ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" ) @@ -70,34 +69,15 @@ func copyItimerValOut(t *kernel.Task, addr usermem.Addr, itv *linux.ItimerVal) e } } -func findTimer(t *kernel.Task, which int32) (*ktime.Timer, error) { - switch which { - case linux.ITIMER_REAL: - return t.ThreadGroup().Timer().RealTimer, nil - case linux.ITIMER_VIRTUAL: - return t.ThreadGroup().Timer().VirtualTimer, nil - case linux.ITIMER_PROF: - return t.ThreadGroup().Timer().ProfTimer, nil - default: - return nil, syscall.EINVAL - } -} - // Getitimer implements linux syscall getitimer(2). func Getitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { timerID := args[0].Int() val := args[1].Pointer() - timer, err := findTimer(t, timerID) + olditv, err := t.Getitimer(timerID) if err != nil { return 0, nil, err } - value, interval := ktime.SpecFromSetting(timer.Get()) - olditv := linux.ItimerVal{ - Value: linux.DurationToTimeval(value), - Interval: linux.DurationToTimeval(interval), - } - return 0, nil, copyItimerValOut(t, val, &olditv) } @@ -107,29 +87,14 @@ func Setitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys newVal := args[1].Pointer() oldVal := args[2].Pointer() - timer, err := findTimer(t, timerID) + newitv, err := copyItimerValIn(t, newVal) if err != nil { return 0, nil, err } - - itv, err := copyItimerValIn(t, newVal) + olditv, err := t.Setitimer(timerID, newitv) if err != nil { return 0, nil, err } - // Just like linux, we cap the timer value and interval with the max - // number that int64 can represent which is roughly 292 years. - s, err := ktime.SettingFromSpec(itv.Value.ToDuration(), - itv.Interval.ToDuration(), timer.Clock()) - if err != nil { - return 0, nil, err - } - - valueNS, intervalNS := ktime.SpecFromSetting(timer.Swap(s)) - olditv := linux.ItimerVal{ - Value: linux.DurationToTimeval(valueNS), - Interval: linux.DurationToTimeval(intervalNS), - } - return 0, nil, copyItimerValOut(t, oldVal, &olditv) } @@ -137,21 +102,19 @@ func Setitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys func Alarm(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { duration := time.Duration(args[0].Uint()) * time.Second - timer := t.ThreadGroup().Timer().RealTimer - s, err := ktime.SettingFromSpec(duration, 0, timer.Clock()) + olditv, err := t.Setitimer(linux.ITIMER_REAL, linux.ItimerVal{ + Value: linux.DurationToTimeval(duration), + }) if err != nil { return 0, nil, err } - - value, _ := ktime.SpecFromSetting(timer.Swap(s)) - sec := int64(value) / nsecPerSec - nsec := int64(value) % nsecPerSec - // We can't return 0 if we have an alarm pending ... - if (sec == 0 && nsec > 0) || nsec >= nsecPerSec/2 { - sec++ + olddur := olditv.Value.ToDuration() + secs := olddur.Round(time.Second).Nanoseconds() / nsecPerSec + if secs == 0 && olddur != 0 { + // We can't return 0 if an alarm was previously scheduled. + secs = 1 } - - return uintptr(sec), nil, nil + return uintptr(secs), nil, nil } // TimerCreate implements linux syscall timer_create(2). -- cgit v1.2.3