github.com/m3db/m3@v1.5.0/src/x/mmap/mmap.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package mmap
    22  
    23  import (
    24  	"fmt"
    25  	"os"
    26  
    27  	xerrors "github.com/m3db/m3/src/x/errors"
    28  )
    29  
    30  // FileOpener is the signature of a function that MmapFiles can use
    31  // to open files
    32  type FileOpener func(filePath string) (*os.File, error)
    33  
    34  // Package-level global for easy mocking
    35  var mmapFdFn = Fd
    36  
    37  // FileDesc contains the fields required for Mmaping a file using MmapFiles
    38  type FileDesc struct {
    39  	// file is the *os.File ref to store
    40  	File **os.File
    41  	// bytes is the []byte slice ref to store the mmap'd address
    42  	Descriptor *Descriptor
    43  	// options specifies options to use when mmaping a file
    44  	Options Options
    45  }
    46  
    47  // Options contains the options for mmap'ing a file
    48  type Options struct {
    49  	// read is whether to make mmap bytes ref readable
    50  	Read bool
    51  	// write is whether to make mmap bytes ref writable
    52  	Write bool
    53  	// hugeTLB is the mmap huge TLB options
    54  	HugeTLB HugeTLBOptions
    55  	// ReporterOptions is the reporter options
    56  	ReporterOptions ReporterOptions
    57  }
    58  
    59  // Descriptor is a descriptor of a successful mmap
    60  type Descriptor struct {
    61  	Bytes           []byte
    62  	Warning         error
    63  	ReporterOptions ReporterOptions
    64  }
    65  
    66  // HugeTLBOptions contains all options related to huge TLB
    67  type HugeTLBOptions struct {
    68  	// enabled determines if using the huge TLB flag is enabled for platforms
    69  	// that support it
    70  	Enabled bool
    71  	// threshold determines if the size being mmap'd is greater or equal
    72  	// to this value to use or not use the huge TLB flag if enabled
    73  	Threshold int64
    74  }
    75  
    76  // ReporterOptions contains all options to tracking mmap calls
    77  type ReporterOptions struct {
    78  	// Context is the context to report to reporter for this
    79  	Context Context
    80  	// Reporter if set will receive events for reporting
    81  	Reporter Reporter
    82  }
    83  
    84  // Context provides context about the current mmap for reporting purposes
    85  type Context struct {
    86  	Size     int64
    87  	Name     string
    88  	Metadata map[string]string
    89  }
    90  
    91  // Reporter implements the reporting of mmap.
    92  type Reporter interface {
    93  	// ReportMap reports the mapping of an mmap and allows an error to be
    94  	// returned in case the reporter want's to deny allowing this map call.
    95  	ReportMap(ctx Context) error
    96  	// ReportUnmap reports the unmapping of an mmap and allows an error to be
    97  	// returned in case the reporter want's to deny allowing this unmap call.
    98  	ReportUnmap(ctx Context) error
    99  }
   100  
   101  // FilesResult contains the result of calling MmapFiles
   102  type FilesResult struct {
   103  	Warning error
   104  }
   105  
   106  // Files is a utility function for mmap'ing a group of files at once
   107  func Files(opener FileOpener, files map[string]FileDesc) (FilesResult, error) {
   108  	multiWarn := xerrors.NewMultiError()
   109  	multiErr := xerrors.NewMultiError()
   110  
   111  	for filePath, fileDesc := range files {
   112  		fd, err := opener(filePath)
   113  		if err != nil {
   114  			multiErr = multiErr.Add(errorWithFilename(filePath, err))
   115  			break
   116  		}
   117  
   118  		desc, err := File(fd, fileDesc.Options)
   119  		if err != nil {
   120  			multiErr = multiErr.Add(errorWithFilename(filePath, err))
   121  			break
   122  		}
   123  		if desc.Warning != nil {
   124  			multiWarn = multiWarn.Add(errorWithFilename(filePath, desc.Warning))
   125  		}
   126  
   127  		*fileDesc.File = fd
   128  		*fileDesc.Descriptor = desc
   129  	}
   130  
   131  	if multiErr.FinalError() == nil {
   132  		return FilesResult{Warning: multiWarn.FinalError()}, nil
   133  	}
   134  
   135  	// If we have encountered an error when opening the files,
   136  	// close the ones that have been opened.
   137  	for filePath, fileDesc := range files {
   138  		if *fileDesc.File != nil {
   139  			multiErr = multiErr.Add(errorWithFilename(filePath, (*fileDesc.File).Close()))
   140  		}
   141  		if fileDesc.Descriptor != nil {
   142  			multiErr = multiErr.Add(errorWithFilename(filePath, Munmap(*fileDesc.Descriptor)))
   143  		}
   144  	}
   145  
   146  	return FilesResult{Warning: multiWarn.FinalError()}, multiErr.FinalError()
   147  }
   148  
   149  // File mmap's a file
   150  func File(file *os.File, opts Options) (Descriptor, error) {
   151  	name := file.Name()
   152  	stat, err := os.Stat(name)
   153  	if err != nil {
   154  		return Descriptor{}, fmt.Errorf("mmap file could not stat %s: %v", name, err)
   155  	}
   156  	if stat.IsDir() {
   157  		return Descriptor{}, fmt.Errorf("mmap target is directory: %s", name)
   158  	}
   159  	return mmapFdFn(int64(file.Fd()), 0, stat.Size(), opts)
   160  }
   161  
   162  func errorWithFilename(name string, err error) error {
   163  	if err == nil {
   164  		return nil
   165  	}
   166  	return fmt.Errorf("file %s encountered err: %s", name, err.Error())
   167  }