|
4 | 4 | //
|
5 | 5 | // Audited for iOS 18.0
|
6 | 6 | // Status: WIP
|
| 7 | +// Modified from https://github.com/jcmosc/Compute/blob/0a6b21a4cdeb9bbdd95e7e914c4e18bed37a2456/Sources/ComputeCxx/Data/Table.cpp [MIT License] |
7 | 8 |
|
8 | 9 | #include "table.hpp"
|
9 | 10 | #include "page.hpp"
|
10 |
| -#include "page.hpp" |
| 11 | +#include "page_const.hpp" |
| 12 | +#include "zone.hpp" |
11 | 13 | #include "../Util/assert.hpp"
|
12 | 14 | #include <sys/mman.h>
|
13 | 15 | #include <malloc/malloc.h>
|
@@ -81,36 +83,157 @@ void table::grow_region() OG_NOEXCEPT {
|
81 | 83 | _data_capacity = new_size + page_size;
|
82 | 84 | }
|
83 | 85 |
|
84 |
| -void table::make_pages_reusable(uint32_t page_index, bool flag) OG_NOEXCEPT { |
85 |
| - precondition_failure("TODO"); |
| 86 | +void table::make_pages_reusable(uint32_t page_index, bool reusable) OG_NOEXCEPT { |
| 87 | + static constexpr uint32_t mapped_pages_size = page_size * pages_per_map; // 64 * 512 = 0x8000 |
| 88 | + void *mapped_pages_address = reinterpret_cast<void *>(region_base() + ((page_index * page_size) & ~(mapped_pages_size - 1))); |
| 89 | + int advice = reusable ? MADV_FREE_REUSABLE : MADV_FREE_REUSE; |
| 90 | + madvise(mapped_pages_address, mapped_pages_size, advice); |
| 91 | + static bool unmap_reusable = []() -> bool { |
| 92 | + char *result = getenv("OG_UNMAP_REUSABLE"); |
| 93 | + if (result) { |
| 94 | + return atoi(result) != 0; |
| 95 | + } |
| 96 | + return false; |
| 97 | + }(); |
| 98 | + if (unmap_reusable) { |
| 99 | + int protection = reusable ? PROT_NONE : (PROT_READ | PROT_WRITE); |
| 100 | + mprotect(mapped_pages_address, mapped_pages_size, protection); |
| 101 | + } |
| 102 | + _reusable_pages_num += reusable ? mapped_pages_size : -mapped_pages_size; |
86 | 103 | }
|
87 | 104 |
|
88 |
| -ptr<page> table::alloc_page(zone *zone, uint32_t size) OG_NOEXCEPT { |
89 |
| - precondition_failure("TODO"); |
| 105 | +// TO BE AUDITED |
| 106 | +ptr<page> table::alloc_page(zone *zone, uint32_t needed_size) OG_NOEXCEPT { |
| 107 | + lock(); |
| 108 | + |
| 109 | + uint32_t needed_pages = (needed_size + page_mask) / page_size; |
| 110 | + |
| 111 | + // assume we'll have to append a new page |
| 112 | + uint32_t new_page_index = _page_maps.size() * pages_per_map; |
| 113 | + |
| 114 | + // scan for consecutive free pages |
| 115 | + if (!_page_maps.empty() && _used_pages_num <= _page_maps.size() * pages_per_map) { |
| 116 | + |
| 117 | + uint32_t start_map_index = _map_search_start; |
| 118 | + for (int i = 0; i < _page_maps.size(); i++) { |
| 119 | + int map_index = start_map_index + i; |
| 120 | + if (map_index >= _page_maps.size()) { |
| 121 | + map_index -= _page_maps.size(); // wrap around |
| 122 | + } |
| 123 | + |
| 124 | + page_map_type free_pages_map = _page_maps[map_index].flip(); |
| 125 | + while (free_pages_map.any()) { |
| 126 | + |
| 127 | + int candidate_bit = std::countr_zero(static_cast<uint64_t>(free_pages_map.to_ullong())); |
| 128 | + |
| 129 | + // scan ahead to find enough consecutive free pages |
| 130 | + bool found = false; |
| 131 | + if (needed_pages > 1) { |
| 132 | + for (int j = 1; j < needed_pages; j++) { |
| 133 | + int next_page_index = (map_index * pages_per_map) + candidate_bit + j; |
| 134 | + int next_map_index = next_page_index / pages_per_map; |
| 135 | + if (next_map_index == _page_maps.size()) { |
| 136 | + // There are not enough maps, but the trailing pages are contiguous so this page is |
| 137 | + // usable |
| 138 | + found = true; |
| 139 | + break; |
| 140 | + } |
| 141 | + if (_page_maps[next_map_index].test(next_page_index % pages_per_map)) { |
| 142 | + // next page is used, remove this page from free_pages_map |
| 143 | + free_pages_map.reset(candidate_bit); |
| 144 | + break; |
| 145 | + } |
| 146 | + } |
| 147 | + found = true; |
| 148 | + } else { |
| 149 | + // only need one page |
| 150 | + found = true; |
| 151 | + } |
| 152 | + |
| 153 | + if (found) { |
| 154 | + new_page_index = (map_index * pages_per_map) + candidate_bit; |
| 155 | + _map_search_start = map_index; |
| 156 | + break; |
| 157 | + } |
| 158 | + } |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + // update maps |
| 163 | + for (int i = 0; i < needed_pages; i++) { |
| 164 | + uint32_t page_index = new_page_index + i; |
| 165 | + uint32_t map_index = page_index / pages_per_map; |
| 166 | + |
| 167 | + if (map_index == _page_maps.size()) { |
| 168 | + _page_maps.push_back(0); |
| 169 | + _page_metadata_maps.push_back(0); |
| 170 | + } else if (_page_maps[map_index] == 0) { |
| 171 | + make_pages_reusable(page_index, false); |
| 172 | + } |
| 173 | + |
| 174 | + _page_maps[map_index].set(page_index % pages_per_map); |
| 175 | + if (i == 0) { |
| 176 | + _page_metadata_maps[map_index].set(page_index % pages_per_map); |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + _used_pages_num += needed_pages; |
| 181 | + |
| 182 | + uint32_t new_region_size = (new_page_index + needed_pages) * page_size; |
| 183 | + while (region_capacity() < new_region_size) { |
| 184 | + grow_region(); |
| 185 | + } |
| 186 | + |
| 187 | + // ptr offsets are "one"-based, so that we can treat 0 as null. |
| 188 | + ptr<page> new_page = ptr<page>((new_page_index + 1) * page_size); |
| 189 | + new_page->zone = zone; |
| 190 | + new_page->previous = nullptr; |
| 191 | + new_page->total = (needed_size + page_mask) & page_alignment; |
| 192 | + new_page->in_use = sizeof(page); |
| 193 | + unlock(); |
| 194 | + return new_page; |
90 | 195 | }
|
91 | 196 |
|
| 197 | +// TO BE AUDITED |
92 | 198 | void table::dealloc_page_locked(ptr<page> page) OG_NOEXCEPT {
|
93 |
| - precondition_failure("TODO"); |
| 199 | + int32_t total_bytes = page->total; |
| 200 | + int32_t num_pages = total_bytes / page_size; |
| 201 | + |
| 202 | + _used_pages_num -= num_pages; |
| 203 | + |
| 204 | + // convert the page address (starts at 512) to an index (starts at 0) |
| 205 | + int32_t page_index = (page / page_size) - 1; |
| 206 | + for (int32_t i = 0; i != num_pages; i += 1) { |
| 207 | + |
| 208 | + int32_t next_page_index = page_index + i; |
| 209 | + int32_t next_map_index = next_page_index / pages_per_map; |
| 210 | + |
| 211 | + _page_maps[next_map_index].reset(next_page_index % pages_per_map); |
| 212 | + if (i == 0) { |
| 213 | + _page_metadata_maps[next_map_index].reset(next_page_index % pages_per_map); |
| 214 | + } |
| 215 | + |
| 216 | + if (_page_maps[next_map_index].none()) { |
| 217 | + make_pages_reusable(next_page_index, true); |
| 218 | + } |
| 219 | + } |
94 | 220 | }
|
95 | 221 |
|
| 222 | +// TO BE AUDITED |
96 | 223 | uint64_t table::raw_page_seed(ptr<page> page) OG_NOEXCEPT {
|
97 |
| - precondition_failure("TODO"); |
98 |
| -// page.assert_valid(); |
99 |
| -// |
100 |
| -// lock(); |
101 |
| -// |
102 |
| -// uint32_t page_index = (page / page_size) - 1; |
103 |
| -// uint32_t map_index = page_index / pages_per_map; |
104 |
| -// |
105 |
| -// uint64_t result = 0; |
106 |
| -// if (map_index < _page_metadata_maps.size() && _page_metadata_maps[map_index].test(page_index % page_size)) { |
107 |
| -// auto raw_zone_info = page->zone->info().to_raw_value(); |
108 |
| -// result = raw_zone_info | (1 < 8); |
109 |
| -// } |
110 |
| -// |
111 |
| -// unlock(); |
112 |
| -// |
113 |
| -// return result; |
| 224 | + page.assert_valid(*this); |
| 225 | + lock(); |
| 226 | + uint32_t page_index = (page / page_size) - 1; |
| 227 | + uint32_t map_index = page_index / pages_per_map; |
| 228 | + |
| 229 | + // FIXME |
| 230 | + uint64_t result = 0; |
| 231 | + if (map_index < _page_metadata_maps.size() && _page_metadata_maps[map_index].test(page_index % page_size)) { |
| 232 | + auto raw_zone_info = page->zone->info().to_raw_value(); |
| 233 | + result = raw_zone_info | (1 < 8); |
| 234 | + } |
| 235 | + unlock(); |
| 236 | + return result; |
114 | 237 | }
|
115 | 238 |
|
116 | 239 | void table::print() const OG_NOEXCEPT {
|
|
0 commit comments