Home | History | Annotate | Line # | Download | only in unit
      1 #include "test/jemalloc_test.h"
      2 
      3 #include "jemalloc/internal/hpa.h"
      4 #include "jemalloc/internal/nstime.h"
      5 
      6 #define SHARD_IND 111
      7 
      8 #define ALLOC_MAX (HUGEPAGE)
      9 
     10 typedef struct test_data_s test_data_t;
     11 struct test_data_s {
     12 	/*
     13 	 * Must be the first member -- we convert back and forth between the
     14 	 * test_data_t and the hpa_shard_t;
     15 	 */
     16 	hpa_shard_t   shard;
     17 	hpa_central_t central;
     18 	base_t       *base;
     19 	edata_cache_t shard_edata_cache;
     20 
     21 	emap_t emap;
     22 };
     23 
     24 static hpa_shard_opts_t test_hpa_shard_opts_aggressive = {
     25     /* slab_max_alloc */
     26     HUGEPAGE,
     27     /* hugification_threshold */
     28     0.9 * HUGEPAGE,
     29     /* dirty_mult */
     30     FXP_INIT_PERCENT(11),
     31     /* deferral_allowed */
     32     true,
     33     /* hugify_delay_ms */
     34     0,
     35     /* hugify_sync */
     36     false,
     37     /* min_purge_interval_ms */
     38     5,
     39     /* experimental_max_purge_nhp */
     40     -1,
     41     /* purge_threshold */
     42     HUGEPAGE - 5 * PAGE,
     43     /* min_purge_delay_ms */
     44     10,
     45     /* hugify_style */
     46     hpa_hugify_style_eager};
     47 
     48 static hpa_shard_t *
     49 create_test_data(const hpa_hooks_t *hooks, hpa_shard_opts_t *opts) {
     50 	bool    err;
     51 	base_t *base = base_new(TSDN_NULL, /* ind */ SHARD_IND,
     52 	    &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
     53 	assert_ptr_not_null(base, "");
     54 
     55 	test_data_t *test_data = malloc(sizeof(test_data_t));
     56 	assert_ptr_not_null(test_data, "");
     57 
     58 	test_data->base = base;
     59 
     60 	err = edata_cache_init(&test_data->shard_edata_cache, base);
     61 	assert_false(err, "");
     62 
     63 	err = emap_init(&test_data->emap, test_data->base, /* zeroed */ false);
     64 	assert_false(err, "");
     65 
     66 	err = hpa_central_init(&test_data->central, test_data->base, hooks);
     67 	assert_false(err, "");
     68 	sec_opts_t sec_opts;
     69 	sec_opts.nshards = 0;
     70 	tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
     71 	err = hpa_shard_init(tsdn, &test_data->shard, &test_data->central,
     72 	    &test_data->emap, test_data->base, &test_data->shard_edata_cache,
     73 	    SHARD_IND, opts, &sec_opts);
     74 	assert_false(err, "");
     75 
     76 	return (hpa_shard_t *)test_data;
     77 }
     78 
     79 static void
     80 destroy_test_data(hpa_shard_t *shard) {
     81 	test_data_t *test_data = (test_data_t *)shard;
     82 	base_delete(TSDN_NULL, test_data->base);
     83 	free(test_data);
     84 }
     85 
     86 static uintptr_t defer_bump_ptr = HUGEPAGE * 123;
     87 static void *
     88 defer_test_map(size_t size) {
     89 	void *result = (void *)defer_bump_ptr;
     90 	defer_bump_ptr += size;
     91 	return result;
     92 }
     93 
     94 static void
     95 defer_test_unmap(void *ptr, size_t size) {
     96 	(void)ptr;
     97 	(void)size;
     98 }
     99 
    100 static size_t ndefer_purge_calls = 0;
    101 static size_t npurge_size = 0;
    102 static void
    103 defer_test_purge(void *ptr, size_t size) {
    104 	(void)ptr;
    105 	npurge_size = size;
    106 	++ndefer_purge_calls;
    107 }
    108 
    109 static bool defer_vectorized_purge_called = false;
    110 static bool
    111 defer_vectorized_purge(void *vec, size_t vlen, size_t nbytes) {
    112 	(void)vec;
    113 	(void)nbytes;
    114 	++ndefer_purge_calls;
    115 	defer_vectorized_purge_called = true;
    116 	return false;
    117 }
    118 
    119 static size_t ndefer_hugify_calls = 0;
    120 static bool
    121 defer_test_hugify(void *ptr, size_t size, bool sync) {
    122 	++ndefer_hugify_calls;
    123 	return false;
    124 }
    125 
    126 static size_t ndefer_dehugify_calls = 0;
    127 static void
    128 defer_test_dehugify(void *ptr, size_t size) {
    129 	++ndefer_dehugify_calls;
    130 }
    131 
    132 static nstime_t defer_curtime;
    133 static void
    134 defer_test_curtime(nstime_t *r_time, bool first_reading) {
    135 	*r_time = defer_curtime;
    136 }
    137 
    138 static uint64_t
    139 defer_test_ms_since(nstime_t *past_time) {
    140 	return (nstime_ns(&defer_curtime) - nstime_ns(past_time)) / 1000 / 1000;
    141 }
    142 
    143 TEST_BEGIN(test_hpa_hugify_style_none_huge_no_syscall_thp_always) {
    144 	test_skip_if(!hpa_supported() || (opt_process_madvise_max_batch != 0));
    145 
    146 	hpa_hooks_t hooks;
    147 	hooks.map = &defer_test_map;
    148 	hooks.unmap = &defer_test_unmap;
    149 	hooks.purge = &defer_test_purge;
    150 	hooks.hugify = &defer_test_hugify;
    151 	hooks.dehugify = &defer_test_dehugify;
    152 	hooks.curtime = &defer_test_curtime;
    153 	hooks.ms_since = &defer_test_ms_since;
    154 	hooks.vectorized_purge = &defer_vectorized_purge;
    155 
    156 	hpa_shard_opts_t opts = test_hpa_shard_opts_aggressive;
    157 	opts.deferral_allowed = true;
    158 	opts.purge_threshold = PAGE;
    159 	opts.min_purge_delay_ms = 0;
    160 	opts.hugification_threshold = HUGEPAGE * 0.25;
    161 	opts.dirty_mult = FXP_INIT_PERCENT(10);
    162 	opts.hugify_style = hpa_hugify_style_none;
    163 	opts.min_purge_interval_ms = 0;
    164 	opts.hugify_delay_ms = 0;
    165 
    166 	hpa_shard_t *shard = create_test_data(&hooks, &opts);
    167 	bool         deferred_work_generated = false;
    168 	/* Current time = 10ms */
    169 	nstime_init(&defer_curtime, 10 * 1000 * 1000);
    170 
    171 	/* Fake that system is in thp_always mode */
    172 	system_thp_mode_t old_mode = init_system_thp_mode;
    173 	init_system_thp_mode = system_thp_mode_always;
    174 
    175 	tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
    176 	enum { NALLOCS = HUGEPAGE_PAGES };
    177 	edata_t *edatas[NALLOCS];
    178 	ndefer_purge_calls = 0;
    179 	for (int i = 0; i < NALLOCS / 2; i++) {
    180 		edatas[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false,
    181 		    false, false, &deferred_work_generated);
    182 		expect_ptr_not_null(edatas[i], "Unexpected null edata");
    183 	}
    184 	hpdata_t *ps = psset_pick_alloc(&shard->psset, PAGE);
    185 	expect_true(hpdata_huge_get(ps),
    186 	    "Page should be huge because thp=always and hugify_style is none");
    187 
    188 	ndefer_hugify_calls = 0;
    189 	ndefer_purge_calls = 0;
    190 	hpa_shard_do_deferred_work(tsdn, shard);
    191 	expect_zu_eq(ndefer_hugify_calls, 0, "style=none, no syscall");
    192 	expect_zu_eq(ndefer_dehugify_calls, 0, "style=none, no syscall");
    193 	expect_zu_eq(ndefer_purge_calls, 1, "purge should happen");
    194 
    195 	destroy_test_data(shard);
    196 	init_system_thp_mode = old_mode;
    197 }
    198 TEST_END
    199 
    200 int
    201 main(void) {
    202 	return test_no_reentrancy(
    203 	    test_hpa_hugify_style_none_huge_no_syscall_thp_always);
    204 }
    205