Plumb restore context to afterLoad()

This allows for external information to be passed to restore code, like
host FDs to be remapped.

Updates #1956

PiperOrigin-RevId: 612540749
This commit is contained in:
Fabricio Voznika
2024-03-04 12:21:50 -08:00
committed by gVisor bot
parent c6b06ab1a5
commit c087777e37
46 changed files with 214 additions and 155 deletions
+4 -4
View File
@@ -38,7 +38,7 @@ import (
"gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/sync"
) )
// contextID is the package for context.Context.Value keys. // contextID is the package for anyContext.Context.Value keys.
type contextID int type contextID int
const ( const (
@@ -51,13 +51,13 @@ const (
_AT_HWCAP2 = 26 _AT_HWCAP2 = 26
) )
// context represents context.Context. // anyContext represents context.Context.
type context interface { type anyContext interface {
Value(key any) any Value(key any) any
} }
// FromContext returns the FeatureSet from the context, if available. // FromContext returns the FeatureSet from the context, if available.
func FromContext(ctx context) FeatureSet { func FromContext(ctx anyContext) FeatureSet {
v := ctx.Value(CtxFeatureSet) v := ctx.Value(CtxFeatureSet)
if v == nil { if v == nil {
return FeatureSet{} // Panics if used. return FeatureSet{} // Panics if used.
+3 -1
View File
@@ -17,6 +17,8 @@
package cpuid package cpuid
import "context"
// Static is a static CPUID function. // Static is a static CPUID function.
// //
// +stateify savable // +stateify savable
@@ -90,7 +92,7 @@ func (s Static) ToFeatureSet() FeatureSet {
} }
// afterLoad calls normalize. // afterLoad calls normalize.
func (s Static) afterLoad() { func (s Static) afterLoad(context.Context) {
s.normalize() s.normalize()
} }
+2 -1
View File
@@ -17,6 +17,7 @@
package refs_template package refs_template
import ( import (
"context"
"fmt" "fmt"
"gvisor.dev/gvisor/pkg/atomicbitops" "gvisor.dev/gvisor/pkg/atomicbitops"
@@ -156,7 +157,7 @@ func (r *Refs) DecRef(destroy func()) {
} }
} }
func (r *Refs) afterLoad() { func (r *Refs) afterLoad(context.Context) {
if r.ReadRefs() > 0 { if r.ReadRefs() > 0 {
refs.Register(r) refs.Register(r)
} }
+3 -1
View File
@@ -19,6 +19,8 @@
package arch package arch
import ( import (
"context"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu" "gvisor.dev/gvisor/pkg/sentry/arch/fpu"
) )
@@ -35,6 +37,6 @@ type State struct {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (s *State) afterLoad() { func (s *State) afterLoad(context.Context) {
s.afterLoadFPState() s.afterLoadFPState()
} }
+2 -1
View File
@@ -15,6 +15,7 @@
package nvproxy package nvproxy
import ( import (
goContext "context"
"fmt" "fmt"
"gvisor.dev/gvisor/pkg/abi/nvgpu" "gvisor.dev/gvisor/pkg/abi/nvgpu"
@@ -30,7 +31,7 @@ func (n *nvproxy) beforeSave() {
n.objsLive = nil n.objsLive = nil
} }
func (n *nvproxy) afterLoad() { func (n *nvproxy) afterLoad(goContext.Context) {
Init() Init()
abiCons, ok := abis[n.version] abiCons, ok := abis[n.version]
if !ok { if !ok {
+3 -1
View File
@@ -14,8 +14,10 @@
package devtmpfs package devtmpfs
import "context"
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (fst *FilesystemType) afterLoad() { func (fst *FilesystemType) afterLoad(context.Context) {
if fst.fs != nil { if fst.fs != nil {
// Ensure that we don't create another filesystem. // Ensure that we don't create another filesystem.
fst.initOnce.Do(func() {}) fst.initOnce.Do(func() {})
+3 -1
View File
@@ -14,6 +14,8 @@
package fuse package fuse
func (fRes *futureResponse) afterLoad() { import "context"
func (fRes *futureResponse) afterLoad(context.Context) {
fRes.ch = make(chan struct{}) fRes.ch = make(chan struct{})
} }
+5 -4
View File
@@ -15,6 +15,7 @@
package gofer package gofer
import ( import (
goContext "context"
"fmt" "fmt"
"io" "io"
@@ -128,7 +129,7 @@ func (d *dentry) beforeSave() {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (d *dentry) afterLoad() { func (d *dentry) afterLoad(goContext.Context) {
d.readFD = atomicbitops.FromInt32(-1) d.readFD = atomicbitops.FromInt32(-1)
d.writeFD = atomicbitops.FromInt32(-1) d.writeFD = atomicbitops.FromInt32(-1)
d.mmapFD = atomicbitops.FromInt32(-1) d.mmapFD = atomicbitops.FromInt32(-1)
@@ -138,12 +139,12 @@ func (d *dentry) afterLoad() {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (d *directfsDentry) afterLoad() { func (d *directfsDentry) afterLoad(goContext.Context) {
d.controlFD = -1 d.controlFD = -1
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (d *dentryPlatformFile) afterLoad() { func (d *dentryPlatformFile) afterLoad(goContext.Context) {
if d.hostFileMapper.IsInited() { if d.hostFileMapper.IsInited() {
// Ensure that we don't call d.hostFileMapper.Init() again. // Ensure that we don't call d.hostFileMapper.Init() again.
d.hostFileMapperInitOnce.Do(func() {}) d.hostFileMapperInitOnce.Do(func() {})
@@ -151,7 +152,7 @@ func (d *dentryPlatformFile) afterLoad() {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (fd *specialFileFD) afterLoad() { func (fd *specialFileFD) afterLoad(goContext.Context) {
fd.handle.fd = -1 fd.handle.fd = -1
if fd.hostFileMapper.IsInited() { if fd.hostFileMapper.IsInited() {
// Ensure that we don't call fd.hostFileMapper.Init() again. // Ensure that we don't call fd.hostFileMapper.Init() again.
+2 -1
View File
@@ -15,6 +15,7 @@
package host package host
import ( import (
"context"
"fmt" "fmt"
"io" "io"
@@ -57,7 +58,7 @@ func (i *inode) beforeSave() {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (i *inode) afterLoad() { func (i *inode) afterLoad(context.Context) {
if i.epollable { if i.epollable {
if err := unix.SetNonblock(i.hostFD, true); err != nil { if err := unix.SetNonblock(i.hostFD, true); err != nil {
panic(fmt.Sprintf("host.inode.afterLoad: failed to set host FD %d non-blocking: %v", i.hostFD, err)) panic(fmt.Sprintf("host.inode.afterLoad: failed to set host FD %d non-blocking: %v", i.hostFD, err))
@@ -14,6 +14,8 @@
package iouringfs package iouringfs
import "context"
// beforeSave is invoked by stateify. // beforeSave is invoked by stateify.
func (fd *FileDescription) beforeSave() { func (fd *FileDescription) beforeSave() {
if fd.running.Load() != 0 { if fd.running.Load() != 0 {
@@ -22,7 +24,7 @@ func (fd *FileDescription) beforeSave() {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (fd *FileDescription) afterLoad() { func (fd *FileDescription) afterLoad(context.Context) {
// Remap shared buffers. // Remap shared buffers.
fd.remap = true fd.remap = true
fd.runC = make(chan struct{}, 1) fd.runC = make(chan struct{}, 1)
+4 -2
View File
@@ -15,18 +15,20 @@
package kernfs package kernfs
import ( import (
"context"
"gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/refs"
) )
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (d *Dentry) afterLoad() { func (d *Dentry) afterLoad(context.Context) {
if d.refs.Load() >= 0 { if d.refs.Load() >= 0 {
refs.Register(d) refs.Register(d)
} }
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (i *inodePlatformFile) afterLoad() { func (i *inodePlatformFile) afterLoad(context.Context) {
if i.fileMapper.IsInited() { if i.fileMapper.IsInited() {
// Ensure that we don't call i.fileMapper.Init() again. // Ensure that we don't call i.fileMapper.Init() again.
i.fileMapperInitOnce.Do(func() {}) i.fileMapperInitOnce.Do(func() {})
+3 -1
View File
@@ -15,10 +15,12 @@
package overlay package overlay
import ( import (
"context"
"gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/refs"
) )
func (d *dentry) afterLoad() { func (d *dentry) afterLoad(context.Context) {
if d.refs.Load() != -1 { if d.refs.Load() != -1 {
refs.Register(d) refs.Register(d)
} }
+2 -1
View File
@@ -15,6 +15,7 @@
package tmpfs package tmpfs
import ( import (
goContext "context"
"fmt" "fmt"
"gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/context"
@@ -23,7 +24,7 @@ import (
) )
// afterLoad is called by stateify. // afterLoad is called by stateify.
func (fs *filesystem) afterLoad() { func (fs *filesystem) afterLoad(goContext.Context) {
if !fs.privateMF { if !fs.privateMF {
fs.mf = fs.mfp.MemoryFile() fs.mf = fs.mfp.MemoryFile()
} }
+3 -1
View File
@@ -14,7 +14,9 @@
package fsutil package fsutil
import "context"
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (f *HostFileMapper) afterLoad() { func (f *HostFileMapper) afterLoad(context.Context) {
f.mappings = make(map[uint64]mapping) f.mappings = make(map[uint64]mapping)
} }
+3 -1
View File
@@ -15,6 +15,8 @@
package inet package inet
import ( import (
goContext "context"
"gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -151,7 +153,7 @@ func (n *Namespace) init() {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (n *Namespace) afterLoad() { func (n *Namespace) afterLoad(goContext.Context) {
n.init() n.init()
} }
+3 -1
View File
@@ -15,11 +15,13 @@
package pipe package pipe
import ( import (
"context"
"gvisor.dev/gvisor/pkg/safemem" "gvisor.dev/gvisor/pkg/safemem"
) )
// afterLoad is called by stateify. // afterLoad is called by stateify.
func (p *Pipe) afterLoad() { func (p *Pipe) afterLoad(context.Context) {
p.bufBlocks[0] = safemem.BlockFromSafeSlice(p.buf) p.bufBlocks[0] = safemem.BlockFromSafeSlice(p.buf)
p.bufBlocks[1] = p.bufBlocks[0] p.bufBlocks[1] = p.bufBlocks[0]
p.bufBlockSeq = safemem.BlockSeqFromSlice(p.bufBlocks[:]) p.bufBlockSeq = safemem.BlockSeqFromSlice(p.bufBlocks[:])
+1 -1
View File
@@ -630,7 +630,7 @@ func (t *Task) loadSeccomp(seccompData *taskSeccomp) {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (t *Task) afterLoad() { func (t *Task) afterLoad(gocontext.Context) {
t.updateInfoLocked() t.updateInfoLocked()
if ts := t.seccomp.Load().(*taskSeccomp); ts != nil { if ts := t.seccomp.Load().(*taskSeccomp); ts != nil {
ts.populateCache(t) ts.populateCache(t)
+3 -1
View File
@@ -15,6 +15,8 @@
package kernel package kernel
import ( import (
"context"
"gvisor.dev/gvisor/pkg/sentry/time" "gvisor.dev/gvisor/pkg/sentry/time"
) )
@@ -36,6 +38,6 @@ func (t *Timekeeper) beforeSave() {
} }
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (t *Timekeeper) afterLoad() { func (t *Timekeeper) afterLoad(context.Context) {
t.restored = make(chan struct{}) t.restored = make(chan struct{})
} }
+51 -51
View File
@@ -126,52 +126,52 @@ type AIOContext struct {
} }
// destroy marks the context dead. // destroy marks the context dead.
func (ctx *AIOContext) destroy() { func (aio *AIOContext) destroy() {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
ctx.dead = true aio.dead = true
ctx.checkForDone() aio.checkForDone()
} }
// Preconditions: ctx.mu must be held by caller. // Preconditions: ctx.mu must be held by caller.
func (ctx *AIOContext) checkForDone() { func (aio *AIOContext) checkForDone() {
if ctx.dead && ctx.outstanding == 0 { if aio.dead && aio.outstanding == 0 {
close(ctx.requestReady) close(aio.requestReady)
ctx.requestReady = nil aio.requestReady = nil
} }
} }
// Prepare reserves space for a new request, returning nil if available. // Prepare reserves space for a new request, returning nil if available.
// Returns EAGAIN if the context is busy and EINVAL if the context is dead. // Returns EAGAIN if the context is busy and EINVAL if the context is dead.
func (ctx *AIOContext) Prepare() error { func (aio *AIOContext) Prepare() error {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
if ctx.dead { if aio.dead {
// Context died after the caller looked it up. // Context died after the caller looked it up.
return linuxerr.EINVAL return linuxerr.EINVAL
} }
if ctx.outstanding >= ctx.maxOutstanding { if aio.outstanding >= aio.maxOutstanding {
// Context is busy. // Context is busy.
return linuxerr.EAGAIN return linuxerr.EAGAIN
} }
ctx.outstanding++ aio.outstanding++
return nil return nil
} }
// PopRequest pops a completed request if available, this function does not do // PopRequest pops a completed request if available, this function does not do
// any blocking. Returns false if no request is available. // any blocking. Returns false if no request is available.
func (ctx *AIOContext) PopRequest() (any, bool) { func (aio *AIOContext) PopRequest() (any, bool) {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
// Is there anything ready? // Is there anything ready?
if e := ctx.results.Front(); e != nil { if e := aio.results.Front(); e != nil {
if ctx.outstanding == 0 { if aio.outstanding == 0 {
panic("AIOContext outstanding is going negative") panic("AIOContext outstanding is going negative")
} }
ctx.outstanding-- aio.outstanding--
ctx.results.Remove(e) aio.results.Remove(e)
ctx.checkForDone() aio.checkForDone()
return e.data, true return e.data, true
} }
return nil, false return nil, false
@@ -179,17 +179,17 @@ func (ctx *AIOContext) PopRequest() (any, bool) {
// FinishRequest finishes a pending request. It queues up the data // FinishRequest finishes a pending request. It queues up the data
// and notifies listeners. // and notifies listeners.
func (ctx *AIOContext) FinishRequest(data any) { func (aio *AIOContext) FinishRequest(data any) {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
// Push to the list and notify opportunistically. The channel notify // Push to the list and notify opportunistically. The channel notify
// here is guaranteed to be safe because outstanding must be non-zero. // here is guaranteed to be safe because outstanding must be non-zero.
// The requestReady channel is only closed when outstanding reaches zero. // The requestReady channel is only closed when outstanding reaches zero.
ctx.results.PushBack(&ioResult{data: data}) aio.results.PushBack(&ioResult{data: data})
select { select {
case ctx.requestReady <- struct{}{}: case aio.requestReady <- struct{}{}:
default: default:
} }
} }
@@ -197,46 +197,46 @@ func (ctx *AIOContext) FinishRequest(data any) {
// WaitChannel returns a channel that is notified when an AIO request is // WaitChannel returns a channel that is notified when an AIO request is
// completed. Returns nil if the context is destroyed and there are no more // completed. Returns nil if the context is destroyed and there are no more
// outstanding requests. // outstanding requests.
func (ctx *AIOContext) WaitChannel() chan struct{} { func (aio *AIOContext) WaitChannel() chan struct{} {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
return ctx.requestReady return aio.requestReady
} }
// Dead returns true if the context has been destroyed. // Dead returns true if the context has been destroyed.
func (ctx *AIOContext) Dead() bool { func (aio *AIOContext) Dead() bool {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
return ctx.dead return aio.dead
} }
// CancelPendingRequest forgets about a request that hasn't yet completed. // CancelPendingRequest forgets about a request that hasn't yet completed.
func (ctx *AIOContext) CancelPendingRequest() { func (aio *AIOContext) CancelPendingRequest() {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
if ctx.outstanding == 0 { if aio.outstanding == 0 {
panic("AIOContext outstanding is going negative") panic("AIOContext outstanding is going negative")
} }
ctx.outstanding-- aio.outstanding--
ctx.checkForDone() aio.checkForDone()
} }
// Drain drops all completed requests. Pending requests remain untouched. // Drain drops all completed requests. Pending requests remain untouched.
func (ctx *AIOContext) Drain() { func (aio *AIOContext) Drain() {
ctx.mu.Lock() aio.mu.Lock()
defer ctx.mu.Unlock() defer aio.mu.Unlock()
if ctx.outstanding == 0 { if aio.outstanding == 0 {
return return
} }
size := uint32(ctx.results.Len()) size := uint32(aio.results.Len())
if ctx.outstanding < size { if aio.outstanding < size {
panic("AIOContext outstanding is going negative") panic("AIOContext outstanding is going negative")
} }
ctx.outstanding -= size aio.outstanding -= size
ctx.results.Reset() aio.results.Reset()
ctx.checkForDone() aio.checkForDone()
} }
// aioMappable implements memmap.MappingIdentity and memmap.Mappable for AIO // aioMappable implements memmap.MappingIdentity and memmap.Mappable for AIO
+4 -2
View File
@@ -14,7 +14,9 @@
package mm package mm
import "context"
// afterLoad is invoked by stateify. // afterLoad is invoked by stateify.
func (ctx *AIOContext) afterLoad() { func (aio *AIOContext) afterLoad(context.Context) {
ctx.requestReady = make(chan struct{}, 1) aio.requestReady = make(chan struct{}, 1)
} }

Some files were not shown because too many files have changed in this diff Show More