...
Run Format

Source file src/runtime/mem_linux.go

Documentation: runtime

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime
     6  
     7  import (
     8  	"runtime/internal/sys"
     9  	"unsafe"
    10  )
    11  
    12  const (
    13  	_EACCES = 13
    14  	_EINVAL = 22
    15  )
    16  
    17  // Don't split the stack as this method may be invoked without a valid G, which
    18  // prevents us from allocating more stack.
    19  //go:nosplit
    20  func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer {
    21  	p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
    22  	if err != 0 {
    23  		if err == _EACCES {
    24  			print("runtime: mmap: access denied\n")
    25  			exit(2)
    26  		}
    27  		if err == _EAGAIN {
    28  			print("runtime: mmap: too much locked memory (check 'ulimit -l').\n")
    29  			exit(2)
    30  		}
    31  		return nil
    32  	}
    33  	mSysStatInc(sysStat, n)
    34  	return p
    35  }
    36  
    37  func sysUnused(v unsafe.Pointer, n uintptr) {
    38  	// By default, Linux's "transparent huge page" support will
    39  	// merge pages into a huge page if there's even a single
    40  	// present regular page, undoing the effects of the DONTNEED
    41  	// below. On amd64, that means khugepaged can turn a single
    42  	// 4KB page to 2MB, bloating the process's RSS by as much as
    43  	// 512X. (See issue #8832 and Linux kernel bug
    44  	// https://bugzilla.kernel.org/show_bug.cgi?id=93111)
    45  	//
    46  	// To work around this, we explicitly disable transparent huge
    47  	// pages when we release pages of the heap. However, we have
    48  	// to do this carefully because changing this flag tends to
    49  	// split the VMA (memory mapping) containing v in to three
    50  	// VMAs in order to track the different values of the
    51  	// MADV_NOHUGEPAGE flag in the different regions. There's a
    52  	// default limit of 65530 VMAs per address space (sysctl
    53  	// vm.max_map_count), so we must be careful not to create too
    54  	// many VMAs (see issue #12233).
    55  	//
    56  	// Since huge pages are huge, there's little use in adjusting
    57  	// the MADV_NOHUGEPAGE flag on a fine granularity, so we avoid
    58  	// exploding the number of VMAs by only adjusting the
    59  	// MADV_NOHUGEPAGE flag on a large granularity. This still
    60  	// gets most of the benefit of huge pages while keeping the
    61  	// number of VMAs under control. With hugePageSize = 2MB, even
    62  	// a pessimal heap can reach 128GB before running out of VMAs.
    63  	if sys.HugePageSize != 0 {
    64  		var s uintptr = sys.HugePageSize // division by constant 0 is a compile-time error :(
    65  
    66  		// If it's a large allocation, we want to leave huge
    67  		// pages enabled. Hence, we only adjust the huge page
    68  		// flag on the huge pages containing v and v+n-1, and
    69  		// only if those aren't aligned.
    70  		var head, tail uintptr
    71  		if uintptr(v)%s != 0 {
    72  			// Compute huge page containing v.
    73  			head = uintptr(v) &^ (s - 1)
    74  		}
    75  		if (uintptr(v)+n)%s != 0 {
    76  			// Compute huge page containing v+n-1.
    77  			tail = (uintptr(v) + n - 1) &^ (s - 1)
    78  		}
    79  
    80  		// Note that madvise will return EINVAL if the flag is
    81  		// already set, which is quite likely. We ignore
    82  		// errors.
    83  		if head != 0 && head+sys.HugePageSize == tail {
    84  			// head and tail are different but adjacent,
    85  			// so do this in one call.
    86  			madvise(unsafe.Pointer(head), 2*sys.HugePageSize, _MADV_NOHUGEPAGE)
    87  		} else {
    88  			// Advise the huge pages containing v and v+n-1.
    89  			if head != 0 {
    90  				madvise(unsafe.Pointer(head), sys.HugePageSize, _MADV_NOHUGEPAGE)
    91  			}
    92  			if tail != 0 && tail != head {
    93  				madvise(unsafe.Pointer(tail), sys.HugePageSize, _MADV_NOHUGEPAGE)
    94  			}
    95  		}
    96  	}
    97  
    98  	if uintptr(v)&(physPageSize-1) != 0 || n&(physPageSize-1) != 0 {
    99  		// madvise will round this to any physical page
   100  		// *covered* by this range, so an unaligned madvise
   101  		// will release more memory than intended.
   102  		throw("unaligned sysUnused")
   103  	}
   104  
   105  	madvise(v, n, _MADV_DONTNEED)
   106  }
   107  
   108  func sysUsed(v unsafe.Pointer, n uintptr) {
   109  	if sys.HugePageSize != 0 {
   110  		// Partially undo the NOHUGEPAGE marks from sysUnused
   111  		// for whole huge pages between v and v+n. This may
   112  		// leave huge pages off at the end points v and v+n
   113  		// even though allocations may cover these entire huge
   114  		// pages. We could detect this and undo NOHUGEPAGE on
   115  		// the end points as well, but it's probably not worth
   116  		// the cost because when neighboring allocations are
   117  		// freed sysUnused will just set NOHUGEPAGE again.
   118  		var s uintptr = sys.HugePageSize
   119  
   120  		// Round v up to a huge page boundary.
   121  		beg := (uintptr(v) + (s - 1)) &^ (s - 1)
   122  		// Round v+n down to a huge page boundary.
   123  		end := (uintptr(v) + n) &^ (s - 1)
   124  
   125  		if beg < end {
   126  			madvise(unsafe.Pointer(beg), end-beg, _MADV_HUGEPAGE)
   127  		}
   128  	}
   129  }
   130  
   131  // Don't split the stack as this function may be invoked without a valid G,
   132  // which prevents us from allocating more stack.
   133  //go:nosplit
   134  func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) {
   135  	mSysStatDec(sysStat, n)
   136  	munmap(v, n)
   137  }
   138  
   139  func sysFault(v unsafe.Pointer, n uintptr) {
   140  	mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0)
   141  }
   142  
   143  func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer {
   144  	p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
   145  	if err != 0 {
   146  		return nil
   147  	}
   148  	return p
   149  }
   150  
   151  func sysMap(v unsafe.Pointer, n uintptr, sysStat *uint64) {
   152  	mSysStatInc(sysStat, n)
   153  
   154  	p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0)
   155  	if err == _ENOMEM {
   156  		throw("runtime: out of memory")
   157  	}
   158  	if p != v || err != 0 {
   159  		throw("runtime: cannot map pages in arena address space")
   160  	}
   161  }
   162  

View as plain text