hpa_central.c revision 1.1 1 #include "test/jemalloc_test.h"
2
3 #include "test/hpa.h"
4
5 #include "jemalloc/internal/hpa.h"
6 #include "jemalloc/internal/hpa_central.h"
7
8 #define HPA_TEST_ARENA_IND 125
9 #define HPA_TEST_EDEN_SIZE (128 * HUGEPAGE)
10
11 static bool hpa_test_map_fail;
12 static unsigned hpa_test_map_calls;
13 static unsigned hpa_test_unmap_calls;
14 static unsigned hpa_test_hugify_calls;
15 static void *hpa_test_last_map;
16 static size_t hpa_test_last_map_size;
17 static void *hpa_test_last_unmap;
18 static size_t hpa_test_last_unmap_size;
19 static void *hpa_test_last_hugify;
20 static size_t hpa_test_last_hugify_size;
21
22 static void
23 hpa_test_hooks_reset(void) {
24 hpa_test_map_fail = false;
25 hpa_test_map_calls = 0;
26 hpa_test_unmap_calls = 0;
27 hpa_test_hugify_calls = 0;
28 hpa_test_last_map = NULL;
29 hpa_test_last_map_size = 0;
30 hpa_test_last_unmap = NULL;
31 hpa_test_last_unmap_size = 0;
32 hpa_test_last_hugify = NULL;
33 hpa_test_last_hugify_size = 0;
34 }
35
36 static void *
37 hpa_test_map(size_t size) {
38 hpa_test_map_calls++;
39 hpa_test_last_map_size = size;
40 if (hpa_test_map_fail) {
41 hpa_test_last_map = NULL;
42 return NULL;
43 }
44 bool commit = true;
45 void *ret = pages_map(NULL, size, HUGEPAGE, &commit);
46 assert_true(commit, "HPA test mappings should be committed");
47 hpa_test_last_map = ret;
48 return ret;
49 }
50
51 static void
52 hpa_test_unmap(void *ptr, size_t size) {
53 hpa_test_unmap_calls++;
54 hpa_test_last_unmap = ptr;
55 hpa_test_last_unmap_size = size;
56 pages_unmap(ptr, size);
57 }
58
59 static void
60 hpa_test_purge(void *ptr, size_t size) {
61 }
62
63 static bool
64 hpa_test_hugify(void *ptr, size_t size, bool sync) {
65 hpa_test_hugify_calls++;
66 hpa_test_last_hugify = ptr;
67 hpa_test_last_hugify_size = size;
68 return false;
69 }
70
71 static void
72 hpa_test_dehugify(void *ptr, size_t size) {
73 }
74
75 static void
76 hpa_test_curtime(nstime_t *r_time, bool first_reading) {
77 nstime_init(r_time, 0);
78 }
79
80 static uint64_t
81 hpa_test_ms_since(nstime_t *r_time) {
82 return 0;
83 }
84
85 static bool
86 hpa_test_vectorized_purge(void *vec, size_t vlen, size_t nbytes) {
87 return true;
88 }
89
90 static hpa_hooks_t hpa_test_hooks = {
91 hpa_test_map,
92 hpa_test_unmap,
93 hpa_test_purge,
94 hpa_test_hugify,
95 hpa_test_dehugify,
96 hpa_test_curtime,
97 hpa_test_ms_since,
98 hpa_test_vectorized_purge
99 };
100
101 static bool hpa_base_fail_after_new;
102 static unsigned hpa_base_alloc_calls;
103
104 static void
105 hpa_base_hooks_reset(void) {
106 hpa_base_fail_after_new = false;
107 hpa_base_alloc_calls = 0;
108 }
109
110 static void *
111 hpa_base_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
112 size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {
113 if (hpa_base_fail_after_new && hpa_base_alloc_calls > 0) {
114 hpa_base_alloc_calls++;
115 return NULL;
116 }
117 hpa_base_alloc_calls++;
118 return pages_map(new_addr, size, alignment, commit);
119 }
120
121 static bool
122 hpa_base_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,
123 bool committed, unsigned arena_ind) {
124 pages_unmap(addr, size);
125 return false;
126 }
127
128 static void
129 hpa_base_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,
130 bool committed, unsigned arena_ind) {
131 pages_unmap(addr, size);
132 }
133
134 static extent_hooks_t hpa_base_hooks = {
135 hpa_base_alloc,
136 hpa_base_dalloc,
137 hpa_base_destroy,
138 NULL, /* commit */
139 NULL, /* decommit */
140 NULL, /* purge_lazy */
141 NULL, /* purge_forced */
142 NULL, /* split */
143 NULL /* merge */
144 };
145
146 static hpdata_t *
147 hpa_central_extract_with_lock(hpa_central_t *central, malloc_mutex_t *mtx,
148 uint64_t age, bool hugify_eager, bool *oom) {
149 tsdn_t *tsdn = tsdn_fetch();
150 malloc_mutex_lock(tsdn, mtx);
151 hpdata_t *ret = hpa_central_extract(tsdn, central, PAGE, age,
152 hugify_eager, oom);
153 malloc_mutex_unlock(tsdn, mtx);
154 return ret;
155 }
156
157 static void
158 hpa_test_shard_grow_mtx_init(malloc_mutex_t *mtx) {
159 assert_false(malloc_mutex_init(mtx, "hpa_test_shard_grow",
160 WITNESS_RANK_HPA_SHARD_GROW, malloc_mutex_rank_exclusive),
161 "Unexpected mutex initialization failure");
162 }
163
164 TEST_BEGIN(test_hpa_central_extract_eden) {
165 test_skip_if(!hpa_supported() || hpa_hugepage_size_exceeds_limit());
166 hpa_test_hooks_reset();
167
168 tsdn_t *tsdn = tsdn_fetch();
169 base_t *base = base_new(tsdn, HPA_TEST_ARENA_IND,
170 &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
171 assert_ptr_not_null(base, "Unexpected base_new failure");
172
173 hpa_central_t central;
174 assert_false(hpa_central_init(¢ral, base, &hpa_test_hooks),
175 "Unexpected hpa_central_init failure");
176 malloc_mutex_t shard_grow_mtx;
177 hpa_test_shard_grow_mtx_init(&shard_grow_mtx);
178
179 void *eden = NULL;
180 for (unsigned i = 0; i < 128; i++) {
181 bool oom = true;
182 hpdata_t *ps = hpa_central_extract_with_lock(¢ral,
183 &shard_grow_mtx, 1000 + i, /* hugify_eager */ true, &oom);
184 expect_false(oom, "Unexpected HPA central OOM");
185 expect_ptr_not_null(ps, "Unexpected HPA central extraction failure");
186 if (i == 0) {
187 eden = hpa_test_last_map;
188 expect_u_eq(1, hpa_test_map_calls,
189 "First extraction should map eden");
190 expect_zu_eq(HPA_TEST_EDEN_SIZE, hpa_test_last_map_size,
191 "Unexpected eden mapping size");
192 expect_u_eq(1, hpa_test_hugify_calls,
193 "Eager extraction should hugify the whole eden");
194 expect_ptr_eq(eden, hpa_test_last_hugify,
195 "Hugify should apply to eden");
196 expect_zu_eq(HPA_TEST_EDEN_SIZE,
197 hpa_test_last_hugify_size,
198 "Hugify should cover the whole eden");
199 }
200 expect_ptr_eq((void *)((byte_t *)eden + i * HUGEPAGE),
201 hpdata_addr_get(ps), "Unexpected extracted pageslab addr");
202 expect_u64_eq(1000 + i, hpdata_age_get(ps),
203 "Unexpected hpdata age");
204 expect_true(hpdata_huge_get(ps),
205 "Eager extraction should mark hpdata huge");
206 }
207
208 expect_ptr_null(central.eden, "Exact final extraction should empty eden");
209 expect_zu_eq(0, central.eden_len,
210 "Exact final extraction should clear eden length");
211 expect_u_eq(1, hpa_test_map_calls,
212 "All pageslabs should come from one eden mapping");
213 expect_u_eq(0, hpa_test_unmap_calls,
214 "Successful extraction should not unmap eden");
215
216 pages_unmap(eden, HPA_TEST_EDEN_SIZE);
217 base_delete(tsdn, base);
218 }
219 TEST_END
220
221 TEST_BEGIN(test_hpa_central_failure_paths) {
222 test_skip_if(!hpa_supported() || hpa_hugepage_size_exceeds_limit());
223 hpa_test_hooks_reset();
224
225 tsdn_t *tsdn = tsdn_fetch();
226 base_t *base = base_new(tsdn, HPA_TEST_ARENA_IND + 1,
227 &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
228 assert_ptr_not_null(base, "Unexpected base_new failure");
229 hpa_central_t central;
230 assert_false(hpa_central_init(¢ral, base, &hpa_test_hooks),
231 "Unexpected hpa_central_init failure");
232 malloc_mutex_t shard_grow_mtx;
233 hpa_test_shard_grow_mtx_init(&shard_grow_mtx);
234
235 hpa_test_map_fail = true;
236 bool oom = false;
237 hpdata_t *ps = hpa_central_extract_with_lock(¢ral,
238 &shard_grow_mtx, 1, /* hugify_eager */ false, &oom);
239 expect_ptr_null(ps, "Map failure should not return hpdata");
240 expect_true(oom, "Map failure should report OOM");
241 expect_u_eq(1, hpa_test_map_calls, "Expected one map attempt");
242 expect_u_eq(0, hpa_test_unmap_calls,
243 "Map failure should not call unmap");
244 base_delete(tsdn, base);
245
246 hpa_base_hooks_reset();
247 hpa_test_hooks_reset();
248 base = base_new(tsdn, HPA_TEST_ARENA_IND + 2, &hpa_base_hooks,
249 /* metadata_use_hooks */ true);
250 assert_ptr_not_null(base, "Unexpected base_new failure");
251 hpa_base_fail_after_new = true;
252 while (base_alloc(tsdn, base, sizeof(hpdata_t), CACHELINE) != NULL) {
253 }
254
255 assert_false(hpa_central_init(¢ral, base, &hpa_test_hooks),
256 "Unexpected hpa_central_init failure");
257 malloc_mutex_t shard_grow_mtx2;
258 hpa_test_shard_grow_mtx_init(&shard_grow_mtx2);
259 oom = false;
260 ps = hpa_central_extract_with_lock(¢ral, &shard_grow_mtx2, 2,
261 /* hugify_eager */ false, &oom);
262 expect_ptr_null(ps, "Metadata OOM should not return hpdata");
263 expect_true(oom, "Metadata allocation failure should report OOM");
264 expect_u_eq(1, hpa_test_map_calls,
265 "Metadata OOM should happen after mapping eden once");
266 expect_u_eq(1, hpa_test_unmap_calls,
267 "Metadata OOM should unmap the freshly mapped eden");
268 expect_ptr_eq(hpa_test_last_map, hpa_test_last_unmap,
269 "Metadata OOM should unmap the eden it just mapped");
270 expect_zu_eq(HPA_TEST_EDEN_SIZE, hpa_test_last_unmap_size,
271 "Metadata OOM should unmap the full eden");
272 base_delete(tsdn, base);
273 }
274 TEST_END
275
276 int
277 main(void) {
278 return test_no_reentrancy(test_hpa_central_extract_eden,
279 test_hpa_central_failure_paths);
280 }
281