diff options
-rw-r--r-- | pkg/sentry/fsimpl/host/host.go | 24 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/host/host_state_autogen.go | 90 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/host/save_restore.go | 8 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/kernfs/kernfs_state_autogen.go | 56 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/kernfs/mmap_util.go (renamed from pkg/sentry/fsimpl/host/mmap.go) | 81 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/kernfs/save_restore.go | 23 |
6 files changed, 168 insertions, 114 deletions
diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go index eeed0f97d..39b902a3e 100644 --- a/pkg/sentry/fsimpl/host/host.go +++ b/pkg/sentry/fsimpl/host/host.go @@ -48,6 +48,7 @@ type inode struct { kernfs.InodeNoStatFS kernfs.InodeNotDirectory kernfs.InodeNotSymlink + kernfs.CachedMappable kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid. locks vfs.FileLocks @@ -96,16 +97,6 @@ type inode struct { // Event queue for blocking operations. queue waiter.Queue - // mapsMu protects mappings. - mapsMu sync.Mutex `state:"nosave"` - - // If this file is mmappable, mappings tracks mappings of hostFD into - // memmap.MappingSpaces. - mappings memmap.MappingSet - - // pf implements platform.File for mappings of hostFD. - pf inodePlatformFile - // If haveBuf is non-zero, hostFD represents a pipe, and buf contains data // read from the pipe from previous calls to inode.beforeSave(). haveBuf // and buf are protected by bufMu. haveBuf is accessed using atomic memory @@ -135,7 +126,7 @@ func newInode(ctx context.Context, fs *filesystem, hostFD int, savable bool, fil isTTY: isTTY, savable: savable, } - i.pf.inode = i + i.CachedMappable.Init(hostFD) i.EnableLeakCheck() // If the hostFD can return EWOULDBLOCK when set to non-blocking, do so and @@ -439,14 +430,7 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre oldpgend, _ := usermem.PageRoundUp(oldSize) newpgend, _ := usermem.PageRoundUp(s.Size) if oldpgend != newpgend { - i.mapsMu.Lock() - i.mappings.Invalidate(memmap.MappableRange{newpgend, oldpgend}, memmap.InvalidateOpts{ - // Compare Linux's mm/truncate.c:truncate_setsize() => - // truncate_pagecache() => - // mm/memory.c:unmap_mapping_range(evencows=1). - InvalidatePrivate: true, - }) - i.mapsMu.Unlock() + i.CachedMappable.InvalidateRange(memmap.MappableRange{newpgend, oldpgend}) } } } @@ -797,7 +781,7 @@ func (f *fileDescription) ConfigureMMap(_ context.Context, opts *memmap.MMapOpts return syserror.ENODEV } i := f.inode - i.pf.fileMapperInitOnce.Do(i.pf.fileMapper.Init) + i.CachedMappable.InitFileMapperOnce() return vfs.GenericConfigureMMap(&f.vfsfd, i, opts) } diff --git a/pkg/sentry/fsimpl/host/host_state_autogen.go b/pkg/sentry/fsimpl/host/host_state_autogen.go index 6de6c002c..705f8010a 100644 --- a/pkg/sentry/fsimpl/host/host_state_autogen.go +++ b/pkg/sentry/fsimpl/host/host_state_autogen.go @@ -37,6 +37,7 @@ func (i *inode) StateFields() []string { "InodeNoStatFS", "InodeNotDirectory", "InodeNotSymlink", + "CachedMappable", "InodeTemporary", "locks", "inodeRefs", @@ -48,8 +49,6 @@ func (i *inode) StateFields() []string { "isTTY", "savable", "queue", - "mappings", - "pf", "haveBuf", "buf", } @@ -60,42 +59,40 @@ func (i *inode) StateSave(stateSinkObject state.Sink) { stateSinkObject.Save(0, &i.InodeNoStatFS) stateSinkObject.Save(1, &i.InodeNotDirectory) stateSinkObject.Save(2, &i.InodeNotSymlink) - stateSinkObject.Save(3, &i.InodeTemporary) - stateSinkObject.Save(4, &i.locks) - stateSinkObject.Save(5, &i.inodeRefs) - stateSinkObject.Save(6, &i.hostFD) - stateSinkObject.Save(7, &i.ino) - stateSinkObject.Save(8, &i.ftype) - stateSinkObject.Save(9, &i.mayBlock) - stateSinkObject.Save(10, &i.seekable) - stateSinkObject.Save(11, &i.isTTY) - stateSinkObject.Save(12, &i.savable) - stateSinkObject.Save(13, &i.queue) - stateSinkObject.Save(14, &i.mappings) - stateSinkObject.Save(15, &i.pf) - stateSinkObject.Save(16, &i.haveBuf) - stateSinkObject.Save(17, &i.buf) + stateSinkObject.Save(3, &i.CachedMappable) + stateSinkObject.Save(4, &i.InodeTemporary) + stateSinkObject.Save(5, &i.locks) + stateSinkObject.Save(6, &i.inodeRefs) + stateSinkObject.Save(7, &i.hostFD) + stateSinkObject.Save(8, &i.ino) + stateSinkObject.Save(9, &i.ftype) + stateSinkObject.Save(10, &i.mayBlock) + stateSinkObject.Save(11, &i.seekable) + stateSinkObject.Save(12, &i.isTTY) + stateSinkObject.Save(13, &i.savable) + stateSinkObject.Save(14, &i.queue) + stateSinkObject.Save(15, &i.haveBuf) + stateSinkObject.Save(16, &i.buf) } func (i *inode) StateLoad(stateSourceObject state.Source) { stateSourceObject.Load(0, &i.InodeNoStatFS) stateSourceObject.Load(1, &i.InodeNotDirectory) stateSourceObject.Load(2, &i.InodeNotSymlink) - stateSourceObject.Load(3, &i.InodeTemporary) - stateSourceObject.Load(4, &i.locks) - stateSourceObject.Load(5, &i.inodeRefs) - stateSourceObject.Load(6, &i.hostFD) - stateSourceObject.Load(7, &i.ino) - stateSourceObject.Load(8, &i.ftype) - stateSourceObject.Load(9, &i.mayBlock) - stateSourceObject.Load(10, &i.seekable) - stateSourceObject.Load(11, &i.isTTY) - stateSourceObject.Load(12, &i.savable) - stateSourceObject.Load(13, &i.queue) - stateSourceObject.Load(14, &i.mappings) - stateSourceObject.Load(15, &i.pf) - stateSourceObject.Load(16, &i.haveBuf) - stateSourceObject.Load(17, &i.buf) + stateSourceObject.Load(3, &i.CachedMappable) + stateSourceObject.Load(4, &i.InodeTemporary) + stateSourceObject.Load(5, &i.locks) + stateSourceObject.Load(6, &i.inodeRefs) + stateSourceObject.Load(7, &i.hostFD) + stateSourceObject.Load(8, &i.ino) + stateSourceObject.Load(9, &i.ftype) + stateSourceObject.Load(10, &i.mayBlock) + stateSourceObject.Load(11, &i.seekable) + stateSourceObject.Load(12, &i.isTTY) + stateSourceObject.Load(13, &i.savable) + stateSourceObject.Load(14, &i.queue) + stateSourceObject.Load(15, &i.haveBuf) + stateSourceObject.Load(16, &i.buf) stateSourceObject.AfterLoad(i.afterLoad) } @@ -201,34 +198,6 @@ func (r *inodeRefs) StateLoad(stateSourceObject state.Source) { stateSourceObject.AfterLoad(r.afterLoad) } -func (i *inodePlatformFile) StateTypeName() string { - return "pkg/sentry/fsimpl/host.inodePlatformFile" -} - -func (i *inodePlatformFile) StateFields() []string { - return []string{ - "inode", - "fdRefs", - "fileMapper", - } -} - -func (i *inodePlatformFile) beforeSave() {} - -func (i *inodePlatformFile) StateSave(stateSinkObject state.Sink) { - i.beforeSave() - stateSinkObject.Save(0, &i.inode) - stateSinkObject.Save(1, &i.fdRefs) - stateSinkObject.Save(2, &i.fileMapper) -} - -func (i *inodePlatformFile) StateLoad(stateSourceObject state.Source) { - stateSourceObject.Load(0, &i.inode) - stateSourceObject.Load(1, &i.fdRefs) - stateSourceObject.Load(2, &i.fileMapper) - stateSourceObject.AfterLoad(i.afterLoad) -} - func (c *ConnectedEndpoint) StateTypeName() string { return "pkg/sentry/fsimpl/host.ConnectedEndpoint" } @@ -300,7 +269,6 @@ func init() { state.Register((*filesystem)(nil)) state.Register((*fileDescription)(nil)) state.Register((*inodeRefs)(nil)) - state.Register((*inodePlatformFile)(nil)) state.Register((*ConnectedEndpoint)(nil)) state.Register((*TTYFileDescription)(nil)) } diff --git a/pkg/sentry/fsimpl/host/save_restore.go b/pkg/sentry/fsimpl/host/save_restore.go index 7e32a8863..8800652a9 100644 --- a/pkg/sentry/fsimpl/host/save_restore.go +++ b/pkg/sentry/fsimpl/host/save_restore.go @@ -68,11 +68,3 @@ func (i *inode) afterLoad() { } } } - -// afterLoad is invoked by stateify. -func (i *inodePlatformFile) afterLoad() { - if i.fileMapper.IsInited() { - // Ensure that we don't call i.fileMapper.Init() again. - i.fileMapperInitOnce.Do(func() {}) - } -} diff --git a/pkg/sentry/fsimpl/kernfs/kernfs_state_autogen.go b/pkg/sentry/fsimpl/kernfs/kernfs_state_autogen.go index 1a62f05aa..9bac6798d 100644 --- a/pkg/sentry/fsimpl/kernfs/kernfs_state_autogen.go +++ b/pkg/sentry/fsimpl/kernfs/kernfs_state_autogen.go @@ -664,6 +664,60 @@ func (d *Dentry) StateLoad(stateSourceObject state.Source) { stateSourceObject.Load(9, &d.inode) } +func (i *inodePlatformFile) StateTypeName() string { + return "pkg/sentry/fsimpl/kernfs.inodePlatformFile" +} + +func (i *inodePlatformFile) StateFields() []string { + return []string{ + "hostFD", + "fdRefs", + "fileMapper", + } +} + +func (i *inodePlatformFile) beforeSave() {} + +func (i *inodePlatformFile) StateSave(stateSinkObject state.Sink) { + i.beforeSave() + stateSinkObject.Save(0, &i.hostFD) + stateSinkObject.Save(1, &i.fdRefs) + stateSinkObject.Save(2, &i.fileMapper) +} + +func (i *inodePlatformFile) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &i.hostFD) + stateSourceObject.Load(1, &i.fdRefs) + stateSourceObject.Load(2, &i.fileMapper) + stateSourceObject.AfterLoad(i.afterLoad) +} + +func (i *CachedMappable) StateTypeName() string { + return "pkg/sentry/fsimpl/kernfs.CachedMappable" +} + +func (i *CachedMappable) StateFields() []string { + return []string{ + "mappings", + "pf", + } +} + +func (i *CachedMappable) beforeSave() {} + +func (i *CachedMappable) StateSave(stateSinkObject state.Sink) { + i.beforeSave() + stateSinkObject.Save(0, &i.mappings) + stateSinkObject.Save(1, &i.pf) +} + +func (i *CachedMappable) afterLoad() {} + +func (i *CachedMappable) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &i.mappings) + stateSourceObject.Load(1, &i.pf) +} + func (l *slotList) StateTypeName() string { return "pkg/sentry/fsimpl/kernfs.slotList" } @@ -860,6 +914,8 @@ func init() { state.Register((*InodeNoStatFS)(nil)) state.Register((*Filesystem)(nil)) state.Register((*Dentry)(nil)) + state.Register((*inodePlatformFile)(nil)) + state.Register((*CachedMappable)(nil)) state.Register((*slotList)(nil)) state.Register((*slotEntry)(nil)) state.Register((*StaticDirectoryRefs)(nil)) diff --git a/pkg/sentry/fsimpl/host/mmap.go b/pkg/sentry/fsimpl/kernfs/mmap_util.go index 3d7eb2f96..bd6a134b4 100644 --- a/pkg/sentry/fsimpl/host/mmap.go +++ b/pkg/sentry/fsimpl/kernfs/mmap_util.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package host +package kernfs import ( "gvisor.dev/gvisor/pkg/context" @@ -26,11 +26,14 @@ import ( // inodePlatformFile implements memmap.File. It exists solely because inode // cannot implement both kernfs.Inode.IncRef and memmap.File.IncRef. // -// inodePlatformFile should only be used if inode.canMap is true. -// // +stateify savable type inodePlatformFile struct { - *inode + // hostFD contains the host fd that this file was originally created from, + // which must be available at time of restore. + // + // This field is initialized at creation time and is immutable. + // inodePlatformFile does not own hostFD and hence should not close it. + hostFD int // fdRefsMu protects fdRefs. fdRefsMu sync.Mutex `state:"nosave"` @@ -46,9 +49,9 @@ type inodePlatformFile struct { fileMapperInitOnce sync.Once `state:"nosave"` } +var _ memmap.File = (*inodePlatformFile)(nil) + // IncRef implements memmap.File.IncRef. -// -// Precondition: i.inode.canMap must be true. func (i *inodePlatformFile) IncRef(fr memmap.FileRange) { i.fdRefsMu.Lock() i.fdRefs.IncRefAndAccount(fr) @@ -56,8 +59,6 @@ func (i *inodePlatformFile) IncRef(fr memmap.FileRange) { } // DecRef implements memmap.File.DecRef. -// -// Precondition: i.inode.canMap must be true. func (i *inodePlatformFile) DecRef(fr memmap.FileRange) { i.fdRefsMu.Lock() i.fdRefs.DecRefAndAccount(fr) @@ -65,8 +66,6 @@ func (i *inodePlatformFile) DecRef(fr memmap.FileRange) { } // MapInternal implements memmap.File.MapInternal. -// -// Precondition: i.inode.canMap must be true. func (i *inodePlatformFile) MapInternal(fr memmap.FileRange, at usermem.AccessType) (safemem.BlockSeq, error) { return i.fileMapper.MapInternal(fr, i.hostFD, at.Write) } @@ -76,10 +75,32 @@ func (i *inodePlatformFile) FD() int { return i.hostFD } -// AddMapping implements memmap.Mappable.AddMapping. +// CachedMappable implements memmap.Mappable. This utility can be embedded in a +// kernfs.Inode that represents a host file to make the inode mappable. +// CachedMappable caches the mappings of the host file. CachedMappable must be +// initialized (via Init) with a hostFD before use. // -// Precondition: i.inode.canMap must be true. -func (i *inode) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) error { +// +stateify savable +type CachedMappable struct { + // mapsMu protects mappings. + mapsMu sync.Mutex `state:"nosave"` + + // mappings tracks mappings of hostFD into memmap.MappingSpaces. + mappings memmap.MappingSet + + // pf implements memmap.File for mappings backed by a host fd. + pf inodePlatformFile +} + +var _ memmap.Mappable = (*CachedMappable)(nil) + +// Init initializes i.pf. This must be called before using CachedMappable. +func (i *CachedMappable) Init(hostFD int) { + i.pf.hostFD = hostFD +} + +// AddMapping implements memmap.Mappable.AddMapping. +func (i *CachedMappable) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) error { i.mapsMu.Lock() mapped := i.mappings.AddMapping(ms, ar, offset, writable) for _, r := range mapped { @@ -90,9 +111,7 @@ func (i *inode) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar userm } // RemoveMapping implements memmap.Mappable.RemoveMapping. -// -// Precondition: i.inode.canMap must be true. -func (i *inode) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) { +func (i *CachedMappable) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) { i.mapsMu.Lock() unmapped := i.mappings.RemoveMapping(ms, ar, offset, writable) for _, r := range unmapped { @@ -102,16 +121,12 @@ func (i *inode) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar us } // CopyMapping implements memmap.Mappable.CopyMapping. -// -// Precondition: i.inode.canMap must be true. -func (i *inode) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, writable bool) error { +func (i *CachedMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, writable bool) error { return i.AddMapping(ctx, ms, dstAR, offset, writable) } // Translate implements memmap.Mappable.Translate. -// -// Precondition: i.inode.canMap must be true. -func (i *inode) Translate(ctx context.Context, required, optional memmap.MappableRange, at usermem.AccessType) ([]memmap.Translation, error) { +func (i *CachedMappable) Translate(ctx context.Context, required, optional memmap.MappableRange, at usermem.AccessType) ([]memmap.Translation, error) { mr := optional return []memmap.Translation{ { @@ -124,10 +139,26 @@ func (i *inode) Translate(ctx context.Context, required, optional memmap.Mappabl } // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable. -// -// Precondition: i.inode.canMap must be true. -func (i *inode) InvalidateUnsavable(ctx context.Context) error { +func (i *CachedMappable) InvalidateUnsavable(ctx context.Context) error { // We expect the same host fd across save/restore, so all translations // should be valid. return nil } + +// InvalidateRange invalidates the passed range on i.mappings. +func (i *CachedMappable) InvalidateRange(r memmap.MappableRange) { + i.mapsMu.Lock() + i.mappings.Invalidate(r, memmap.InvalidateOpts{ + // Compare Linux's mm/truncate.c:truncate_setsize() => + // truncate_pagecache() => + // mm/memory.c:unmap_mapping_range(evencows=1). + InvalidatePrivate: true, + }) + i.mapsMu.Unlock() +} + +// InitFileMapperOnce initializes the host file mapper. It ensures that the +// file mapper is initialized just once. +func (i *CachedMappable) InitFileMapperOnce() { + i.pf.fileMapperInitOnce.Do(i.pf.fileMapper.Init) +} diff --git a/pkg/sentry/fsimpl/kernfs/save_restore.go b/pkg/sentry/fsimpl/kernfs/save_restore.go new file mode 100644 index 000000000..1f48de6f1 --- /dev/null +++ b/pkg/sentry/fsimpl/kernfs/save_restore.go @@ -0,0 +1,23 @@ +// Copyright 2020 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kernfs + +// afterLoad is invoked by stateify. +func (i *inodePlatformFile) afterLoad() { + if i.fileMapper.IsInited() { + // Ensure that we don't call i.fileMapper.Init() again. + i.fileMapperInitOnce.Do(func() {}) + } +} |