xref: /xnu-12377.81.4/osfmk/mach/vm_reclaim.h (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2021 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #ifndef _VM_RECLAIM_H_
30 #define _VM_RECLAIM_H_
31 
32 #if defined(__LP64__)
33 
34 #include <Availability.h>
35 #include <mach/error.h>
36 #include <mach/mach_types.h>
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <sys/cdefs.h>
40 
41 __BEGIN_DECLS
42 
43 /// The action to be performed by the kernel on reclamation of an entry
44 ///  - `VM_RECLAIM_FREE` - Free the backing memory contents but preserve the
45 ///    mapping (analagous to `madvise(MADV_FREE_REUSABLE)`)
46 ///  - `VM_RECLAIM_DEALLOCATE` - Deallocate the virtual mapping and
47 ///    its backing memory (analagous to `munmap()`)
48 __enum_decl(mach_vm_reclaim_action_t, uint8_t, {
49 	VM_RECLAIM_FREE       = 1,
50 	VM_RECLAIM_DEALLOCATE = 2,
51 });
52 
53 /// Describes the state of a memory region in the ring
54 __enum_decl(mach_vm_reclaim_state_t, uint32_t, {
55 	VM_RECLAIM_UNRECLAIMED = 1,
56 	VM_RECLAIM_FREED       = 2,
57 	VM_RECLAIM_DEALLOCATED = 3,
58 	VM_RECLAIM_BUSY        = 4,
59 });
60 
61 #define err_vm_reclaim(e) (err_vm | err_sub(1) | e)
62 
63 __enum_decl(mach_vm_reclaim_error_t, mach_error_t, {
64 	VM_RECLAIM_SUCCESS             = ERR_SUCCESS,
65 	VM_RECLAIM_INVALID_ARGUMENT    = err_vm_reclaim(1),
66 	VM_RECLAIM_NOT_SUPPORTED       = err_vm_reclaim(2),
67 	VM_RECLAIM_INVALID_REGION_SIZE = err_vm_reclaim(3),
68 	VM_RECLAIM_INVALID_CAPACITY    = err_vm_reclaim(4),
69 	VM_RECLAIM_INVALID_ID          = err_vm_reclaim(5),
70 	VM_RECLAIM_RESOURCE_SHORTAGE   = err_vm_reclaim(6),
71 	VM_RECLAIM_INVALID_RING        = err_vm_reclaim(7),
72 });
73 
74 /// The handle for a deferred reclamation ring
75 typedef struct mach_vm_reclaim_ring_s *mach_vm_reclaim_ring_t;
76 
77 /// Counts a number of memory regions ("entries") that can be represented in a
78 /// ring
79 typedef uint32_t mach_vm_reclaim_count_t;
80 
81 /// A unique id representing a memory region ("entry") placed into a
82 /// reclamation ring
83 typedef uint64_t mach_vm_reclaim_id_t;
84 
85 #if !KERNEL
86 /// A null-value reclaim ID. May be used to distinguish regions that have not
87 /// yet been entered into a ring from those that have.
88 #define VM_RECLAIM_ID_NULL UINT64_MAX
89 
90 /// The maximum virtual size supported for an individual region to be marked free.
91 #define VM_RECLAIM_REGION_SIZE_MAX ((mach_vm_size_t)UINT32_MAX)
92 
93 /// Allocate & initialize a deferred reclamation ring.
94 ///
95 /// Will allocate and initialize a ring to be shared with the kernel.
96 /// Only one ring may be initialized per task. Not all platforms/devices
97 /// support deferred reclamation.
98 ///
99 /// It is recommended that callers start with a modestly sized initial capacity
100 /// and resize as the capacity is exhausted to minimize unneeded memory usage.
101 /// Callers should pass capacities that have been rounded via
102 /// ``mach_vm_reclaim_round_capacity()``.
103 ///
104 /// - Parameters:
105 ///   - ring: a handle to the newly allocated reclaim ring (out)
106 ///   - initial_capacity: The initial capacity (in number of regions) of the
107 ///     reclaim ring to allocate
108 ///   - max_capacity: The maximum capacity that the ring may eventually grow
109 ///     to with ``mach_vm_reclaim_ring_resize()``.
110 ///
111 /// - Returns: If the current device does not support deferred reclamation,
112 ///   returns `VM_RECLAIM_UNSUPPORTED`. If the provided max_capacity is not
113 ///   properly rounded and exceeds system limits, returns
114 ///   `VM_RECLAIM_INVALID_CAPACITY`. If a ring has already been instantiaed,
115 ///   returns `VM_RECLAIM_RESOURCE_SHORTAGE`.
116 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
117 mach_vm_reclaim_error_t mach_vm_reclaim_ring_allocate(
118 	mach_vm_reclaim_ring_t *ring,
119 	mach_vm_reclaim_count_t initial_capacity,
120 	mach_vm_reclaim_count_t max_capacity);
121 
122 /// Re-size a deferred reclamation ring.
123 ///
124 /// Note that all outstanding reclamation requests will be completed as part of
125 /// the resize operation.
126 ///
127 /// `mach_vm_reclaim_resize()` is *not* thread-safe w.r.t. itself and other
128 /// reclamation operations (i.e. ``mach_vm_reclaim_try_enter()``,
129 /// ``mach_vm_reclaim_try_cancel()``). Callers must provide their own
130 /// synchronization.
131 ///
132 /// - Parameters:
133 ///   - ring: The ring to resize.
134 ///   - capacity: The new capacity (in number of regions). Must be <= the max
135 ///     capacity specified during ring allocation.
136 ///
137 /// - Returns: If the requested capacity exceeds the maximum capacity specified
138 ///   when the ring was allocated, returns `VM_RECLAIM_INVALID_CAPACITY`.
139 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
140 mach_vm_reclaim_error_t mach_vm_reclaim_ring_resize(
141 	mach_vm_reclaim_ring_t ring,
142 	mach_vm_reclaim_count_t capacity);
143 
144 /// Get the maximum number of memory regions that can be simultaneously placed
145 /// (i.e. marked free) in the ring.
146 ///
147 /// - Parameters:
148 ///   - ring: a reclaim ring
149 ///   - capacity: the capacity of the specified ring (out)
150 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
151 mach_vm_reclaim_error_t mach_vm_reclaim_ring_capacity(
152 	mach_vm_reclaim_ring_t ring,
153 	mach_vm_reclaim_count_t *capacity);
154 
155 /// Round the given ring capacity to the maximum size that could fit
156 /// within the closest vm page size multiple. Will round down if the requested
157 /// capacity exceeds the maximum allowable capacity.
158 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
159 mach_vm_reclaim_count_t mach_vm_reclaim_round_capacity(
160 	mach_vm_reclaim_count_t capacity);
161 
162 /// Force the kernel to reclaim at least num_entries_to_reclaim entries from
163 /// the ring (if present).
164 ///
165 /// ``mach_vm_reclaim_synchronize()`` _is_ thread-safe w.r.t. all other
166 /// mach_vm_reclaim operations.
167 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
168 mach_vm_reclaim_error_t mach_vm_reclaim_ring_flush(
169 	mach_vm_reclaim_ring_t ring,
170 	mach_vm_reclaim_count_t num_entries_to_reclaim);
171 
172 /// Attempt to enter a reclamation request into the ring.
173 ///
174 /// This will update the userspace reclaim ring accounting, but will not
175 /// inform the kernel about the new bytes in the ring. If the kernel should be informed,
176 /// should_update_kernel_accounting will be set to true and the caller should call
177 /// ``mach_vm_reclaim_update_kernel_accounting()``.
178 /// ``mach_vm_reclaim_update_kernel_accounting()`` may result in synchronous
179 /// reclamation operations, so this gives the caller an opportunity to first
180 /// drop any locks.
181 ///
182 /// The `id` in/out parameter may be used to place the memory region in an
183 /// otherwise unused entry in the ring previously associated with a request
184 /// that has been cancelled. This interface will provide a maximally compact
185 /// ring, minimizing the likelihood of exhausting the ring's capacity. This
186 /// efficiency comes at the cost of LRU approximation because reclamations will
187 /// always occur in ascending order of ID. If a non-null ID is specified, the
188 /// caller must ensure it is not currently occupied by another memory region
189 /// (i.e. the caller must have called ``mach_vm_reclaim_mark_used()`` on this
190 /// ID since the last free operation).
191 ///
192 /// If the ring is full, the caller may wish to synchronously reclaim part
193 /// of the ring via ``mach_vm_reclaim_flush()`` or attempt to grow the ring
194 /// via ``mach_vm_reclaim_ring_resize()``.
195 ///
196 /// `mach_vm_reclaim_try_enter()` is *not* thread-safe w.r.t. itself and other
197 /// reclamation operations (i.e. ``mach_vm_reclaim_resize()``,
198 /// ``mach_vm_reclaim_try_cancel()``). Callers must provide their own
199 /// synchronization.
200 ///
201 /// - Parameters:
202 ///   - ring: The ring in which to place the memory region
203 ///   - region_start: The starting address of the memory region to be freed
204 ///   - region_size: The size of the memory region to be freed (in bytes) --
205 ///     must be <= ``MACH_VM_RECLAIM_REGION_SIZE_MAX``.
206 ///   - action: How to reclaim the entry. See ``mach_vm_reclaim_action_t``.
207 ///   - id: (in/out) The desired ID of the reclaim entry for later re-use. If the
208 ///     requested ID is ``VM_RECLAIM_ID_NULL``, then an new ID will be
209 ///     chosen and written out on success. If the specified ID is unavailable
210 ///     or no ID was specified and the ring is at capacity, then
211 ///     ``VM_RECLAIM_ID_NULL`` will be written out.
212 ///   - should_update_kernel_accounting: Out-parameter indicating if kernel
213 ///     accounting should be updated via
214 ///     ``mach_vm_reclaim_update_kernel_accounting()``
215 ///
216 /// - Returns: If region_size` is greater than ``VM_RECLAIM_REGION_SIZE_MAX``,
217 ///   returns `VM_RECLAIM_INVALID_REGION_SIZE`.
218 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
219 mach_vm_reclaim_error_t mach_vm_reclaim_try_enter(
220 	mach_vm_reclaim_ring_t ring,
221 	mach_vm_address_t region_start,
222 	mach_vm_size_t region_size,
223 	mach_vm_reclaim_action_t action,
224 	mach_vm_reclaim_id_t *id,
225 	bool *should_update_kernel_accounting);
226 
227 /// Attempt to cancel a previously entered reclamation request.
228 ///
229 /// This operation will attempt to remove the request from the ring, ensuring
230 /// the memory region will not be reclaimed. The state of the memory region after the
231 /// cancellation attempt will be written out to `state` on success. Callers
232 /// should check if the memory region is safe to re-use via
233 /// ``mach_vm_reclaim_is_reusable()``.
234 ///
235 /// Subsequent calls to ``mach_vm_reclaim_try_cancel()`` with the same id will
236 /// result in undefined behavior.
237 ///
238 /// This will update the userspace reclaim ring accounting, but will not
239 /// inform the kernel about the new bytes in the ring. If the kernel should be informed,
240 /// should_update_kernel_accounting will be set to true and the caller should call
241 /// ``mach_vm_reclaim_update_kernel_accounting()``. That syscall might reclaim the ring, so
242 /// this gives the caller an opportunity to first drop any locks.
243 ///
244 /// `mach_vm_reclaim_try_cancel()` is *not* thread-safe w.r.t. itself and other
245 /// reclamation operations (i.e. ``mach_vm_reclaim_resize()``,
246 /// ``mach_vm_reclaim_try_enter()``. Callers must provide their own
247 /// synchronization.
248 ///
249 ///  - Parameters:
250 ///    - ring: The ring to re-use the entry from
251 ///    - id: The unique id of the entry to re-use
252 ///    - region_start: The virtual address of the memory region to re-use. Used to
253 ///      assert that the entry is the same one originally placed in the ring
254 ///    - region_size: The virtual size of the region to re-use. Used to assert that
255 ///      the re-used entry is the same one the caller expects
256 ///    - action: The reclamation action requested when the reclamation request was entered.
257 ///    - state: The state of the memory region after the cancellation request (out).
258 ///    - should_update_kernel_accounting: Out-parameter indicating if kernel
259 ///      accounting should be updated via
260 ///      ``mach_vm_reclaim_update_kernel_accounting()``
261 ///
262 ///  - Returns: `VM_RECLAIM_SUCCESS` on success
263 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
264 mach_vm_reclaim_error_t mach_vm_reclaim_try_cancel(
265 	mach_vm_reclaim_ring_t ring,
266 	mach_vm_reclaim_id_t id,
267 	mach_vm_address_t region_start,
268 	mach_vm_size_t region_size,
269 	mach_vm_reclaim_action_t action,
270 	mach_vm_reclaim_state_t *state,
271 	bool *should_update_kernel_accounting);
272 
273 /// Query the current state of region specified by a given reclaim ID.
274 ///
275 /// Note that this a read-only, thread-safe operation that may race with other
276 /// threads. For example, a state of `VM_RECLAIM_UNRECLAIMED` does not
277 /// guarantee that the region will not be immediately reclaimed or busied by
278 /// another thread.
279 ///
280 /// - Parameters:
281 ///   - ring: The reclaim ring containing the memory region
282 ///   - id: The ID of the region whose state to query
283 ///   - action: The reclaim action specified when the memory region was freed
284 ///     to the ring
285 ///   - state: The state of the memory region will be written out on success
286 ///
287 /// - Returns: `VM_RECLAIM_SUCCESS` on success.
288 mach_vm_reclaim_error_t mach_vm_reclaim_query_state(
289 	mach_vm_reclaim_ring_t ring,
290 	mach_vm_reclaim_id_t id,
291 	mach_vm_reclaim_action_t action,
292 	mach_vm_reclaim_state_t *state);
293 
294 /// Return whether the given memory region state is safe for re-use.
295 bool mach_vm_reclaim_is_reusable(
296 	mach_vm_reclaim_state_t state);
297 
298 /// Let the kernel know how much VA is in the ring.
299 ///
300 /// The kernel may choose to reclaim from the ring on this thread.
301 /// This should be called whenever `mach_vm_reclaim_mark_[free|used]()` returns true in
302 /// `should_update_kernel_accounting`. It may be called at any other time
303 /// if the caller wants to update the kernel's accounting and is
304 /// thread safe w.r.t. all other mach_vm_reclaim calls.
305 __SPI_AVAILABLE(macos(15.4), ios(18.4), tvos(18.4), visionos(2.4))
306 mach_vm_reclaim_error_t mach_vm_reclaim_update_kernel_accounting(
307 	mach_vm_reclaim_ring_t ring);
308 
309 #endif // !KERNEL
310 
311 __END_DECLS
312 
313 #endif // __LP64__
314 
315 #endif // _VM_RECLAIM_H_
316