Decouple filemem from platform and move it to pgalloc.MemoryFile.

This is in preparation for improved page cache reclaim, which requires
greater integration between the page cache and page allocator.

PiperOrigin-RevId: 238444706
Change-Id: Id24141b3678d96c7d7dc24baddd9be555bffafe4
This commit is contained in:
Jamie Liu
2019-03-14 08:12:48 -07:00
committed by Shentubot
parent fb9919881c
commit 8f4634997b
60 changed files with 523 additions and 446 deletions
+2
View File
@@ -13,6 +13,8 @@ go_library(
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
"//pkg/sentry/limits",
"//pkg/sentry/memutil",
"//pkg/sentry/pgalloc",
"//pkg/sentry/platform",
"//pkg/sentry/platform/ptrace",
"//pkg/sentry/uniqueid",
@@ -16,6 +16,7 @@
package contexttest
import (
"os"
"sync/atomic"
"testing"
"time"
@@ -24,6 +25,8 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
"gvisor.googlesource.com/gvisor/pkg/sentry/memutil"
"gvisor.googlesource.com/gvisor/pkg/sentry/pgalloc"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform/ptrace"
"gvisor.googlesource.com/gvisor/pkg/sentry/uniqueid"
@@ -35,6 +38,17 @@ import (
// Note that some filesystems may require a minimal kernel for testing, which
// this test context does not provide. For such tests, see kernel/contexttest.
func Context(tb testing.TB) context.Context {
const memfileName = "contexttest-memory"
memfd, err := memutil.CreateMemFD(memfileName, 0)
if err != nil {
tb.Fatalf("error creating application memory file: %v", err)
}
memfile := os.NewFile(uintptr(memfd), memfileName)
mf, err := pgalloc.NewMemoryFile(memfile)
if err != nil {
memfile.Close()
tb.Fatalf("error creating pgalloc.MemoryFile: %v", err)
}
p, err := ptrace.New()
if err != nil {
tb.Fatal(err)
@@ -43,6 +57,7 @@ func Context(tb testing.TB) context.Context {
return &TestContext{
Context: context.Background(),
l: limits.NewLimitSet(),
mf: mf,
platform: p,
otherValues: make(map[interface{}]interface{}),
}
@@ -53,6 +68,7 @@ func Context(tb testing.TB) context.Context {
type TestContext struct {
context.Context
l *limits.LimitSet
mf *pgalloc.MemoryFile
platform platform.Platform
otherValues map[interface{}]interface{}
}
@@ -94,6 +110,10 @@ func (t *TestContext) Value(key interface{}) interface{} {
switch key {
case limits.CtxLimits:
return t.l
case pgalloc.CtxMemoryFile:
return t.mf
case pgalloc.CtxMemoryFileProvider:
return t
case platform.CtxPlatform:
return t.platform
case uniqueid.CtxGlobalUniqueID:
@@ -112,6 +132,11 @@ func (t *TestContext) Value(key interface{}) interface{} {
}
}
// MemoryFile implements pgalloc.MemoryFileProvider.MemoryFile.
func (t *TestContext) MemoryFile() *pgalloc.MemoryFile {
return t.mf
}
// RootContext returns a Context that may be used in tests that need root
// credentials. Uses ptrace as the platform.Platform.
func RootContext(tb testing.TB) context.Context {
-1
View File
@@ -23,7 +23,6 @@ go_library(
"//pkg/sentry/fs/tmpfs",
"//pkg/sentry/kernel/time",
"//pkg/sentry/memmap",
"//pkg/sentry/platform",
"//pkg/sentry/usage",
"//pkg/sentry/usermem",
"//pkg/syserror",
+1
View File
@@ -17,6 +17,7 @@ go_library(
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/kernel",
"//pkg/sentry/memmap",
"//pkg/sentry/pgalloc",
"//pkg/sentry/platform",
"//pkg/sentry/usage",
"//pkg/sentry/usermem",
+11 -10
View File
@@ -25,6 +25,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
"gvisor.googlesource.com/gvisor/pkg/sentry/memmap"
"gvisor.googlesource.com/gvisor/pkg/sentry/pgalloc"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
@@ -74,9 +75,9 @@ func NewDevice(ctx context.Context, owner fs.FileOwner, fp fs.FilePermissions) *
// ioctl.
func (bd *Device) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
return fs.NewFile(ctx, d, flags, &Proc{
bd: bd,
task: kernel.TaskFromContext(ctx),
platform: platform.FromContext(ctx),
bd: bd,
task: kernel.TaskFromContext(ctx),
mfp: pgalloc.MemoryFileProviderFromContext(ctx),
}), nil
}
@@ -88,14 +89,14 @@ type Proc struct {
fsutil.FileNoFsync `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
bd *Device
task *kernel.Task
platform platform.Platform
bd *Device
task *kernel.Task
mfp pgalloc.MemoryFileProvider
// mu protects fr.
mu sync.Mutex `state:"nosave"`
// mapped is memory allocated from platform.Memory() by AddMapping.
// mapped is memory allocated from mfp.MemoryFile() by AddMapping.
mapped platform.FileRange
}
@@ -104,7 +105,7 @@ func (bp *Proc) Release() {
bp.mu.Lock()
defer bp.mu.Unlock()
if bp.mapped.Length() != 0 {
bp.platform.Memory().DecRef(bp.mapped)
bp.mfp.MemoryFile().DecRef(bp.mapped)
}
}
@@ -204,7 +205,7 @@ func (bp *Proc) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar userm
}
// Binder only allocates and maps a single page up-front
// (drivers/android/binder.c:binder_mmap() => binder_update_page_range()).
fr, err := bp.platform.Memory().Allocate(usermem.PageSize, usage.Anonymous)
fr, err := bp.mfp.MemoryFile().Allocate(usermem.PageSize, usage.Anonymous)
if err != nil {
return err
}
@@ -241,7 +242,7 @@ func (bp *Proc) Translate(ctx context.Context, required, optional memmap.Mappabl
return []memmap.Translation{
{
Source: memmap.MappableRange{0, usermem.PageSize},
File: bp.platform.Memory(),
File: bp.mfp.MemoryFile(),
Offset: bp.mapped.Start,
},
}, err
+1 -1
View File
@@ -27,7 +27,7 @@ go_library(
"//pkg/sentry/fs/tmpfs",
"//pkg/sentry/memmap",
"//pkg/sentry/mm",
"//pkg/sentry/platform",
"//pkg/sentry/pgalloc",
"//pkg/sentry/safemem",
"//pkg/sentry/usermem",
"//pkg/syserror",
+2 -2
View File
@@ -21,7 +21,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
"gvisor.googlesource.com/gvisor/pkg/sentry/memmap"
"gvisor.googlesource.com/gvisor/pkg/sentry/mm"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
"gvisor.googlesource.com/gvisor/pkg/sentry/pgalloc"
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
@@ -115,7 +115,7 @@ var _ fs.FileOperations = (*zeroFileOperations)(nil)
// ConfigureMMap implements fs.FileOperations.ConfigureMMap.
func (*zeroFileOperations) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.MMapOpts) error {
m, err := mm.NewSharedAnonMappable(opts.Length, platform.FromContext(ctx))
m, err := mm.NewSharedAnonMappable(opts.Length, pgalloc.MemoryFileProviderFromContext(ctx))
if err != nil {
return err
}
+1
View File
@@ -85,6 +85,7 @@ go_library(
"//pkg/sentry/fs",
"//pkg/sentry/kernel/time",
"//pkg/sentry/memmap",
"//pkg/sentry/pgalloc",
"//pkg/sentry/platform",
"//pkg/sentry/safemem",
"//pkg/sentry/socket/unix/transport",
+6 -5
View File
@@ -112,11 +112,12 @@ finds the file that was mapped and its `CachingInodeOperations`. It then calls
It may choose to allocate more memory (i.e. do "readahead") to minimize
subsequent faults.
Memory that is allocated comes from a host tmpfs file (see `filemem.FileMem`).
The host tmpfs file memory is brought up to date with the contents of the mapped
file on its filesystem. The region of the host tmpfs file that reflects the
mapped file is then mapped into the host address space of the application so
that subsequent memory accesses do not repeatedly generate a `SIGSEGV`.
Memory that is allocated comes from a host tmpfs file (see
`pgalloc.MemoryFile`). The host tmpfs file memory is brought up to date with the
contents of the mapped file on its filesystem. The region of the host tmpfs file
that reflects the mapped file is then mapped into the host address space of the
application so that subsequent memory accesses do not repeatedly generate a
`SIGSEGV`.
The range that was allocated, including any extra memory allocation to minimize
faults, is marked dirty due to the write fault. This overcounts dirty memory if
+12 -11
View File
@@ -21,6 +21,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/memmap"
"gvisor.googlesource.com/gvisor/pkg/sentry/pgalloc"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
"gvisor.googlesource.com/gvisor/pkg/sentry/safemem"
"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
@@ -77,7 +78,7 @@ func (seg FileRangeIterator) FileRangeOf(mr memmap.MappableRange) platform.FileR
}
// Fill attempts to ensure that all memmap.Mappable offsets in required are
// mapped to a platform.File offset, by allocating from mem with the given
// mapped to a platform.File offset, by allocating from mf with the given
// memory usage kind and invoking readAt to store data into memory. (If readAt
// returns a successful partial read, Fill will call it repeatedly until all
// bytes have been read.) EOF is handled consistently with the requirements of
@@ -90,7 +91,7 @@ func (seg FileRangeIterator) FileRangeOf(mr memmap.MappableRange) platform.FileR
//
// Preconditions: required.Length() > 0. optional.IsSupersetOf(required).
// required and optional must be page-aligned.
func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.MappableRange, mem platform.Memory, kind usage.MemoryKind, readAt func(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error)) error {
func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.MappableRange, mf *pgalloc.MemoryFile, kind usage.MemoryKind, readAt func(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error)) error {
gap := frs.LowerBoundGap(required.Start)
for gap.Ok() && gap.Start() < required.End {
if gap.Range().Length() == 0 {
@@ -100,7 +101,7 @@ func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.Map
gr := gap.Range().Intersect(optional)
// Read data into the gap.
fr, err := platform.AllocateAndFill(mem, gr.Length(), kind, safemem.ReaderFunc(func(dsts safemem.BlockSeq) (uint64, error) {
fr, err := mf.AllocateAndFill(gr.Length(), kind, safemem.ReaderFunc(func(dsts safemem.BlockSeq) (uint64, error) {
var done uint64
for !dsts.IsEmpty() {
n, err := readAt(ctx, dsts, gr.Start+done)
@@ -108,7 +109,7 @@ func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.Map
dsts = dsts.DropFirst64(n)
if err != nil {
if err == io.EOF {
// platform.AllocateAndFill truncates down to a page
// MemoryFile.AllocateAndFill truncates down to a page
// boundary, but FileRangeSet.Fill is supposed to
// zero-fill to the end of the page in this case.
donepgaddr, ok := usermem.Addr(done).RoundUp()
@@ -143,20 +144,20 @@ func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.Map
// corresponding platform.FileRanges.
//
// Preconditions: mr must be page-aligned.
func (frs *FileRangeSet) Drop(mr memmap.MappableRange, mem platform.Memory) {
func (frs *FileRangeSet) Drop(mr memmap.MappableRange, mf *pgalloc.MemoryFile) {
seg := frs.LowerBoundSegment(mr.Start)
for seg.Ok() && seg.Start() < mr.End {
seg = frs.Isolate(seg, mr)
mem.DecRef(seg.FileRange())
mf.DecRef(seg.FileRange())
seg = frs.Remove(seg).NextSegment()
}
}
// DropAll removes all segments in mr, freeing the corresponding
// platform.FileRanges.
func (frs *FileRangeSet) DropAll(mem platform.Memory) {
func (frs *FileRangeSet) DropAll(mf *pgalloc.MemoryFile) {
for seg := frs.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
mem.DecRef(seg.FileRange())
mf.DecRef(seg.FileRange())
}
frs.RemoveAll()
}
@@ -164,7 +165,7 @@ func (frs *FileRangeSet) DropAll(mem platform.Memory) {
// Truncate updates frs to reflect Mappable truncation to the given length:
// bytes after the new EOF on the same page are zeroed, and pages after the new
// EOF are freed.
func (frs *FileRangeSet) Truncate(end uint64, mem platform.Memory) {
func (frs *FileRangeSet) Truncate(end uint64, mf *pgalloc.MemoryFile) {
pgendaddr, ok := usermem.Addr(end).RoundUp()
if ok {
pgend := uint64(pgendaddr)
@@ -173,7 +174,7 @@ func (frs *FileRangeSet) Truncate(end uint64, mem platform.Memory) {
frs.SplitAt(pgend)
seg := frs.LowerBoundSegment(pgend)
for seg.Ok() {
mem.DecRef(seg.FileRange())
mf.DecRef(seg.FileRange())
seg = frs.Remove(seg).NextSegment()
}
@@ -189,7 +190,7 @@ func (frs *FileRangeSet) Truncate(end uint64, mem platform.Memory) {
if seg.Ok() {
fr := seg.FileRange()
fr.Start += end - seg.Start()
ims, err := mem.MapInternal(fr, usermem.Write)
ims, err := mf.MapInternal(fr, usermem.Write)
if err != nil {
// There's no good recourse from here. This means
// that we can't keep cached memory consistent with
+22 -20
View File
@@ -25,6 +25,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/memmap"
"gvisor.googlesource.com/gvisor/pkg/sentry/pgalloc"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
"gvisor.googlesource.com/gvisor/pkg/sentry/safemem"
"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
@@ -62,8 +63,8 @@ type CachingInodeOperations struct {
// backingFile is a handle to a cached file object.
backingFile CachedFileObject
// platform is used to allocate memory that caches backingFile's contents.
platform platform.Platform
// mfp is used to allocate memory that caches backingFile's contents.
mfp pgalloc.MemoryFileProvider
// forcePageCache indicates the sentry page cache should be used regardless
// of whether the platform supports host mapped I/O or not. This must not be
@@ -96,7 +97,7 @@ type CachingInodeOperations struct {
dataMu sync.RWMutex `state:"nosave"`
// cache maps offsets into the cached file to offsets into
// platform.Memory() that store the file's data.
// mfp.MemoryFile() that store the file's data.
//
// cache is protected by dataMu.
cache FileRangeSet
@@ -148,13 +149,13 @@ type CachedFileObject interface {
// NewCachingInodeOperations returns a new CachingInodeOperations backed by
// a CachedFileObject and its initial unstable attributes.
func NewCachingInodeOperations(ctx context.Context, backingFile CachedFileObject, uattr fs.UnstableAttr, forcePageCache bool) *CachingInodeOperations {
p := platform.FromContext(ctx)
if p == nil {
panic(fmt.Sprintf("context.Context %T lacks non-nil value for key %T", ctx, platform.CtxPlatform))
mfp := pgalloc.MemoryFileProviderFromContext(ctx)
if mfp == nil {
panic(fmt.Sprintf("context.Context %T lacks non-nil value for key %T", ctx, pgalloc.CtxMemoryFileProvider))
}
return &CachingInodeOperations{
backingFile: backingFile,
platform: p,
mfp: mfp,
forcePageCache: forcePageCache,
attr: uattr,
hostFileMapper: NewHostFileMapper(),
@@ -311,7 +312,7 @@ func (c *CachingInodeOperations) Truncate(ctx context.Context, inode *fs.Inode,
// written back.
c.dataMu.Lock()
defer c.dataMu.Unlock()
c.cache.Truncate(uint64(size), c.platform.Memory())
c.cache.Truncate(uint64(size), c.mfp.MemoryFile())
c.dirty.KeepClean(memmap.MappableRange{uint64(size), oldpgend})
return nil
@@ -323,7 +324,7 @@ func (c *CachingInodeOperations) WriteOut(ctx context.Context, inode *fs.Inode)
// Write dirty pages back.
c.dataMu.Lock()
err := SyncDirtyAll(ctx, &c.cache, &c.dirty, uint64(c.attr.Size), c.platform.Memory(), c.backingFile.WriteFromBlocksAt)
err := SyncDirtyAll(ctx, &c.cache, &c.dirty, uint64(c.attr.Size), c.mfp.MemoryFile(), c.backingFile.WriteFromBlocksAt)
c.dataMu.Unlock()
if err != nil {
c.attrMu.Unlock()
@@ -527,7 +528,7 @@ func (rw *inodeReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) {
return 0, nil
}
mem := rw.c.platform.Memory()
mem := rw.c.mfp.MemoryFile()
var done uint64
seg, gap := rw.c.cache.Find(uint64(rw.offset))
for rw.offset < end {
@@ -613,7 +614,7 @@ func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error
return 0, nil
}
mem := rw.c.platform.Memory()
mf := rw.c.mfp.MemoryFile()
var done uint64
seg, gap := rw.c.cache.Find(uint64(rw.offset))
for rw.offset < end {
@@ -622,7 +623,7 @@ func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error
case seg.Ok() && seg.Start() < mr.End:
// Get internal mappings from the cache.
segMR := seg.Range().Intersect(mr)
ims, err := mem.MapInternal(seg.FileRangeOf(segMR), usermem.Write)
ims, err := mf.MapInternal(seg.FileRangeOf(segMR), usermem.Write)
if err != nil {
rw.maybeGrowFile()
rw.c.dataMu.Unlock()
@@ -711,13 +712,13 @@ func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.Ma
// Writeback dirty mapped memory now that there are no longer any
// mappings that reference it. This is our naive memory eviction
// strategy.
mem := c.platform.Memory()
mf := c.mfp.MemoryFile()
c.dataMu.Lock()
for _, r := range unmapped {
if err := SyncDirty(ctx, r, &c.cache, &c.dirty, uint64(c.attr.Size), c.platform.Memory(), c.backingFile.WriteFromBlocksAt); err != nil {
if err := SyncDirty(ctx, r, &c.cache, &c.dirty, uint64(c.attr.Size), mf, c.backingFile.WriteFromBlocksAt); err != nil {
log.Warningf("Failed to writeback cached data %v: %v", r, err)
}
c.cache.Drop(r, mem)
c.cache.Drop(r, mf)
c.dirty.KeepClean(r)
}
c.dataMu.Unlock()
@@ -760,8 +761,8 @@ func (c *CachingInodeOperations) Translate(ctx context.Context, required, option
optional.End = pgend
}
mem := c.platform.Memory()
cerr := c.cache.Fill(ctx, required, maxFillRange(required, optional), mem, usage.PageCache, c.backingFile.ReadToBlocksAt)
mf := c.mfp.MemoryFile()
cerr := c.cache.Fill(ctx, required, maxFillRange(required, optional), mf, usage.PageCache, c.backingFile.ReadToBlocksAt)
var ts []memmap.Translation
var translatedEnd uint64
@@ -769,7 +770,7 @@ func (c *CachingInodeOperations) Translate(ctx context.Context, required, option
segMR := seg.Range().Intersect(optional)
ts = append(ts, memmap.Translation{
Source: segMR,
File: mem,
File: mf,
Offset: seg.FileRangeOf(segMR).Start,
})
if at.Write {
@@ -820,16 +821,17 @@ func (c *CachingInodeOperations) InvalidateUnsavable(ctx context.Context) error
// Sync the cache's contents so that if we have a host fd after restore,
// the remote file's contents are coherent.
mf := c.mfp.MemoryFile()
c.dataMu.Lock()
defer c.dataMu.Unlock()
if err := SyncDirtyAll(ctx, &c.cache, &c.dirty, uint64(c.attr.Size), c.platform.Memory(), c.backingFile.WriteFromBlocksAt); err != nil {
if err := SyncDirtyAll(ctx, &c.cache, &c.dirty, uint64(c.attr.Size), mf, c.backingFile.WriteFromBlocksAt); err != nil {
return err
}
// Discard the cache so that it's not stored in saved state. This is safe
// because per InvalidateUnsavable invariants, no new translations can have
// been returned after we invalidated all existing translations above.
c.cache.DropAll(c.platform.Memory())
c.cache.DropAll(mf)
c.dirty.RemoveAll()
return nil
+3 -3
View File
@@ -44,10 +44,10 @@ func (d *meminfoData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle)
return nil, 0
}
mem := d.k.Platform.Memory()
mem.UpdateUsage()
mf := d.k.MemoryFile()
mf.UpdateUsage()
snapshot, totalUsage := usage.MemoryAccounting.Copy()
totalSize := usage.TotalMemory(mem.TotalSize(), totalUsage)
totalSize := usage.TotalMemory(mf.TotalSize(), totalUsage)
anon := snapshot.Anonymous + snapshot.Tmpfs
file := snapshot.PageCache + snapshot.Mapped
// We don't actually have active/inactive LRUs, so just make up numbers.
+12 -12
View File
@@ -52,7 +52,7 @@ type fileInodeOperations struct {
fsutil.InodeSimpleExtendedAttributes
// kernel is used to allocate platform memory that stores the file's contents.
// kernel is used to allocate memory that stores the file's contents.
kernel *kernel.Kernel
// memUsage is the default memory usage that will be reported by this file.
@@ -85,7 +85,7 @@ type fileInodeOperations struct {
var _ fs.InodeOperations = (*fileInodeOperations)(nil)
// NewInMemoryFile returns a new file backed by p.Memory().
// NewInMemoryFile returns a new file backed by Kernel.MemoryFile().
func NewInMemoryFile(ctx context.Context, usage usage.MemoryKind, uattr fs.UnstableAttr) fs.InodeOperations {
return &fileInodeOperations{
attr: uattr,
@@ -98,7 +98,7 @@ func NewInMemoryFile(ctx context.Context, usage usage.MemoryKind, uattr fs.Unsta
func (f *fileInodeOperations) Release(context.Context) {
f.dataMu.Lock()
defer f.dataMu.Unlock()
f.data.DropAll(f.kernel.Platform.Memory())
f.data.DropAll(f.kernel.MemoryFile())
}
// Mappable implements fs.InodeOperations.Mappable.
@@ -202,7 +202,7 @@ func (f *fileInodeOperations) Truncate(ctx context.Context, _ *fs.Inode, size in
// and can remove them.
f.dataMu.Lock()
defer f.dataMu.Unlock()
f.data.Truncate(uint64(size), f.kernel.Platform.Memory())
f.data.Truncate(uint64(size), f.kernel.MemoryFile())
return nil
}
@@ -312,7 +312,7 @@ func (rw *fileReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) {
return 0, nil
}
mem := rw.f.kernel.Platform.Memory()
mf := rw.f.kernel.MemoryFile()
var done uint64
seg, gap := rw.f.data.Find(uint64(rw.offset))
for rw.offset < end {
@@ -320,7 +320,7 @@ func (rw *fileReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) {
switch {
case seg.Ok():
// Get internal mappings.
ims, err := mem.MapInternal(seg.FileRangeOf(seg.Range().Intersect(mr)), usermem.Read)
ims, err := mf.MapInternal(seg.FileRangeOf(seg.Range().Intersect(mr)), usermem.Read)
if err != nil {
return done, err
}
@@ -378,7 +378,7 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error)
}
}()
mem := rw.f.kernel.Platform.Memory()
mf := rw.f.kernel.MemoryFile()
// Page-aligned mr for when we need to allocate memory. RoundUp can't
// overflow since end is an int64.
pgstartaddr := usermem.Addr(rw.offset).RoundDown()
@@ -392,7 +392,7 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error)
switch {
case seg.Ok():
// Get internal mappings.
ims, err := mem.MapInternal(seg.FileRangeOf(seg.Range().Intersect(mr)), usermem.Write)
ims, err := mf.MapInternal(seg.FileRangeOf(seg.Range().Intersect(mr)), usermem.Write)
if err != nil {
return done, err
}
@@ -412,7 +412,7 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error)
case gap.Ok():
// Allocate memory for the write.
gapMR := gap.Range().Intersect(pgMR)
fr, err := mem.Allocate(gapMR.Length(), rw.f.memUsage)
fr, err := mf.Allocate(gapMR.Length(), rw.f.memUsage)
if err != nil {
return done, err
}
@@ -467,8 +467,8 @@ func (f *fileInodeOperations) Translate(ctx context.Context, required, optional
optional.End = pgend
}
mem := f.kernel.Platform.Memory()
cerr := f.data.Fill(ctx, required, optional, mem, f.memUsage, func(_ context.Context, dsts safemem.BlockSeq, _ uint64) (uint64, error) {
mf := f.kernel.MemoryFile()
cerr := f.data.Fill(ctx, required, optional, mf, f.memUsage, func(_ context.Context, dsts safemem.BlockSeq, _ uint64) (uint64, error) {
// Newly-allocated pages are zeroed, so we don't need to do anything.
return dsts.NumBytes(), nil
})
@@ -479,7 +479,7 @@ func (f *fileInodeOperations) Translate(ctx context.Context, required, optional
segMR := seg.Range().Intersect(optional)
ts = append(ts, memmap.Translation{
Source: segMR,
File: mem,
File: mf,
Offset: seg.FileRangeOf(segMR).Start,
})
translatedEnd = segMR.End
+1 -1
View File
@@ -74,7 +74,7 @@ type Dir struct {
// InodeOperation methods to it.
ramfsDir *ramfs.Dir
// kernel is used to allocate platform memory as storage for tmpfs Files.
// kernel is used to allocate memory as storage for tmpfs Files.
kernel *kernel.Kernel
}
+2 -1
View File
@@ -173,6 +173,7 @@ go_library(
"//pkg/sentry/loader",
"//pkg/sentry/memmap",
"//pkg/sentry/mm",
"//pkg/sentry/pgalloc",
"//pkg/sentry/platform",
"//pkg/sentry/safemem",
"//pkg/sentry/socket/netlink/port",
@@ -212,7 +213,7 @@ go_test(
"//pkg/sentry/kernel/kdefs",
"//pkg/sentry/kernel/sched",
"//pkg/sentry/limits",
"//pkg/sentry/platform",
"//pkg/sentry/pgalloc",
"//pkg/sentry/time",
"//pkg/sentry/usage",
"//pkg/sentry/usermem",
+1
View File
@@ -12,6 +12,7 @@ go_library(
"//pkg/sentry/context",
"//pkg/sentry/context/contexttest",
"//pkg/sentry/kernel",
"//pkg/sentry/pgalloc",
"//pkg/sentry/platform",
],
)
@@ -22,6 +22,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
"gvisor.googlesource.com/gvisor/pkg/sentry/pgalloc"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
)
@@ -33,6 +34,7 @@ func Context(tb testing.TB) context.Context {
k := &kernel.Kernel{
Platform: platform.FromContext(ctx),
}
k.SetMemoryFile(pgalloc.MemoryFileFromContext(ctx))
ctx.(*contexttest.TestContext).RegisterValue(kernel.CtxKernel, k)
return ctx
}
+34 -23
View File
@@ -58,6 +58,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
"gvisor.googlesource.com/gvisor/pkg/sentry/loader"
"gvisor.googlesource.com/gvisor/pkg/sentry/mm"
"gvisor.googlesource.com/gvisor/pkg/sentry/pgalloc"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/netlink/port"
sentrytime "gvisor.googlesource.com/gvisor/pkg/sentry/time"
@@ -89,12 +90,14 @@ type Kernel struct {
// All of the following fields are immutable unless otherwise specified.
// Platform is the platform that is used to execute tasks in the
// created Kernel. It is embedded so that Kernel can directly serve as
// Platform in mm logic and also serve as platform.MemoryProvider in
// filemem S/R logic.
// Platform is the platform that is used to execute tasks in the created
// Kernel. See comment on pgalloc.MemoryFileProvider for why Platform is
// embedded anonymously (the same issue applies).
platform.Platform `state:"nosave"`
// mf provides application memory.
mf *pgalloc.MemoryFile `state:"nosave"`
// See InitKernelArgs for the meaning of these fields.
featureSet *cpuid.FeatureSet
timekeeper *Timekeeper
@@ -229,7 +232,8 @@ type InitKernelArgs struct {
// Init initialize the Kernel with no tasks.
//
// Callers must manually set Kernel.Platform before caling Init.
// Callers must manually set Kernel.Platform and call Kernel.SetMemoryFile
// before calling Init.
func (k *Kernel) Init(args InitKernelArgs) error {
if args.FeatureSet == nil {
return fmt.Errorf("FeatureSet is nil")
@@ -332,15 +336,9 @@ func (k *Kernel) SaveTo(w io.Writer) error {
log.Infof("Kernel save stats: %s", &stats)
log.Infof("Kernel save took [%s].", time.Since(kernelStart))
// Save the memory state.
//
// FIXME: In the future, this should not be dispatched via
// an abstract memory type. This should be dispatched to a single
// memory implementation that belongs to the kernel. (There is
// currently a single implementation anyways, it just needs to be
// "unabstracted" and reparented appropriately.)
// Save the memory file's state.
memoryStart := time.Now()
if err := k.Platform.Memory().SaveTo(w); err != nil {
if err := k.mf.SaveTo(w); err != nil {
return err
}
log.Infof("Memory save took [%s].", time.Since(memoryStart))
@@ -418,13 +416,9 @@ func (ts *TaskSet) unregisterEpollWaiters() {
}
// LoadFrom returns a new Kernel loaded from args.
func (k *Kernel) LoadFrom(r io.Reader, p platform.Platform, net inet.Stack) error {
func (k *Kernel) LoadFrom(r io.Reader, net inet.Stack) error {
loadStart := time.Now()
if p == nil {
return fmt.Errorf("Platform is nil")
}
k.Platform = p
k.networkStack = net
initAppCores := k.applicationCores
@@ -438,11 +432,9 @@ func (k *Kernel) LoadFrom(r io.Reader, p platform.Platform, net inet.Stack) erro
log.Infof("Kernel load stats: %s", &stats)
log.Infof("Kernel load took [%s].", time.Since(kernelStart))
// Load the memory state.
//
// See the note in SaveTo.
// Load the memory file's state.
memoryStart := time.Now()
if err := k.Platform.Memory().LoadFrom(r); err != nil {
if err := k.mf.LoadFrom(r); err != nil {
return err
}
log.Infof("Memory load took [%s].", time.Since(memoryStart))
@@ -597,6 +589,10 @@ func (ctx *createProcessContext) Value(key interface{}) interface{} {
return ctx.k.RealtimeClock()
case limits.CtxLimits:
return ctx.args.Limits
case pgalloc.CtxMemoryFile:
return ctx.k.mf
case pgalloc.CtxMemoryFileProvider:
return ctx.k
case platform.CtxPlatform:
return ctx.k
case uniqueid.CtxGlobalUniqueID:
@@ -1018,6 +1014,17 @@ func (k *Kernel) NowMonotonic() int64 {
return now
}
// SetMemoryFile sets Kernel.mf. SetMemoryFile must be called before Init or
// LoadFrom.
func (k *Kernel) SetMemoryFile(mf *pgalloc.MemoryFile) {
k.mf = mf
}
// MemoryFile implements pgalloc.MemoryFileProvider.MemoryFile.
func (k *Kernel) MemoryFile() *pgalloc.MemoryFile {
return k.mf
}
// SupervisorContext returns a Context with maximum privileges in k. It should
// only be used by goroutines outside the control of the emulated kernel
// defined by e.
@@ -1083,7 +1090,7 @@ func (k *Kernel) ListSockets(family int) []*refs.WeakRef {
socks := []*refs.WeakRef{}
if table, ok := k.socketTable[family]; ok {
socks = make([]*refs.WeakRef, 0, len(table))
for s, _ := range table {
for s := range table {
socks = append(socks, s)
}
}
@@ -1123,6 +1130,10 @@ func (ctx supervisorContext) Value(key interface{}) interface{} {
case limits.CtxLimits:
// No limits apply.
return limits.NewLimitSet()
case pgalloc.CtxMemoryFile:
return ctx.k.mf
case pgalloc.CtxMemoryFileProvider:
return ctx.k
case platform.CtxPlatform:
return ctx.k
case uniqueid.CtxGlobalUniqueID:
+1 -1
View File
@@ -95,7 +95,7 @@ func (m *MemoryEvents) run() {
}
func (m *MemoryEvents) emit() {
totalPlatform, err := m.k.Platform.Memory().TotalUsage()
totalPlatform, err := m.k.MemoryFile().TotalUsage()
if err != nil {
log.Warningf("Failed to fetch memory usage for memory events: %v", err)
return
+1
View File
@@ -20,6 +20,7 @@ go_library(
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
"//pkg/sentry/memmap",
"//pkg/sentry/pgalloc",
"//pkg/sentry/platform",
"//pkg/sentry/usage",
"//pkg/sentry/usermem",

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