1 #include "test/jemalloc_test.h" 2 3 #define HPDATA_ADDR ((void *)(10 * HUGEPAGE)) 4 #define HPDATA_AGE 123 5 6 TEST_BEGIN(test_reserve_alloc) { 7 hpdata_t hpdata; 8 hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE); 9 10 /* Allocating a page at a time, we should do first fit. */ 11 for (size_t i = 0; i < HUGEPAGE_PAGES; i++) { 12 expect_true(hpdata_consistent(&hpdata), ""); 13 expect_zu_eq(HUGEPAGE_PAGES - i, 14 hpdata_longest_free_range_get(&hpdata), ""); 15 void *alloc = hpdata_reserve_alloc(&hpdata, PAGE); 16 expect_ptr_eq((char *)HPDATA_ADDR + i * PAGE, alloc, ""); 17 expect_true(hpdata_consistent(&hpdata), ""); 18 } 19 expect_true(hpdata_consistent(&hpdata), ""); 20 expect_zu_eq(0, hpdata_longest_free_range_get(&hpdata), ""); 21 22 /* 23 * Build up a bigger free-range, 2 pages at a time, until we've got 6 24 * adjacent free pages total. Pages 8-13 should be unreserved after 25 * this. 26 */ 27 hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 10 * PAGE, 2 * PAGE); 28 expect_true(hpdata_consistent(&hpdata), ""); 29 expect_zu_eq(2, hpdata_longest_free_range_get(&hpdata), ""); 30 31 hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 12 * PAGE, 2 * PAGE); 32 expect_true(hpdata_consistent(&hpdata), ""); 33 expect_zu_eq(4, hpdata_longest_free_range_get(&hpdata), ""); 34 35 hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 8 * PAGE, 2 * PAGE); 36 expect_true(hpdata_consistent(&hpdata), ""); 37 expect_zu_eq(6, hpdata_longest_free_range_get(&hpdata), ""); 38 39 /* 40 * Leave page 14 reserved, but free page 15 (this test the case where 41 * unreserving combines two ranges). 42 */ 43 hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 15 * PAGE, PAGE); 44 /* 45 * Longest free range shouldn't change; we've got a free range of size 46 * 6, then a reserved page, then another free range. 47 */ 48 expect_true(hpdata_consistent(&hpdata), ""); 49 expect_zu_eq(6, hpdata_longest_free_range_get(&hpdata), ""); 50 51 /* After freeing page 14, the two ranges get combined. */ 52 hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 14 * PAGE, PAGE); 53 expect_true(hpdata_consistent(&hpdata), ""); 54 expect_zu_eq(8, hpdata_longest_free_range_get(&hpdata), ""); 55 } 56 TEST_END 57 58 TEST_BEGIN(test_purge_simple) { 59 hpdata_t hpdata; 60 hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE); 61 62 void *alloc = hpdata_reserve_alloc(&hpdata, HUGEPAGE_PAGES / 2 * PAGE); 63 expect_ptr_eq(alloc, HPDATA_ADDR, ""); 64 65 /* Create HUGEPAGE_PAGES / 4 dirty inactive pages at the beginning. */ 66 hpdata_unreserve(&hpdata, alloc, HUGEPAGE_PAGES / 4 * PAGE); 67 68 expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 2, ""); 69 70 hpdata_alloc_allowed_set(&hpdata, false); 71 hpdata_purge_state_t purge_state; 72 size_t to_purge = hpdata_purge_begin(&hpdata, &purge_state); 73 expect_zu_eq(HUGEPAGE_PAGES / 4, to_purge, ""); 74 75 void *purge_addr; 76 size_t purge_size; 77 bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 78 &purge_size); 79 expect_true(got_result, ""); 80 expect_ptr_eq(HPDATA_ADDR, purge_addr, ""); 81 expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, ""); 82 83 got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 84 &purge_size); 85 expect_false(got_result, "Unexpected additional purge range: " 86 "extent at %p of size %zu", purge_addr, purge_size); 87 88 hpdata_purge_end(&hpdata, &purge_state); 89 expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 4, ""); 90 } 91 TEST_END 92 93 /* 94 * We only test intervening dalloc's not intervening allocs; the latter are 95 * disallowed as a purging precondition (because they interfere with purging 96 * across a retained extent, saving a purge call). 97 */ 98 TEST_BEGIN(test_purge_intervening_dalloc) { 99 hpdata_t hpdata; 100 hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE); 101 102 /* Allocate the first 3/4 of the pages. */ 103 void *alloc = hpdata_reserve_alloc(&hpdata, 3 * HUGEPAGE_PAGES / 4 * PAGE); 104 expect_ptr_eq(alloc, HPDATA_ADDR, ""); 105 106 /* Free the first 1/4 and the third 1/4 of the pages. */ 107 hpdata_unreserve(&hpdata, alloc, HUGEPAGE_PAGES / 4 * PAGE); 108 hpdata_unreserve(&hpdata, 109 (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE), 110 HUGEPAGE_PAGES / 4 * PAGE); 111 112 expect_zu_eq(hpdata_ntouched_get(&hpdata), 3 * HUGEPAGE_PAGES / 4, ""); 113 114 hpdata_alloc_allowed_set(&hpdata, false); 115 hpdata_purge_state_t purge_state; 116 size_t to_purge = hpdata_purge_begin(&hpdata, &purge_state); 117 expect_zu_eq(HUGEPAGE_PAGES / 2, to_purge, ""); 118 119 void *purge_addr; 120 size_t purge_size; 121 /* First purge. */ 122 bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 123 &purge_size); 124 expect_true(got_result, ""); 125 expect_ptr_eq(HPDATA_ADDR, purge_addr, ""); 126 expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, ""); 127 128 /* Deallocate the second 1/4 before the second purge occurs. */ 129 hpdata_unreserve(&hpdata, 130 (void *)((uintptr_t)alloc + 1 * HUGEPAGE_PAGES / 4 * PAGE), 131 HUGEPAGE_PAGES / 4 * PAGE); 132 133 /* Now continue purging. */ 134 got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 135 &purge_size); 136 expect_true(got_result, ""); 137 expect_ptr_eq( 138 (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE), 139 purge_addr, ""); 140 expect_zu_ge(HUGEPAGE_PAGES / 4 * PAGE, purge_size, ""); 141 142 got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 143 &purge_size); 144 expect_false(got_result, "Unexpected additional purge range: " 145 "extent at %p of size %zu", purge_addr, purge_size); 146 147 hpdata_purge_end(&hpdata, &purge_state); 148 149 expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 4, ""); 150 } 151 TEST_END 152 153 TEST_BEGIN(test_purge_over_retained) { 154 void *purge_addr; 155 size_t purge_size; 156 157 hpdata_t hpdata; 158 hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE); 159 160 /* Allocate the first 3/4 of the pages. */ 161 void *alloc = hpdata_reserve_alloc(&hpdata, 3 * HUGEPAGE_PAGES / 4 * PAGE); 162 expect_ptr_eq(alloc, HPDATA_ADDR, ""); 163 164 /* Free the second quarter. */ 165 void *second_quarter = 166 (void *)((uintptr_t)alloc + HUGEPAGE_PAGES / 4 * PAGE); 167 hpdata_unreserve(&hpdata, second_quarter, HUGEPAGE_PAGES / 4 * PAGE); 168 169 expect_zu_eq(hpdata_ntouched_get(&hpdata), 3 * HUGEPAGE_PAGES / 4, ""); 170 171 /* Purge the second quarter. */ 172 hpdata_alloc_allowed_set(&hpdata, false); 173 hpdata_purge_state_t purge_state; 174 size_t to_purge_dirty = hpdata_purge_begin(&hpdata, &purge_state); 175 expect_zu_eq(HUGEPAGE_PAGES / 4, to_purge_dirty, ""); 176 177 bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 178 &purge_size); 179 expect_true(got_result, ""); 180 expect_ptr_eq(second_quarter, purge_addr, ""); 181 expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, ""); 182 183 got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 184 &purge_size); 185 expect_false(got_result, "Unexpected additional purge range: " 186 "extent at %p of size %zu", purge_addr, purge_size); 187 hpdata_purge_end(&hpdata, &purge_state); 188 189 expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 2, ""); 190 191 /* Free the first and third quarter. */ 192 hpdata_unreserve(&hpdata, HPDATA_ADDR, HUGEPAGE_PAGES / 4 * PAGE); 193 hpdata_unreserve(&hpdata, 194 (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE), 195 HUGEPAGE_PAGES / 4 * PAGE); 196 197 /* 198 * Purge again. The second quarter is retained, so we can safely 199 * re-purge it. We expect a single purge of 3/4 of the hugepage, 200 * purging half its pages. 201 */ 202 to_purge_dirty = hpdata_purge_begin(&hpdata, &purge_state); 203 expect_zu_eq(HUGEPAGE_PAGES / 2, to_purge_dirty, ""); 204 205 got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 206 &purge_size); 207 expect_true(got_result, ""); 208 expect_ptr_eq(HPDATA_ADDR, purge_addr, ""); 209 expect_zu_eq(3 * HUGEPAGE_PAGES / 4 * PAGE, purge_size, ""); 210 211 got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr, 212 &purge_size); 213 expect_false(got_result, "Unexpected additional purge range: " 214 "extent at %p of size %zu", purge_addr, purge_size); 215 hpdata_purge_end(&hpdata, &purge_state); 216 217 expect_zu_eq(hpdata_ntouched_get(&hpdata), 0, ""); 218 } 219 TEST_END 220 221 TEST_BEGIN(test_hugify) { 222 hpdata_t hpdata; 223 hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE); 224 225 void *alloc = hpdata_reserve_alloc(&hpdata, HUGEPAGE / 2); 226 expect_ptr_eq(alloc, HPDATA_ADDR, ""); 227 228 expect_zu_eq(HUGEPAGE_PAGES / 2, hpdata_ntouched_get(&hpdata), ""); 229 230 hpdata_hugify(&hpdata); 231 232 /* Hugeifying should have increased the dirty page count. */ 233 expect_zu_eq(HUGEPAGE_PAGES, hpdata_ntouched_get(&hpdata), ""); 234 } 235 TEST_END 236 237 int main(void) { 238 return test_no_reentrancy( 239 test_reserve_alloc, 240 test_purge_simple, 241 test_purge_intervening_dalloc, 242 test_purge_over_retained, 243 test_hugify); 244 } 245