| 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 | #ifdef PRIVATE |
| 33 | #if defined(__LP64__) |
| 34 | |
| 35 | #include <mach/mach_types.h> |
| 36 | #include <mach/kern_return.h> |
| 37 | #include <stdbool.h> |
| 38 | |
| 39 | __BEGIN_DECLS |
| 40 | |
| 41 | typedef struct mach_vm_reclaim_indices_v1_s { |
| 42 | _Atomic uint64_t head; |
| 43 | _Atomic uint64_t tail; |
| 44 | _Atomic uint64_t busy; |
| 45 | } mach_vm_reclaim_indices_v1_t; |
| 46 | |
| 47 | // The action to be performed by the kernel on reclamation of an entry |
| 48 | __enum_decl(mach_vm_reclaim_behavior_v1_t, uint16_t, { |
| 49 | // Deallocate (unmap) the entry |
| 50 | MACH_VM_RECLAIM_DEALLOCATE = 0, |
| 51 | // Mark the entry as clean and leave mapped (VM_BEHAVIOR_REUSABLE) |
| 52 | MACH_VM_RECLAIM_REUSABLE = 1, |
| 53 | }); |
| 54 | |
| 55 | typedef struct mach_vm_reclaim_entry_v1_s { |
| 56 | mach_vm_address_t address; |
| 57 | uint32_t size; |
| 58 | mach_vm_reclaim_behavior_v1_t behavior; |
| 59 | uint16_t flags; |
| 60 | } mach_vm_reclaim_entry_v1_t; |
| 61 | |
| 62 | /* |
| 63 | * Contains the data used for synchronization with the kernel. This structure |
| 64 | * should be page-aligned. |
| 65 | */ |
| 66 | typedef struct mach_vm_reclaim_buffer_v1_s { |
| 67 | mach_vm_reclaim_indices_v1_t indices; |
| 68 | /* align to multiple of entry size */ |
| 69 | uint64_t _unused; |
| 70 | /* |
| 71 | * The ringbuffer entries themselves populate the remainder of this |
| 72 | * buffer's vm allocation. |
| 73 | */ |
| 74 | mach_vm_reclaim_entry_v1_t entries[0]; |
| 75 | } *mach_vm_reclaim_buffer_v1_t; |
| 76 | |
| 77 | #if !KERNEL |
| 78 | #define VM_RECLAIM_INDEX_NULL UINT64_MAX |
| 79 | |
| 80 | /* |
| 81 | * Userspace interface for placing items in the reclamation buffer and trying to take them back out. |
| 82 | * Note that these interfaces are NOT thread safe. It is the caller's responsibility to synchronize concurrent |
| 83 | * operations on the same buffer. |
| 84 | * |
| 85 | * These operations are implemented in libsyscall. |
| 86 | */ |
| 87 | |
| 88 | typedef struct mach_vm_reclaim_ringbuffer_v1_s { |
| 89 | mach_vm_reclaim_buffer_v1_t buffer; |
| 90 | mach_vm_size_t buffer_len; |
| 91 | uint64_t va_in_buffer; |
| 92 | uint64_t last_accounting_given_to_kernel; |
| 93 | } *mach_vm_reclaim_ringbuffer_v1_t; |
| 94 | |
| 95 | kern_return_t mach_vm_reclaim_ringbuffer_init(mach_vm_reclaim_ringbuffer_v1_t ringbuffer); |
| 96 | |
| 97 | /* |
| 98 | * Mark the given range as free. |
| 99 | * Returns a unique identifier for the range that can be used by reclaim_mark_used |
| 100 | * This will update the userspace reclaim buffer accounting, but will not |
| 101 | * inform the kernel about the new bytes in the buffer. If the kernel should be informed, |
| 102 | * should_update_kernel_accounting will be set to true and the caller should call |
| 103 | * mach_vm_reclaim_update_kernel_accounting. That syscall might reclaim the buffer, so |
| 104 | * this gives the caller an opportunity to first drop any locks. |
| 105 | */ |
| 106 | uint64_t mach_vm_reclaim_mark_free( |
| 107 | mach_vm_reclaim_ringbuffer_v1_t buffer, |
| 108 | mach_vm_address_t start_addr, |
| 109 | uint32_t size, |
| 110 | mach_vm_reclaim_behavior_v1_t behavior, |
| 111 | bool *should_update_kernel_accounting); |
| 112 | |
| 113 | /* |
| 114 | * Attempt to take back the range determined by id. |
| 115 | * Returns true iff range can now be used. |
| 116 | * Subsequent calls to reclaim_mark_used with the same id are not supported & may return true or false. |
| 117 | */ |
| 118 | bool mach_vm_reclaim_mark_used( |
| 119 | mach_vm_reclaim_ringbuffer_v1_t buffer, |
| 120 | uint64_t id, |
| 121 | mach_vm_address_t start_addr, |
| 122 | uint32_t size); |
| 123 | |
| 124 | /* |
| 125 | * Check if the range is available for re-use. |
| 126 | * Returns true if the range is still available. Note that this doesn't claim the range, so it may be reclaimed in parallel. |
| 127 | * Note that a return value of false does not guarantee that the kernel has reclaimed the range already (it may just be considering it). |
| 128 | */ |
| 129 | bool mach_vm_reclaim_is_available( |
| 130 | const mach_vm_reclaim_ringbuffer_v1_t buffer, |
| 131 | uint64_t id); |
| 132 | |
| 133 | /* |
| 134 | * Check if the range has been reclaimed. |
| 135 | * Returns true if the range is no longer available for re-use. |
| 136 | */ |
| 137 | bool mach_vm_reclaim_is_reclaimed( |
| 138 | const mach_vm_reclaim_ringbuffer_v1_t buffer, |
| 139 | uint64_t id); |
| 140 | |
| 141 | /* |
| 142 | * Force the kernel to reclaim at least num_entries_to_reclaim entries from the ringbuffer (if present). |
| 143 | * Note that mach_vm_reclaim_mark_free automatically handles the full ringbuffer case. |
| 144 | */ |
| 145 | kern_return_t mach_vm_reclaim_synchronize( |
| 146 | mach_vm_reclaim_ringbuffer_v1_t ringbuffer, |
| 147 | mach_vm_size_t num_entries_to_reclaim); |
| 148 | |
| 149 | /* |
| 150 | * Let the kernel know how much VA is in the ringbuffer. |
| 151 | * The kernel may choose to reclaim from the ringbuffer on this thread. |
| 152 | * This should be called whenever mach_vm_reclaim_mark_free returns true in |
| 153 | * should_update_kernel_accounting. It may be called at any other time |
| 154 | * if the caller wants to update the kernel's accounting & is |
| 155 | * thread safe w.r.t. all other mach_vm_reclaim calls. |
| 156 | */ |
| 157 | kern_return_t mach_vm_reclaim_update_kernel_accounting( |
| 158 | const mach_vm_reclaim_ringbuffer_v1_t ring_buffer); |
| 159 | |
| 160 | #endif /* !KENREL */ |
| 161 | |
| 162 | __END_DECLS |
| 163 | |
| 164 | #endif /* PRIVATE */ |
| 165 | |
| 166 | #endif /* __LP64__ */ |
| 167 | |
| 168 | #endif /* _VM_RECLAIM_H_ */ |
| 169 | |