Remove "AddressSpace activation".

PiperOrigin-RevId: 869302553
This commit is contained in:
Jamie Liu
2026-02-12 14:55:06 -08:00
committed by gVisor bot
parent a41a6099f9
commit d7996a5d9b
51 changed files with 378 additions and 606 deletions
+6 -11
View File
@@ -78,17 +78,12 @@ type Blocker interface {
BlockWithTimeoutOn(waiter.Waitable, waiter.EventMask, time.Duration) (time.Duration, bool)
// UninterruptibleSleepStart indicates the beginning of an uninterruptible
// sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE). If deactivate
// is true and the Context represents a Task, the Task's AddressSpace is
// deactivated.
UninterruptibleSleepStart(deactivate bool)
// sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE).
UninterruptibleSleepStart()
// UninterruptibleSleepFinish indicates the end of an uninterruptible sleep
// state that was begun by a previous call to UninterruptibleSleepStart. If
// activate is true and the Context represents a Task, the Task's
// AddressSpace is activated. Normally activate is the same value as the
// deactivate parameter passed to UninterruptibleSleepStart.
UninterruptibleSleepFinish(activate bool)
// state that was begun by a previous call to UninterruptibleSleepStart.
UninterruptibleSleepFinish()
}
// NoTask is an implementation of Blocker that does not block.
@@ -180,10 +175,10 @@ func (nt *NoTask) BlockWithTimeoutOn(w waiter.Waitable, mask waiter.EventMask, t
}
// UninterruptibleSleepStart implmenents Blocker.UninterruptedSleepStart.
func (*NoTask) UninterruptibleSleepStart(bool) {}
func (*NoTask) UninterruptibleSleepStart() {}
// UninterruptibleSleepFinish implmenents Blocker.UninterruptibleSleepFinish.
func (*NoTask) UninterruptibleSleepFinish(bool) {}
func (*NoTask) UninterruptibleSleepFinish() {}
// Context represents a thread of execution (hereafter "goroutine" to reflect
// Go idiosyncrasy). It carries state associated with the goroutine across API
+2 -2
View File
@@ -38,8 +38,8 @@ type GoferClient struct {
// NewGoferClient establishes the LISAFS connection to the dev gofer server.
// It takes ownership of fd. contName is the owning container name.
func NewGoferClient(ctx context.Context, contName string, fd int) (*GoferClient, error) {
ctx.UninterruptibleSleepStart(false)
defer ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepStart()
defer ctx.UninterruptibleSleepFinish()
sock, err := unet.NewSocket(fd)
if err != nil {
+12
View File
@@ -101,6 +101,18 @@ func (v Addr) ToRange(length uint64) (AddrRange, bool) {
return AddrRange{v, end}, ok
}
// MustToRange is equivalent to ToRange, but panics if the end of the range
// wraps around.
//
//go:nosplit
func (v Addr) MustToRange(length uint64) AddrRange {
ar, ok := v.ToRange(length)
if !ok {
panic("hostarch.Addr.ToRange() wraps")
}
return ar
}
// IsPageAligned returns true if ar.Start.IsPageAligned() and
// ar.End.IsPageAligned().
func (ar AddrRange) IsPageAligned() bool {
+4 -4
View File
@@ -290,9 +290,9 @@ func (c *Client) CloseFD(ctx context.Context, fd FDID, flush bool) {
req := CloseReq{FDs: toClose}
var resp CloseResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := c.SndRcvMessage(Close, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
if err != nil {
log.Warningf("lisafs: batch closing FDs returned error: %v", err)
}
@@ -305,9 +305,9 @@ func (c *Client) SyncFDs(ctx context.Context, fds []FDID) error {
}
req := FsyncReq{FDs: fds}
var resp FsyncResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := c.SndRcvMessage(FSync, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
+60 -60
View File
@@ -72,9 +72,9 @@ func (f *ClientFD) OpenAt(ctx context.Context, flags uint32) (FDID, int, error)
}
var respFD [1]int
var resp OpenAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(OpenAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, respFD[:], req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.OpenFD, respFD[0], err
}
@@ -90,18 +90,18 @@ func (f *ClientFD) OpenCreateAt(ctx context.Context, name string, flags uint32,
var respFD [1]int
var resp OpenCreateAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(OpenCreateAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, respFD[:], req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Child, resp.NewFD, respFD[0], err
}
// StatTo makes the Fstat RPC and populates stat with the result.
func (f *ClientFD) StatTo(ctx context.Context, stat *linux.Statx) error {
req := StatReq{FD: f.fd}
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FStat, uint32(req.SizeBytes()), req.MarshalUnsafe, stat.CheckedUnmarshal, nil, req.String, stat.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -109,9 +109,9 @@ func (f *ClientFD) StatTo(ctx context.Context, stat *linux.Statx) error {
func (f *ClientFD) Sync(ctx context.Context) error {
req := FsyncReq{FDs: []FDID{f.fd}}
var resp FsyncResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FSync, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -172,9 +172,9 @@ func (f *ClientFD) Read(ctx context.Context, dst []byte, offset uint64) (uint64,
// allocate a temporary buffer during unmarshalling.
// PReadResp.CheckedUnmarshal expects this to be set.
resp.Buf = buf
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(PRead, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
if err != nil {
return 0, err
}
@@ -208,9 +208,9 @@ func (f *ClientFD) Write(ctx context.Context, src []byte, offset uint64) (uint64
}
var resp PWriteResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(PWrite, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Count, err
})
}
@@ -225,9 +225,9 @@ func (f *ClientFD) MkdirAt(ctx context.Context, name string, mode linux.FileMode
req.GID = gid
var resp MkdirAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(MkdirAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.ChildDir, err
}
@@ -242,9 +242,9 @@ func (f *ClientFD) SymlinkAt(ctx context.Context, name, target string, uid UID,
}
var resp SymlinkAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(SymlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Symlink, err
}
@@ -257,9 +257,9 @@ func (f *ClientFD) LinkAt(ctx context.Context, targetFD FDID, name string) (Inod
}
var resp LinkAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(LinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Link, err
}
@@ -275,9 +275,9 @@ func (f *ClientFD) MknodAt(ctx context.Context, name string, mode linux.FileMode
req.Major = primitive.Uint32(major)
var resp MknodAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(MknodAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Child, err
}
@@ -301,9 +301,9 @@ func (f *ClientFD) SetStat(ctx context.Context, stat *linux.Statx) (uint32, erro
}
var resp SetStatResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(SetStat, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
if err != nil {
return 0, nil, err
}
@@ -321,9 +321,9 @@ func (f *ClientFD) WalkMultiple(ctx context.Context, names []string) (WalkStatus
}
var resp WalkResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Status, resp.Inodes, err
}
@@ -336,9 +336,9 @@ func (f *ClientFD) Walk(ctx context.Context, name string) (Inode, error) {
var inode [1]Inode
resp := WalkResp{Inodes: inode[:]}
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
if err != nil {
return Inode{}, err
}
@@ -372,18 +372,18 @@ func (f *ClientFD) WalkStat(ctx context.Context, names []string) ([]linux.Statx,
}
var resp WalkStatResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(WalkStat, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Stats, err
}
// StatFSTo makes the FStatFS RPC and populates statFS with the result.
func (f *ClientFD) StatFSTo(ctx context.Context, statFS *StatFS) error {
req := FStatFSReq{FD: f.fd}
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FStatFS, uint32(req.SizeBytes()), req.MarshalUnsafe, statFS.CheckedUnmarshal, nil, req.String, statFS.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -396,9 +396,9 @@ func (f *ClientFD) Allocate(ctx context.Context, mode, offset, length uint64) er
Length: length,
}
var resp FAllocateResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FAllocate, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -406,9 +406,9 @@ func (f *ClientFD) Allocate(ctx context.Context, mode, offset, length uint64) er
func (f *ClientFD) ReadLinkAt(ctx context.Context) (string, error) {
req := ReadLinkAtReq{FD: f.fd}
var resp ReadLinkAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(ReadLinkAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return string(resp.Target), err
}
@@ -420,9 +420,9 @@ func (f *ClientFD) Flush(ctx context.Context) error {
}
req := FlushReq{FD: f.fd}
var resp FlushResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(Flush, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -439,9 +439,9 @@ func (f *ClientFD) BindAt(ctx context.Context, sockType linux.SockType, name str
req.Mode = mode
req.UID = uid
req.GID = gid
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(BindAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, hostSocketFD[:], req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
if err == nil && hostSocketFD[0] < 0 {
// No host socket fd? We can't proceed.
// Clean up any resources the gofer sent to us.
@@ -484,13 +484,13 @@ func (f *ClientFD) Connect(ctx context.Context, sockType linux.SockType, euid UI
UID: euid,
GID: egid,
}
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err = f.client.SndRcvMessage(ConnectWithCreds, uint32(reqWithCreds.SizeBytes()), reqWithCreds.MarshalUnsafe, resp.CheckedUnmarshal, sockFD[:], reqWithCreds.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
} else {
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err = f.client.SndRcvMessage(Connect, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, sockFD[:], req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
}
if err == nil && sockFD[0] < 0 {
@@ -507,9 +507,9 @@ func (f *ClientFD) UnlinkAt(ctx context.Context, name string, flags uint32) erro
Flags: primitive.Uint32(flags),
}
var resp UnlinkAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(UnlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -523,9 +523,9 @@ func (f *ClientFD) RenameAt(ctx context.Context, oldName string, newDirFD FDID,
NewName: SizedString(newName),
}
var resp RenameAtResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(RenameAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -537,9 +537,9 @@ func (f *ClientFD) Getdents64(ctx context.Context, count int32) ([]Dirent64, err
}
var resp Getdents64Resp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(Getdents64, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Dirents, err
}
@@ -551,9 +551,9 @@ func (f *ClientFD) ListXattr(ctx context.Context, size uint64) ([]string, error)
}
var resp FListXattrResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FListXattr, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return resp.Xattrs, err
}
@@ -566,9 +566,9 @@ func (f *ClientFD) GetXattr(ctx context.Context, name string, size uint64) (stri
}
var resp FGetXattrResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FGetXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return string(resp.Value), err
}
@@ -581,9 +581,9 @@ func (f *ClientFD) SetXattr(ctx context.Context, name string, value string, flag
Flags: primitive.Uint32(flags),
}
var resp FSetXattrResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FSetXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -594,9 +594,9 @@ func (f *ClientFD) RemoveXattr(ctx context.Context, name string) error {
Name: SizedString(name),
}
var resp FRemoveXattrResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(FRemoveXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -635,9 +635,9 @@ func (f *ClientBoundSocketFD) Listen(ctx context.Context, backlog int32) error {
Backlog: backlog,
}
var resp ListenResp
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(Listen, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
@@ -648,9 +648,9 @@ func (f *ClientBoundSocketFD) Accept(ctx context.Context) (int, error) {
}
var resp AcceptResp
var hostSocketFD [1]int
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := f.client.SndRcvMessage(Accept, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, hostSocketFD[:], req.String, resp.String)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
if err == nil && hostSocketFD[0] < 0 {
err = unix.EBADF
}
+15
View File
@@ -1508,6 +1508,21 @@ func EmitMetricUpdate() {
}
}
// EmitMetricUpdateWithTimeout is equivalent to EmitMetricUpdate, but only
// waits for timeout before returning. Metrics may be emitted after
// EmitMetricUpdateWithTimeout returns.
func EmitMetricUpdateWithTimeout(timeout time.Duration) {
ch := make(chan struct{})
go func() {
EmitMetricUpdate()
close(ch)
}()
select {
case <-ch:
case <-time.After(timeout):
}
}
// SnapshotOptions controls how snapshots are exported in GetSnapshot.
type SnapshotOptions struct {
// Filter, if set, should return true for metrics that should be written to
+2 -2
View File
@@ -636,8 +636,8 @@ func (fs *filesystem) initClientAndGetRoot(ctx context.Context) (lisafs.Inode, i
return lisafs.Inode{}, -1, err
}
ctx.UninterruptibleSleepStart(false)
defer ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepStart()
defer ctx.UninterruptibleSleepFinish()
var (
rootInode lisafs.Inode
+6 -6
View File
@@ -52,9 +52,9 @@ func (h *handle) readToBlocksAt(ctx context.Context, dsts safemem.BlockSeq, offs
return 0, nil
}
if h.fd >= 0 {
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
n, err := hostfd.Preadv2(h.fd, dsts, int64(offset), 0 /* flags */)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return n, err
}
rw := getHandleReadWriter(ctx, h, int64(offset))
@@ -67,9 +67,9 @@ func (h *handle) writeFromBlocksAt(ctx context.Context, srcs safemem.BlockSeq, o
return 0, nil
}
if h.fd >= 0 {
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
n, err := hostfd.Pwritev2(h.fd, srcs, int64(offset), 0 /* flags */)
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return n, err
}
rw := getHandleReadWriter(ctx, h, int64(offset))
@@ -91,9 +91,9 @@ func (h *handle) sync(ctx context.Context) error {
// If we have a host FD, fsyncing it is likely to be faster than an fsync
// RPC.
if h.fd >= 0 {
ctx.UninterruptibleSleepStart(false)
ctx.UninterruptibleSleepStart()
err := unix.Fsync(int(h.fd))
ctx.UninterruptibleSleepFinish(false)
ctx.UninterruptibleSleepFinish()
return err
}
if h.fdLisa.Ok() {
+1 -5
View File
@@ -498,11 +498,7 @@ func (fd *FileDescription) handleReadv(t *kernel.Task, sqe *linux.IOUringSqe, fl
return 0, linuxerr.EINVAL
}
// AddressSpaceActive is set to true as we are doing this from the task goroutine.And this is a
// case as we currently don't support neither IOPOLL nor SQPOLL modes.
dst, err := t.IovecsIOSequence(hostarch.Addr(sqe.AddrOrSpliceOff), int(sqe.Len), usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.IovecsIOSequence(hostarch.Addr(sqe.AddrOrSpliceOff), int(sqe.Len), usermem.IOOpts{})
if err != nil {
return 0, err
}
+4 -1
View File
@@ -136,7 +136,10 @@ func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup, mntns
if err != nil {
return nil, err
}
m := mm.NewMemoryManager(k, k.MemoryFile(), k.SleepForAddressSpaceActivation)
m, err := mm.NewMemoryManager(k, k.MemoryFile())
if err != nil {
return nil, err
}
m.SetExecutable(ctx, exe)
creds := auth.CredentialsFromContext(ctx)
+2
View File
@@ -1512,6 +1512,8 @@ func (k *Kernel) IsPaused() bool {
}
// ReceiveTaskStates receives full states for all tasks.
//
// Precondition: The kernel must be paused.
func (k *Kernel) ReceiveTaskStates() {
k.extMu.Lock()
k.tasks.PullFullState()
-3
View File
@@ -147,9 +147,6 @@ func (p *Pipe) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arc
iocc := usermem.IOCopyContext{
IO: io,
Ctx: ctx,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}
_, err := primitive.CopyInt32Out(&iocc, args[2].Pointer(), int32(v))
return 0, err
+2 -12
View File
@@ -1092,13 +1092,9 @@ func (t *Task) Ptrace(req int64, pid ThreadID, addr, data hostarch.Addr) error {
// may not yet have reached Task.doStop; wait for it to do so. This is safe
// because there's no way for target to initiate a ptrace-stop and then
// block (by calling Task.block) before entering it.
//
// Caveat: If tasks were just restored, the tracee's first call to
// Task.Activate (in Task.run) occurs before its first call to Task.doStop,
// which may block if the tracer's address space is active.
t.UninterruptibleSleepStart(true)
t.UninterruptibleSleepStart()
target.waitGoroutineStoppedOrExited()
t.UninterruptibleSleepFinish(true)
t.UninterruptibleSleepFinish()
// Resuming commands end the ptrace stop, but only if successful.
// PTRACE_LISTEN ends the ptrace stop if trapNotifyPending is already set on the
@@ -1206,9 +1202,6 @@ func (t *Task) Ptrace(req int64, pid ThreadID, addr, data hostarch.Addr) error {
Ctx: t,
IO: t.MemoryManager(),
Addr: ar.Start,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}, int(ar.Length()), target.Kernel().FeatureSet())
if err != nil {
return err
@@ -1233,9 +1226,6 @@ func (t *Task) Ptrace(req int64, pid ThreadID, addr, data hostarch.Addr) error {
Ctx: t,
IO: t.MemoryManager(),
Addr: ar.Start,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}, int(ar.Length()), target.Kernel().FeatureSet())
if err != nil {
return err
-12
View File
@@ -46,9 +46,6 @@ func (t *Task) ptraceArch(target *Task, req int64, addr, data hostarch.Addr) err
Ctx: t,
IO: t.MemoryManager(),
Addr: data,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
})
return err
@@ -58,9 +55,6 @@ func (t *Task) ptraceArch(target *Task, req int64, addr, data hostarch.Addr) err
Ctx: t,
IO: t.MemoryManager(),
Addr: data,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}, len(*s))
return err
@@ -69,9 +63,6 @@ func (t *Task) ptraceArch(target *Task, req int64, addr, data hostarch.Addr) err
Ctx: t,
IO: t.MemoryManager(),
Addr: data,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
})
if err == nil {
target.p.FullStateChanged()
@@ -84,9 +75,6 @@ func (t *Task) ptraceArch(target *Task, req int64, addr, data hostarch.Addr) err
Ctx: t,
IO: t.MemoryManager(),
Addr: data,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}, len(*s))
if err == nil {
target.p.FullStateChanged()
+1 -3
View File
@@ -367,9 +367,7 @@ func (t *Task) rseqAddrInterrupt() {
// NOTE(b/143949567): We don't support any rseq flags, so we always
// restart if we are in the critical section, and thus *always* clear
// critAddrAddr.
if _, err := t.MemoryManager().ZeroOut(t, critAddrAddr, int64(t.Arch().Width()), usermem.IOOpts{
AddressSpaceActive: true,
}); err != nil {
if _, err := t.MemoryManager().ZeroOut(t, critAddrAddr, int64(t.Arch().Width()), usermem.IOOpts{}); err != nil {
t.Debugf("Failed to clear critical section address from %#x for rseq: %v", critAddrAddr, err)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
+5 -12
View File
@@ -211,18 +211,16 @@ func (t *Task) block(C <-chan struct{}, timerChan <-chan struct{}) error {
}
}
// prepareSleep prepares to sleep.
// prepareSleep is called immediately before the task goroutine blocks.
func (t *Task) prepareSleep() {
t.assertTaskGoroutine()
t.p.PrepareSleep()
t.Deactivate()
t.accountTaskGoroutineEnter(TaskGoroutineBlockedInterruptible)
}
// completeSleep reactivates the address space.
// completeSleep is called immediately after the task goroutine unblocks.
func (t *Task) completeSleep() {
t.accountTaskGoroutineLeave(TaskGoroutineBlockedInterruptible)
t.Activate()
}
// Interrupted implements context.Context.Interrupted.
@@ -237,20 +235,15 @@ func (t *Task) Interrupted() bool {
}
// UninterruptibleSleepStart implements context.Context.UninterruptibleSleepStart.
func (t *Task) UninterruptibleSleepStart(deactivate bool) {
func (t *Task) UninterruptibleSleepStart() {
t.assertTaskGoroutine()
if deactivate {
t.Deactivate()
}
t.p.PrepareUninterruptibleSleep()
t.accountTaskGoroutineEnter(TaskGoroutineBlockedUninterruptible)
}
// UninterruptibleSleepFinish implements context.Context.UninterruptibleSleepFinish.
func (t *Task) UninterruptibleSleepFinish(activate bool) {
func (t *Task) UninterruptibleSleepFinish() {
t.accountTaskGoroutineLeave(TaskGoroutineBlockedUninterruptible)
if activate {
t.Activate()
}
}
// interrupted returns true if interrupt or interruptSelf has been called at
+1 -1
View File
@@ -344,7 +344,7 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
}
if args.Flags&linux.CLONE_CHILD_SETTID != 0 {
ctid := nt.ThreadID()
ctid.CopyOut(nt.CopyContext(t, usermem.IOOpts{AddressSpaceActive: false}), hostarch.Addr(args.ChildTID))
ctid.CopyOut(nt.CopyContext(t, usermem.IOOpts{}), hostarch.Addr(args.ChildTID))
}
ntid := t.tg.pidns.IDOfTask(nt)
if args.Flags&linux.CLONE_PARENT_SETTID != 0 {
+1 -5
View File
@@ -380,7 +380,7 @@ func (r *runExecveAfterSiblingExitStop) execute(t *Task) taskRunState {
}
// Switch to the new process.
t.MemoryManager().Deactivate()
t.p.PrepareExecve()
// Update credentials to reflect the execve. This should precede switching
// MMs to ensure that dumpability has been reset first, if needed.
t.creds.Store(r.newCreds)
@@ -396,10 +396,6 @@ func (r *runExecveAfterSiblingExitStop) execute(t *Task) taskRunState {
t.unstopVforkParent()
t.p.FullStateChanged()
// NOTE(b/30316266): All locks must be dropped prior to calling Activate.
if err := t.MemoryManager().Activate(t); err != nil {
panic("unable to activate mm: " + err.Error())
}
t.ptraceExec(oldTID)
return (*runSyscallExit)(nil)
+2 -3
View File
@@ -278,9 +278,7 @@ func (*runExitMain) execute(t *Task) taskRunState {
// Handle the robust futex list.
t.exitRobustList()
// Deactivate the address space and update max RSS before releasing the
// task's MM.
t.Deactivate()
// Update max RSS before releasing the task's MM.
t.tg.pidns.owner.mu.Lock()
t.updateRSSLocked()
t.tg.pidns.owner.mu.Unlock()
@@ -288,6 +286,7 @@ func (*runExitMain) execute(t *Task) taskRunState {
// Release the task image resources. Accessing these fields must be
// done with t.mu held, but the mm.DecUsers() call must be done outside
// of that lock.
t.p.PrepareExit()
t.mu.Lock()
mm := t.image.MemoryManager
t.image.MemoryManager = nil
+3 -9
View File
@@ -32,23 +32,17 @@ func (t *Task) Futex() *futex.Manager {
// SwapUint32 implements futex.Target.SwapUint32.
func (t *Task) SwapUint32(addr hostarch.Addr, new uint32) (uint32, error) {
return t.MemoryManager().SwapUint32(t, addr, new, usermem.IOOpts{
AddressSpaceActive: true,
})
return t.MemoryManager().SwapUint32(t, addr, new, usermem.IOOpts{})
}
// CompareAndSwapUint32 implements futex.Target.CompareAndSwapUint32.
func (t *Task) CompareAndSwapUint32(addr hostarch.Addr, old, new uint32) (uint32, error) {
return t.MemoryManager().CompareAndSwapUint32(t, addr, old, new, usermem.IOOpts{
AddressSpaceActive: true,
})
return t.MemoryManager().CompareAndSwapUint32(t, addr, old, new, usermem.IOOpts{})
}
// LoadUint32 implements futex.Target.LoadUint32.
func (t *Task) LoadUint32(addr hostarch.Addr) (uint32, error) {
return t.MemoryManager().LoadUint32(t, addr, usermem.IOOpts{
AddressSpaceActive: true,
})
return t.MemoryManager().LoadUint32(t, addr, usermem.IOOpts{})
}
// GetSharedKey implements futex.Target.GetSharedKey.
+4 -1
View File
@@ -141,7 +141,10 @@ func (t *Task) Stack() *arch.Stack {
// args.MemoryManager does not need to be set by the caller.
func (k *Kernel) LoadTaskImage(ctx context.Context, args loader.LoadArgs) (*TaskImage, *auth.Credentials, bool, *syserr.Error) {
// Prepare a new user address space to load into.
m := mm.NewMemoryManager(k, k.mf, k.SleepForAddressSpaceActivation)
m, merr := mm.NewMemoryManager(k, k.mf)
if merr != nil {
return nil, nil, false, syserr.FromError(merr)
}
defer m.DecUsers(ctx)
args.MemoryManager = m
+1 -11
View File
@@ -68,13 +68,6 @@ func (t *Task) run(threadID uintptr) {
t.blockingTimer = ktime.NewSampledTimer(t.k.MonotonicClock(), t.blockingTimerListener)
defer t.blockingTimer.Destroy()
// Activate our address space.
t.Activate()
// The corresponding t.Deactivate occurs in the exit path
// (runExitMain.execute) so that when
// Platform.CooperativelySharesAddressSpace() == true, we give up the
// AddressSpace before the task goroutine finishes executing.
// If this is a newly-started task, it should check for participation in
// group stops. If this is a task resuming after restore, it was
// interrupted by saving. In either case, the task is initially
@@ -124,10 +117,7 @@ func (t *Task) doStop() {
if t.stopCount.Load() == 0 {
return
}
t.Deactivate()
// NOTE(b/30316266): t.Activate() must be called without any locks held, so
// this defer must precede the defer for unlocking the signal mutex.
defer t.Activate()
t.p.PrepareStop()
t.accountTaskGoroutineEnter(TaskGoroutineStopped)
defer t.accountTaskGoroutineLeave(TaskGoroutineStopped)
t.tg.signalHandlers.mu.Lock()
+2 -2
View File
@@ -206,6 +206,8 @@ func (ts *TaskSet) BeginExternalStop() {
}
// PullFullState receives full states for all tasks.
//
// Precondition: The kernel must be paused.
func (ts *TaskSet) PullFullState() {
ts.mu.Lock()
defer ts.mu.Unlock()
@@ -213,11 +215,9 @@ func (ts *TaskSet) PullFullState() {
return
}
for t := range ts.Root.tids {
t.Activate()
if mm := t.MemoryManager(); mm != nil {
t.p.PullFullState(t.MemoryManager().AddressSpace(), t.Arch())
}
t.Deactivate()
}
}
+9 -35
View File
@@ -28,51 +28,27 @@ import (
const iovecLength = 16
// Activate ensures that the task has an active address space.
func (t *Task) Activate() {
if mm := t.MemoryManager(); mm != nil {
if err := mm.Activate(t); err != nil {
panic("unable to activate mm: " + err.Error())
}
}
}
// Deactivate relinquishes the task's active address space.
func (t *Task) Deactivate() {
if mm := t.MemoryManager(); mm != nil {
mm.Deactivate()
}
}
// CopyInBytes is a fast version of CopyIn if the caller can serialize the
// data without reflection and pass in a byte slice.
// CopyInBytes is a legacy wrapper for t.MemoryManager().CopyIn.
//
// This Task's AddressSpace must be active.
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) CopyInBytes(addr hostarch.Addr, dst []byte) (int, error) {
return t.MemoryManager().CopyIn(t, addr, dst, usermem.IOOpts{
AddressSpaceActive: true,
})
return t.MemoryManager().CopyIn(t, addr, dst, usermem.IOOpts{})
}
// CopyOutBytes is a fast version of CopyOut if the caller can serialize the
// data without reflection and pass in a byte slice.
// CopyOutBytes is a legacy wrapper for t.MemoryManager().CopyOut.
//
// This Task's AddressSpace must be active.
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) CopyOutBytes(addr hostarch.Addr, src []byte) (int, error) {
return t.MemoryManager().CopyOut(t, addr, src, usermem.IOOpts{
AddressSpaceActive: true,
})
return t.MemoryManager().CopyOut(t, addr, src, usermem.IOOpts{})
}
// CopyInString copies a NUL-terminated string of length at most maxlen in from
// the task's memory. The copy will fail with syscall.EFAULT if it traverses
// user memory that is unmapped or not readable by the user.
//
// This Task's AddressSpace must be active.
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) CopyInString(addr hostarch.Addr, maxlen int) (string, error) {
return usermem.CopyStringIn(t, t.MemoryManager(), addr, maxlen, usermem.IOOpts{
AddressSpaceActive: true,
})
return usermem.CopyStringIn(t, t.MemoryManager(), addr, maxlen, usermem.IOOpts{})
}
// CopyInVector copies a NULL-terminated vector of strings from the task's
@@ -88,7 +64,7 @@ func (t *Task) CopyInString(addr hostarch.Addr, maxlen int) (string, error) {
// { "a", "b", "c" } => 6 (3 for lengths, 3 for elements)
// { "abc" } => 4 (3 for length, 1 for elements)
//
// This Task's AddressSpace must be active.
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) CopyInVector(addr hostarch.Addr, maxElemSize, maxTotalSize int) ([]string, error) {
var v []string
for {
@@ -124,7 +100,6 @@ func (t *Task) CopyInVector(addr hostarch.Addr, maxElemSize, maxTotalSize int) (
//
// Preconditions: Same as usermem.IO.CopyOut, plus:
// - The caller must be running on the task goroutine.
// - t's AddressSpace must be active.
func (t *Task) CopyOutIovecs(addr hostarch.Addr, src hostarch.AddrRangeSeq) error {
switch t.Arch().Width() {
case 8:
@@ -154,7 +129,6 @@ func (t *Task) CopyOutIovecs(addr hostarch.Addr, src hostarch.AddrRangeSeq) erro
//
// Preconditions: Same as usermem.IO.CopyIn, plus:
// * The caller must be running on the task goroutine.
// * t's AddressSpace must be active.
func (t *Task) CopyInIovecs(addr hostarch.Addr, numIovecs int) (hostarch.AddrRangeSeq, error) {
// Special case to avoid allocating allocating a single hostaddr.AddrRange.
if numIovecs == 1 {
+2 -145
View File
@@ -27,149 +27,15 @@ import (
// AddressSpace returns the platform.AddressSpace bound to mm.
//
// Preconditions: The caller must have called mm.Activate().
// Preconditions: mm.users != 0.
func (mm *MemoryManager) AddressSpace() platform.AddressSpace {
if mm.active.Load() == 0 {
panic("trying to use inactive address space?")
}
return mm.as
}
// Activate ensures this MemoryManager has a platform.AddressSpace.
//
// The caller must not hold any locks when calling Activate.
//
// When this MemoryManager is no longer needed by a task, it should call
// Deactivate to release the reference.
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 {
active := mm.active.Load()
if active == 0 {
// Fall back to the slow path.
break
}
if mm.active.CompareAndSwap(active, active+1) {
return nil
}
}
for {
// Slow path: may need to synchronize with other goroutines changing
// mm.active to or from zero.
mm.activeMu.Lock()
// Inline Unlock instead of using a defer for performance since this
// method is commonly in the hot-path.
// Check if we raced with another goroutine performing activation.
if mm.active.Load() > 0 {
// This can't race; Deactivate can't decrease mm.active from 1 to 0
// without holding activeMu.
mm.active.Add(1)
mm.activeMu.Unlock()
return nil
}
// Do we have a context? If so, then we never unmapped it. This can
// only be the case if !mm.p.CooperativelySchedulesAddressSpace().
if mm.as != nil {
mm.active.Store(1)
mm.activeMu.Unlock()
return nil
}
// Get a new address space. We must force unmapping by passing nil to
// NewAddressSpace if requested. (As in the nil interface object, not a
// typed nil.)
mappingsID := (any)(mm)
if mm.unmapAllOnActivate {
mappingsID = nil
}
as, c, err := mm.p.NewAddressSpace(mappingsID)
if err != nil {
mm.activeMu.Unlock()
return err
}
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.
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
}
// Okay, we could restore all mappings at this point.
// But forget that. Let's just let them fault in.
mm.as = as
// Unmapping is done, if necessary.
mm.unmapAllOnActivate = false
// Now that m.as has been assigned, we can set m.active to a non-zero value
// to enable the fast path.
mm.active.Store(1)
mm.activeMu.Unlock()
return nil
}
}
// Deactivate releases a reference to the MemoryManager.
func (mm *MemoryManager) Deactivate() {
// Fast path: this is not the last goroutine to deactivate the
// MemoryManager.
for {
active := mm.active.Load()
if active == 1 {
// Fall back to the slow path.
break
}
if mm.active.CompareAndSwap(active, active-1) {
return
}
}
mm.activeMu.Lock()
// Same as Activate.
// Still active?
if mm.active.Add(-1) > 0 {
mm.activeMu.Unlock()
return
}
// Can we hold on to the address space?
if !mm.p.CooperativelySchedulesAddressSpace() {
mm.activeMu.Unlock()
return
}
// Release the address space.
mm.as.Release()
// Lost it.
mm.as = nil
mm.activeMu.Unlock()
}
// mapASLocked maps addresses in ar into mm.as.
//
// Preconditions:
// - mm.activeMu must be locked.
// - mm.as != nil.
// - ar.Length() != 0.
// - ar must be page-aligned.
// - pseg == mm.pmas.LowerBoundSegment(ar.Start).
@@ -261,21 +127,12 @@ func (mm *MemoryManager) unmapASLocked(ar hostarch.AddrRange) {
return
}
if mm.as == nil {
// No AddressSpace? Force all mappings to be unmapped on the next
// Activate.
mm.unmapAllOnActivate = true
// Being called from mm.DecUsers() after AddressSpace release.
return
}
// unmapASLocked doesn't require vmas or pmas to exist for ar, so it can be
// passed ranges that include addresses that can't be mapped by the
// application.
ar = ar.Intersect(mm.applicationAddrRange())
// Note that this AddressSpace may or may not be active. If the
// platform does not require cooperative sharing of AddressSpaces, they
// are retained between Deactivate/Activate calls. Despite not being
// active, it is still valid to perform operations on these address
// spaces.
mm.as.Unmap(ar.Start, uint64(ar.Length()))
}
+65 -29
View File
@@ -90,7 +90,7 @@ func (mm *MemoryManager) checkIOVec(ars hostarch.AddrRangeSeq) bool {
}
func (mm *MemoryManager) asioEnabled(opts usermem.IOOpts) bool {
return mm.haveASIO && !opts.IgnorePermissions && opts.AddressSpaceActive
return mm.haveASIO && !opts.IgnorePermissions
}
// translateIOError converts errors to EFAULT, as is usually reported for all
@@ -107,7 +107,7 @@ func translateIOError(ctx context.Context, err error) error {
// CopyOut implements usermem.IO.CopyOut.
func (mm *MemoryManager) CopyOut(ctx context.Context, addr hostarch.Addr, src []byte, opts usermem.IOOpts) (int, error) {
ar, ok := mm.CheckIORange(addr, int64(len(src)))
_, ok := mm.CheckIORange(addr, int64(len(src)))
if !ok {
return 0, linuxerr.EFAULT
}
@@ -118,7 +118,7 @@ func (mm *MemoryManager) CopyOut(ctx context.Context, addr hostarch.Addr, src []
// Do AddressSpace IO if applicable.
if mm.asioEnabled(opts) && len(src) < copyMapMinBytes {
return mm.asCopyOut(ctx, addr, src)
return mm.asCopyOut(ctx, addr, src, opts)
}
// Go through internal mappings.
@@ -127,14 +127,10 @@ func (mm *MemoryManager) CopyOut(ctx context.Context, addr hostarch.Addr, src []
// traverse an unnecessary layer of buffering. This can be fixed by
// inlining mm.withInternalMappings() and passing src subslices directly to
// memmap.File.BufferWriteAt().
n64, err := mm.withInternalMappings(ctx, ar, hostarch.Write, opts.IgnorePermissions, func(ims safemem.BlockSeq) (uint64, error) {
n, err := safemem.CopySeq(ims, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(src)))
return n, translateIOError(ctx, err)
})
return int(n64), err
return mm.imCopyOut(ctx, addr, src, opts)
}
func (mm *MemoryManager) asCopyOut(ctx context.Context, addr hostarch.Addr, src []byte) (int, error) {
func (mm *MemoryManager) asCopyOut(ctx context.Context, addr hostarch.Addr, src []byte, opts usermem.IOOpts) (int, error) {
var done int
for {
n, err := mm.as.CopyOut(addr+hostarch.Addr(done), src[done:])
@@ -149,13 +145,25 @@ func (mm *MemoryManager) asCopyOut(ctx context.Context, addr hostarch.Addr, src
}
continue
}
if _, ok := err.(platform.AddressSpaceIOUnavailable); ok {
// Fall back to using internal mappings.
return mm.imCopyOut(ctx, addr+hostarch.Addr(done), src[done:], opts)
}
return done, translateIOError(ctx, err)
}
}
func (mm *MemoryManager) imCopyOut(ctx context.Context, addr hostarch.Addr, src []byte, opts usermem.IOOpts) (int, error) {
n64, err := mm.withInternalMappings(ctx, addr.MustToRange(uint64(len(src))), hostarch.Write, opts.IgnorePermissions, func(ims safemem.BlockSeq) (uint64, error) {
n, err := safemem.CopySeq(ims, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(src)))
return n, translateIOError(ctx, err)
})
return int(n64), err
}
// CopyIn implements usermem.IO.CopyIn.
func (mm *MemoryManager) CopyIn(ctx context.Context, addr hostarch.Addr, dst []byte, opts usermem.IOOpts) (int, error) {
ar, ok := mm.CheckIORange(addr, int64(len(dst)))
_, ok := mm.CheckIORange(addr, int64(len(dst)))
if !ok {
return 0, linuxerr.EFAULT
}
@@ -166,7 +174,7 @@ func (mm *MemoryManager) CopyIn(ctx context.Context, addr hostarch.Addr, dst []b
// Do AddressSpace IO if applicable.
if mm.asioEnabled(opts) && len(dst) < copyMapMinBytes {
return mm.asCopyIn(ctx, addr, dst)
return mm.asCopyIn(ctx, addr, dst, opts)
}
// Go through internal mappings.
@@ -175,14 +183,10 @@ func (mm *MemoryManager) CopyIn(ctx context.Context, addr hostarch.Addr, dst []b
// traverse an unnecessary layer of buffering. This can be fixed by
// inlining mm.withInternalMappings() and passing dst subslices directly to
// memmap.File.BufferReadAt().
n64, err := mm.withInternalMappings(ctx, ar, hostarch.Read, opts.IgnorePermissions, func(ims safemem.BlockSeq) (uint64, error) {
n, err := safemem.CopySeq(safemem.BlockSeqOf(safemem.BlockFromSafeSlice(dst)), ims)
return n, translateIOError(ctx, err)
})
return int(n64), err
return mm.imCopyIn(ctx, addr, dst, opts)
}
func (mm *MemoryManager) asCopyIn(ctx context.Context, addr hostarch.Addr, dst []byte) (int, error) {
func (mm *MemoryManager) asCopyIn(ctx context.Context, addr hostarch.Addr, dst []byte, opts usermem.IOOpts) (int, error) {
var done int
for {
n, err := mm.as.CopyIn(addr+hostarch.Addr(done), dst[done:])
@@ -197,13 +201,25 @@ func (mm *MemoryManager) asCopyIn(ctx context.Context, addr hostarch.Addr, dst [
}
continue
}
if _, ok := err.(platform.AddressSpaceIOUnavailable); ok {
// Fall back to using internal mappings.
return mm.imCopyIn(ctx, addr+hostarch.Addr(done), dst[done:], opts)
}
return done, translateIOError(ctx, err)
}
}
func (mm *MemoryManager) imCopyIn(ctx context.Context, addr hostarch.Addr, dst []byte, opts usermem.IOOpts) (int, error) {
n64, err := mm.withInternalMappings(ctx, addr.MustToRange(uint64(len(dst))), hostarch.Read, opts.IgnorePermissions, func(ims safemem.BlockSeq) (uint64, error) {
n, err := safemem.CopySeq(safemem.BlockSeqOf(safemem.BlockFromSafeSlice(dst)), ims)
return n, translateIOError(ctx, err)
})
return int(n64), err
}
// ZeroOut implements usermem.IO.ZeroOut.
func (mm *MemoryManager) ZeroOut(ctx context.Context, addr hostarch.Addr, toZero int64, opts usermem.IOOpts) (int64, error) {
ar, ok := mm.CheckIORange(addr, toZero)
_, ok := mm.CheckIORange(addr, toZero)
if !ok {
return 0, linuxerr.EFAULT
}
@@ -214,17 +230,14 @@ func (mm *MemoryManager) ZeroOut(ctx context.Context, addr hostarch.Addr, toZero
// Do AddressSpace IO if applicable.
if mm.asioEnabled(opts) && toZero < copyMapMinBytes {
return mm.asZeroOut(ctx, addr, toZero)
return mm.asZeroOut(ctx, addr, toZero, opts)
}
// Go through internal mappings.
return mm.withInternalMappings(ctx, ar, hostarch.Write, opts.IgnorePermissions, func(dsts safemem.BlockSeq) (uint64, error) {
n, err := safemem.ZeroSeq(dsts)
return n, translateIOError(ctx, err)
})
return mm.imZeroOut(ctx, addr, toZero, opts)
}
func (mm *MemoryManager) asZeroOut(ctx context.Context, addr hostarch.Addr, toZero int64) (int64, error) {
func (mm *MemoryManager) asZeroOut(ctx context.Context, addr hostarch.Addr, toZero int64, opts usermem.IOOpts) (int64, error) {
var done int64
for {
n, err := mm.as.ZeroOut(addr+hostarch.Addr(done), uintptr(toZero-done))
@@ -239,10 +252,21 @@ func (mm *MemoryManager) asZeroOut(ctx context.Context, addr hostarch.Addr, toZe
}
continue
}
if _, ok := err.(platform.AddressSpaceIOUnavailable); ok {
// Fall back to using internal mappings.
return mm.imZeroOut(ctx, addr+hostarch.Addr(done), toZero-done, opts)
}
return done, translateIOError(ctx, err)
}
}
func (mm *MemoryManager) imZeroOut(ctx context.Context, addr hostarch.Addr, toZero int64, opts usermem.IOOpts) (int64, error) {
return mm.withInternalMappings(ctx, addr.MustToRange(uint64(toZero)), hostarch.Write, opts.IgnorePermissions, func(dsts safemem.BlockSeq) (uint64, error) {
n, err := safemem.ZeroSeq(dsts)
return n, translateIOError(ctx, err)
})
}
// CopyOutFrom implements usermem.IO.CopyOutFrom.
func (mm *MemoryManager) CopyOutFrom(ctx context.Context, ars hostarch.AddrRangeSeq, src safemem.Reader, opts usermem.IOOpts) (int64, error) {
if !mm.checkIOVec(ars) {
@@ -273,7 +297,7 @@ func (mm *MemoryManager) CopyOutFrom(ctx context.Context, ars hostarch.AddrRange
if cplen > int64(bufN)-done {
cplen = int64(bufN) - done
}
n, err := mm.asCopyOut(ctx, ar.Start, buf[int(done):int(done+cplen)])
n, err := mm.asCopyOut(ctx, ar.Start, buf[int(done):int(done+cplen)], opts)
done += int64(n)
if err != nil {
return done, err
@@ -306,7 +330,7 @@ func (mm *MemoryManager) CopyInTo(ctx context.Context, ars hostarch.AddrRangeSeq
for !ars.IsEmpty() {
ar := ars.Head()
var n int
n, bufErr = mm.asCopyIn(ctx, ar.Start, buf[done:done+int(ar.Length())])
n, bufErr = mm.asCopyIn(ctx, ar.Start, buf[done:done+int(ar.Length())], opts)
done += n
if bufErr != nil {
break
@@ -351,7 +375,7 @@ func (mm *MemoryManager) SwapUint32(ctx context.Context, addr hostarch.Addr, new
}
// Do AddressSpace IO if applicable.
if mm.haveASIO && opts.AddressSpaceActive && !opts.IgnorePermissions {
if mm.asioEnabled(opts) {
for {
old, err := mm.as.SwapUint32(addr, new)
if err == nil {
@@ -363,6 +387,10 @@ func (mm *MemoryManager) SwapUint32(ctx context.Context, addr hostarch.Addr, new
}
continue
}
if _, ok := err.(platform.AddressSpaceIOUnavailable); ok {
// Fall back to using internal mappings.
break
}
return 0, translateIOError(ctx, err)
}
}
@@ -394,7 +422,7 @@ func (mm *MemoryManager) CompareAndSwapUint32(ctx context.Context, addr hostarch
}
// Do AddressSpace IO if applicable.
if mm.haveASIO && opts.AddressSpaceActive && !opts.IgnorePermissions {
if mm.asioEnabled(opts) {
for {
prev, err := mm.as.CompareAndSwapUint32(addr, old, new)
if err == nil {
@@ -406,6 +434,10 @@ func (mm *MemoryManager) CompareAndSwapUint32(ctx context.Context, addr hostarch
}
continue
}
if _, ok := err.(platform.AddressSpaceIOUnavailable); ok {
// Fall back to using internal mappings.
break
}
return 0, translateIOError(ctx, err)
}
}
@@ -437,7 +469,7 @@ func (mm *MemoryManager) LoadUint32(ctx context.Context, addr hostarch.Addr, opt
}
// Do AddressSpace IO if applicable.
if mm.haveASIO && opts.AddressSpaceActive && !opts.IgnorePermissions {
if mm.asioEnabled(opts) {
for {
val, err := mm.as.LoadUint32(addr)
if err == nil {
@@ -449,6 +481,10 @@ func (mm *MemoryManager) LoadUint32(ctx context.Context, addr hostarch.Addr, opt
}
continue
}
if _, ok := err.(platform.AddressSpaceIOUnavailable); ok {
// Fall back to using internal mappings.
break
}
return 0, translateIOError(ctx, err)
}
}
+28 -26
View File
@@ -28,17 +28,21 @@ import (
)
// NewMemoryManager returns a new MemoryManager with no mappings and 1 user.
func NewMemoryManager(p platform.Platform, mf *pgalloc.MemoryFile, sleepForActivation bool) *MemoryManager {
return &MemoryManager{
p: p,
mf: mf,
haveASIO: p.SupportsAddressSpaceIO(),
users: atomicbitops.FromInt32(1),
auxv: arch.Auxv{},
dumpability: atomicbitops.FromInt32(int32(UserDumpable)),
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
sleepForActivation: sleepForActivation,
func NewMemoryManager(p platform.Platform, mf *pgalloc.MemoryFile) (*MemoryManager, error) {
as, err := p.NewAddressSpace()
if err != nil {
return nil, err
}
return &MemoryManager{
p: p,
mf: mf,
haveASIO: p.SupportsAddressSpaceIO(),
users: atomicbitops.FromInt32(1),
as: as,
auxv: arch.Auxv{},
dumpability: atomicbitops.FromInt32(int32(UserDumpable)),
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
}, nil
}
// SetMmapLayout initializes mm's layout from the given arch.Context64.
@@ -56,6 +60,11 @@ func (mm *MemoryManager) SetMmapLayout(ac *arch.Context64, r *limits.LimitSet) (
// Fork creates a copy of mm with 1 user, as for Linux syscalls fork() or
// clone() (without CLONE_VM).
func (mm *MemoryManager) Fork(ctx context.Context) (*MemoryManager, error) {
as, err := mm.p.NewAddressSpace()
if err != nil {
return nil, err
}
mm.AddressSpace().PreFork()
defer mm.AddressSpace().PostFork()
mm.metadataMu.Lock()
@@ -77,6 +86,7 @@ func (mm *MemoryManager) Fork(ctx context.Context) (*MemoryManager, error) {
haveASIO: mm.haveASIO,
layout: mm.layout,
users: atomicbitops.FromInt32(1),
as: as,
brk: mm.brk,
usageAS: mm.usageAS,
dataAS: mm.dataAS,
@@ -89,11 +99,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: atomicbitops.FromInt32(mm.dumpability.Load()),
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
sleepForActivation: mm.sleepForActivation,
vdsoSigReturnAddr: mm.vdsoSigReturnAddr,
executable: mm.executable,
dumpability: atomicbitops.FromInt32(mm.dumpability.Load()),
aioManager: aioManager{contexts: make(map[uint64]*AIOContext)},
vdsoSigReturnAddr: mm.vdsoSigReturnAddr,
}
// Copy vmas.
@@ -117,6 +126,7 @@ func (mm *MemoryManager) Fork(ctx context.Context) (*MemoryManager, error) {
if vma.mappable != nil {
if err := vma.mappable.AddMapping(ctx, mm2, vmaAR, vma.off, vma.canWriteMappableLocked()); err != nil {
_, droppedIDs = mm2.removeVMAsLocked(ctx, mm2.applicationAddrRange(), droppedIDs)
as.Release()
return nil, err
}
}
@@ -271,17 +281,9 @@ func (mm *MemoryManager) DecUsers(ctx context.Context) {
exe.DecRef(ctx)
}
mm.activeMu.Lock()
// Sanity check.
if mm.active.Load() != 0 {
panic("active address space lost?")
}
// Make sure the AddressSpace is returned.
if mm.as != nil {
mm.as.Release()
mm.as = nil
}
mm.activeMu.Unlock()
// User count 0 => no concurrent access to mm.as.
mm.as.Release()
mm.as = nil
var droppedIDs []memmap.MappingIdentity
mm.mappingMu.Lock()
+3 -27
View File
@@ -158,28 +158,9 @@ type MemoryManager struct {
// maxRSS is protected by activeMu.
maxRSS uint64
// as is the platform.AddressSpace that pmas are mapped into. active is the
// number of contexts that require as to be non-nil; if active == 0, as may
// be nil.
//
// as is protected by activeMu. active is manipulated with atomic memory
// operations; transitions to and from zero are additionally protected by
// activeMu. (This is because such transitions may need to be atomic with
// changes to as.)
as platform.AddressSpace `state:"nosave"`
active atomicbitops.Int32 `state:"zerovalue"`
// unmapAllOnActivate indicates that the next Activate call should activate
// an empty AddressSpace.
//
// This is used to ensure that an AddressSpace cached in
// NewAddressSpace is not used after some change in the MemoryManager
// or VMAs has made that AddressSpace stale.
//
// unmapAllOnActivate is protected by activeMu. It must only be set when
// there is no active or cached AddressSpace. If as != nil, then
// invalidations should be propagated immediately.
unmapAllOnActivate bool `state:"nosave"`
// as is the platform.AddressSpace that pmas are mapped into. as is immutable
// until users becomes 0, at which point as becomes nil.
as platform.AddressSpace `state:"nosave"`
// If captureInvalidations is true, calls to MM.Invalidate() are recorded
// in capturedInvalidations rather than being applied immediately to pmas.
@@ -228,11 +209,6 @@ type MemoryManager struct {
// 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
// vdsoSigReturnAddr is the address of 'vdso_sigreturn'.
vdsoSigReturnAddr uint64
+15 -12
View File
@@ -29,9 +29,12 @@ import (
"gvisor.dev/gvisor/pkg/usermem"
)
func testMemoryManagerWithMmapDirection(ctx context.Context, mmapDirection arch.MmapDirection) *MemoryManager {
func testMemoryManagerWithMmapDirection(ctx context.Context, t *testing.T, mmapDirection arch.MmapDirection) *MemoryManager {
p := platform.FromContext(ctx)
mm := NewMemoryManager(p, pgalloc.MemoryFileFromContext(ctx), false)
mm, err := NewMemoryManager(p, pgalloc.MemoryFileFromContext(ctx))
if err != nil {
t.Fatalf("failed to create MemoryManager: %s", err)
}
mm.layout = arch.MmapLayout{
MinAddr: p.MinUserAddress(),
MaxAddr: p.MaxUserAddress(),
@@ -42,8 +45,8 @@ func testMemoryManagerWithMmapDirection(ctx context.Context, mmapDirection arch.
return mm
}
func testMemoryManager(ctx context.Context) *MemoryManager {
return testMemoryManagerWithMmapDirection(ctx, arch.MmapBottomUp)
func testMemoryManager(ctx context.Context, t *testing.T) *MemoryManager {
return testMemoryManagerWithMmapDirection(ctx, t, arch.MmapBottomUp)
}
func (mm *MemoryManager) realUsageAS() uint64 {
@@ -52,7 +55,7 @@ func (mm *MemoryManager) realUsageAS() uint64 {
func TestUsageASUpdates(t *testing.T) {
ctx := contexttest.Context(t)
mm := testMemoryManager(ctx)
mm := testMemoryManager(ctx, t)
defer mm.DecUsers(ctx)
addr, err := mm.MMap(ctx, memmap.MMapOpts{
@@ -87,7 +90,7 @@ func (mm *MemoryManager) realDataAS() uint64 {
func TestDataASUpdates(t *testing.T) {
ctx := contexttest.Context(t)
mm := testMemoryManager(ctx)
mm := testMemoryManager(ctx, t)
defer mm.DecUsers(ctx)
addr, err := mm.MMap(ctx, memmap.MMapOpts{
@@ -133,7 +136,7 @@ func TestBrkDataLimitUpdates(t *testing.T) {
limitSet.Set(limits.Data, limits.Limit{}, true /* privileged */) // zero RLIMIT_DATA
ctx := contexttest.WithLimitSet(contexttest.Context(t), limitSet)
mm := testMemoryManager(ctx)
mm := testMemoryManager(ctx, t)
defer mm.DecUsers(ctx)
// Try to extend the brk by one page and expect doing so to fail.
@@ -146,7 +149,7 @@ func TestBrkDataLimitUpdates(t *testing.T) {
// TestIOAfterUnmap ensures that IO fails after unmap.
func TestIOAfterUnmap(t *testing.T) {
ctx := contexttest.Context(t)
mm := testMemoryManager(ctx)
mm := testMemoryManager(ctx, t)
defer mm.DecUsers(ctx)
addr, err := mm.MMap(ctx, memmap.MMapOpts{
@@ -186,7 +189,7 @@ func TestIOAfterUnmap(t *testing.T) {
// TestIOAfterMProtect tests IO interaction with mprotect permissions.
func TestIOAfterMProtect(t *testing.T) {
ctx := contexttest.Context(t)
mm := testMemoryManager(ctx)
mm := testMemoryManager(ctx, t)
defer mm.DecUsers(ctx)
addr, err := mm.MMap(ctx, memmap.MMapOpts{
@@ -239,7 +242,7 @@ func TestIOAfterMProtect(t *testing.T) {
// prepared after destruction.
func TestAIOPrepareAfterDestroy(t *testing.T) {
ctx := contexttest.Context(t)
mm := testMemoryManager(ctx)
mm := testMemoryManager(ctx, t)
defer mm.DecUsers(ctx)
id, err := mm.NewAIOContext(ctx, 1)
@@ -264,7 +267,7 @@ func TestAIOPrepareAfterDestroy(t *testing.T) {
// looked up after memory manager is destroyed.
func TestAIOLookupAfterDestroy(t *testing.T) {
ctx := contexttest.Context(t)
mm := testMemoryManager(ctx)
mm := testMemoryManager(ctx, t)
id, err := mm.NewAIOContext(ctx, 1)
if err != nil {
@@ -332,7 +335,7 @@ func TestGetAllocationDirection(t *testing.T) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
ctx := contexttest.Context(t)
mm := testMemoryManagerWithMmapDirection(ctx, test.mmapDirection)
mm := testMemoryManagerWithMmapDirection(ctx, t, test.mmapDirection)
actual := mm.getAllocationDirection(test.ar, test.vma)
if actual != test.expected {
t.Errorf("Unexpected allocation direction. Expected: %s, Actual: %s", test.expected, actual)
+7
View File
@@ -41,6 +41,13 @@ func (mm *MemoryManager) InvalidateUnsavable(ctx context.Context) error {
func (mm *MemoryManager) afterLoad(ctx goContext.Context) {
mm.mf = pgalloc.MemoryFileFromContext(ctx)
mm.haveASIO = mm.p.SupportsAddressSpaceIO()
if mm.users.Load() != 0 {
as, err := mm.p.NewAddressSpace()
if err != nil {
panic(fmt.Sprintf("failed to create AddressSpace after restore: %v", err))
}
mm.as = as
}
}
// afterLoad is invoked by stateify.
+1 -2
View File
@@ -50,8 +50,7 @@ func TryCPUIDEmulate(ctx context.Context, mm MemoryManager, ac *arch.Context64)
taskWrapper: taskWrapper{ctx},
}
if _, err := mm.CopyIn(&tasklessCtx, hostarch.Addr(s.Regs.Rip), inst, usermem.IOOpts{
IgnorePermissions: true,
AddressSpaceActive: true,
IgnorePermissions: true,
}); err != nil {
return false
}
+14 -1
View File
@@ -151,9 +151,22 @@ func (c *platformContext) PullFullState(as platform.AddressSpace, ac *arch.Conte
return nil
}
// PrepareSleep implements platform.Context.platform.Context.
// PrepareSleep implements platform.Context.PrepareSleep.
func (*platformContext) PrepareSleep() {}
// PrepareUninterruptibleSleep implements
// platform.Context.PrepareUninterruptibleSleep.
func (*platformContext) PrepareUninterruptibleSleep() {}
// PrepareStop implements platform.Context.PrepareStop.
func (*platformContext) PrepareStop() {}
// PrepareExecve implements platform.Context.PrepareExecve.
func (*platformContext) PrepareExecve() {}
// PrepareExit implements platform.Context.PrepareExit.
func (*platformContext) PrepareExit() {}
// LastCPUNumber implements platform.Context.LastCPUNumber.
func (c *platformContext) LastCPUNumber() int32 {
return c.lastUsedCPU.Load()
+2 -7
View File
@@ -136,11 +136,6 @@ func (*KVM) SupportsAddressSpaceIO() bool {
return false
}
// CooperativelySchedulesAddressSpace implements platform.Platform.CooperativelySchedulesAddressSpace.
func (*KVM) CooperativelySchedulesAddressSpace() bool {
return false
}
// MapUnit implements platform.Platform.MapUnit.
func (*KVM) MapUnit() uint64 {
// We greedily creates PTEs in MapFile, so extremely large mappings can
@@ -161,7 +156,7 @@ func (*KVM) MaxUserAddress() hostarch.Addr {
}
// NewAddressSpace returns a new pagetable root.
func (k *KVM) NewAddressSpace(any) (platform.AddressSpace, <-chan struct{}, error) {
func (k *KVM) NewAddressSpace() (platform.AddressSpace, error) {
// Allocate page tables and install system mappings.
pageTables := pagetables.NewWithUpper(newAllocator(), k.machine.upperSharedPageTables, ring0.KernelStartAddress)
@@ -170,7 +165,7 @@ func (k *KVM) NewAddressSpace(any) (platform.AddressSpace, <-chan struct{}, erro
machine: k.machine,
pageTables: pageTables,
dirtySet: k.machine.newDirtySet(),
}, nil, nil
}, nil
}
// ConcurrencyCount implements platform.Platform.ConcurrencyCount.
+1 -1
View File
@@ -133,7 +133,7 @@ func applicationTest(t testHarness, useHostMappings bool, targetFn uintptr, fn f
kvmTest(t, func(k *KVM) {
// Create new page tables.
as, _, err := k.NewAddressSpace(nil /* invalidator */)
as, err := k.NewAddressSpace()
if err != nil {
t.Fatalf("can't create new address space: %v", err)
}
+27 -24
View File
@@ -43,15 +43,6 @@ type Platform interface {
// unchanged over the lifetime of the Platform.
SupportsAddressSpaceIO() bool
// CooperativelySchedulesAddressSpace returns true if the Platform has a
// limited number of AddressSpaces, such that mm.MemoryManager.Deactivate
// should call AddressSpace.Release when there are no goroutines that
// require the mm.MemoryManager to have an active AddressSpace.
//
// The value returned by CooperativelySchedulesAddressSpace is guaranteed
// to remain unchanged over the lifetime of the Platform.
CooperativelySchedulesAddressSpace() bool
// DetectsCPUPreemption returns true if Contexts returned by the Platform
// can reliably return ErrContextCPUPreempted.
DetectsCPUPreemption() bool
@@ -77,21 +68,7 @@ type Platform interface {
MaxUserAddress() hostarch.Addr
// NewAddressSpace returns a new memory context for this platform.
//
// If mappingsID is not nil, the platform may assume that (1) all calls
// to NewAddressSpace with the same mappingsID represent the same
// (mutable) set of mappings, and (2) the set of mappings has not
// changed since the last time AddressSpace.Release was called on an
// AddressSpace returned by a call to NewAddressSpace with the same
// mappingsID.
//
// If a new AddressSpace cannot be created immediately, a nil
// AddressSpace is returned, along with channel that is closed when
// the caller should retry a call to NewAddressSpace.
//
// In general, this blocking behavior only occurs when
// CooperativelySchedulesAddressSpace (above) returns false.
NewAddressSpace(mappingsID any) (AddressSpace, <-chan struct{}, error)
NewAddressSpace() (AddressSpace, error)
// NewContext returns a new execution context.
NewContext(context.Context) Context
@@ -301,6 +278,21 @@ type Context interface {
// interruptible sleep state.
PrepareSleep()
// PrepareUninterruptibleSleep is called when the thread switches to the
// uninterruptible sleep state.
PrepareUninterruptibleSleep()
// PrepareStop is called when the thread enters a kernel.TaskStop.
PrepareStop()
// PrepareExecve is called when the thread invokes execve(), immediately
// before switching MMs.
PrepareExecve()
// PrepareExit is called when the thread exits, immediately before
// releasing its MM.
PrepareExit()
// LastCPUNumber returns the last CPU number that this context was running on.
// If the context never ran on a CPU, it may return any valid CPU number, as long as the first
// call to Switch will detect that the CPU number is incorrect and return ErrContextCPUPreempted.
@@ -487,6 +479,17 @@ func (f SegmentationFault) Error() string {
return fmt.Sprintf("segmentation fault at %#x", f.Addr)
}
// AddressSpaceIOUnavailable is an error returned by AddressSpaceIO methods
// when AddressSpaceIO is unavailable for this operation due to implementation
// limitations, such that the caller should fall back to I/O through other
// means (rather than returning an error).
type AddressSpaceIOUnavailable struct{}
// Error implements error.Error.
func (AddressSpaceIOUnavailable) Error() string {
return "platform.AddressSpaceIO currently unavailable"
}
// Requirements is used to specify platform specific requirements.
type Requirements struct {
// RequiresCapSysPtrace indicates that the sandbox has to be started with
+15 -8
View File
@@ -210,6 +210,19 @@ func (c *context) PullFullState(as platform.AddressSpace, ac *arch.Context64) er
// PrepareSleep implements platform.Context.platform.PrepareSleep.
func (*context) PrepareSleep() {}
// PrepareUninterruptibleSleep implements
// platform.Context.platform.PrepareUninterruptibleSleep.
func (*context) PrepareUninterruptibleSleep() {}
// PrepareStop implements platform.Context.PrepareStop.
func (*context) PrepareStop() {}
// PrepareExecve implements platform.Context.PrepareExecve.
func (*context) PrepareExecve() {}
// PrepareExit implements platform.Context.PrepareExit.
func (*context) PrepareExit() {}
// PTrace represents a collection of ptrace subprocesses.
type PTrace struct {
platform.MMapMinAddr
@@ -244,11 +257,6 @@ func (*PTrace) SupportsAddressSpaceIO() bool {
return false
}
// CooperativelySchedulesAddressSpace implements platform.Platform.CooperativelySchedulesAddressSpace.
func (*PTrace) CooperativelySchedulesAddressSpace() bool {
return false
}
// MapUnit implements platform.Platform.MapUnit.
func (*PTrace) MapUnit() uint64 {
// The host kernel manages page tables and arbitrary-sized mappings
@@ -263,9 +271,8 @@ func (*PTrace) MaxUserAddress() hostarch.Addr {
}
// NewAddressSpace returns a new subprocess.
func (p *PTrace) NewAddressSpace(any) (platform.AddressSpace, <-chan struct{}, error) {
as, err := newSubprocess(globalPool.master.createStub)
return as, nil, err
func (p *PTrace) NewAddressSpace() (platform.AddressSpace, error) {
return newSubprocess(globalPool.master.createStub)
}
// ConcurrencyCount implements platform.Platform.ConcurrencyCount.
+18 -9
View File
@@ -225,7 +225,7 @@ func (c *platformContext) Release() {
}
}
// PrepareSleep implements platform.Context.platform.PrepareSleep.
// PrepareSleep implements platform.Context.PrepareSleep.
func (c *platformContext) PrepareSleep() {
ctx := c.sharedContext
if ctx == nil {
@@ -237,6 +237,21 @@ func (c *platformContext) PrepareSleep() {
}
}
// PrepareUninterruptibleSleep implements
// platform.Context.PrepareUninterruptibleSleep.
func (*platformContext) PrepareUninterruptibleSleep() {}
// PrepareStop implements platform.Context.PrepareStop.
func (c *platformContext) PrepareStop() {
c.PrepareSleep()
}
// PrepareExecve implements platform.Context.PrepareExecve.
func (*platformContext) PrepareExecve() {}
// PrepareExit implements platform.Context.PrepareExit.
func (*platformContext) PrepareExit() {}
// Systrap represents a collection of seccomp subprocesses.
type Systrap struct {
platform.NoCPUPreemptionDetection
@@ -315,11 +330,6 @@ func (*Systrap) SupportsAddressSpaceIO() bool {
return false
}
// CooperativelySchedulesAddressSpace implements platform.Platform.CooperativelySchedulesAddressSpace.
func (*Systrap) CooperativelySchedulesAddressSpace() bool {
return false
}
// MapUnit implements platform.Platform.MapUnit.
func (*Systrap) MapUnit() uint64 {
// The host kernel manages page tables and arbitrary-sized mappings
@@ -334,9 +344,8 @@ func (*Systrap) MaxUserAddress() hostarch.Addr {
}
// NewAddressSpace returns a new subprocess.
func (p *Systrap) NewAddressSpace(any) (platform.AddressSpace, <-chan struct{}, error) {
as, err := newSubprocess(globalPool.source.createStub, p.memoryFile, true)
return as, nil, err
func (p *Systrap) NewAddressSpace() (platform.AddressSpace, error) {
return newSubprocess(globalPool.source.createStub, p.memoryFile, true)
}
// NewContext returns an interruptible platformContext.
@@ -111,7 +111,7 @@ func (s *State) newTrapLocked(ctx context.Context, mm memoryManager) (hostarch.A
// next unused trap.
s.nextTrap = 1
s.tableAddr = addr
} else if _, err := hdr.CopyIn(task.OwnCopyContext(usermem.IOOpts{AddressSpaceActive: false}), addr); err != nil {
} else if _, err := hdr.CopyIn(task.OwnCopyContext(usermem.IOOpts{}), addr); err != nil {
return 0, err
} else {
// Read an index of a next unused trap.
@@ -216,7 +216,7 @@ func (s *State) PatchSyscall(ctx context.Context, ac *arch.Context64, mm memoryM
patchAddr := ac.IP() - uintptr(len(jmpInst))
prevCode := make([]uint8, len(jmpInst))
if _, err := primitive.CopyUint8SliceIn(task.OwnCopyContext(usermem.IOOpts{AddressSpaceActive: false}), hostarch.Addr(patchAddr), prevCode); err != nil {
if _, err := primitive.CopyUint8SliceIn(task, hostarch.Addr(patchAddr), prevCode); err != nil {
return err
}
@@ -313,7 +313,7 @@ func (s *State) HandleFault(ctx context.Context, ac *arch.Context64, mm memoryMa
code := make([]uint8, len(jmpInst))
ip := ac.IP() - faultInstOffset
if _, err := primitive.CopyUint8SliceIn(task.OwnCopyContext(usermem.IOOpts{AddressSpaceActive: false}), hostarch.Addr(ip), code); err != nil {
if _, err := primitive.CopyUint8SliceIn(task, hostarch.Addr(ip), code); err != nil {
return err
}
+1 -12
View File
@@ -64,9 +64,7 @@ func ioctl(ctx context.Context, fd int, io usermem.IO, sysno uintptr, args arch.
}
var buf [4]byte
hostarch.ByteOrder.PutUint32(buf[:], uint32(val))
_, err := io.CopyOut(ctx, args[2].Pointer(), buf[:], usermem.IOOpts{
AddressSpaceActive: true,
})
_, err := io.CopyOut(ctx, args[2].Pointer(), buf[:], usermem.IOOpts{})
return 0, err
case linux.SIOCGIFFLAGS,
linux.SIOCGIFHWADDR,
@@ -78,9 +76,6 @@ func ioctl(ctx context.Context, fd int, io usermem.IO, sysno uintptr, args arch.
cc := &usermem.IOCopyContext{
Ctx: ctx,
IO: io,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}
var ifr linux.IFReq
if _, err := ifr.CopyIn(cc, args[2].Pointer()); err != nil {
@@ -95,9 +90,6 @@ func ioctl(ctx context.Context, fd int, io usermem.IO, sysno uintptr, args arch.
cc := &usermem.IOCopyContext{
Ctx: ctx,
IO: io,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}
var ifc linux.IFConf
if _, err := ifc.CopyIn(cc, args[2].Pointer()); err != nil {
@@ -127,9 +119,6 @@ func ioctl(ctx context.Context, fd int, io usermem.IO, sysno uintptr, args arch.
cc := &usermem.IOCopyContext{
Ctx: ctx,
IO: io,
Opts: usermem.IOOpts{
AddressSpaceActive: true,
},
}
var ifr linux.IFReq
if _, err := ifr.CopyIn(cc, args[2].Pointer()); err != nil {
+5 -15
View File
@@ -471,9 +471,7 @@ func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, sysno uintp
syscall.SIOCGIFTXQLEN:
var ifr linux.IFReq
ifrBuf := ctx.(*kernel.Task).CopyScratchBuffer(linux.SizeOfIFReq)
if _, err := io.CopyIn(ctx, args[2].Pointer(), ifrBuf, usermem.IOOpts{
AddressSpaceActive: true,
}); err != nil {
if _, err := io.CopyIn(ctx, args[2].Pointer(), ifrBuf, usermem.IOOpts{}); err != nil {
return 0, err
}
// Decode ifr from ifrBuf
@@ -485,9 +483,7 @@ func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, sysno uintp
}
copy(ifrBuf[0:linux.IFNAMSIZ], ifr.IFName[0:linux.IFNAMSIZ])
copy(ifrBuf[linux.IFNAMSIZ:linux.SizeOfIFReq], ifr.Data[0:linux.SizeOfIFReq-linux.IFNAMSIZ])
_, err := io.CopyOut(ctx, arg, ifrBuf, usermem.IOOpts{
AddressSpaceActive: true,
})
_, err := io.CopyOut(ctx, arg, ifrBuf, usermem.IOOpts{})
return 0, err
case syscall.SIOCGIFCONF:
@@ -495,9 +491,7 @@ func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, sysno uintp
// will need to populate the array of ifreqs.
var ifc linux.IFConf
ifcBuf := ctx.(*kernel.Task).CopyScratchBuffer(linux.SizeOfIFConf)
if _, err := io.CopyIn(ctx, arg, ifcBuf, usermem.IOOpts{
AddressSpaceActive: true,
}); err != nil {
if _, err := io.CopyIn(ctx, arg, ifcBuf, usermem.IOOpts{}); err != nil {
return 0, err
}
// Decode ifc from ifcBuf
@@ -510,9 +504,7 @@ func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, sysno uintp
}
hostarch.ByteOrder.PutUint32(ifcBuf[0:4], uint32(ifc.Len))
hostarch.ByteOrder.PutUint64(ifcBuf[8:], ifc.Ptr)
_, err := io.CopyOut(ctx, arg, ifcBuf, usermem.IOOpts{
AddressSpaceActive: true,
})
_, err := io.CopyOut(ctx, arg, ifcBuf, usermem.IOOpts{})
return 0, err
case linux.SIOCGIFMEM, linux.SIOCGIFPFLAGS, linux.SIOCGMIIPHY, linux.SIOCGMIIREG:
@@ -532,9 +524,7 @@ func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, sysno uintp
return 0, err
}
_, err := io.CopyOut(ctx, arg, buf, usermem.IOOpts{
AddressSpaceActive: true,
})
_, err := io.CopyOut(ctx, arg, buf, usermem.IOOpts{})
return 0, err
}
+1 -1
View File
@@ -61,7 +61,7 @@ func getFilePath(t *kernel.Task, fd int32) string {
}
func getIovecSize(t *kernel.Task, addr hostarch.Addr, iovcnt int) uint64 {
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{AddressSpaceActive: true})
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{})
if err != nil {
return 0
}
+4 -11
View File
@@ -81,9 +81,9 @@ func IoDestroy(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintp
}
// The task cannot be interrupted during the wait. Equivalent to
// TASK_UNINTERRUPTIBLE in Linux.
t.UninterruptibleSleepStart(true /* deactivate */)
t.UninterruptibleSleepStart()
<-ch
t.UninterruptibleSleepFinish(true /* activate */)
t.UninterruptibleSleepFinish()
}
}
@@ -187,19 +187,12 @@ func memoryFor(t *kernel.Task, cb *linux.IOCallback) (usermem.IOSequence, error)
return usermem.IOSequence{}, linuxerr.EINVAL
}
// Since this I/O will be asynchronous with respect to t's task goroutine,
// we have no guarantee that t's AddressSpace will be active during the
// I/O.
switch cb.OpCode {
case linux.IOCB_CMD_PREAD, linux.IOCB_CMD_PWRITE:
return t.SingleIOSequence(hostarch.Addr(cb.Buf), bytes, usermem.IOOpts{
AddressSpaceActive: false,
})
return t.SingleIOSequence(hostarch.Addr(cb.Buf), bytes, usermem.IOOpts{})
case linux.IOCB_CMD_PREADV, linux.IOCB_CMD_PWRITEV:
return t.IovecsIOSequence(hostarch.Addr(cb.Buf), bytes, usermem.IOOpts{
AddressSpaceActive: false,
})
return t.IovecsIOSequence(hostarch.Addr(cb.Buf), bytes, usermem.IOOpts{})
case linux.IOCB_CMD_FSYNC, linux.IOCB_CMD_FDSYNC, linux.IOCB_CMD_NOOP:
return usermem.IOSequence{}, nil
+1 -3
View File
@@ -58,9 +58,7 @@ func getdents(t *kernel.Task, args arch.SyscallArguments, isGetdents64 bool) (ui
// We want to be sure of the allowed buffer size before calling IterDirents,
// because this function depends on IterDirents saving state of which dirent
// was the last one that was successfully operated on.
allowedSize, err := t.MemoryManager().EnsurePMAsExist(t, addr, int64(size), usermem.IOOpts{
AddressSpaceActive: true,
})
allowedSize, err := t.MemoryManager().EnsurePMAsExist(t, addr, int64(size), usermem.IOOpts{})
if allowedSize == 0 {
return 0, nil, err
}
+1 -3
View File
@@ -92,9 +92,7 @@ func copyOutNodemask(t *kernel.Task, addr hostarch.Addr, maxnode uint32, val uin
return linuxerr.EFAULT
}
remUint64 := (bits - 1) / 64
if _, err := t.MemoryManager().ZeroOut(t, remAddr, int64(remUint64)*8, usermem.IOOpts{
AddressSpaceActive: true,
}); err != nil {
if _, err := t.MemoryManager().ZeroOut(t, remAddr, int64(remUint64)*8, usermem.IOOpts{}); err != nil {
return err
}
}
+1 -4
View File
@@ -86,7 +86,6 @@ func processVMOp(t *kernel.Task, args arch.SyscallArguments, op processVMOpType)
}
localOps := processVMOps{
mm: t.MemoryManager(),
ioOpts: usermem.IOOpts{AddressSpaceActive: true},
iovecs: localIovecs,
}
remoteIovecs, err := t.CopyInIovecsAsSlice(rvec, riovcnt)
@@ -97,10 +96,8 @@ func processVMOp(t *kernel.Task, args arch.SyscallArguments, op processVMOpType)
iovecs: remoteIovecs,
}
if remoteTask == t {
// No need to take remoteTask.mu to fetch the memory manager,
// and we can assume address space is active.
// No need to take remoteTask.mu to fetch the memory manager.
remoteOps.mm = t.MemoryManager()
remoteOps.ioOpts = usermem.IOOpts{AddressSpaceActive: true}
} else {
// Grab the remoteTask memory manager, and pin it by adding
// ourselves as a user.
+1 -3
View File
@@ -56,9 +56,7 @@ func GetRandom(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintp
return 0, nil, linuxerr.EFAULT
}
n, err := t.MemoryManager().CopyOutFrom(t, hostarch.AddrRangeSeqOf(ar), safemem.FromIOReader{rand.Reader}, usermem.IOOpts{
AddressSpaceActive: true,
})
n, err := t.MemoryManager().CopyOutFrom(t, hostarch.AddrRangeSeqOf(ar), safemem.FromIOReader{rand.Reader}, usermem.IOOpts{})
if n > 0 {
return uintptr(n), nil, nil
}
+10 -30
View File
@@ -52,9 +52,7 @@ func Read(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *
}
// Get the destination of the read.
dst, err := t.SingleIOSequence(addr, si, usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.SingleIOSequence(addr, si, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -77,9 +75,7 @@ func Readv(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
defer file.DecRef(t)
// Get the destination of the read.
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -157,9 +153,7 @@ func Pread64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr
}
// Get the destination of the read.
dst, err := t.SingleIOSequence(addr, si, usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.SingleIOSequence(addr, si, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -188,9 +182,7 @@ func Preadv(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
}
// Get the destination of the read.
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -226,9 +218,7 @@ func Preadv2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr
}
// Get the destination of the read.
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -306,9 +296,7 @@ func Write(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
}
// Get the source of the write.
src, err := t.SingleIOSequence(addr, si, usermem.IOOpts{
AddressSpaceActive: true,
})
src, err := t.SingleIOSequence(addr, si, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -331,9 +319,7 @@ func Writev(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
defer file.DecRef(t)
// Get the source of the write.
src, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{
AddressSpaceActive: true,
})
src, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -410,9 +396,7 @@ func Pwrite64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintpt
}
// Get the source of the write.
src, err := t.SingleIOSequence(addr, si, usermem.IOOpts{
AddressSpaceActive: true,
})
src, err := t.SingleIOSequence(addr, si, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -441,9 +425,7 @@ func Pwritev(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr
}
// Get the source of the write.
src, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{
AddressSpaceActive: true,
})
src, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
@@ -479,9 +461,7 @@ func Pwritev2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintpt
}
// Get the source of the write.
src, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{
AddressSpaceActive: true,
})
src, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{})
if err != nil {
return 0, nil, err
}
+4 -12
View File
@@ -800,9 +800,7 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr hostarch.Addr, flags
if msg.IovLen > linux.UIO_MAXIOV {
return 0, linuxerr.EMSGSIZE
}
dst, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{})
if err != nil {
return 0, err
}
@@ -904,9 +902,7 @@ func recvFrom(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, fla
flags |= linux.MSG_DONTWAIT
}
dst, err := t.SingleIOSequence(bufPtr, int(bufLen), usermem.IOOpts{
AddressSpaceActive: true,
})
dst, err := t.SingleIOSequence(bufPtr, int(bufLen), usermem.IOOpts{})
if err != nil {
return 0, err
}
@@ -1086,9 +1082,7 @@ func sendSingleMsg(t *kernel.Task, s socket.Socket, file *vfs.FileDescription, m
if msg.IovLen > linux.UIO_MAXIOV {
return 0, linuxerr.EMSGSIZE
}
src, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{
AddressSpaceActive: true,
})
src, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{})
if err != nil {
return 0, err
}
@@ -1153,9 +1147,7 @@ func sendTo(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, flags
}
}
src, err := t.SingleIOSequence(bufPtr, bl, usermem.IOOpts{
AddressSpaceActive: true,
})
src, err := t.SingleIOSequence(bufPtr, bl, usermem.IOOpts{})
if err != nil {
return 0, err
}
+1 -3
View File
@@ -541,9 +541,7 @@ func Getcpu(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
}
// We always return node 0.
if node != 0 {
if _, err := t.MemoryManager().ZeroOut(t, node, 4, usermem.IOOpts{
AddressSpaceActive: true,
}); err != nil {
if _, err := t.MemoryManager().ZeroOut(t, node, 4, usermem.IOOpts{}); err != nil {
return 0, nil, err
}
}
+2 -10
View File
@@ -367,16 +367,8 @@ func (w *Watchdog) doAction(action Action, forceStack bool, stuckTasks map[int64
tracebackAllWithStuckSuffix(stuckTasks, msg.Bytes())
// Attempt to flush metrics, timeout and move on in case metrics are stuck as well.
metricsEmitted := make(chan struct{}, 1)
go func() { // S/R-SAFE: watchdog is stopped during save and restarted after restore.
// Flush metrics before killing process.
metric.EmitMetricUpdate()
metricsEmitted <- struct{}{}
}()
select {
case <-metricsEmitted:
case <-time.After(1 * time.Second):
}
metric.EmitMetricUpdateWithTimeout(time.Second)
panic(fmt.Sprintf("%s\nStack for running G's are skipped while panicking.", msg.String()))
default:
-5
View File
@@ -130,11 +130,6 @@ type IOOpts struct {
// by mmap(2) or mprotect(2) will be ignored. (Memory protections required
// by the target of the mapping are never ignored.)
IgnorePermissions bool
// If AddressSpaceActive is true, the IO implementation may assume that it
// has an active AddressSpace and can therefore use AddressSpace copying
// without performing activation. See mm/io.go for details.
AddressSpaceActive bool
}
// IOReadWriter is an io.ReadWriter that reads from / writes to addresses