Home | History | Annotate | Line # | Download | only in unit
      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(&central, 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(&central,
    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(&central, 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(&central,
    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(&central, 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(&central, &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