1// Copyright 2022 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
5package runtime
6
7import "unsafe"
8
9// OS memory management abstraction layer
10//
11// Regions of the address space managed by the runtime may be in one of four
12// states at any given time:
13// 1) None - Unreserved and unmapped, the default state of any region.
14// 2) Reserved - Owned by the runtime, but accessing it would cause a fault.
15//               Does not count against the process' memory footprint.
16// 3) Prepared - Reserved, intended not to be backed by physical memory (though
17//               an OS may implement this lazily). Can transition efficiently to
18//               Ready. Accessing memory in such a region is undefined (may
19//               fault, may give back unexpected zeroes, etc.).
20// 4) Ready - may be accessed safely.
21//
22// This set of states is more than is strictly necessary to support all the
23// currently supported platforms. One could get by with just None, Reserved, and
24// Ready. However, the Prepared state gives us flexibility for performance
25// purposes. For example, on POSIX-y operating systems, Reserved is usually a
26// private anonymous mmap'd region with PROT_NONE set, and to transition
27// to Ready would require setting PROT_READ|PROT_WRITE. However the
28// underspecification of Prepared lets us use just MADV_FREE to transition from
29// Ready to Prepared. Thus with the Prepared state we can set the permission
30// bits just once early on, we can efficiently tell the OS that it's free to
31// take pages away from us when we don't strictly need them.
32//
33// This file defines a cross-OS interface for a common set of helpers
34// that transition memory regions between these states. The helpers call into
35// OS-specific implementations that handle errors, while the interface boundary
36// implements cross-OS functionality, like updating runtime accounting.
37
38// sysAlloc transitions an OS-chosen region of memory from None to Ready.
39// More specifically, it obtains a large chunk of zeroed memory from the
40// operating system, typically on the order of a hundred kilobytes
41// or a megabyte. This memory is always immediately available for use.
42//
43// sysStat must be non-nil.
44//
45// Don't split the stack as this function may be invoked without a valid G,
46// which prevents us from allocating more stack.
47//
48//go:nosplit
49func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer {
50	sysStat.add(int64(n))
51	gcController.mappedReady.Add(int64(n))
52	return sysAllocOS(n)
53}
54
55// sysUnused transitions a memory region from Ready to Prepared. It notifies the
56// operating system that the physical pages backing this memory region are no
57// longer needed and can be reused for other purposes. The contents of a
58// sysUnused memory region are considered forfeit and the region must not be
59// accessed again until sysUsed is called.
60func sysUnused(v unsafe.Pointer, n uintptr) {
61	gcController.mappedReady.Add(-int64(n))
62	sysUnusedOS(v, n)
63}
64
65// sysUsed transitions a memory region from Prepared to Ready. It notifies the
66// operating system that the memory region is needed and ensures that the region
67// may be safely accessed. This is typically a no-op on systems that don't have
68// an explicit commit step and hard over-commit limits, but is critical on
69// Windows, for example.
70//
71// This operation is idempotent for memory already in the Prepared state, so
72// it is safe to refer, with v and n, to a range of memory that includes both
73// Prepared and Ready memory. However, the caller must provide the exact amount
74// of Prepared memory for accounting purposes.
75func sysUsed(v unsafe.Pointer, n, prepared uintptr) {
76	gcController.mappedReady.Add(int64(prepared))
77	sysUsedOS(v, n)
78}
79
80// sysHugePage does not transition memory regions, but instead provides a
81// hint to the OS that it would be more efficient to back this memory region
82// with pages of a larger size transparently.
83func sysHugePage(v unsafe.Pointer, n uintptr) {
84	sysHugePageOS(v, n)
85}
86
87// sysNoHugePage does not transition memory regions, but instead provides a
88// hint to the OS that it would be less efficient to back this memory region
89// with pages of a larger size transparently.
90func sysNoHugePage(v unsafe.Pointer, n uintptr) {
91	sysNoHugePageOS(v, n)
92}
93
94// sysHugePageCollapse attempts to immediately back the provided memory region
95// with huge pages. It is best-effort and may fail silently.
96func sysHugePageCollapse(v unsafe.Pointer, n uintptr) {
97	sysHugePageCollapseOS(v, n)
98}
99
100// sysFree transitions a memory region from any state to None. Therefore, it
101// returns memory unconditionally. It is used if an out-of-memory error has been
102// detected midway through an allocation or to carve out an aligned section of
103// the address space. It is okay if sysFree is a no-op only if sysReserve always
104// returns a memory region aligned to the heap allocator's alignment
105// restrictions.
106//
107// sysStat must be non-nil.
108//
109// Don't split the stack as this function may be invoked without a valid G,
110// which prevents us from allocating more stack.
111//
112//go:nosplit
113func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
114	sysStat.add(-int64(n))
115	gcController.mappedReady.Add(-int64(n))
116	sysFreeOS(v, n)
117}
118
119// sysFault transitions a memory region from Ready to Reserved. It
120// marks a region such that it will always fault if accessed. Used only for
121// debugging the runtime.
122//
123// TODO(mknyszek): Currently it's true that all uses of sysFault transition
124// memory from Ready to Reserved, but this may not be true in the future
125// since on every platform the operation is much more general than that.
126// If a transition from Prepared is ever introduced, create a new function
127// that elides the Ready state accounting.
128func sysFault(v unsafe.Pointer, n uintptr) {
129	gcController.mappedReady.Add(-int64(n))
130	sysFaultOS(v, n)
131}
132
133// sysReserve transitions a memory region from None to Reserved. It reserves
134// address space in such a way that it would cause a fatal fault upon access
135// (either via permissions or not committing the memory). Such a reservation is
136// thus never backed by physical memory.
137//
138// If the pointer passed to it is non-nil, the caller wants the
139// reservation there, but sysReserve can still choose another
140// location if that one is unavailable.
141//
142// NOTE: sysReserve returns OS-aligned memory, but the heap allocator
143// may use larger alignment, so the caller must be careful to realign the
144// memory obtained by sysReserve.
145func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer {
146	return sysReserveOS(v, n)
147}
148
149// sysMap transitions a memory region from Reserved to Prepared. It ensures the
150// memory region can be efficiently transitioned to Ready.
151//
152// sysStat must be non-nil.
153func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
154	sysStat.add(int64(n))
155	sysMapOS(v, n)
156}
157