Add option to skip stuck tasks waiting for address space

PiperOrigin-RevId: 297192390
This commit is contained in:
Fabricio Voznika 2020-02-25 13:42:34 -08:00 committed by gVisor bot
parent 430992a67a
commit 72e3f3a3ee
8 changed files with 41 additions and 25 deletions

View File

@ -247,6 +247,10 @@ type Kernel struct {
// VFS keeps the filesystem state used across the kernel.
vfs vfs.VirtualFilesystem
// If set to true, report address space activation waits as if the task is in
// external wait so that the watchdog doesn't report the task stuck.
SleepForAddressSpaceActivation bool
}
// InitKernelArgs holds arguments to Init.

View File

@ -140,7 +140,7 @@ func (k *Kernel) LoadTaskImage(ctx context.Context, args loader.LoadArgs) (*Task
}
// Prepare a new user address space to load into.
m := mm.NewMemoryManager(k, k)
m := mm.NewMemoryManager(k, k, k.SleepForAddressSpaceActivation)
defer m.DecUsers(ctx)
args.MemoryManager = m

View File

@ -220,7 +220,7 @@ func (r *runSyscallAfterExecStop) execute(t *Task) taskRunState {
t.mu.Unlock()
t.unstopVforkParent()
// NOTE(b/30316266): All locks must be dropped prior to calling Activate.
t.MemoryManager().Activate()
t.MemoryManager().Activate(t)
t.ptraceExec(oldTID)
return (*runSyscallExit)(nil)

View File

@ -30,7 +30,7 @@ var MAX_RW_COUNT = int(usermem.Addr(math.MaxInt32).RoundDown())
// Activate ensures that the task has an active address space.
func (t *Task) Activate() {
if mm := t.MemoryManager(); mm != nil {
if err := mm.Activate(); err != nil {
if err := mm.Activate(t); err != nil {
panic("unable to activate mm: " + err.Error())
}
}

View File

@ -18,6 +18,7 @@ import (
"fmt"
"sync/atomic"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/platform"
"gvisor.dev/gvisor/pkg/usermem"
)
@ -38,7 +39,7 @@ func (mm *MemoryManager) AddressSpace() platform.AddressSpace {
//
// When this MemoryManager is no longer needed by a task, it should call
// Deactivate to release the reference.
func (mm *MemoryManager) Activate() error {
func (mm *MemoryManager) Activate(ctx context.Context) error {
// Fast path: the MemoryManager already has an active
// platform.AddressSpace, and we just need to indicate that we need it too.
for {
@ -91,16 +92,20 @@ func (mm *MemoryManager) Activate() error {
if as == nil {
// AddressSpace is unavailable, we must wait.
//
// activeMu must not be held while waiting, as the user
// of the address space we are waiting on may attempt
// to take activeMu.
//
// Don't call UninterruptibleSleepStart to register the
// wait to allow the watchdog stuck task to trigger in
// case a process is starved waiting for the address
// space.
// activeMu must not be held while waiting, as the user of the address
// space we are waiting on may attempt to take activeMu.
mm.activeMu.Unlock()
sleep := mm.p.CooperativelySchedulesAddressSpace() && mm.sleepForActivation
if sleep {
// Mark this task sleeping while waiting for the address space to
// prevent the watchdog from reporting it as a stuck task.
ctx.UninterruptibleSleepStart(false)
}
<-c
if sleep {
ctx.UninterruptibleSleepFinish(false)
}
continue
}

View File

@ -28,16 +28,17 @@ import (
)
// NewMemoryManager returns a new MemoryManager with no mappings and 1 user.
func NewMemoryManager(p platform.Platform, mfp pgalloc.MemoryFileProvider) *MemoryManager {
func NewMemoryManager(p platform.Platform, mfp pgalloc.MemoryFileProvider, sleepForActivation bool) *MemoryManager {
return &MemoryManager{
p: p,
mfp: mfp,
haveASIO: p.SupportsAddressSpaceIO(),
privateRefs: &privateRefs{},
users: 1,
auxv: arch.Auxv{},
dumpability: UserDumpable,
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
p: p,
mfp: mfp,
haveASIO: p.SupportsAddressSpaceIO(),
privateRefs: &privateRefs{},
users: 1,
auxv: arch.Auxv{},
dumpability: UserDumpable,
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
sleepForActivation: sleepForActivation,
}
}
@ -79,9 +80,10 @@ func (mm *MemoryManager) Fork(ctx context.Context) (*MemoryManager, error) {
envv: mm.envv,
auxv: append(arch.Auxv(nil), mm.auxv...),
// IncRef'd below, once we know that there isn't an error.
executable: mm.executable,
dumpability: mm.dumpability,
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
executable: mm.executable,
dumpability: mm.dumpability,
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
sleepForActivation: mm.sleepForActivation,
}
// Copy vmas.

View File

@ -226,6 +226,11 @@ type MemoryManager struct {
// aioManager keeps track of AIOContexts used for async IOs. AIOManager
// must be cloned when CLONE_VM is used.
aioManager aioManager
// sleepForActivation indicates whether the task should report to be sleeping
// before trying to activate the address space. When set to true, delays in
// activation are not reported as stuck tasks by the watchdog.
sleepForActivation bool
}
// vma represents a virtual memory area.

View File

@ -31,7 +31,7 @@ import (
func testMemoryManager(ctx context.Context) *MemoryManager {
p := platform.FromContext(ctx)
mfp := pgalloc.MemoryFileProviderFromContext(ctx)
mm := NewMemoryManager(p, mfp)
mm := NewMemoryManager(p, mfp, false)
mm.layout = arch.MmapLayout{
MinAddr: p.MinUserAddress(),
MaxAddr: p.MaxUserAddress(),