github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/fsimpl/kernfs/mmap_util.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kernfs 16 17 import ( 18 "github.com/MerlinKodo/gvisor/pkg/context" 19 "github.com/MerlinKodo/gvisor/pkg/hostarch" 20 "github.com/MerlinKodo/gvisor/pkg/safemem" 21 "github.com/MerlinKodo/gvisor/pkg/sentry/fsutil" 22 "github.com/MerlinKodo/gvisor/pkg/sentry/memmap" 23 "github.com/MerlinKodo/gvisor/pkg/sync" 24 ) 25 26 // inodePlatformFile implements memmap.File. It exists solely because inode 27 // cannot implement both kernfs.Inode.IncRef and memmap.File.IncRef. 28 // 29 // +stateify savable 30 type inodePlatformFile struct { 31 // hostFD contains the host fd that this file was originally created from, 32 // which must be available at time of restore. 33 // 34 // This field is initialized at creation time and is immutable. 35 // inodePlatformFile does not own hostFD and hence should not close it. 36 hostFD int 37 38 // fdRefsMu protects fdRefs. 39 fdRefsMu sync.Mutex `state:"nosave"` 40 41 // fdRefs counts references on memmap.File offsets. It is used solely for 42 // memory accounting. 43 fdRefs fsutil.FrameRefSet 44 45 // fileMapper caches mappings of the host file represented by this inode. 46 fileMapper fsutil.HostFileMapper 47 48 // fileMapperInitOnce is used to lazily initialize fileMapper. 49 fileMapperInitOnce sync.Once `state:"nosave"` 50 } 51 52 var _ memmap.File = (*inodePlatformFile)(nil) 53 54 // IncRef implements memmap.File.IncRef. 55 func (i *inodePlatformFile) IncRef(fr memmap.FileRange, memCgID uint32) { 56 i.fdRefsMu.Lock() 57 i.fdRefs.IncRefAndAccount(fr, memCgID) 58 i.fdRefsMu.Unlock() 59 } 60 61 // DecRef implements memmap.File.DecRef. 62 func (i *inodePlatformFile) DecRef(fr memmap.FileRange) { 63 i.fdRefsMu.Lock() 64 i.fdRefs.DecRefAndAccount(fr) 65 i.fdRefsMu.Unlock() 66 } 67 68 // MapInternal implements memmap.File.MapInternal. 69 func (i *inodePlatformFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) { 70 return i.fileMapper.MapInternal(fr, i.hostFD, at.Write) 71 } 72 73 // FD implements memmap.File.FD. 74 func (i *inodePlatformFile) FD() int { 75 return i.hostFD 76 } 77 78 // CachedMappable implements memmap.Mappable. This utility can be embedded in a 79 // kernfs.Inode that represents a host file to make the inode mappable. 80 // CachedMappable caches the mappings of the host file. CachedMappable must be 81 // initialized (via Init) with a hostFD before use. 82 // 83 // +stateify savable 84 type CachedMappable struct { 85 // mapsMu protects mappings. 86 mapsMu sync.Mutex `state:"nosave"` 87 88 // mappings tracks mappings of hostFD into memmap.MappingSpaces. 89 mappings memmap.MappingSet 90 91 // pf implements memmap.File for mappings backed by a host fd. 92 pf inodePlatformFile 93 } 94 95 var _ memmap.Mappable = (*CachedMappable)(nil) 96 97 // Init initializes i.pf. This must be called before using CachedMappable. 98 func (i *CachedMappable) Init(hostFD int) { 99 i.pf.hostFD = hostFD 100 } 101 102 // AddMapping implements memmap.Mappable.AddMapping. 103 func (i *CachedMappable) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error { 104 i.mapsMu.Lock() 105 mapped := i.mappings.AddMapping(ms, ar, offset, writable) 106 for _, r := range mapped { 107 i.pf.fileMapper.IncRefOn(r) 108 } 109 i.mapsMu.Unlock() 110 return nil 111 } 112 113 // RemoveMapping implements memmap.Mappable.RemoveMapping. 114 func (i *CachedMappable) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) { 115 i.mapsMu.Lock() 116 unmapped := i.mappings.RemoveMapping(ms, ar, offset, writable) 117 for _, r := range unmapped { 118 i.pf.fileMapper.DecRefOn(r) 119 } 120 i.mapsMu.Unlock() 121 } 122 123 // CopyMapping implements memmap.Mappable.CopyMapping. 124 func (i *CachedMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error { 125 return i.AddMapping(ctx, ms, dstAR, offset, writable) 126 } 127 128 // Translate implements memmap.Mappable.Translate. 129 func (i *CachedMappable) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) { 130 mr := optional 131 return []memmap.Translation{ 132 { 133 Source: mr, 134 File: &i.pf, 135 Offset: mr.Start, 136 Perms: hostarch.AnyAccess, 137 }, 138 }, nil 139 } 140 141 // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable. 142 func (i *CachedMappable) InvalidateUnsavable(ctx context.Context) error { 143 // We expect the same host fd across save/restore, so all translations 144 // should be valid. 145 return nil 146 } 147 148 // InvalidateRange invalidates the passed range on i.mappings. 149 func (i *CachedMappable) InvalidateRange(r memmap.MappableRange) { 150 i.mapsMu.Lock() 151 i.mappings.Invalidate(r, memmap.InvalidateOpts{ 152 // Compare Linux's mm/truncate.c:truncate_setsize() => 153 // truncate_pagecache() => 154 // mm/memory.c:unmap_mapping_range(evencows=1). 155 InvalidatePrivate: true, 156 }) 157 i.mapsMu.Unlock() 158 } 159 160 // InitFileMapperOnce initializes the host file mapper. It ensures that the 161 // file mapper is initialized just once. 162 func (i *CachedMappable) InitFileMapperOnce() { 163 i.pf.fileMapperInitOnce.Do(i.pf.fileMapper.Init) 164 }