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