1 1.1 christos #ifndef JEMALLOC_INTERNAL_HPDATA_H 2 1.1 christos #define JEMALLOC_INTERNAL_HPDATA_H 3 1.1 christos 4 1.1 christos #include "jemalloc/internal/fb.h" 5 1.1 christos #include "jemalloc/internal/ph.h" 6 1.1 christos #include "jemalloc/internal/ql.h" 7 1.1 christos #include "jemalloc/internal/typed_list.h" 8 1.1 christos 9 1.1 christos /* 10 1.1 christos * The metadata representation we use for extents in hugepages. While the PAC 11 1.1 christos * uses the edata_t to represent both active and inactive extents, the HP only 12 1.1 christos * uses the edata_t for active ones; instead, inactive extent state is tracked 13 1.1 christos * within hpdata associated with the enclosing hugepage-sized, hugepage-aligned 14 1.1 christos * region of virtual address space. 15 1.1 christos * 16 1.1 christos * An hpdata need not be "truly" backed by a hugepage (which is not necessarily 17 1.1 christos * an observable property of any given region of address space). It's just 18 1.1 christos * hugepage-sized and hugepage-aligned; it's *potentially* huge. 19 1.1 christos */ 20 1.1 christos typedef struct hpdata_s hpdata_t; 21 1.1 christos ph_structs(hpdata_age_heap, hpdata_t); 22 1.1 christos struct hpdata_s { 23 1.1 christos /* 24 1.1 christos * We likewise follow the edata convention of mangling names and forcing 25 1.1 christos * the use of accessors -- this lets us add some consistency checks on 26 1.1 christos * access. 27 1.1 christos */ 28 1.1 christos 29 1.1 christos /* 30 1.1 christos * The address of the hugepage in question. This can't be named h_addr, 31 1.1 christos * since that conflicts with a macro defined in Windows headers. 32 1.1 christos */ 33 1.1 christos void *h_address; 34 1.1 christos /* Its age (measured in psset operations). */ 35 1.1 christos uint64_t h_age; 36 1.1 christos /* Whether or not we think the hugepage is mapped that way by the OS. */ 37 1.1 christos bool h_huge; 38 1.1 christos 39 1.1 christos /* 40 1.1 christos * For some properties, we keep parallel sets of bools; h_foo_allowed 41 1.1 christos * and h_in_psset_foo_container. This is a decoupling mechanism to 42 1.1 christos * avoid bothering the hpa (which manages policies) from the psset 43 1.1 christos * (which is the mechanism used to enforce those policies). This allows 44 1.1 christos * all the container management logic to live in one place, without the 45 1.1 christos * HPA needing to know or care how that happens. 46 1.1 christos */ 47 1.1 christos 48 1.1 christos /* 49 1.1 christos * Whether or not the hpdata is allowed to be used to serve allocations, 50 1.1 christos * and whether or not the psset is currently tracking it as such. 51 1.1 christos */ 52 1.1 christos bool h_alloc_allowed; 53 1.1 christos bool h_in_psset_alloc_container; 54 1.1 christos 55 1.1 christos /* 56 1.1 christos * The same, but with purging. There's no corresponding 57 1.1 christos * h_in_psset_purge_container, because the psset (currently) always 58 1.1 christos * removes hpdatas from their containers during updates (to implement 59 1.1 christos * LRU for purging). 60 1.1 christos */ 61 1.1 christos bool h_purge_allowed; 62 1.1 christos 63 1.1 christos /* And with hugifying. */ 64 1.1 christos bool h_hugify_allowed; 65 1.1 christos /* When we became a hugification candidate. */ 66 1.1 christos nstime_t h_time_hugify_allowed; 67 1.1 christos bool h_in_psset_hugify_container; 68 1.1 christos 69 1.1 christos /* Whether or not a purge or hugify is currently happening. */ 70 1.1 christos bool h_mid_purge; 71 1.1 christos bool h_mid_hugify; 72 1.1 christos 73 1.1 christos /* 74 1.1 christos * Whether or not the hpdata is being updated in the psset (i.e. if 75 1.1 christos * there has been a psset_update_begin call issued without a matching 76 1.1 christos * psset_update_end call). Eventually this will expand to other types 77 1.1 christos * of updates. 78 1.1 christos */ 79 1.1 christos bool h_updating; 80 1.1 christos 81 1.1 christos /* Whether or not the hpdata is in a psset. */ 82 1.1 christos bool h_in_psset; 83 1.1 christos 84 1.1 christos union { 85 1.1 christos /* When nonempty (and also nonfull), used by the psset bins. */ 86 1.1 christos hpdata_age_heap_link_t age_link; 87 1.1 christos /* 88 1.1 christos * When empty (or not corresponding to any hugepage), list 89 1.1 christos * linkage. 90 1.1 christos */ 91 1.1 christos ql_elm(hpdata_t) ql_link_empty; 92 1.1 christos }; 93 1.1 christos 94 1.1 christos /* 95 1.1 christos * Linkage for the psset to track candidates for purging and hugifying. 96 1.1 christos */ 97 1.1 christos ql_elm(hpdata_t) ql_link_purge; 98 1.1 christos ql_elm(hpdata_t) ql_link_hugify; 99 1.1 christos 100 1.1 christos /* The length of the largest contiguous sequence of inactive pages. */ 101 1.1 christos size_t h_longest_free_range; 102 1.1 christos 103 1.1 christos /* Number of active pages. */ 104 1.1 christos size_t h_nactive; 105 1.1 christos 106 1.1 christos /* A bitmap with bits set in the active pages. */ 107 1.1 christos fb_group_t active_pages[FB_NGROUPS(HUGEPAGE_PAGES)]; 108 1.1 christos 109 1.1 christos /* 110 1.1 christos * Number of dirty or active pages, and a bitmap tracking them. One 111 1.1 christos * way to think of this is as which pages are dirty from the OS's 112 1.1 christos * perspective. 113 1.1 christos */ 114 1.1 christos size_t h_ntouched; 115 1.1 christos 116 1.1 christos /* The touched pages (using the same definition as above). */ 117 1.1 christos fb_group_t touched_pages[FB_NGROUPS(HUGEPAGE_PAGES)]; 118 1.1 christos }; 119 1.1 christos 120 1.1 christos TYPED_LIST(hpdata_empty_list, hpdata_t, ql_link_empty) 121 1.1 christos TYPED_LIST(hpdata_purge_list, hpdata_t, ql_link_purge) 122 1.1 christos TYPED_LIST(hpdata_hugify_list, hpdata_t, ql_link_hugify) 123 1.1 christos 124 1.1 christos ph_proto(, hpdata_age_heap, hpdata_t); 125 1.1 christos 126 1.1 christos static inline void * 127 1.1 christos hpdata_addr_get(const hpdata_t *hpdata) { 128 1.1 christos return hpdata->h_address; 129 1.1 christos } 130 1.1 christos 131 1.1 christos static inline void 132 1.1 christos hpdata_addr_set(hpdata_t *hpdata, void *addr) { 133 1.1 christos assert(HUGEPAGE_ADDR2BASE(addr) == addr); 134 1.1 christos hpdata->h_address = addr; 135 1.1 christos } 136 1.1 christos 137 1.1 christos static inline uint64_t 138 1.1 christos hpdata_age_get(const hpdata_t *hpdata) { 139 1.1 christos return hpdata->h_age; 140 1.1 christos } 141 1.1 christos 142 1.1 christos static inline void 143 1.1 christos hpdata_age_set(hpdata_t *hpdata, uint64_t age) { 144 1.1 christos hpdata->h_age = age; 145 1.1 christos } 146 1.1 christos 147 1.1 christos static inline bool 148 1.1 christos hpdata_huge_get(const hpdata_t *hpdata) { 149 1.1 christos return hpdata->h_huge; 150 1.1 christos } 151 1.1 christos 152 1.1 christos static inline bool 153 1.1 christos hpdata_alloc_allowed_get(const hpdata_t *hpdata) { 154 1.1 christos return hpdata->h_alloc_allowed; 155 1.1 christos } 156 1.1 christos 157 1.1 christos static inline void 158 1.1 christos hpdata_alloc_allowed_set(hpdata_t *hpdata, bool alloc_allowed) { 159 1.1 christos hpdata->h_alloc_allowed = alloc_allowed; 160 1.1 christos } 161 1.1 christos 162 1.1 christos static inline bool 163 1.1 christos hpdata_in_psset_alloc_container_get(const hpdata_t *hpdata) { 164 1.1 christos return hpdata->h_in_psset_alloc_container; 165 1.1 christos } 166 1.1 christos 167 1.1 christos static inline void 168 1.1 christos hpdata_in_psset_alloc_container_set(hpdata_t *hpdata, bool in_container) { 169 1.1 christos assert(in_container != hpdata->h_in_psset_alloc_container); 170 1.1 christos hpdata->h_in_psset_alloc_container = in_container; 171 1.1 christos } 172 1.1 christos 173 1.1 christos static inline bool 174 1.1 christos hpdata_purge_allowed_get(const hpdata_t *hpdata) { 175 1.1 christos return hpdata->h_purge_allowed; 176 1.1 christos } 177 1.1 christos 178 1.1 christos static inline void 179 1.1 christos hpdata_purge_allowed_set(hpdata_t *hpdata, bool purge_allowed) { 180 1.1 christos assert(purge_allowed == false || !hpdata->h_mid_purge); 181 1.1 christos hpdata->h_purge_allowed = purge_allowed; 182 1.1 christos } 183 1.1 christos 184 1.1 christos static inline bool 185 1.1 christos hpdata_hugify_allowed_get(const hpdata_t *hpdata) { 186 1.1 christos return hpdata->h_hugify_allowed; 187 1.1 christos } 188 1.1 christos 189 1.1 christos static inline void 190 1.1 christos hpdata_allow_hugify(hpdata_t *hpdata, nstime_t now) { 191 1.1 christos assert(!hpdata->h_mid_hugify); 192 1.1 christos hpdata->h_hugify_allowed = true; 193 1.1 christos hpdata->h_time_hugify_allowed = now; 194 1.1 christos } 195 1.1 christos 196 1.1 christos static inline nstime_t 197 1.1 christos hpdata_time_hugify_allowed(hpdata_t *hpdata) { 198 1.1 christos return hpdata->h_time_hugify_allowed; 199 1.1 christos } 200 1.1 christos 201 1.1 christos static inline void 202 1.1 christos hpdata_disallow_hugify(hpdata_t *hpdata) { 203 1.1 christos hpdata->h_hugify_allowed = false; 204 1.1 christos } 205 1.1 christos 206 1.1 christos static inline bool 207 1.1 christos hpdata_in_psset_hugify_container_get(const hpdata_t *hpdata) { 208 1.1 christos return hpdata->h_in_psset_hugify_container; 209 1.1 christos } 210 1.1 christos 211 1.1 christos static inline void 212 1.1 christos hpdata_in_psset_hugify_container_set(hpdata_t *hpdata, bool in_container) { 213 1.1 christos assert(in_container != hpdata->h_in_psset_hugify_container); 214 1.1 christos hpdata->h_in_psset_hugify_container = in_container; 215 1.1 christos } 216 1.1 christos 217 1.1 christos static inline bool 218 1.1 christos hpdata_mid_purge_get(const hpdata_t *hpdata) { 219 1.1 christos return hpdata->h_mid_purge; 220 1.1 christos } 221 1.1 christos 222 1.1 christos static inline void 223 1.1 christos hpdata_mid_purge_set(hpdata_t *hpdata, bool mid_purge) { 224 1.1 christos assert(mid_purge != hpdata->h_mid_purge); 225 1.1 christos hpdata->h_mid_purge = mid_purge; 226 1.1 christos } 227 1.1 christos 228 1.1 christos static inline bool 229 1.1 christos hpdata_mid_hugify_get(const hpdata_t *hpdata) { 230 1.1 christos return hpdata->h_mid_hugify; 231 1.1 christos } 232 1.1 christos 233 1.1 christos static inline void 234 1.1 christos hpdata_mid_hugify_set(hpdata_t *hpdata, bool mid_hugify) { 235 1.1 christos assert(mid_hugify != hpdata->h_mid_hugify); 236 1.1 christos hpdata->h_mid_hugify = mid_hugify; 237 1.1 christos } 238 1.1 christos 239 1.1 christos static inline bool 240 1.1 christos hpdata_changing_state_get(const hpdata_t *hpdata) { 241 1.1 christos return hpdata->h_mid_purge || hpdata->h_mid_hugify; 242 1.1 christos } 243 1.1 christos 244 1.1 christos 245 1.1 christos static inline bool 246 1.1 christos hpdata_updating_get(const hpdata_t *hpdata) { 247 1.1 christos return hpdata->h_updating; 248 1.1 christos } 249 1.1 christos 250 1.1 christos static inline void 251 1.1 christos hpdata_updating_set(hpdata_t *hpdata, bool updating) { 252 1.1 christos assert(updating != hpdata->h_updating); 253 1.1 christos hpdata->h_updating = updating; 254 1.1 christos } 255 1.1 christos 256 1.1 christos static inline bool 257 1.1 christos hpdata_in_psset_get(const hpdata_t *hpdata) { 258 1.1 christos return hpdata->h_in_psset; 259 1.1 christos } 260 1.1 christos 261 1.1 christos static inline void 262 1.1 christos hpdata_in_psset_set(hpdata_t *hpdata, bool in_psset) { 263 1.1 christos assert(in_psset != hpdata->h_in_psset); 264 1.1 christos hpdata->h_in_psset = in_psset; 265 1.1 christos } 266 1.1 christos 267 1.1 christos static inline size_t 268 1.1 christos hpdata_longest_free_range_get(const hpdata_t *hpdata) { 269 1.1 christos return hpdata->h_longest_free_range; 270 1.1 christos } 271 1.1 christos 272 1.1 christos static inline void 273 1.1 christos hpdata_longest_free_range_set(hpdata_t *hpdata, size_t longest_free_range) { 274 1.1 christos assert(longest_free_range <= HUGEPAGE_PAGES); 275 1.1 christos hpdata->h_longest_free_range = longest_free_range; 276 1.1 christos } 277 1.1 christos 278 1.1 christos static inline size_t 279 1.1 christos hpdata_nactive_get(hpdata_t *hpdata) { 280 1.1 christos return hpdata->h_nactive; 281 1.1 christos } 282 1.1 christos 283 1.1 christos static inline size_t 284 1.1 christos hpdata_ntouched_get(hpdata_t *hpdata) { 285 1.1 christos return hpdata->h_ntouched; 286 1.1 christos } 287 1.1 christos 288 1.1 christos static inline size_t 289 1.1 christos hpdata_ndirty_get(hpdata_t *hpdata) { 290 1.1 christos return hpdata->h_ntouched - hpdata->h_nactive; 291 1.1 christos } 292 1.1 christos 293 1.1 christos static inline size_t 294 1.1 christos hpdata_nretained_get(hpdata_t *hpdata) { 295 1.1 christos return HUGEPAGE_PAGES - hpdata->h_ntouched; 296 1.1 christos } 297 1.1 christos 298 1.1 christos static inline void 299 1.1 christos hpdata_assert_empty(hpdata_t *hpdata) { 300 1.1 christos assert(fb_empty(hpdata->active_pages, HUGEPAGE_PAGES)); 301 1.1 christos assert(hpdata->h_nactive == 0); 302 1.1 christos } 303 1.1 christos 304 1.1 christos /* 305 1.1 christos * Only used in tests, and in hpdata_assert_consistent, below. Verifies some 306 1.1 christos * consistency properties of the hpdata (e.g. that cached counts of page stats 307 1.1 christos * match computed ones). 308 1.1 christos */ 309 1.1 christos static inline bool 310 1.1 christos hpdata_consistent(hpdata_t *hpdata) { 311 1.1 christos if(fb_urange_longest(hpdata->active_pages, HUGEPAGE_PAGES) 312 1.1 christos != hpdata_longest_free_range_get(hpdata)) { 313 1.1 christos return false; 314 1.1 christos } 315 1.1 christos if (fb_scount(hpdata->active_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES) 316 1.1 christos != hpdata->h_nactive) { 317 1.1 christos return false; 318 1.1 christos } 319 1.1 christos if (fb_scount(hpdata->touched_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES) 320 1.1 christos != hpdata->h_ntouched) { 321 1.1 christos return false; 322 1.1 christos } 323 1.1 christos if (hpdata->h_ntouched < hpdata->h_nactive) { 324 1.1 christos return false; 325 1.1 christos } 326 1.1 christos if (hpdata->h_huge && hpdata->h_ntouched != HUGEPAGE_PAGES) { 327 1.1 christos return false; 328 1.1 christos } 329 1.1 christos if (hpdata_changing_state_get(hpdata) 330 1.1 christos && ((hpdata->h_purge_allowed) || hpdata->h_hugify_allowed)) { 331 1.1 christos return false; 332 1.1 christos } 333 1.1 christos if (hpdata_hugify_allowed_get(hpdata) 334 1.1 christos != hpdata_in_psset_hugify_container_get(hpdata)) { 335 1.1 christos return false; 336 1.1 christos } 337 1.1 christos return true; 338 1.1 christos } 339 1.1 christos 340 1.1 christos static inline void 341 1.1 christos hpdata_assert_consistent(hpdata_t *hpdata) { 342 1.1 christos assert(hpdata_consistent(hpdata)); 343 1.1 christos } 344 1.1 christos 345 1.1 christos static inline bool 346 1.1 christos hpdata_empty(hpdata_t *hpdata) { 347 1.1 christos return hpdata->h_nactive == 0; 348 1.1 christos } 349 1.1 christos 350 1.1 christos static inline bool 351 1.1 christos hpdata_full(hpdata_t *hpdata) { 352 1.1 christos return hpdata->h_nactive == HUGEPAGE_PAGES; 353 1.1 christos } 354 1.1 christos 355 1.1 christos void hpdata_init(hpdata_t *hpdata, void *addr, uint64_t age); 356 1.1 christos 357 1.1 christos /* 358 1.1 christos * Given an hpdata which can serve an allocation request, pick and reserve an 359 1.1 christos * offset within that allocation. 360 1.1 christos */ 361 1.1 christos void *hpdata_reserve_alloc(hpdata_t *hpdata, size_t sz); 362 1.1 christos void hpdata_unreserve(hpdata_t *hpdata, void *begin, size_t sz); 363 1.1 christos 364 1.1 christos /* 365 1.1 christos * The hpdata_purge_prepare_t allows grabbing the metadata required to purge 366 1.1 christos * subranges of a hugepage while holding a lock, drop the lock during the actual 367 1.1 christos * purging of them, and reacquire it to update the metadata again. 368 1.1 christos */ 369 1.1 christos typedef struct hpdata_purge_state_s hpdata_purge_state_t; 370 1.1 christos struct hpdata_purge_state_s { 371 1.1 christos size_t npurged; 372 1.1 christos size_t ndirty_to_purge; 373 1.1 christos fb_group_t to_purge[FB_NGROUPS(HUGEPAGE_PAGES)]; 374 1.1 christos size_t next_purge_search_begin; 375 1.1 christos }; 376 1.1 christos 377 1.1 christos /* 378 1.1 christos * Initializes purge state. The access to hpdata must be externally 379 1.1 christos * synchronized with other hpdata_* calls. 380 1.1 christos * 381 1.1 christos * You can tell whether or not a thread is purging or hugifying a given hpdata 382 1.1 christos * via hpdata_changing_state_get(hpdata). Racing hugification or purging 383 1.1 christos * operations aren't allowed. 384 1.1 christos * 385 1.1 christos * Once you begin purging, you have to follow through and call hpdata_purge_next 386 1.1 christos * until you're done, and then end. Allocating out of an hpdata undergoing 387 1.1 christos * purging is not allowed. 388 1.1 christos * 389 1.1 christos * Returns the number of dirty pages that will be purged. 390 1.1 christos */ 391 1.1 christos size_t hpdata_purge_begin(hpdata_t *hpdata, hpdata_purge_state_t *purge_state); 392 1.1 christos 393 1.1 christos /* 394 1.1 christos * If there are more extents to purge, sets *r_purge_addr and *r_purge_size to 395 1.1 christos * true, and returns true. Otherwise, returns false to indicate that we're 396 1.1 christos * done. 397 1.1 christos * 398 1.1 christos * This requires exclusive access to the purge state, but *not* to the hpdata. 399 1.1 christos * In particular, unreserve calls are allowed while purging (i.e. you can dalloc 400 1.1 christos * into one part of the hpdata while purging a different part). 401 1.1 christos */ 402 1.1 christos bool hpdata_purge_next(hpdata_t *hpdata, hpdata_purge_state_t *purge_state, 403 1.1 christos void **r_purge_addr, size_t *r_purge_size); 404 1.1 christos /* 405 1.1 christos * Updates the hpdata metadata after all purging is done. Needs external 406 1.1 christos * synchronization. 407 1.1 christos */ 408 1.1 christos void hpdata_purge_end(hpdata_t *hpdata, hpdata_purge_state_t *purge_state); 409 1.1 christos 410 1.1 christos void hpdata_hugify(hpdata_t *hpdata); 411 1.1 christos void hpdata_dehugify(hpdata_t *hpdata); 412 1.1 christos 413 1.1 christos #endif /* JEMALLOC_INTERNAL_HPDATA_H */ 414