gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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  	"gvisor.dev/gvisor/pkg/context"
    19  	"gvisor.dev/gvisor/pkg/hostarch"
    20  	"gvisor.dev/gvisor/pkg/safemem"
    21  	"gvisor.dev/gvisor/pkg/sentry/fsutil"
    22  	"gvisor.dev/gvisor/pkg/sentry/memmap"
    23  	"gvisor.dev/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  	memmap.NoBufferedIOFallback
    32  
    33  	// hostFD contains the host fd that this file was originally created from,
    34  	// which must be available at time of restore.
    35  	//
    36  	// This field is initialized at creation time and is immutable.
    37  	// inodePlatformFile does not own hostFD and hence should not close it.
    38  	hostFD int
    39  
    40  	// fdRefsMu protects fdRefs.
    41  	fdRefsMu sync.Mutex `state:"nosave"`
    42  
    43  	// fdRefs counts references on memmap.File offsets. It is used solely for
    44  	// memory accounting.
    45  	fdRefs fsutil.FrameRefSet
    46  
    47  	// fileMapper caches mappings of the host file represented by this inode.
    48  	fileMapper fsutil.HostFileMapper
    49  
    50  	// fileMapperInitOnce is used to lazily initialize fileMapper.
    51  	fileMapperInitOnce sync.Once `state:"nosave"`
    52  }
    53  
    54  var _ memmap.File = (*inodePlatformFile)(nil)
    55  
    56  // IncRef implements memmap.File.IncRef.
    57  func (i *inodePlatformFile) IncRef(fr memmap.FileRange, memCgID uint32) {
    58  	i.fdRefsMu.Lock()
    59  	i.fdRefs.IncRefAndAccount(fr, memCgID)
    60  	i.fdRefsMu.Unlock()
    61  }
    62  
    63  // DecRef implements memmap.File.DecRef.
    64  func (i *inodePlatformFile) DecRef(fr memmap.FileRange) {
    65  	i.fdRefsMu.Lock()
    66  	i.fdRefs.DecRefAndAccount(fr)
    67  	i.fdRefsMu.Unlock()
    68  }
    69  
    70  // MapInternal implements memmap.File.MapInternal.
    71  func (i *inodePlatformFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) {
    72  	return i.fileMapper.MapInternal(fr, i.hostFD, at.Write)
    73  }
    74  
    75  // FD implements memmap.File.FD.
    76  func (i *inodePlatformFile) FD() int {
    77  	return i.hostFD
    78  }
    79  
    80  // CachedMappable implements memmap.Mappable. This utility can be embedded in a
    81  // kernfs.Inode that represents a host file  to make the inode mappable.
    82  // CachedMappable caches the mappings of the host file. CachedMappable must be
    83  // initialized (via Init) with a hostFD before use.
    84  //
    85  // +stateify savable
    86  type CachedMappable struct {
    87  	// mapsMu protects mappings.
    88  	mapsMu sync.Mutex `state:"nosave"`
    89  
    90  	// mappings tracks mappings of hostFD into memmap.MappingSpaces.
    91  	mappings memmap.MappingSet
    92  
    93  	// pf implements memmap.File for mappings backed by a host fd.
    94  	pf inodePlatformFile
    95  }
    96  
    97  var _ memmap.Mappable = (*CachedMappable)(nil)
    98  
    99  // Init initializes i.pf. This must be called before using CachedMappable.
   100  func (i *CachedMappable) Init(hostFD int) {
   101  	i.pf.hostFD = hostFD
   102  }
   103  
   104  // AddMapping implements memmap.Mappable.AddMapping.
   105  func (i *CachedMappable) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
   106  	i.mapsMu.Lock()
   107  	mapped := i.mappings.AddMapping(ms, ar, offset, writable)
   108  	for _, r := range mapped {
   109  		i.pf.fileMapper.IncRefOn(r)
   110  	}
   111  	i.mapsMu.Unlock()
   112  	return nil
   113  }
   114  
   115  // RemoveMapping implements memmap.Mappable.RemoveMapping.
   116  func (i *CachedMappable) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
   117  	i.mapsMu.Lock()
   118  	unmapped := i.mappings.RemoveMapping(ms, ar, offset, writable)
   119  	for _, r := range unmapped {
   120  		i.pf.fileMapper.DecRefOn(r)
   121  	}
   122  	i.mapsMu.Unlock()
   123  }
   124  
   125  // CopyMapping implements memmap.Mappable.CopyMapping.
   126  func (i *CachedMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
   127  	return i.AddMapping(ctx, ms, dstAR, offset, writable)
   128  }
   129  
   130  // Translate implements memmap.Mappable.Translate.
   131  func (i *CachedMappable) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
   132  	mr := optional
   133  	return []memmap.Translation{
   134  		{
   135  			Source: mr,
   136  			File:   &i.pf,
   137  			Offset: mr.Start,
   138  			Perms:  hostarch.AnyAccess,
   139  		},
   140  	}, nil
   141  }
   142  
   143  // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
   144  func (i *CachedMappable) InvalidateUnsavable(ctx context.Context) error {
   145  	// We expect the same host fd across save/restore, so all translations
   146  	// should be valid.
   147  	return nil
   148  }
   149  
   150  // InvalidateRange invalidates the passed range on i.mappings.
   151  func (i *CachedMappable) InvalidateRange(r memmap.MappableRange) {
   152  	i.mapsMu.Lock()
   153  	i.mappings.Invalidate(r, memmap.InvalidateOpts{
   154  		// Compare Linux's mm/truncate.c:truncate_setsize() =>
   155  		// truncate_pagecache() =>
   156  		// mm/memory.c:unmap_mapping_range(evencows=1).
   157  		InvalidatePrivate: true,
   158  	})
   159  	i.mapsMu.Unlock()
   160  }
   161  
   162  // InitFileMapperOnce initializes the host file mapper. It ensures that the
   163  // file mapper is initialized just once.
   164  func (i *CachedMappable) InitFileMapperOnce() {
   165  	i.pf.fileMapperInitOnce.Do(i.pf.fileMapper.Init)
   166  }