1 1.1 christos #include "test/jemalloc_test.h" 2 1.1 christos 3 1.1 christos #include "jemalloc/internal/hpa.h" 4 1.1 christos #include "jemalloc/internal/nstime.h" 5 1.1 christos 6 1.1 christos #define SHARD_IND 111 7 1.1 christos 8 1.1 christos #define ALLOC_MAX (HUGEPAGE) 9 1.1 christos 10 1.1 christos typedef struct test_data_s test_data_t; 11 1.1 christos struct test_data_s { 12 1.1 christos /* 13 1.1 christos * Must be the first member -- we convert back and forth between the 14 1.1 christos * test_data_t and the hpa_shard_t; 15 1.1 christos */ 16 1.1 christos hpa_shard_t shard; 17 1.1 christos hpa_central_t central; 18 1.1 christos base_t *base; 19 1.1 christos edata_cache_t shard_edata_cache; 20 1.1 christos 21 1.1 christos emap_t emap; 22 1.1 christos }; 23 1.1 christos 24 1.1 christos static hpa_shard_opts_t test_hpa_shard_opts = { 25 1.1 christos /* slab_max_alloc */ 26 1.1 christos HUGEPAGE, 27 1.1 christos /* hugification_threshold */ 28 1.1 christos 0.9 * HUGEPAGE, 29 1.1 christos /* dirty_mult */ 30 1.1 christos FXP_INIT_PERCENT(10), 31 1.1 christos /* deferral_allowed */ 32 1.1 christos true, 33 1.1 christos /* hugify_delay_ms */ 34 1.1 christos 0, 35 1.1 christos /* hugify_sync */ 36 1.1 christos false, 37 1.1 christos /* min_purge_interval_ms */ 38 1.1 christos 5, 39 1.1 christos /* experimental_max_purge_nhp */ 40 1.1 christos -1, 41 1.1 christos /* purge_threshold */ 42 1.1 christos PAGE, 43 1.1 christos /* min_purge_delay_ms */ 44 1.1 christos 10, 45 1.1 christos /* hugify_style */ 46 1.1 christos hpa_hugify_style_lazy}; 47 1.1 christos 48 1.1 christos static hpa_shard_t * 49 1.1 christos create_test_data(const hpa_hooks_t *hooks, hpa_shard_opts_t *opts, 50 1.1 christos const sec_opts_t *sec_opts) { 51 1.1 christos bool err; 52 1.1 christos base_t *base = base_new(TSDN_NULL, /* ind */ SHARD_IND, 53 1.1 christos &ehooks_default_extent_hooks, /* metadata_use_hooks */ true); 54 1.1 christos assert_ptr_not_null(base, ""); 55 1.1 christos 56 1.1 christos test_data_t *test_data = malloc(sizeof(test_data_t)); 57 1.1 christos assert_ptr_not_null(test_data, ""); 58 1.1 christos 59 1.1 christos test_data->base = base; 60 1.1 christos 61 1.1 christos err = edata_cache_init(&test_data->shard_edata_cache, base); 62 1.1 christos assert_false(err, ""); 63 1.1 christos 64 1.1 christos err = emap_init(&test_data->emap, test_data->base, /* zeroed */ false); 65 1.1 christos assert_false(err, ""); 66 1.1 christos 67 1.1 christos err = hpa_central_init(&test_data->central, test_data->base, hooks); 68 1.1 christos assert_false(err, ""); 69 1.1 christos tsdn_t *tsdn = tsd_tsdn(tsd_fetch()); 70 1.1 christos err = hpa_shard_init(tsdn, &test_data->shard, &test_data->central, 71 1.1 christos &test_data->emap, test_data->base, &test_data->shard_edata_cache, 72 1.1 christos SHARD_IND, opts, sec_opts); 73 1.1 christos assert_false(err, ""); 74 1.1 christos 75 1.1 christos return (hpa_shard_t *)test_data; 76 1.1 christos } 77 1.1 christos 78 1.1 christos static void 79 1.1 christos destroy_test_data(hpa_shard_t *shard) { 80 1.1 christos test_data_t *test_data = (test_data_t *)shard; 81 1.1 christos base_delete(TSDN_NULL, test_data->base); 82 1.1 christos free(test_data); 83 1.1 christos } 84 1.1 christos 85 1.1 christos static uintptr_t defer_bump_ptr = HUGEPAGE * 123; 86 1.1 christos static void * 87 1.1 christos defer_test_map(size_t size) { 88 1.1 christos void *result = (void *)defer_bump_ptr; 89 1.1 christos defer_bump_ptr += size; 90 1.1 christos return result; 91 1.1 christos } 92 1.1 christos 93 1.1 christos static void 94 1.1 christos defer_test_unmap(void *ptr, size_t size) { 95 1.1 christos (void)ptr; 96 1.1 christos (void)size; 97 1.1 christos } 98 1.1 christos 99 1.1 christos static size_t ndefer_purge_calls = 0; 100 1.1 christos static size_t npurge_size = 0; 101 1.1 christos static void 102 1.1 christos defer_test_purge(void *ptr, size_t size) { 103 1.1 christos (void)ptr; 104 1.1 christos npurge_size = size; 105 1.1 christos ++ndefer_purge_calls; 106 1.1 christos } 107 1.1 christos 108 1.1 christos static bool defer_vectorized_purge_called = false; 109 1.1 christos static bool 110 1.1 christos defer_vectorized_purge(void *vec, size_t vlen, size_t nbytes) { 111 1.1 christos (void)vec; 112 1.1 christos (void)nbytes; 113 1.1 christos ++ndefer_purge_calls; 114 1.1 christos defer_vectorized_purge_called = true; 115 1.1 christos return false; 116 1.1 christos } 117 1.1 christos 118 1.1 christos static size_t ndefer_hugify_calls = 0; 119 1.1 christos static bool 120 1.1 christos defer_test_hugify(void *ptr, size_t size, bool sync) { 121 1.1 christos ++ndefer_hugify_calls; 122 1.1 christos return false; 123 1.1 christos } 124 1.1 christos 125 1.1 christos static size_t ndefer_dehugify_calls = 0; 126 1.1 christos static void 127 1.1 christos defer_test_dehugify(void *ptr, size_t size) { 128 1.1 christos ++ndefer_dehugify_calls; 129 1.1 christos } 130 1.1 christos 131 1.1 christos static nstime_t defer_curtime; 132 1.1 christos static void 133 1.1 christos defer_test_curtime(nstime_t *r_time, bool first_reading) { 134 1.1 christos *r_time = defer_curtime; 135 1.1 christos } 136 1.1 christos 137 1.1 christos static uint64_t 138 1.1 christos defer_test_ms_since(nstime_t *past_time) { 139 1.1 christos return (nstime_ns(&defer_curtime) - nstime_ns(past_time)) / 1000 / 1000; 140 1.1 christos } 141 1.1 christos 142 1.1 christos // test that freed pages stay in SEC and hpa thinks they are active 143 1.1 christos 144 1.1 christos TEST_BEGIN(test_hpa_sec) { 145 1.1 christos test_skip_if(!hpa_supported()); 146 1.1 christos 147 1.1 christos hpa_hooks_t hooks; 148 1.1 christos hooks.map = &defer_test_map; 149 1.1 christos hooks.unmap = &defer_test_unmap; 150 1.1 christos hooks.purge = &defer_test_purge; 151 1.1 christos hooks.hugify = &defer_test_hugify; 152 1.1 christos hooks.dehugify = &defer_test_dehugify; 153 1.1 christos hooks.curtime = &defer_test_curtime; 154 1.1 christos hooks.ms_since = &defer_test_ms_since; 155 1.1 christos hooks.vectorized_purge = &defer_vectorized_purge; 156 1.1 christos 157 1.1 christos hpa_shard_opts_t opts = test_hpa_shard_opts; 158 1.1 christos 159 1.1 christos enum { NALLOCS = 8 }; 160 1.1 christos sec_opts_t sec_opts; 161 1.1 christos sec_opts.nshards = 1; 162 1.1 christos sec_opts.max_alloc = 2 * PAGE; 163 1.1 christos sec_opts.max_bytes = NALLOCS * PAGE; 164 1.1 christos sec_opts.batch_fill_extra = 4; 165 1.1 christos 166 1.1 christos hpa_shard_t *shard = create_test_data(&hooks, &opts, &sec_opts); 167 1.1 christos bool deferred_work_generated = false; 168 1.1 christos tsdn_t *tsdn = tsd_tsdn(tsd_fetch()); 169 1.1 christos 170 1.1 christos /* alloc 1 PAGE, confirm sec has fill_extra bytes. */ 171 1.1 christos edata_t *edata1 = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false, false, 172 1.1 christos false, &deferred_work_generated); 173 1.1 christos expect_ptr_not_null(edata1, "Unexpected null edata"); 174 1.1 christos hpa_shard_stats_t hpa_stats; 175 1.1 christos memset(&hpa_stats, 0, sizeof(hpa_shard_stats_t)); 176 1.1 christos hpa_shard_stats_merge(tsdn, shard, &hpa_stats); 177 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.nactive, 178 1.1 christos 1 + sec_opts.batch_fill_extra, ""); 179 1.1 christos expect_zu_eq(hpa_stats.secstats.bytes, PAGE * sec_opts.batch_fill_extra, 180 1.1 christos "sec should have fill extra pages"); 181 1.1 christos 182 1.1 christos /* Alloc/dealloc NALLOCS times and confirm extents are in sec. */ 183 1.1 christos edata_t *edatas[NALLOCS]; 184 1.1 christos for (int i = 0; i < NALLOCS; i++) { 185 1.1 christos edatas[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false, 186 1.1 christos false, false, &deferred_work_generated); 187 1.1 christos expect_ptr_not_null(edatas[i], "Unexpected null edata"); 188 1.1 christos } 189 1.1 christos memset(&hpa_stats, 0, sizeof(hpa_shard_stats_t)); 190 1.1 christos hpa_shard_stats_merge(tsdn, shard, &hpa_stats); 191 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.nactive, 2 + NALLOCS, ""); 192 1.1 christos expect_zu_eq(hpa_stats.secstats.bytes, PAGE, "2 refills (at 0 and 4)"); 193 1.1 christos 194 1.1 christos for (int i = 0; i < NALLOCS - 1; i++) { 195 1.1 christos pai_dalloc( 196 1.1 christos tsdn, &shard->pai, edatas[i], &deferred_work_generated); 197 1.1 christos } 198 1.1 christos memset(&hpa_stats, 0, sizeof(hpa_shard_stats_t)); 199 1.1 christos hpa_shard_stats_merge(tsdn, shard, &hpa_stats); 200 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.nactive, (2 + NALLOCS), ""); 201 1.1 christos expect_zu_eq( 202 1.1 christos hpa_stats.secstats.bytes, sec_opts.max_bytes, "sec should be full"); 203 1.1 christos 204 1.1 christos /* this one should flush 1 + 0.25 * 8 = 3 extents */ 205 1.1 christos pai_dalloc( 206 1.1 christos tsdn, &shard->pai, edatas[NALLOCS - 1], &deferred_work_generated); 207 1.1 christos memset(&hpa_stats, 0, sizeof(hpa_shard_stats_t)); 208 1.1 christos hpa_shard_stats_merge(tsdn, shard, &hpa_stats); 209 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.nactive, (NALLOCS - 1), ""); 210 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.ndirty, 3, ""); 211 1.1 christos expect_zu_eq(hpa_stats.secstats.bytes, 0.75 * sec_opts.max_bytes, 212 1.1 christos "sec should be full"); 213 1.1 christos 214 1.1 christos /* Next allocation should come from SEC and not increase active */ 215 1.1 christos edata_t *edata2 = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false, false, 216 1.1 christos false, &deferred_work_generated); 217 1.1 christos expect_ptr_not_null(edata2, "Unexpected null edata"); 218 1.1 christos memset(&hpa_stats, 0, sizeof(hpa_shard_stats_t)); 219 1.1 christos hpa_shard_stats_merge(tsdn, shard, &hpa_stats); 220 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.nactive, NALLOCS - 1, ""); 221 1.1 christos expect_zu_eq(hpa_stats.secstats.bytes, 0.75 * sec_opts.max_bytes - PAGE, 222 1.1 christos "sec should have max_bytes minus one page that just came from it"); 223 1.1 christos 224 1.1 christos /* We return this one and it stays in the cache */ 225 1.1 christos pai_dalloc(tsdn, &shard->pai, edata2, &deferred_work_generated); 226 1.1 christos memset(&hpa_stats, 0, sizeof(hpa_shard_stats_t)); 227 1.1 christos hpa_shard_stats_merge(tsdn, shard, &hpa_stats); 228 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.nactive, NALLOCS - 1, ""); 229 1.1 christos expect_zu_eq(hpa_stats.psset_stats.merged.ndirty, 3, ""); 230 1.1 christos expect_zu_eq(hpa_stats.secstats.bytes, 0.75 * sec_opts.max_bytes, ""); 231 1.1 christos 232 1.1 christos destroy_test_data(shard); 233 1.1 christos } 234 1.1 christos TEST_END 235 1.1 christos 236 1.1 christos int 237 1.1 christos main(void) { 238 1.1 christos return test_no_reentrancy(test_hpa_sec); 239 1.1 christos } 240