2016-03-01 15:57:46 -07:00
|
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
2014-11-11 15:05:02 -07:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
2015-11-11 10:39:30 -07:00
|
|
|
import (
|
|
|
|
"runtime/internal/sys"
|
|
|
|
"unsafe"
|
|
|
|
)
|
2014-11-11 15:05:02 -07:00
|
|
|
|
|
|
|
const (
|
runtime: support smaller physical pages than PhysPageSize
Most operations need an upper bound on the physical page size, which
is what sys.PhysPageSize is for (this is checked at runtime init on
Linux). However, a few operations need a *lower* bound on the physical
page size. Introduce a "minPhysPageSize" constant to act as this lower
bound and use it where it makes sense:
1) In addrspace_free, we have to query each page in the given range.
Currently we increment by the upper bound on the physical page
size, which means we may skip over pages if the true size is
smaller. Worse, we currently pass a result buffer that only has
enough room for one page. If there are actually multiple pages in
the range passed to mincore, the kernel will overflow this buffer.
Fix these problems by incrementing by the lower-bound on the
physical page size and by passing "1" for the length, which the
kernel will round up to the true physical page size.
2) In the write barrier, the bad pointer check tests for pointers to
the first physical page, which are presumably small integers
masquerading as pointers. However, if physical pages are smaller
than we think, we may have legitimate pointers below
sys.PhysPageSize. Hence, use minPhysPageSize for this test since
pointers should never fall below that.
In particular, this applies to ARM64 and MIPS. The runtime is
configured to use 64kB pages on ARM64, but by default Linux uses 4kB
pages. Similarly, the runtime assumes 16kB pages on MIPS, but both 4kB
and 16kB kernel configurations are common. This also applies to ARM on
systems where the runtime is recompiled to deal with a larger page
size. It is also a step toward making the runtime use only a
dynamically-queried page size.
Change-Id: I1fdfd18f6e7cbca170cc100354b9faa22fde8a69
Reviewed-on: https://go-review.googlesource.com/25020
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Austin Clements <austin@google.com>
2016-07-18 14:01:22 -06:00
|
|
|
_EACCES = 13
|
|
|
|
_EINVAL = 22
|
2014-11-11 15:05:02 -07:00
|
|
|
)
|
|
|
|
|
2015-04-16 15:32:18 -06:00
|
|
|
// Don't split the stack as this method may be invoked without a valid G, which
|
|
|
|
// prevents us from allocating more stack.
|
2014-11-11 15:05:02 -07:00
|
|
|
//go:nosplit
|
2015-04-16 15:32:18 -06:00
|
|
|
func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer {
|
2017-10-16 18:28:29 -06:00
|
|
|
p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
|
|
|
|
if err != 0 {
|
|
|
|
if err == _EACCES {
|
2014-11-11 15:05:02 -07:00
|
|
|
print("runtime: mmap: access denied\n")
|
|
|
|
exit(2)
|
|
|
|
}
|
2017-10-16 18:28:29 -06:00
|
|
|
if err == _EAGAIN {
|
2014-11-11 15:05:02 -07:00
|
|
|
print("runtime: mmap: too much locked memory (check 'ulimit -l').\n")
|
|
|
|
exit(2)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-04-16 15:32:18 -06:00
|
|
|
mSysStatInc(sysStat, n)
|
2014-11-11 15:05:02 -07:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
func sysUnused(v unsafe.Pointer, n uintptr) {
|
2015-09-30 09:52:54 -06:00
|
|
|
// By default, Linux's "transparent huge page" support will
|
|
|
|
// merge pages into a huge page if there's even a single
|
|
|
|
// present regular page, undoing the effects of the DONTNEED
|
|
|
|
// below. On amd64, that means khugepaged can turn a single
|
|
|
|
// 4KB page to 2MB, bloating the process's RSS by as much as
|
|
|
|
// 512X. (See issue #8832 and Linux kernel bug
|
|
|
|
// https://bugzilla.kernel.org/show_bug.cgi?id=93111)
|
|
|
|
//
|
|
|
|
// To work around this, we explicitly disable transparent huge
|
|
|
|
// pages when we release pages of the heap. However, we have
|
|
|
|
// to do this carefully because changing this flag tends to
|
|
|
|
// split the VMA (memory mapping) containing v in to three
|
|
|
|
// VMAs in order to track the different values of the
|
|
|
|
// MADV_NOHUGEPAGE flag in the different regions. There's a
|
|
|
|
// default limit of 65530 VMAs per address space (sysctl
|
|
|
|
// vm.max_map_count), so we must be careful not to create too
|
|
|
|
// many VMAs (see issue #12233).
|
|
|
|
//
|
|
|
|
// Since huge pages are huge, there's little use in adjusting
|
|
|
|
// the MADV_NOHUGEPAGE flag on a fine granularity, so we avoid
|
|
|
|
// exploding the number of VMAs by only adjusting the
|
|
|
|
// MADV_NOHUGEPAGE flag on a large granularity. This still
|
|
|
|
// gets most of the benefit of huge pages while keeping the
|
|
|
|
// number of VMAs under control. With hugePageSize = 2MB, even
|
|
|
|
// a pessimal heap can reach 128GB before running out of VMAs.
|
2015-11-11 10:39:30 -07:00
|
|
|
if sys.HugePageSize != 0 {
|
|
|
|
var s uintptr = sys.HugePageSize // division by constant 0 is a compile-time error :(
|
2015-09-30 09:52:54 -06:00
|
|
|
|
|
|
|
// If it's a large allocation, we want to leave huge
|
|
|
|
// pages enabled. Hence, we only adjust the huge page
|
|
|
|
// flag on the huge pages containing v and v+n-1, and
|
|
|
|
// only if those aren't aligned.
|
|
|
|
var head, tail uintptr
|
|
|
|
if uintptr(v)%s != 0 {
|
|
|
|
// Compute huge page containing v.
|
|
|
|
head = uintptr(v) &^ (s - 1)
|
|
|
|
}
|
|
|
|
if (uintptr(v)+n)%s != 0 {
|
|
|
|
// Compute huge page containing v+n-1.
|
|
|
|
tail = (uintptr(v) + n - 1) &^ (s - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note that madvise will return EINVAL if the flag is
|
|
|
|
// already set, which is quite likely. We ignore
|
|
|
|
// errors.
|
2015-11-11 10:39:30 -07:00
|
|
|
if head != 0 && head+sys.HugePageSize == tail {
|
2015-09-30 09:52:54 -06:00
|
|
|
// head and tail are different but adjacent,
|
|
|
|
// so do this in one call.
|
2015-11-11 10:39:30 -07:00
|
|
|
madvise(unsafe.Pointer(head), 2*sys.HugePageSize, _MADV_NOHUGEPAGE)
|
2015-09-30 09:52:54 -06:00
|
|
|
} else {
|
|
|
|
// Advise the huge pages containing v and v+n-1.
|
|
|
|
if head != 0 {
|
2015-11-11 10:39:30 -07:00
|
|
|
madvise(unsafe.Pointer(head), sys.HugePageSize, _MADV_NOHUGEPAGE)
|
2015-09-30 09:52:54 -06:00
|
|
|
}
|
|
|
|
if tail != 0 && tail != head {
|
2015-11-11 10:39:30 -07:00
|
|
|
madvise(unsafe.Pointer(tail), sys.HugePageSize, _MADV_NOHUGEPAGE)
|
2015-09-30 09:52:54 -06:00
|
|
|
}
|
|
|
|
}
|
2015-02-23 14:58:05 -07:00
|
|
|
}
|
2015-09-30 09:52:54 -06:00
|
|
|
|
2016-07-18 10:24:02 -06:00
|
|
|
if uintptr(v)&(physPageSize-1) != 0 || n&(physPageSize-1) != 0 {
|
2016-04-14 11:40:35 -06:00
|
|
|
// madvise will round this to any physical page
|
|
|
|
// *covered* by this range, so an unaligned madvise
|
|
|
|
// will release more memory than intended.
|
|
|
|
throw("unaligned sysUnused")
|
|
|
|
}
|
|
|
|
|
2014-11-11 15:05:02 -07:00
|
|
|
madvise(v, n, _MADV_DONTNEED)
|
|
|
|
}
|
|
|
|
|
|
|
|
func sysUsed(v unsafe.Pointer, n uintptr) {
|
2015-11-11 10:39:30 -07:00
|
|
|
if sys.HugePageSize != 0 {
|
2015-09-30 09:52:54 -06:00
|
|
|
// Partially undo the NOHUGEPAGE marks from sysUnused
|
|
|
|
// for whole huge pages between v and v+n. This may
|
|
|
|
// leave huge pages off at the end points v and v+n
|
|
|
|
// even though allocations may cover these entire huge
|
|
|
|
// pages. We could detect this and undo NOHUGEPAGE on
|
|
|
|
// the end points as well, but it's probably not worth
|
|
|
|
// the cost because when neighboring allocations are
|
|
|
|
// freed sysUnused will just set NOHUGEPAGE again.
|
2015-11-11 10:39:30 -07:00
|
|
|
var s uintptr = sys.HugePageSize
|
2015-09-30 09:52:54 -06:00
|
|
|
|
|
|
|
// Round v up to a huge page boundary.
|
|
|
|
beg := (uintptr(v) + (s - 1)) &^ (s - 1)
|
|
|
|
// Round v+n down to a huge page boundary.
|
|
|
|
end := (uintptr(v) + n) &^ (s - 1)
|
|
|
|
|
|
|
|
if beg < end {
|
|
|
|
madvise(unsafe.Pointer(beg), end-beg, _MADV_HUGEPAGE)
|
|
|
|
}
|
2015-02-23 14:58:05 -07:00
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-04-16 15:32:18 -06:00
|
|
|
// Don't split the stack as this function may be invoked without a valid G,
|
|
|
|
// which prevents us from allocating more stack.
|
|
|
|
//go:nosplit
|
|
|
|
func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) {
|
|
|
|
mSysStatDec(sysStat, n)
|
2014-11-11 15:05:02 -07:00
|
|
|
munmap(v, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
func sysFault(v unsafe.Pointer, n uintptr) {
|
|
|
|
mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0)
|
|
|
|
}
|
|
|
|
|
runtime: remove non-reserved heap logic
Currently large sysReserve calls on some OSes don't actually reserve
the memory, but just check that it can be reserved. This was important
when we called sysReserve to "reserve" many gigabytes for the heap up
front, but now that we map memory in small increments as we need it,
this complication is no longer necessary.
This has one curious side benefit: currently, on Linux, allocations
that are large enough to be rejected by mmap wind up freezing the
application for a long time before it panics. This happens because
sysReserve doesn't reserve the memory, so sysMap calls mmap_fixed,
which calls mmap, which fails because the mapping is too large.
However, mmap_fixed doesn't inspect *why* mmap fails, so it falls back
to probing every page in the desired region individually with mincore
before performing an (otherwise dangerous) MAP_FIXED mapping, which
will also fail. This takes a long time for a large region. Now this
logic is gone, so the mmap failure leads to an immediate panic.
Updates #10460.
Change-Id: I8efe88c611871cdb14f99fadd09db83e0161ca2e
Reviewed-on: https://go-review.googlesource.com/85888
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
2017-12-30 17:35:46 -07:00
|
|
|
func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer {
|
2017-10-16 18:28:29 -06:00
|
|
|
p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
|
|
|
|
if err != 0 {
|
2014-11-11 15:05:02 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
runtime: remove non-reserved heap logic
Currently large sysReserve calls on some OSes don't actually reserve
the memory, but just check that it can be reserved. This was important
when we called sysReserve to "reserve" many gigabytes for the heap up
front, but now that we map memory in small increments as we need it,
this complication is no longer necessary.
This has one curious side benefit: currently, on Linux, allocations
that are large enough to be rejected by mmap wind up freezing the
application for a long time before it panics. This happens because
sysReserve doesn't reserve the memory, so sysMap calls mmap_fixed,
which calls mmap, which fails because the mapping is too large.
However, mmap_fixed doesn't inspect *why* mmap fails, so it falls back
to probing every page in the desired region individually with mincore
before performing an (otherwise dangerous) MAP_FIXED mapping, which
will also fail. This takes a long time for a large region. Now this
logic is gone, so the mmap failure leads to an immediate panic.
Updates #10460.
Change-Id: I8efe88c611871cdb14f99fadd09db83e0161ca2e
Reviewed-on: https://go-review.googlesource.com/85888
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
2017-12-30 17:35:46 -07:00
|
|
|
func sysMap(v unsafe.Pointer, n uintptr, sysStat *uint64) {
|
2015-04-16 15:32:18 -06:00
|
|
|
mSysStatInc(sysStat, n)
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2017-10-16 18:28:29 -06:00
|
|
|
p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0)
|
|
|
|
if err == _ENOMEM {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("runtime: out of memory")
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2017-10-16 18:28:29 -06:00
|
|
|
if p != v || err != 0 {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("runtime: cannot map pages in arena address space")
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
}
|