mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
systrap: preempt long running contexts
A context is preempted if it is running longer than 10ms and there are other contexts in the queue. PiperOrigin-RevId: 533376587
This commit is contained in:
@@ -19,8 +19,10 @@ import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg"
|
||||
"gvisor.dev/gvisor/pkg/syncevent"
|
||||
@@ -186,9 +188,42 @@ func (sc *sharedContext) resetAcked() {
|
||||
atomic.StoreUint32(&sc.shared.Acked, ackReset)
|
||||
}
|
||||
|
||||
const (
|
||||
contextPreemptTimeoutNsec = 10 * 1000 * 1000 // 10ms
|
||||
contextCheckupTimeoutSec = 5
|
||||
stuckContextTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
func (sc *sharedContext) sleepOnState(state sysmsg.ContextState) {
|
||||
if errno := sc.shared.SleepOnState(state, sc); errno != 0 {
|
||||
panic(fmt.Sprintf("error waiting for state: %v", errno))
|
||||
timeout := unix.Timespec{
|
||||
Sec: 0,
|
||||
Nsec: contextPreemptTimeoutNsec,
|
||||
}
|
||||
sentInterruptOnce := false
|
||||
deadline := time.Now().Add(stuckContextTimeout)
|
||||
for sc.state() == state {
|
||||
errno := sc.shared.SleepOnState(state, &timeout)
|
||||
if errno == 0 {
|
||||
continue
|
||||
}
|
||||
if errno != unix.ETIMEDOUT {
|
||||
panic(fmt.Sprintf("error waiting for state: %v", errno))
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
log.Warningf("Systrap task goroutine has been waiting on ThreadContext.State futex too long. ThreadContext: %s", sc.shared)
|
||||
}
|
||||
if sentInterruptOnce {
|
||||
log.Warningf("The context is still running: %s", sc)
|
||||
continue
|
||||
}
|
||||
|
||||
if !sc.isAcked() || sc.subprocess.contextQueue.isEmpty() {
|
||||
continue
|
||||
}
|
||||
sc.NotifyInterrupt()
|
||||
sentInterruptOnce = true
|
||||
timeout.Sec = contextCheckupTimeoutSec
|
||||
timeout.Nsec = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,8 +131,6 @@ go_library(
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/cpuid",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/log",
|
||||
"//pkg/sentry/platform/interrupt",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -20,60 +20,12 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/interrupt"
|
||||
)
|
||||
|
||||
const maxFutexSleepSeconds = 60
|
||||
|
||||
// SleepOnState makes the caller sleep on the Msg.State futex.
|
||||
func (m *Msg) SleepOnState(curState ThreadState, interruptor interrupt.Receiver) syscall.Errno {
|
||||
futexTimeout := unix.Timespec{
|
||||
Sec: maxFutexSleepSeconds,
|
||||
Nsec: 0,
|
||||
}
|
||||
sentInterruptOnce := false
|
||||
errno := syscall.Errno(0)
|
||||
for {
|
||||
_, _, errno = unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&m.State)),
|
||||
linux.FUTEX_WAIT, uintptr(curState), uintptr(unsafe.Pointer(&futexTimeout)), 0, 0)
|
||||
if errno == unix.ETIMEDOUT {
|
||||
interruptor.NotifyInterrupt()
|
||||
if !sentInterruptOnce {
|
||||
log.Warningf("Systrap task goroutine has been waiting on Msg.State futex too long. Msg: %s", m.String())
|
||||
}
|
||||
sentInterruptOnce = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if errno == unix.EAGAIN || errno == unix.EINTR {
|
||||
errno = 0
|
||||
}
|
||||
return errno
|
||||
}
|
||||
|
||||
// SleepOnState makes the caller sleep on the ThreadContext.State futex.
|
||||
func (c *ThreadContext) SleepOnState(curState ContextState, interruptor interrupt.Receiver) syscall.Errno {
|
||||
futexTimeout := unix.Timespec{
|
||||
Sec: maxFutexSleepSeconds,
|
||||
Nsec: 0,
|
||||
}
|
||||
sentInterruptOnce := false
|
||||
errno := syscall.Errno(0)
|
||||
for {
|
||||
_, _, errno = unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&c.State)),
|
||||
linux.FUTEX_WAIT, uintptr(curState), uintptr(unsafe.Pointer(&futexTimeout)), 0, 0)
|
||||
if errno == unix.ETIMEDOUT {
|
||||
interruptor.NotifyInterrupt()
|
||||
if !sentInterruptOnce {
|
||||
log.Warningf("Systrap task goroutine has been waiting on ThreadContext.State futex too long. ThreadContext: %s", c.String())
|
||||
}
|
||||
sentInterruptOnce = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
func (c *ThreadContext) SleepOnState(curState ContextState, timeout *unix.Timespec) syscall.Errno {
|
||||
_, _, errno := unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&c.State)),
|
||||
linux.FUTEX_WAIT, uintptr(curState), uintptr(unsafe.Pointer(timeout)), 0, 0)
|
||||
if errno == unix.EAGAIN || errno == unix.EINTR {
|
||||
errno = 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user