mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Clean up context decoupling experiment.
This change removes code branches and variables only used in coupled-context mode. PiperOrigin-RevId: 529776383
This commit is contained in:
committed by
gVisor bot
parent
05deaf0b0c
commit
d7f590dd00
@@ -44,8 +44,6 @@ go_template_instance(
|
||||
go_library(
|
||||
name = "systrap",
|
||||
srcs = [
|
||||
"context_decoupling_disable.go",
|
||||
"context_decoupling_enable.go",
|
||||
"context_list.go",
|
||||
"context_queue.go",
|
||||
"filters.go",
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build context_decoupling_disabled
|
||||
// +build context_decoupling_disabled
|
||||
|
||||
package systrap
|
||||
|
||||
var contextDecouplingExp bool = false
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !context_decoupling_disabled
|
||||
// +build !context_decoupling_disabled
|
||||
|
||||
package systrap
|
||||
|
||||
// contextDecouplingExp is a global flag that enables thread decoupling mode.
|
||||
// In this mode thread contexts are able to migrate between systrap user
|
||||
// process threads. This also allows and enables the following experimental
|
||||
// optimizations:
|
||||
// - Ability to run M contexts using N threads, where M > N.
|
||||
// - Reduce synchronization overhead between sentry threads and user
|
||||
// threads when switching contexts in and out of the sentry.
|
||||
var contextDecouplingExp bool = true
|
||||
@@ -163,13 +163,6 @@ func (sc *sharedContext) threadID() uint32 {
|
||||
return atomic.LoadUint32(&sc.shared.ThreadID)
|
||||
}
|
||||
|
||||
func (sc *sharedContext) setThreadID(threadID uint32) {
|
||||
if contextDecouplingExp {
|
||||
panic("context decoupled systrap should never explicitly set ThreadID")
|
||||
}
|
||||
atomic.StoreUint32(&sc.shared.ThreadID, threadID)
|
||||
}
|
||||
|
||||
// EnableSentryFastPath indicates that the polling mode is enabled for the
|
||||
// Sentry. It has to be called before putting the context into the context queue.
|
||||
// This function is used if contextDecouplingExp=true because the fastpath
|
||||
@@ -287,6 +280,13 @@ func (q *fastPathDispatcher) deactivateSubprocess(s *subprocess) {
|
||||
q.subprocessList.Remove(s)
|
||||
}
|
||||
|
||||
// deep_sleep_timeout is the timeout after which we stops polling and fall asleep.
|
||||
//
|
||||
// The value is 40µs for 2GHz CPU. This timeout matches the sentry<->stub round
|
||||
// trip in the pure deep sleep case.
|
||||
const deepSleepTimeout = uint64(80000)
|
||||
const handshakeTimeout = uint64(1000)
|
||||
|
||||
// loop is processing contexts in the queue. Only one instance of it can be
|
||||
// running, because it has exclusive access to the list.
|
||||
//
|
||||
|
||||
@@ -141,14 +141,12 @@ func stubInit() {
|
||||
mapLen, _ = hostarch.PageRoundUp(mapLen + sysmsg.PerThreadMemSize*(maxSystemThreads+1))
|
||||
|
||||
// Allocate context queue region
|
||||
if contextDecouplingExp {
|
||||
stubContextQueueRegion = mapLen
|
||||
stubContextQueueRegionLen, _ = hostarch.PageRoundUp(unsafe.Sizeof(contextQueue{}))
|
||||
mapLen += stubContextQueueRegionLen
|
||||
stubContextQueueRegion = mapLen
|
||||
stubContextQueueRegionLen, _ = hostarch.PageRoundUp(unsafe.Sizeof(contextQueue{}))
|
||||
mapLen += stubContextQueueRegionLen
|
||||
|
||||
stubSpinningThreadQueueAddr = mapLen
|
||||
mapLen += sysmsg.SpinningQueueMemSize
|
||||
}
|
||||
stubSpinningThreadQueueAddr = mapLen
|
||||
mapLen += sysmsg.SpinningQueueMemSize
|
||||
|
||||
// Allocate thread context region
|
||||
stubContextRegion = mapLen
|
||||
@@ -220,23 +218,16 @@ func stubInit() {
|
||||
// Initialize stub globals
|
||||
p := (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_deep_sleep_timeout)))
|
||||
*p = deepSleepTimeout
|
||||
p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_handshake_timeout)))
|
||||
*p = handshakeTimeout
|
||||
p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_region)))
|
||||
*p = uint64(stubContextRegion)
|
||||
p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_stub_start)))
|
||||
*p = uint64(stubStart)
|
||||
archState := (*sysmsg.ArchState)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_arch_state)))
|
||||
archState.Init()
|
||||
exp := (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_decoupling_exp)))
|
||||
if contextDecouplingExp {
|
||||
*exp = 1
|
||||
contextQueue := (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_queue_addr)))
|
||||
*contextQueue = uint64(stubContextQueueRegion)
|
||||
|
||||
p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_spinning_queue_addr)))
|
||||
*p = uint64(stubSpinningThreadQueueAddr)
|
||||
}
|
||||
p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_queue_addr)))
|
||||
*p = uint64(stubContextQueueRegion)
|
||||
p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_spinning_queue_addr)))
|
||||
*p = uint64(stubSpinningThreadQueueAddr)
|
||||
|
||||
prepareSeccompRules(stubSysmsgStart, stubSysmsgRules, stubSysmsgRulesLen)
|
||||
|
||||
@@ -253,5 +244,4 @@ func stubInit() {
|
||||
stubEnd = stubStart + mapLen + uintptr(gap)
|
||||
log.Debugf("stubStart %x stubSysmsgStart %x stubSysmsgStack %x, stubContextQueue %x, stubThreadContextRegion %x, mapLen %x", stubStart, stubSysmsgStart, stubSysmsgStack, stubContextQueueRegion, stubContextRegion, mapLen)
|
||||
log.Debugf(archState.String())
|
||||
log.Debugf("contextDecouplingExp=%t", contextDecouplingExp)
|
||||
}
|
||||
|
||||
@@ -315,14 +315,12 @@ func newSubprocess(create func() (*thread, error), memoryFile *pgalloc.MemoryFil
|
||||
sp.mapPrivateRegions()
|
||||
|
||||
// Create the initial sysmsg thread.
|
||||
if contextDecouplingExp {
|
||||
atomic.AddUint32(&sp.contextQueue.numActiveThreads, 1)
|
||||
if _, err := sp.createSysmsgThread(nil, nil, nil); err != nil {
|
||||
atomic.AddUint32(&sp.contextQueue.numActiveThreads, ^uint32(0))
|
||||
return nil, err
|
||||
}
|
||||
sp.numSysmsgThreads++
|
||||
atomic.AddUint32(&sp.contextQueue.numActiveThreads, 1)
|
||||
if err := sp.createSysmsgThread(); err != nil {
|
||||
atomic.AddUint32(&sp.contextQueue.numActiveThreads, ^uint32(0))
|
||||
return nil, err
|
||||
}
|
||||
sp.numSysmsgThreads++
|
||||
|
||||
return sp, nil
|
||||
}
|
||||
@@ -343,27 +341,25 @@ func (s *subprocess) mapSharedRegions() {
|
||||
Dir: pgalloc.TopDown,
|
||||
}
|
||||
|
||||
if contextDecouplingExp {
|
||||
// Map shared regions into the sentry.
|
||||
contextQueueFR, contextQueue := mmapContextQueueForSentry(s.memoryFile, opts)
|
||||
contextQueue.init()
|
||||
// Map shared regions into the sentry.
|
||||
contextQueueFR, contextQueue := mmapContextQueueForSentry(s.memoryFile, opts)
|
||||
contextQueue.init()
|
||||
|
||||
// Map thread context region into the syscall thread.
|
||||
_, err := s.syscallThread.syscall(
|
||||
unix.SYS_MMAP,
|
||||
arch.SyscallArgument{Value: uintptr(stubContextQueueRegion)},
|
||||
arch.SyscallArgument{Value: uintptr(contextQueueFR.Length())},
|
||||
arch.SyscallArgument{Value: uintptr(unix.PROT_READ | unix.PROT_WRITE)},
|
||||
arch.SyscallArgument{Value: uintptr(unix.MAP_SHARED | unix.MAP_FILE | unix.MAP_FIXED)},
|
||||
arch.SyscallArgument{Value: uintptr(s.memoryFile.FD())},
|
||||
arch.SyscallArgument{Value: uintptr(contextQueueFR.Start)})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to mmap context queue region into syscall thread: %v", err))
|
||||
}
|
||||
|
||||
s.contextQueue = contextQueue
|
||||
// Map thread context region into the syscall thread.
|
||||
_, err := s.syscallThread.syscall(
|
||||
unix.SYS_MMAP,
|
||||
arch.SyscallArgument{Value: uintptr(stubContextQueueRegion)},
|
||||
arch.SyscallArgument{Value: uintptr(contextQueueFR.Length())},
|
||||
arch.SyscallArgument{Value: uintptr(unix.PROT_READ | unix.PROT_WRITE)},
|
||||
arch.SyscallArgument{Value: uintptr(unix.MAP_SHARED | unix.MAP_FILE | unix.MAP_FIXED)},
|
||||
arch.SyscallArgument{Value: uintptr(s.memoryFile.FD())},
|
||||
arch.SyscallArgument{Value: uintptr(contextQueueFR.Start)})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to mmap context queue region into syscall thread: %v", err))
|
||||
}
|
||||
|
||||
s.contextQueue = contextQueue
|
||||
|
||||
// Map thread context region into the sentry.
|
||||
threadContextFR, err := s.memoryFile.Allocate(uint64(stubContextRegionLen), opts)
|
||||
if err != nil {
|
||||
@@ -396,18 +392,16 @@ func (s *subprocess) mapSharedRegions() {
|
||||
}
|
||||
|
||||
func (s *subprocess) mapPrivateRegions() {
|
||||
if contextDecouplingExp {
|
||||
_, err := s.syscallThread.syscall(
|
||||
unix.SYS_MMAP,
|
||||
arch.SyscallArgument{Value: uintptr(stubSpinningThreadQueueAddr)},
|
||||
arch.SyscallArgument{Value: uintptr(sysmsg.SpinningQueueMemSize)},
|
||||
arch.SyscallArgument{Value: uintptr(unix.PROT_READ | unix.PROT_WRITE)},
|
||||
arch.SyscallArgument{Value: uintptr(unix.MAP_PRIVATE | unix.MAP_ANONYMOUS | unix.MAP_FIXED)},
|
||||
arch.SyscallArgument{Value: 0},
|
||||
arch.SyscallArgument{Value: 0})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to mmap spinning queue region into syscall thread: %v", err))
|
||||
}
|
||||
_, err := s.syscallThread.syscall(
|
||||
unix.SYS_MMAP,
|
||||
arch.SyscallArgument{Value: uintptr(stubSpinningThreadQueueAddr)},
|
||||
arch.SyscallArgument{Value: uintptr(sysmsg.SpinningQueueMemSize)},
|
||||
arch.SyscallArgument{Value: uintptr(unix.PROT_READ | unix.PROT_WRITE)},
|
||||
arch.SyscallArgument{Value: uintptr(unix.MAP_PRIVATE | unix.MAP_ANONYMOUS | unix.MAP_FIXED)},
|
||||
arch.SyscallArgument{Value: 0},
|
||||
arch.SyscallArgument{Value: 0})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to mmap spinning queue region into syscall thread: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,14 +718,8 @@ func (s *subprocess) decAwakeContexts() {
|
||||
// The second return value is true if a syscall instruction can be replaced on
|
||||
// a function call.
|
||||
func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool, shouldPatchSyscall bool, err error) {
|
||||
// Get sysmsg thread bound to the context; no-op if contextDecoupling is on.
|
||||
regs := &ac.StateData().Regs
|
||||
sysThread, err := s.getSysmsgThread(regs, c, ac)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
// Reset necessary registers.
|
||||
regs := &ac.StateData().Regs
|
||||
s.resetSysemuRegs(regs)
|
||||
ctx := c.sharedContext
|
||||
ctx.shared.Regs = regs.PtraceRegs
|
||||
@@ -749,49 +737,26 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool
|
||||
c.interrupt.Disable()
|
||||
}()
|
||||
|
||||
if contextDecouplingExp {
|
||||
restoreFPState(nil, ctx, 0, c, ac)
|
||||
restoreFPState(ctx, c, ac)
|
||||
|
||||
// Place the context onto the context queue.
|
||||
if ctx.sleeping {
|
||||
ctx.sleeping = false
|
||||
s.incAwakeContexts()
|
||||
}
|
||||
stubFastPathEnabled := dispatcher.stubFastPathEnabled()
|
||||
ctx.setState(sysmsg.ContextStateNone)
|
||||
s.contextQueue.add(ctx, stubFastPathEnabled)
|
||||
s.waitOnState(ctx, stubFastPathEnabled)
|
||||
// Place the context onto the context queue.
|
||||
if ctx.sleeping {
|
||||
ctx.sleeping = false
|
||||
s.incAwakeContexts()
|
||||
}
|
||||
stubFastPathEnabled := dispatcher.stubFastPathEnabled()
|
||||
ctx.setState(sysmsg.ContextStateNone)
|
||||
s.contextQueue.add(ctx, stubFastPathEnabled)
|
||||
s.waitOnState(ctx, stubFastPathEnabled)
|
||||
|
||||
// Check if there's been an error.
|
||||
threadID := ctx.threadID()
|
||||
if threadID != invalidThreadID {
|
||||
if sysThread, ok := s.sysmsgThreads[threadID]; ok && sysThread.msg.Err != 0 {
|
||||
msg := sysThread.msg
|
||||
panic(fmt.Sprintf("stub thread %d failed: err 0x%x line %d: %s", sysThread.thread.tid, msg.Err, msg.Line, msg))
|
||||
}
|
||||
log.Warningf("systrap: found unexpected ThreadContext.ThreadID field, expected %d found %d", invalidThreadID, threadID)
|
||||
}
|
||||
} else {
|
||||
msg := sysThread.msg
|
||||
t := sysThread.thread
|
||||
|
||||
restoreFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac)
|
||||
|
||||
msg.EnableSentryFastPath()
|
||||
sysThread.waitEvent(sysmsg.ThreadStateDone, ctx)
|
||||
|
||||
// Check if there's been an error.
|
||||
if msg.Err != 0 {
|
||||
panic(fmt.Sprintf("stub thread %d failed: err %d line %d: %s", t.tid, msg.Err, msg.Line, msg))
|
||||
}
|
||||
|
||||
if ctx.state() != sysmsg.ContextStateSyscallTrap {
|
||||
var err error
|
||||
sysThread.fpuStateToMsgOffset, err = msg.FPUStateOffset()
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
// Check if there's been an error.
|
||||
threadID := ctx.threadID()
|
||||
if threadID != invalidThreadID {
|
||||
if sysThread, ok := s.sysmsgThreads[threadID]; ok && sysThread.msg.Err != 0 {
|
||||
msg := sysThread.msg
|
||||
panic(fmt.Sprintf("stub thread %d failed: err 0x%x line %d: %s", sysThread.thread.tid, msg.Err, msg.Line, msg))
|
||||
}
|
||||
log.Warningf("systrap: found unexpected ThreadContext.ThreadID field, expected %d found %d", invalidThreadID, threadID)
|
||||
}
|
||||
|
||||
// Copy register state locally.
|
||||
@@ -908,7 +873,7 @@ func (s *subprocess) kickSysmsgThread() bool {
|
||||
s.numSysmsgThreads++
|
||||
s.sysmsgThreadsMu.Unlock()
|
||||
atomic.AddUint32(&s.contextQueue.numActiveThreads, 1)
|
||||
if _, err := s.createSysmsgThread(nil, nil, nil); err != nil {
|
||||
if err := s.createSysmsgThread(); err != nil {
|
||||
log.Warningf("Unable to create a new stub thread: %s", err)
|
||||
atomic.AddUint32(&s.contextQueue.numActiveThreads, ^uint32(0))
|
||||
s.sysmsgThreadsMu.Lock()
|
||||
@@ -982,37 +947,10 @@ func (s *subprocess) PullFullState(c *context, ac *arch.Context64) error {
|
||||
if !c.sharedContext.isActiveInSubprocess(s) {
|
||||
panic("Attempted to PullFullState for context that is not used in subprocess")
|
||||
}
|
||||
if contextDecouplingExp {
|
||||
saveFPState(nil, c.sharedContext, 0, c, ac)
|
||||
} else {
|
||||
sysThread, err := s.getSysmsgThread(&ac.StateData().Regs, c, ac)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
saveFPState(sysThread.msg, c.sharedContext, sysThread.fpuStateToMsgOffset, c, ac)
|
||||
}
|
||||
saveFPState(c.sharedContext, ac)
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSysmsgThread returns a sysmsg thread for the specified context.
|
||||
// (Unused if contextDecouplingExp=true).
|
||||
func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch.Context64) (*sysmsgThread, error) {
|
||||
if contextDecouplingExp {
|
||||
return nil, nil
|
||||
}
|
||||
sysThread := c.sysmsgThread
|
||||
if sysThread != nil && sysThread.subproc != s {
|
||||
// This can happen if a new address space
|
||||
// has been created (e.g. fork).
|
||||
sysThread.destroy()
|
||||
sysThread = nil
|
||||
}
|
||||
if sysThread != nil {
|
||||
return sysThread, nil
|
||||
}
|
||||
return s.createSysmsgThread(tregs, c, ac)
|
||||
}
|
||||
|
||||
var sysmsgThreadPriority int
|
||||
|
||||
func initSysmsgThreadPriority() {
|
||||
@@ -1025,16 +963,8 @@ func initSysmsgThreadPriority() {
|
||||
}
|
||||
|
||||
// createSysmsgThread creates a new sysmsg thread.
|
||||
// If contextDecouplingExp=false, the thread starts working on the given context.
|
||||
// Otherwise the given function parameters are not used, and the thread starts
|
||||
// processing any available context in the context queue.
|
||||
func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *arch.Context64) (*sysmsgThread, error) {
|
||||
if contextDecouplingExp {
|
||||
// We will not bind any specific context to this thread. We will still use
|
||||
// tregs to setup the thread though.
|
||||
tregs = &arch.Registers{}
|
||||
}
|
||||
|
||||
// The thread starts processing any available context in the context queue.
|
||||
func (s *subprocess) createSysmsgThread() error {
|
||||
// Create a new seccomp process.
|
||||
var r requestThread
|
||||
r.thread = make(chan *thread)
|
||||
@@ -1115,12 +1045,6 @@ func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *a
|
||||
|
||||
sysThread.setMsg(sysmsg.StackAddrToMsg(sentryStackAddr))
|
||||
sysThread.msg.Init(threadID)
|
||||
if contextDecouplingExp {
|
||||
sysThread.msg.Context = 0
|
||||
} else {
|
||||
c.sharedContext.setThreadID(threadID)
|
||||
sysThread.msg.Context = uint64(stubContextRegion + uintptr(c.sharedContext.contextID)*sysmsg.AllocatedSizeofThreadContextStruct)
|
||||
}
|
||||
sysThread.msg.Self = uint64(sysmsgStackAddr + sysmsg.MsgOffsetFromSharedStack)
|
||||
sysThread.msg.SyshandlerStack = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr()))
|
||||
sysThread.msg.Syshandler = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_syshandler))
|
||||
@@ -1152,6 +1076,7 @@ func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *a
|
||||
}
|
||||
|
||||
// Prepare to start the BPF process.
|
||||
tregs := &arch.Registers{}
|
||||
s.resetSysemuRegs(tregs)
|
||||
setArchSpecificRegs(sysThread, tregs)
|
||||
if err := p.setRegs(tregs); err != nil {
|
||||
@@ -1167,26 +1092,11 @@ func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *a
|
||||
panic(fmt.Sprintf("can't detach new clone: %v", errno))
|
||||
}
|
||||
|
||||
if !contextDecouplingExp {
|
||||
sysThread.waitEvent(sysmsg.ThreadStateNone, c.sharedContext)
|
||||
if msg := sysThread.msg; msg.Err != 0 {
|
||||
panic(fmt.Sprintf("stub thread failed: %v (line %v)", msg.Err, msg.Line))
|
||||
}
|
||||
|
||||
sysThread.fpuStateToMsgOffset, err = sysThread.msg.FPUStateOffset()
|
||||
if err != nil {
|
||||
sysThread.destroy()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.sysmsgThread = sysThread
|
||||
}
|
||||
|
||||
s.sysmsgThreadsMu.Lock()
|
||||
s.sysmsgThreads[threadID] = sysThread
|
||||
s.sysmsgThreadsMu.Unlock()
|
||||
|
||||
return sysThread, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreFork implements platform.AddressSpace.PreFork.
|
||||
|
||||
@@ -214,11 +214,9 @@ func restoreArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) {
|
||||
}
|
||||
|
||||
func setArchSpecificRegs(sysThread *sysmsgThread, regs *arch.Registers) {
|
||||
if contextDecouplingExp {
|
||||
// Set the start function and initial stack.
|
||||
regs.PtraceRegs.Rip = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_start))
|
||||
regs.PtraceRegs.Rsp = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr()))
|
||||
}
|
||||
// Set the start function and initial stack.
|
||||
regs.PtraceRegs.Rip = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_start))
|
||||
regs.PtraceRegs.Rsp = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr()))
|
||||
|
||||
// Set gs_base; this is the only time we set it and we don't expect it to ever
|
||||
// change for any thread.
|
||||
@@ -229,12 +227,4 @@ func retrieveArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) {
|
||||
}
|
||||
|
||||
func archSpecificSysmsgThreadInit(sysThread *sysmsgThread) {
|
||||
// Send a fake event to stop the BPF process so that it enters the sighandler.
|
||||
// If there is no coupled context we don't want that to happen because the
|
||||
// thread needs to find a context first.
|
||||
if !contextDecouplingExp {
|
||||
if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(sysThread.thread.tgid), uintptr(sysThread.thread.tid), uintptr(unix.SIGSEGV)); e != 0 {
|
||||
panic(fmt.Sprintf("tkill failed: %v", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,13 +192,6 @@ func restoreArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) {
|
||||
}
|
||||
|
||||
func setArchSpecificRegs(sysThread *sysmsgThread, regs *arch.Registers) {
|
||||
if contextDecouplingExp {
|
||||
// Set the start function and initial stack. On ARM __export_start does not
|
||||
// actually get used because we send a signal to the thread upon startup
|
||||
// right away (see archSpecificSysmsgThreadInit below).
|
||||
regs.PtraceRegs.Pc = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_start))
|
||||
regs.PtraceRegs.Sp = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr()))
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) {
|
||||
|
||||
@@ -41,6 +41,12 @@ func afterFork()
|
||||
//go:linkname afterForkInChild syscall.runtime_AfterForkInChild
|
||||
func afterForkInChild()
|
||||
|
||||
//go:linkname cputicks runtime.cputicks
|
||||
func cputicks() int64
|
||||
|
||||
// spinloop is implemented in assembly.
|
||||
func spinloop()
|
||||
|
||||
// getThreadContextFromID returns a ThreadContext struct that corresponds to the
|
||||
// given ID.
|
||||
//
|
||||
@@ -70,29 +76,16 @@ func mmapContextQueueForSentry(memoryFile *pgalloc.MemoryFile, opts pgalloc.Allo
|
||||
return fr, (*contextQueue)(unsafe.Pointer(addr))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func isFPStateInContextRegion(ctx *sharedContext) bool {
|
||||
// If context decoupling experiment is ON then both the sighandler and
|
||||
// syshandler save FPState to the context region since contexts will move
|
||||
// threads. Otherwise only syshandler will save FPState to the region.
|
||||
return contextDecouplingExp || ctx.state() == sysmsg.ContextStateSyscallTrap
|
||||
}
|
||||
|
||||
func saveFPState(msg *sysmsg.Msg, ctx *sharedContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) {
|
||||
func saveFPState(ctx *sharedContext, ac *arch.Context64) {
|
||||
fpState := ac.FloatingPointData().BytePointer()
|
||||
dst := unsafeSlice(uintptr(unsafe.Pointer(fpState)), archState.FpLen())
|
||||
var src []byte
|
||||
if isFPStateInContextRegion(ctx) {
|
||||
src = ctx.shared.FPState[:]
|
||||
} else {
|
||||
src = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), archState.FpLen())
|
||||
}
|
||||
src := ctx.shared.FPState[:]
|
||||
copy(dst, src)
|
||||
}
|
||||
|
||||
// restoreFPStateDecoupledContext writes FPState from c to the thread context
|
||||
// shared memory region if there is any need to do so.
|
||||
func restoreFPState(msg *sysmsg.Msg, ctx *sharedContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) {
|
||||
func restoreFPState(ctx *sharedContext, c *context, ac *arch.Context64) {
|
||||
if !c.needRestoreFPState {
|
||||
return
|
||||
}
|
||||
@@ -101,11 +94,6 @@ func restoreFPState(msg *sysmsg.Msg, ctx *sharedContext, fpuToMsgOffset uint64,
|
||||
|
||||
fpState := ac.FloatingPointData().BytePointer()
|
||||
src := unsafeSlice(uintptr(unsafe.Pointer(fpState)), archState.FpLen())
|
||||
var dst []byte
|
||||
if isFPStateInContextRegion(ctx) {
|
||||
dst = ctx.shared.FPState[:]
|
||||
} else {
|
||||
dst = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), archState.FpLen())
|
||||
}
|
||||
dst := ctx.shared.FPState[:]
|
||||
copy(dst, src)
|
||||
}
|
||||
|
||||
@@ -127,9 +127,7 @@ go_library(
|
||||
visibility = ["//:sandbox"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/abi/linux/errno",
|
||||
"//pkg/cpuid",
|
||||
"//pkg/errors",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/log",
|
||||
"//pkg/sentry/platform/interrupt",
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
// TODO(b/271631387): These globals are shared between AMD64 and ARM64; move to
|
||||
// sysmsg_lib.c.
|
||||
struct arch_state __export_arch_state;
|
||||
uint64_t __export_context_decoupling_exp;
|
||||
uint64_t __export_stub_start;
|
||||
|
||||
long __syscall(long n, long a1, long a2, long a3, long a4, long a5, long a6) {
|
||||
@@ -160,18 +159,11 @@ static void set_fsbase(uint64_t fsbase) {
|
||||
// specific to amd64.
|
||||
struct thread_context *switch_context_amd64(
|
||||
struct sysmsg *sysmsg, struct thread_context *ctx,
|
||||
enum thread_state new_thread_state, enum context_state new_context_state) {
|
||||
enum context_state new_context_state) {
|
||||
struct thread_context *old_ctx = sysmsg->context;
|
||||
|
||||
for (;;) {
|
||||
// TODO(b/271631387): Once stub code globals can be used between objects
|
||||
// move this check into sysmsg_lib:switch_context().
|
||||
if (__export_context_decoupling_exp) {
|
||||
ctx = switch_context(sysmsg, ctx, new_context_state);
|
||||
} else {
|
||||
ctx->state = new_context_state;
|
||||
wait_state(sysmsg, new_thread_state);
|
||||
}
|
||||
ctx = switch_context(sysmsg, ctx, new_context_state);
|
||||
|
||||
// After setting THREAD_STATE_NONE, syshandled can be interrupted by
|
||||
// SIGCHLD. In this case, we consider that the current context contains
|
||||
@@ -207,8 +199,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
|
||||
|
||||
if (sysmsg != sysmsg->self) panic(0xdeaddead);
|
||||
int32_t thread_state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
|
||||
if (__export_context_decoupling_exp &&
|
||||
thread_state == THREAD_STATE_INITIALIZING) {
|
||||
if (thread_state == THREAD_STATE_INITIALIZING) {
|
||||
// This thread was interrupted before it even had a context.
|
||||
return;
|
||||
}
|
||||
@@ -239,13 +230,9 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
|
||||
ucontext->uc_mcontext.gregs[REG_RIP] < __export_stub_start) {
|
||||
ctx->ptregs.fs_base = fs_base;
|
||||
gregs_to_ptregs(ucontext, &ctx->ptregs);
|
||||
if (__export_context_decoupling_exp) {
|
||||
memcpy(ctx->fpstate, (uint8_t *)ucontext->uc_mcontext.fpregs,
|
||||
__export_arch_state.fp_len);
|
||||
} else {
|
||||
sysmsg->fpstate =
|
||||
(unsigned long)ucontext->uc_mcontext.fpregs - (unsigned long)sysmsg;
|
||||
}
|
||||
memcpy(ctx->fpstate, (uint8_t *)ucontext->uc_mcontext.fpregs,
|
||||
__export_arch_state.fp_len);
|
||||
|
||||
__atomic_store_n(&ctx->fpstate_changed, 0, __ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
@@ -332,13 +319,12 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = switch_context_amd64(sysmsg, ctx, THREAD_STATE_EVENT, ctx_state);
|
||||
ctx = switch_context_amd64(sysmsg, ctx, ctx_state);
|
||||
if (fs_base != ctx->ptregs.fs_base) {
|
||||
set_fsbase(ctx->ptregs.fs_base);
|
||||
}
|
||||
|
||||
if (__export_context_decoupling_exp &&
|
||||
__atomic_load_n(&ctx->fpstate_changed, __ATOMIC_ACQUIRE)) {
|
||||
if (__atomic_load_n(&ctx->fpstate_changed, __ATOMIC_ACQUIRE)) {
|
||||
prep_fpstate_for_sigframe(
|
||||
ctx->fpstate, __export_arch_state.fp_len,
|
||||
__export_arch_state.xsave_mode != XSAVE_MODE_FXSAVE);
|
||||
@@ -366,7 +352,7 @@ void __syshandler() {
|
||||
long fs_base = get_fsbase();
|
||||
ctx->ptregs.fs_base = fs_base;
|
||||
|
||||
ctx = switch_context_amd64(sysmsg, ctx, THREAD_STATE_EVENT, ctx_state);
|
||||
ctx = switch_context_amd64(sysmsg, ctx, ctx_state);
|
||||
// switch_context_amd64 changed sysmsg->state to THREAD_STATE_NONE, so we can
|
||||
// only resume the current process, all other actions are
|
||||
// prohibited after this point.
|
||||
@@ -384,8 +370,8 @@ void __export_start(struct sysmsg *sysmsg, void *_ucontext) {
|
||||
}
|
||||
#endif
|
||||
|
||||
struct thread_context *ctx = switch_context_amd64(
|
||||
sysmsg, NULL, THREAD_STATE_EVENT, CONTEXT_STATE_INVALID);
|
||||
struct thread_context *ctx =
|
||||
switch_context_amd64(sysmsg, NULL, CONTEXT_STATE_INVALID);
|
||||
|
||||
restore_state(sysmsg, ctx, _ucontext);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
// TODO(b/271631387): These globals are shared between AMD64 and ARM64; move to
|
||||
// sysmsg_lib.c.
|
||||
struct arch_state __export_arch_state;
|
||||
uint64_t __export_context_decoupling_exp;
|
||||
uint64_t __export_stub_start;
|
||||
|
||||
long __syscall(long n, long a1, long a2, long a3, long a4, long a5, long a6) {
|
||||
@@ -105,8 +104,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
|
||||
|
||||
uint32_t ctx_state = CONTEXT_STATE_INVALID;
|
||||
struct thread_context *ctx = NULL, *old_ctx = NULL;
|
||||
if (__export_context_decoupling_exp &&
|
||||
thread_state == THREAD_STATE_INITIALIZING) {
|
||||
if (thread_state == THREAD_STATE_INITIALIZING) {
|
||||
// Find a new context and exit to restore it.
|
||||
goto init;
|
||||
}
|
||||
@@ -129,11 +127,8 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
|
||||
}
|
||||
uint8_t *fpStatePointer =
|
||||
(uint8_t *)&ucontext->uc_mcontext.__reserved + kSigframeMagicHeaderLen;
|
||||
if (__export_context_decoupling_exp) {
|
||||
memcpy(ctx->fpstate, fpStatePointer, __export_arch_state.fp_len);
|
||||
} else {
|
||||
sysmsg->fpstate = (uint64_t)(fpStatePointer) - (uint64_t)sysmsg;
|
||||
}
|
||||
|
||||
memcpy(ctx->fpstate, fpStatePointer, __export_arch_state.fp_len);
|
||||
ctx->tls = get_tls();
|
||||
ctx->siginfo = *siginfo;
|
||||
switch (signo) {
|
||||
@@ -162,12 +157,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) {
|
||||
|
||||
init:
|
||||
for (;;) {
|
||||
if (__export_context_decoupling_exp) {
|
||||
ctx = switch_context(sysmsg, ctx, ctx_state);
|
||||
} else {
|
||||
ctx->state = ctx_state;
|
||||
wait_state(sysmsg, THREAD_STATE_EVENT);
|
||||
}
|
||||
ctx = switch_context(sysmsg, ctx, ctx_state);
|
||||
|
||||
if (__atomic_load_n(&ctx->interrupt, __ATOMIC_ACQUIRE) != 0) {
|
||||
// This context got interrupted while it was waiting in the queue.
|
||||
@@ -197,8 +187,7 @@ void restore_state(struct sysmsg *sysmsg, struct thread_context *ctx,
|
||||
(struct fpsimd_context *)&ucontext->uc_mcontext.__reserved;
|
||||
uint8_t *fpStatePointer = (uint8_t *)&fpctx->fpsr;
|
||||
|
||||
if (__export_context_decoupling_exp &&
|
||||
__atomic_load_n(&ctx->fpstate_changed, __ATOMIC_ACQUIRE)) {
|
||||
if (__atomic_load_n(&ctx->fpstate_changed, __ATOMIC_ACQUIRE)) {
|
||||
memcpy(fpStatePointer, ctx->fpstate, __export_arch_state.fp_len);
|
||||
}
|
||||
ptregs_to_gregs(ucontext, &ctx->ptregs);
|
||||
|
||||
@@ -26,8 +26,6 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux/errno"
|
||||
"gvisor.dev/gvisor/pkg/errors"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
)
|
||||
|
||||
@@ -111,9 +109,6 @@ const (
|
||||
// ThreadStateDone means that last event has been handled and the stub thread
|
||||
// can be resumed.
|
||||
ThreadStateDone
|
||||
// ThreadStateEvent means that there is a new event which has to be handled by
|
||||
// Sentry. (Used only if !contextDecoupledExp)
|
||||
ThreadStateEvent
|
||||
// ThreadStatePrep means that syshandler started filling the sysmsg struct.
|
||||
ThreadStatePrep
|
||||
// ThreadStateAsleep means that this thread fell asleep because there was not
|
||||
@@ -164,27 +159,6 @@ type Msg struct {
|
||||
Line int32
|
||||
// Debug is a variable to use to get visibility into the stub from the sentry.
|
||||
Debug uint64
|
||||
// fpState is an offset relative to the sighandler stack to the fpState, stored
|
||||
// by the sighandler.
|
||||
fpState uint64
|
||||
// The fast path is the mode when a thread is polling msg->state to
|
||||
// wait for a required state instead of calling FUTEX_WAIT.
|
||||
//
|
||||
// If core tagging is supported by the kernel, the Sentry thread, and a
|
||||
// stub thread share the same cookie and run on two associated
|
||||
// hyper-threads. The thread which is polling msg->state calls the
|
||||
// pause instruction, so the second thread gets almost the entire core
|
||||
// to run its workload.
|
||||
//
|
||||
// If core tagging isn't supported, a polling thread calls sched_yield
|
||||
// to let other processes to run.
|
||||
|
||||
// StubFastPath is set if a stub process uses the fast path to wait for
|
||||
// events. Only the stub thread can set it before switching to the
|
||||
// sentry, but the Sentry can clear it.
|
||||
stubFastPath uint32
|
||||
sentryFastPath uint32
|
||||
AckedEvents uint32
|
||||
// ThreadID is the ID of the sysmsg thread.
|
||||
ThreadID uint32
|
||||
}
|
||||
@@ -292,9 +266,8 @@ type ThreadContext struct {
|
||||
func (m *Msg) Init(threadID uint32) {
|
||||
m.Err = 0
|
||||
m.Line = -1
|
||||
m.stubFastPath = 0
|
||||
m.sentryFastPath = 1
|
||||
m.ThreadID = threadID
|
||||
m.Context = 0
|
||||
}
|
||||
|
||||
// Init initializes the ThreadContext instance.
|
||||
@@ -307,40 +280,6 @@ func (c *ThreadContext) Init(initialThreadID uint32) {
|
||||
c.ThreadID = initialThreadID
|
||||
}
|
||||
|
||||
// StubFastPath returns true if the stub thread in the polling mode.
|
||||
func (m *Msg) StubFastPath() bool {
|
||||
return atomic.LoadUint32(&m.stubFastPath) != 0
|
||||
}
|
||||
|
||||
// DisableStubFastPath disables the polling mode for the stub thread.
|
||||
func (m *Msg) DisableStubFastPath() {
|
||||
atomic.StoreUint32(&m.stubFastPath, 0)
|
||||
}
|
||||
|
||||
// EnableSentryFastPath enables the polling mode for the Sentry. It has to be
|
||||
// called before switching controls to the stub process.
|
||||
// This function is used if contextDecouplingExp=false because the fastpath
|
||||
// is negotiated in Sysmsg.
|
||||
func (m *Msg) EnableSentryFastPath() {
|
||||
m.sentryFastPath = 1
|
||||
}
|
||||
|
||||
// DisableSentryFastPath disables the polling mode for the Sentry.
|
||||
// This function is used if contextDecouplingExp=false because the fastpath
|
||||
// is negotiated in Sysmsg.
|
||||
func (m *Msg) DisableSentryFastPath() {
|
||||
atomic.StoreUint32(&m.sentryFastPath, 0)
|
||||
}
|
||||
|
||||
// FPUStateOffset returns the offset of a saved FPU state to the msg.
|
||||
func (m *Msg) FPUStateOffset() (uint64, error) {
|
||||
offset := m.fpState
|
||||
if int64(offset) > -MsgOffsetFromSharedStack && int64(offset) < 0 {
|
||||
return offset, nil
|
||||
}
|
||||
return 0, errors.New(errno.EFAULT, fmt.Sprintf("FPU offset has been corrupted: %x", offset))
|
||||
}
|
||||
|
||||
func (m *Msg) String() string {
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "sysmsg.Msg{msg: %x state %d", m.Self, m.State)
|
||||
|
||||
@@ -41,7 +41,6 @@ struct arch_state {
|
||||
enum thread_state {
|
||||
THREAD_STATE_NONE,
|
||||
THREAD_STATE_DONE,
|
||||
THREAD_STATE_EVENT,
|
||||
THREAD_STATE_PREP,
|
||||
THREAD_STATE_ASLEEP,
|
||||
THREAD_STATE_INITIALIZING,
|
||||
@@ -66,10 +65,6 @@ struct sysmsg {
|
||||
int32_t err;
|
||||
int32_t err_line;
|
||||
uint64_t debug;
|
||||
uint64_t fpstate;
|
||||
uint32_t stub_fast_path;
|
||||
uint32_t sentry_fast_path;
|
||||
uint32_t acked_events;
|
||||
uint32_t thread_id;
|
||||
};
|
||||
|
||||
@@ -122,7 +117,6 @@ struct thread_context {
|
||||
extern uint64_t __export_pr_sched_core;
|
||||
extern uint64_t __export_deep_sleep_timeout;
|
||||
extern struct arch_state __export_arch_state;
|
||||
extern uint64_t __export_context_decoupling_exp;
|
||||
struct context_queue;
|
||||
extern struct context_queue *__export_context_queue_addr;
|
||||
|
||||
@@ -154,10 +148,7 @@ static void __panic(int err, long line) {
|
||||
struct thread_context *ctx = sysmsg->context;
|
||||
sysmsg->err = err;
|
||||
sysmsg->err_line = line;
|
||||
// Normally sentry waits on sysmsg->state.
|
||||
__atomic_store_n(&sysmsg->state, THREAD_STATE_EVENT, __ATOMIC_RELEASE);
|
||||
sys_futex(&sysmsg->state, FUTEX_WAKE, 1, NULL, NULL, 666);
|
||||
// Under context-decoupling the sentry waits on ctx->state.
|
||||
// Wake up the goroutine waiting on the current context.
|
||||
__atomic_store_n(&ctx->state, CONTEXT_STATE_FAULT, __ATOMIC_RELEASE);
|
||||
sys_futex(&ctx->state, FUTEX_WAKE, 1, NULL, NULL, 666);
|
||||
// crash the stub process.
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
// __export_deep_sleep_timeout is the timeout after which the stub thread stops
|
||||
// polling and fall asleep.
|
||||
uint64_t __export_deep_sleep_timeout;
|
||||
uint64_t __export_handshake_timeout;
|
||||
|
||||
// LINT.IfChange
|
||||
#define MAX_GUEST_CONTEXTS (4095)
|
||||
@@ -372,66 +371,6 @@ struct thread_context *switch_context(struct sysmsg *sysmsg,
|
||||
return get_context(sysmsg);
|
||||
}
|
||||
|
||||
int wait_state(struct sysmsg *sysmsg, enum thread_state new_thread_state) {
|
||||
unsigned long handshake_timeout;
|
||||
uint64_t acked_events_prev;
|
||||
unsigned long start;
|
||||
int ret, v, fast_path;
|
||||
|
||||
acked_events_prev = __atomic_load_n(&sysmsg->acked_events, __ATOMIC_SEQ_CST);
|
||||
// stub_fast_path can be changed non-atomically before we change the state and
|
||||
// wake up the Sentry.
|
||||
sysmsg->stub_fast_path = 1;
|
||||
__atomic_store_n(&sysmsg->state, new_thread_state, __ATOMIC_SEQ_CST);
|
||||
|
||||
fast_path = __atomic_load_n(&sysmsg->sentry_fast_path, __ATOMIC_SEQ_CST);
|
||||
if (!fast_path) {
|
||||
ret = sys_futex(&sysmsg->state, FUTEX_WAKE, 1, NULL, NULL, 0);
|
||||
if (ret < 0) panic(ret);
|
||||
}
|
||||
|
||||
v = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
|
||||
if (v == THREAD_STATE_DONE) goto out;
|
||||
|
||||
handshake_timeout = __export_handshake_timeout;
|
||||
start = rdtsc();
|
||||
while (1) {
|
||||
v = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE);
|
||||
if (v == THREAD_STATE_DONE) goto out;
|
||||
|
||||
// The Sentry can change stub_fast_path to zero if it finds out that the
|
||||
// user task has to sleep.
|
||||
fast_path = __atomic_load_n(&sysmsg->stub_fast_path, __ATOMIC_ACQUIRE);
|
||||
if (fast_path) {
|
||||
unsigned long delta = rdtsc() - start;
|
||||
|
||||
if (delta > __export_deep_sleep_timeout) {
|
||||
fast_path = 0;
|
||||
__atomic_store_n(&sysmsg->stub_fast_path, 0, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
if (handshake_timeout != 0) {
|
||||
if (__atomic_load_n(&sysmsg->acked_events, __ATOMIC_SEQ_CST) !=
|
||||
acked_events_prev) {
|
||||
handshake_timeout = 0;
|
||||
} else if (delta > handshake_timeout) {
|
||||
__syscall(__NR_sched_yield, 0, 0, 0, 0, 0, 0);
|
||||
handshake_timeout += __export_handshake_timeout;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fast_path) {
|
||||
spinloop();
|
||||
} else {
|
||||
sys_futex(&sysmsg->state, FUTEX_WAIT, v, NULL, NULL, 0);
|
||||
}
|
||||
}
|
||||
out:
|
||||
__atomic_fetch_add(&sysmsg->acked_events, 1, __ATOMIC_SEQ_CST);
|
||||
return v;
|
||||
}
|
||||
|
||||
void verify_offsets() {
|
||||
BUILD_BUG_ON(offsetof_sysmsg_self != offsetof(struct sysmsg, self));
|
||||
BUILD_BUG_ON(offsetof_sysmsg_ret_addr != offsetof(struct sysmsg, ret_addr));
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#define offsetof_thread_context_ptregs 0x8 + MAX_FPSTATE_LEN
|
||||
|
||||
#define kTHREAD_STATE_NONE 0
|
||||
#define kTHREAD_STATE_INTERRUPT 4
|
||||
#define kTHREAD_STATE_INTERRUPT 3
|
||||
|
||||
// LINT.ThenChange(sysmsg.h, sysmsg_lib.c)
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ package systrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
@@ -24,7 +23,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/seccomp"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/interrupt"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg"
|
||||
)
|
||||
|
||||
@@ -34,8 +32,7 @@ import (
|
||||
// This type of thread is used to execute user processes.
|
||||
type sysmsgThread struct {
|
||||
// subproc is a link to the subprocess which is used to call native
|
||||
// system calls and track when a sysmsg thread has to be recreated.
|
||||
// Look at getSysmsgThread() for more details.
|
||||
// system calls.
|
||||
subproc *subprocess
|
||||
|
||||
// thread is a thread identifier.
|
||||
@@ -55,7 +52,8 @@ type sysmsgThread struct {
|
||||
fpuStateToMsgOffset uint64
|
||||
}
|
||||
|
||||
// sysmsgStackAddr returns a sysmsg stack address in the thread address space.
|
||||
// sysmsgPerThreadMemAddr returns a sysmsg stack address in the thread address
|
||||
// space.
|
||||
func (p *sysmsgThread) sysmsgPerThreadMemAddr() uintptr {
|
||||
return stubSysmsgStack + sysmsg.PerThreadMemSize*uintptr(p.thread.sysmsgStackID)
|
||||
}
|
||||
@@ -117,22 +115,6 @@ func (p *sysmsgThread) mapPrivateStack(addr uintptr, size uintptr) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *sysmsgThread) waitEvent(switchToState sysmsg.ThreadState, interruptor interrupt.Receiver) {
|
||||
msg := p.msg
|
||||
wakeup := false
|
||||
acked := atomic.LoadUint32(&msg.AckedEvents)
|
||||
if switchToState != sysmsg.ThreadStateNone {
|
||||
msg.State.Set(switchToState)
|
||||
wakeup = msg.StubFastPath() == false
|
||||
} else {
|
||||
acked--
|
||||
}
|
||||
|
||||
if errno := futexWaitForState(msg, sysmsg.ThreadStateEvent, wakeup, acked, interruptor); errno != 0 {
|
||||
panic(fmt.Sprintf("error waiting for state: %v", errno))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *sysmsgThread) Debugf(format string, v ...any) {
|
||||
if !log.IsLogging(log.Debug) {
|
||||
return
|
||||
|
||||
@@ -16,15 +16,11 @@ package systrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/interrupt"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg"
|
||||
)
|
||||
|
||||
@@ -78,82 +74,6 @@ func (p *sysmsgThread) init(sentryAddr, guestAddr uintptr) {
|
||||
}
|
||||
}
|
||||
|
||||
func futexWake(msg *sysmsg.Msg) syscall.Errno {
|
||||
_, _, e := unix.RawSyscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&msg.State)), linux.FUTEX_WAKE, 1, 0, 0, 0)
|
||||
return e
|
||||
}
|
||||
|
||||
//go:linkname cputicks runtime.cputicks
|
||||
func cputicks() int64
|
||||
|
||||
// spinloop is implemented in assembly.
|
||||
func spinloop()
|
||||
|
||||
//go:linkname entersyscall runtime.entersyscall
|
||||
func entersyscall()
|
||||
|
||||
//go:linkname exitsyscall runtime.exitsyscall
|
||||
func exitsyscall()
|
||||
|
||||
// deep_sleep_timeout is the timeout after which we stops polling and fall asleep.
|
||||
//
|
||||
// The value is 40µs for 2GHz CPU. This timeout matches the sentry<->stub round
|
||||
// trip in the pure deep sleep case.
|
||||
const deepSleepTimeout = uint64(80000)
|
||||
const handshakeTimeout = uint64(1000)
|
||||
|
||||
func futexWaitForState(msg *sysmsg.Msg, state sysmsg.ThreadState, wakeup bool, acked uint32, interruptor interrupt.Receiver) syscall.Errno {
|
||||
slowPath := false
|
||||
errno := syscall.Errno(0)
|
||||
start := cputicks()
|
||||
htimeout := handshakeTimeout
|
||||
handshake := false
|
||||
for {
|
||||
curState := msg.State.Get()
|
||||
if curState == state {
|
||||
break
|
||||
}
|
||||
if wakeup {
|
||||
if errno = futexWake(msg); errno != 0 {
|
||||
break
|
||||
}
|
||||
wakeup = false
|
||||
continue
|
||||
}
|
||||
|
||||
if !slowPath {
|
||||
delta := uint64(cputicks() - start)
|
||||
if delta > deepSleepTimeout {
|
||||
msg.DisableSentryFastPath()
|
||||
slowPath = true
|
||||
continue
|
||||
}
|
||||
|
||||
if !handshake {
|
||||
if acked != atomic.LoadUint32(&msg.AckedEvents) {
|
||||
handshake = true
|
||||
continue
|
||||
}
|
||||
if delta > htimeout {
|
||||
htimeout += handshakeTimeout
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if slowPath {
|
||||
errno = msg.SleepOnState(curState, interruptor)
|
||||
if errno != 0 {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
spinloop()
|
||||
}
|
||||
}
|
||||
atomic.AddUint32(&msg.AckedEvents, 1)
|
||||
return errno
|
||||
}
|
||||
|
||||
// sysmsgSigactions installs signal handles for signals which can be triggered
|
||||
// by stubProcess and have to be handled by Sentry.
|
||||
//
|
||||
|
||||
@@ -136,10 +136,6 @@ type context struct {
|
||||
// this is also only meaningful if lastFaultSP is non-nil.
|
||||
lastFaultIP hostarch.Addr
|
||||
|
||||
// sysmsgThread is a sysmsg thread descriptor which is used to execute
|
||||
// application code. (Note: Unused if contextDecouplingExp=true).
|
||||
sysmsgThread *sysmsgThread
|
||||
|
||||
// needRestoreFPState indicates that the FPU state has been changed by
|
||||
// the Sentry and has to be updated on the stub thread.
|
||||
needRestoreFPState bool
|
||||
@@ -278,9 +274,6 @@ func (c *context) Interrupt() {
|
||||
|
||||
// Release releases all platform resources used by the context.
|
||||
func (c *context) Release() {
|
||||
if c.sysmsgThread != nil {
|
||||
c.sysmsgThread.destroy()
|
||||
}
|
||||
if c.sharedContext != nil {
|
||||
c.sharedContext.release()
|
||||
c.sharedContext = nil
|
||||
@@ -289,18 +282,13 @@ func (c *context) Release() {
|
||||
|
||||
// PrepareSleep implements platform.Context.platform.PrepareSleep.
|
||||
func (c *context) PrepareSleep() {
|
||||
if contextDecouplingExp {
|
||||
ctx := c.sharedContext
|
||||
if ctx == nil {
|
||||
return
|
||||
}
|
||||
if !ctx.sleeping {
|
||||
ctx.sleeping = true
|
||||
ctx.subprocess.decAwakeContexts()
|
||||
}
|
||||
ctx := c.sharedContext
|
||||
if ctx == nil {
|
||||
return
|
||||
} else if c.sysmsgThread != nil {
|
||||
c.sysmsgThread.msg.DisableStubFastPath()
|
||||
}
|
||||
if !ctx.sleeping {
|
||||
ctx.sleeping = true
|
||||
ctx.subprocess.decAwakeContexts()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user