123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749 |
- #include "test/jemalloc_test.h"
- #include "jemalloc/internal/psset.h"
- #define PAGESLAB_ADDR ((void *)(1234 * HUGEPAGE))
- #define PAGESLAB_AGE 5678
- #define ALLOC_ARENA_IND 111
- #define ALLOC_ESN 222
- static void
- edata_init_test(edata_t *edata) {
- memset(edata, 0, sizeof(*edata));
- edata_arena_ind_set(edata, ALLOC_ARENA_IND);
- edata_esn_set(edata, ALLOC_ESN);
- }
- static void
- test_psset_fake_purge(hpdata_t *ps) {
- hpdata_purge_state_t purge_state;
- hpdata_alloc_allowed_set(ps, false);
- hpdata_purge_begin(ps, &purge_state);
- void *addr;
- size_t size;
- while (hpdata_purge_next(ps, &purge_state, &addr, &size)) {
- }
- hpdata_purge_end(ps, &purge_state);
- hpdata_alloc_allowed_set(ps, true);
- }
- static void
- test_psset_alloc_new(psset_t *psset, hpdata_t *ps, edata_t *r_edata,
- size_t size) {
- hpdata_assert_empty(ps);
- test_psset_fake_purge(ps);
- psset_insert(psset, ps);
- psset_update_begin(psset, ps);
- void *addr = hpdata_reserve_alloc(ps, size);
- edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
- /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
- /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
- EXTENT_NOT_HEAD);
- edata_ps_set(r_edata, ps);
- psset_update_end(psset, ps);
- }
- static bool
- test_psset_alloc_reuse(psset_t *psset, edata_t *r_edata, size_t size) {
- hpdata_t *ps = psset_pick_alloc(psset, size);
- if (ps == NULL) {
- return true;
- }
- psset_update_begin(psset, ps);
- void *addr = hpdata_reserve_alloc(ps, size);
- edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
- /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
- /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
- EXTENT_NOT_HEAD);
- edata_ps_set(r_edata, ps);
- psset_update_end(psset, ps);
- return false;
- }
- static hpdata_t *
- test_psset_dalloc(psset_t *psset, edata_t *edata) {
- hpdata_t *ps = edata_ps_get(edata);
- psset_update_begin(psset, ps);
- hpdata_unreserve(ps, edata_addr_get(edata), edata_size_get(edata));
- psset_update_end(psset, ps);
- if (hpdata_empty(ps)) {
- psset_remove(psset, ps);
- return ps;
- } else {
- return NULL;
- }
- }
- static void
- edata_expect(edata_t *edata, size_t page_offset, size_t page_cnt) {
- /*
- * Note that allocations should get the arena ind of their home
- * arena, *not* the arena ind of the pageslab allocator.
- */
- expect_u_eq(ALLOC_ARENA_IND, edata_arena_ind_get(edata),
- "Arena ind changed");
- expect_ptr_eq(
- (void *)((uintptr_t)PAGESLAB_ADDR + (page_offset << LG_PAGE)),
- edata_addr_get(edata), "Didn't allocate in order");
- expect_zu_eq(page_cnt << LG_PAGE, edata_size_get(edata), "");
- expect_false(edata_slab_get(edata), "");
- expect_u_eq(SC_NSIZES, edata_szind_get_maybe_invalid(edata),
- "");
- expect_u64_eq(0, edata_sn_get(edata), "");
- expect_d_eq(edata_state_get(edata), extent_state_active, "");
- expect_false(edata_zeroed_get(edata), "");
- expect_true(edata_committed_get(edata), "");
- expect_d_eq(EXTENT_PAI_HPA, edata_pai_get(edata), "");
- expect_false(edata_is_head_get(edata), "");
- }
- TEST_BEGIN(test_empty) {
- bool err;
- hpdata_t pageslab;
- hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
- edata_t alloc;
- edata_init_test(&alloc);
- psset_t psset;
- psset_init(&psset);
- /* Empty psset should return fail allocations. */
- err = test_psset_alloc_reuse(&psset, &alloc, PAGE);
- expect_true(err, "Empty psset succeeded in an allocation.");
- }
- TEST_END
- TEST_BEGIN(test_fill) {
- bool err;
- hpdata_t pageslab;
- hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
- edata_t alloc[HUGEPAGE_PAGES];
- psset_t psset;
- psset_init(&psset);
- edata_init_test(&alloc[0]);
- test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
- for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
- edata_init_test(&alloc[i]);
- err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
- expect_false(err, "Nonempty psset failed page allocation.");
- }
- for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
- edata_t *edata = &alloc[i];
- edata_expect(edata, i, 1);
- }
- /* The pageslab, and thus psset, should now have no allocations. */
- edata_t extra_alloc;
- edata_init_test(&extra_alloc);
- err = test_psset_alloc_reuse(&psset, &extra_alloc, PAGE);
- expect_true(err, "Alloc succeeded even though psset should be empty");
- }
- TEST_END
- TEST_BEGIN(test_reuse) {
- bool err;
- hpdata_t *ps;
- hpdata_t pageslab;
- hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
- edata_t alloc[HUGEPAGE_PAGES];
- psset_t psset;
- psset_init(&psset);
- edata_init_test(&alloc[0]);
- test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
- for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
- edata_init_test(&alloc[i]);
- err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
- expect_false(err, "Nonempty psset failed page allocation.");
- }
- /* Free odd indices. */
- for (size_t i = 0; i < HUGEPAGE_PAGES; i ++) {
- if (i % 2 == 0) {
- continue;
- }
- ps = test_psset_dalloc(&psset, &alloc[i]);
- expect_ptr_null(ps, "Nonempty pageslab evicted");
- }
- /* Realloc into them. */
- for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
- if (i % 2 == 0) {
- continue;
- }
- err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
- expect_false(err, "Nonempty psset failed page allocation.");
- edata_expect(&alloc[i], i, 1);
- }
- /* Now, free the pages at indices 0 or 1 mod 2. */
- for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
- if (i % 4 > 1) {
- continue;
- }
- ps = test_psset_dalloc(&psset, &alloc[i]);
- expect_ptr_null(ps, "Nonempty pageslab evicted");
- }
- /* And realloc 2-page allocations into them. */
- for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
- if (i % 4 != 0) {
- continue;
- }
- err = test_psset_alloc_reuse(&psset, &alloc[i], 2 * PAGE);
- expect_false(err, "Nonempty psset failed page allocation.");
- edata_expect(&alloc[i], i, 2);
- }
- /* Free all the 2-page allocations. */
- for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
- if (i % 4 != 0) {
- continue;
- }
- ps = test_psset_dalloc(&psset, &alloc[i]);
- expect_ptr_null(ps, "Nonempty pageslab evicted");
- }
- /*
- * Free up a 1-page hole next to a 2-page hole, but somewhere in the
- * middle of the pageslab. Index 11 should be right before such a hole
- * (since 12 % 4 == 0).
- */
- size_t index_of_3 = 11;
- ps = test_psset_dalloc(&psset, &alloc[index_of_3]);
- expect_ptr_null(ps, "Nonempty pageslab evicted");
- err = test_psset_alloc_reuse(&psset, &alloc[index_of_3], 3 * PAGE);
- expect_false(err, "Should have been able to find alloc.");
- edata_expect(&alloc[index_of_3], index_of_3, 3);
- /*
- * Free up a 4-page hole at the end. Recall that the pages at offsets 0
- * and 1 mod 4 were freed above, so we just have to free the last
- * allocations.
- */
- ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
- expect_ptr_null(ps, "Nonempty pageslab evicted");
- ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 2]);
- expect_ptr_null(ps, "Nonempty pageslab evicted");
- /* Make sure we can satisfy an allocation at the very end of a slab. */
- size_t index_of_4 = HUGEPAGE_PAGES - 4;
- err = test_psset_alloc_reuse(&psset, &alloc[index_of_4], 4 * PAGE);
- expect_false(err, "Should have been able to find alloc.");
- edata_expect(&alloc[index_of_4], index_of_4, 4);
- }
- TEST_END
- TEST_BEGIN(test_evict) {
- bool err;
- hpdata_t *ps;
- hpdata_t pageslab;
- hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
- edata_t alloc[HUGEPAGE_PAGES];
- psset_t psset;
- psset_init(&psset);
- /* Alloc the whole slab. */
- edata_init_test(&alloc[0]);
- test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
- for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
- edata_init_test(&alloc[i]);
- err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
- expect_false(err, "Unxpected allocation failure");
- }
- /* Dealloc the whole slab, going forwards. */
- for (size_t i = 0; i < HUGEPAGE_PAGES - 1; i++) {
- ps = test_psset_dalloc(&psset, &alloc[i]);
- expect_ptr_null(ps, "Nonempty pageslab evicted");
- }
- ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
- expect_ptr_eq(&pageslab, ps, "Empty pageslab not evicted.");
- err = test_psset_alloc_reuse(&psset, &alloc[0], PAGE);
- expect_true(err, "psset should be empty.");
- }
- TEST_END
- TEST_BEGIN(test_multi_pageslab) {
- bool err;
- hpdata_t *ps;
- hpdata_t pageslab[2];
- hpdata_init(&pageslab[0], PAGESLAB_ADDR, PAGESLAB_AGE);
- hpdata_init(&pageslab[1],
- (void *)((uintptr_t)PAGESLAB_ADDR + HUGEPAGE),
- PAGESLAB_AGE + 1);
- edata_t alloc[2][HUGEPAGE_PAGES];
- psset_t psset;
- psset_init(&psset);
- /* Insert both slabs. */
- edata_init_test(&alloc[0][0]);
- test_psset_alloc_new(&psset, &pageslab[0], &alloc[0][0], PAGE);
- edata_init_test(&alloc[1][0]);
- test_psset_alloc_new(&psset, &pageslab[1], &alloc[1][0], PAGE);
- /* Fill them both up; make sure we do so in first-fit order. */
- for (size_t i = 0; i < 2; i++) {
- for (size_t j = 1; j < HUGEPAGE_PAGES; j++) {
- edata_init_test(&alloc[i][j]);
- err = test_psset_alloc_reuse(&psset, &alloc[i][j], PAGE);
- expect_false(err,
- "Nonempty psset failed page allocation.");
- assert_ptr_eq(&pageslab[i], edata_ps_get(&alloc[i][j]),
- "Didn't pick pageslabs in first-fit");
- }
- }
- /*
- * Free up a 2-page hole in the earlier slab, and a 1-page one in the
- * later one. We should still pick the later one.
- */
- ps = test_psset_dalloc(&psset, &alloc[0][0]);
- expect_ptr_null(ps, "Unexpected eviction");
- ps = test_psset_dalloc(&psset, &alloc[0][1]);
- expect_ptr_null(ps, "Unexpected eviction");
- ps = test_psset_dalloc(&psset, &alloc[1][0]);
- expect_ptr_null(ps, "Unexpected eviction");
- err = test_psset_alloc_reuse(&psset, &alloc[0][0], PAGE);
- expect_ptr_eq(&pageslab[1], edata_ps_get(&alloc[0][0]),
- "Should have picked the fuller pageslab");
- /*
- * Now both slabs have 1-page holes. Free up a second one in the later
- * slab.
- */
- ps = test_psset_dalloc(&psset, &alloc[1][1]);
- expect_ptr_null(ps, "Unexpected eviction");
- /*
- * We should be able to allocate a 2-page object, even though an earlier
- * size class is nonempty.
- */
- err = test_psset_alloc_reuse(&psset, &alloc[1][0], 2 * PAGE);
- expect_false(err, "Allocation should have succeeded");
- }
- TEST_END
- static void
- stats_expect_empty(psset_bin_stats_t *stats) {
- assert_zu_eq(0, stats->npageslabs,
- "Supposedly empty bin had positive npageslabs");
- expect_zu_eq(0, stats->nactive, "Unexpected nonempty bin"
- "Supposedly empty bin had positive nactive");
- }
- static void
- stats_expect(psset_t *psset, size_t nactive) {
- if (nactive == HUGEPAGE_PAGES) {
- expect_zu_eq(1, psset->stats.full_slabs[0].npageslabs,
- "Expected a full slab");
- expect_zu_eq(HUGEPAGE_PAGES,
- psset->stats.full_slabs[0].nactive,
- "Should have exactly filled the bin");
- } else {
- stats_expect_empty(&psset->stats.full_slabs[0]);
- }
- size_t ninactive = HUGEPAGE_PAGES - nactive;
- pszind_t nonempty_pind = PSSET_NPSIZES;
- if (ninactive != 0 && ninactive < HUGEPAGE_PAGES) {
- nonempty_pind = sz_psz2ind(sz_psz_quantize_floor(
- ninactive << LG_PAGE));
- }
- for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
- if (i == nonempty_pind) {
- assert_zu_eq(1,
- psset->stats.nonfull_slabs[i][0].npageslabs,
- "Should have found a slab");
- expect_zu_eq(nactive,
- psset->stats.nonfull_slabs[i][0].nactive,
- "Mismatch in active pages");
- } else {
- stats_expect_empty(&psset->stats.nonfull_slabs[i][0]);
- }
- }
- expect_zu_eq(nactive, psset_nactive(psset), "");
- }
- TEST_BEGIN(test_stats) {
- bool err;
- hpdata_t pageslab;
- hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
- edata_t alloc[HUGEPAGE_PAGES];
- psset_t psset;
- psset_init(&psset);
- stats_expect(&psset, 0);
- edata_init_test(&alloc[0]);
- test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
- for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
- stats_expect(&psset, i);
- edata_init_test(&alloc[i]);
- err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
- expect_false(err, "Nonempty psset failed page allocation.");
- }
- stats_expect(&psset, HUGEPAGE_PAGES);
- hpdata_t *ps;
- for (ssize_t i = HUGEPAGE_PAGES - 1; i >= 0; i--) {
- ps = test_psset_dalloc(&psset, &alloc[i]);
- expect_true((ps == NULL) == (i != 0),
- "test_psset_dalloc should only evict a slab on the last "
- "free");
- stats_expect(&psset, i);
- }
- test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
- stats_expect(&psset, 1);
- psset_update_begin(&psset, &pageslab);
- stats_expect(&psset, 0);
- psset_update_end(&psset, &pageslab);
- stats_expect(&psset, 1);
- }
- TEST_END
- /*
- * Fills in and inserts two pageslabs, with the first better than the second,
- * and each fully allocated (into the allocations in allocs and worse_allocs,
- * each of which should be HUGEPAGE_PAGES long), except for a single free page
- * at the end.
- *
- * (There's nothing magic about these numbers; it's just useful to share the
- * setup between the oldest fit and the insert/remove test).
- */
- static void
- init_test_pageslabs(psset_t *psset, hpdata_t *pageslab,
- hpdata_t *worse_pageslab, edata_t *alloc, edata_t *worse_alloc) {
- bool err;
- hpdata_init(pageslab, (void *)(10 * HUGEPAGE), PAGESLAB_AGE);
- /*
- * This pageslab would be better from an address-first-fit POV, but
- * worse from an age POV.
- */
- hpdata_init(worse_pageslab, (void *)(9 * HUGEPAGE), PAGESLAB_AGE + 1);
- psset_init(psset);
- edata_init_test(&alloc[0]);
- test_psset_alloc_new(psset, pageslab, &alloc[0], PAGE);
- for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
- edata_init_test(&alloc[i]);
- err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
- expect_false(err, "Nonempty psset failed page allocation.");
- expect_ptr_eq(pageslab, edata_ps_get(&alloc[i]),
- "Allocated from the wrong pageslab");
- }
- edata_init_test(&worse_alloc[0]);
- test_psset_alloc_new(psset, worse_pageslab, &worse_alloc[0], PAGE);
- expect_ptr_eq(worse_pageslab, edata_ps_get(&worse_alloc[0]),
- "Allocated from the wrong pageslab");
- /*
- * Make the two pssets otherwise indistinguishable; all full except for
- * a single page.
- */
- for (size_t i = 1; i < HUGEPAGE_PAGES - 1; i++) {
- edata_init_test(&worse_alloc[i]);
- err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
- expect_false(err, "Nonempty psset failed page allocation.");
- expect_ptr_eq(worse_pageslab, edata_ps_get(&alloc[i]),
- "Allocated from the wrong pageslab");
- }
- /* Deallocate the last page from the older pageslab. */
- hpdata_t *evicted = test_psset_dalloc(psset,
- &alloc[HUGEPAGE_PAGES - 1]);
- expect_ptr_null(evicted, "Unexpected eviction");
- }
- TEST_BEGIN(test_oldest_fit) {
- bool err;
- edata_t alloc[HUGEPAGE_PAGES];
- edata_t worse_alloc[HUGEPAGE_PAGES];
- hpdata_t pageslab;
- hpdata_t worse_pageslab;
- psset_t psset;
- init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
- worse_alloc);
- /* The edata should come from the better pageslab. */
- edata_t test_edata;
- edata_init_test(&test_edata);
- err = test_psset_alloc_reuse(&psset, &test_edata, PAGE);
- expect_false(err, "Nonempty psset failed page allocation");
- expect_ptr_eq(&pageslab, edata_ps_get(&test_edata),
- "Allocated from the wrong pageslab");
- }
- TEST_END
- TEST_BEGIN(test_insert_remove) {
- bool err;
- hpdata_t *ps;
- edata_t alloc[HUGEPAGE_PAGES];
- edata_t worse_alloc[HUGEPAGE_PAGES];
- hpdata_t pageslab;
- hpdata_t worse_pageslab;
- psset_t psset;
- init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
- worse_alloc);
- /* Remove better; should still be able to alloc from worse. */
- psset_update_begin(&psset, &pageslab);
- err = test_psset_alloc_reuse(&psset, &worse_alloc[HUGEPAGE_PAGES - 1],
- PAGE);
- expect_false(err, "Removal should still leave an empty page");
- expect_ptr_eq(&worse_pageslab,
- edata_ps_get(&worse_alloc[HUGEPAGE_PAGES - 1]),
- "Allocated out of wrong ps");
- /*
- * After deallocating the previous alloc and reinserting better, it
- * should be preferred for future allocations.
- */
- ps = test_psset_dalloc(&psset, &worse_alloc[HUGEPAGE_PAGES - 1]);
- expect_ptr_null(ps, "Incorrect eviction of nonempty pageslab");
- psset_update_end(&psset, &pageslab);
- err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
- expect_false(err, "psset should be nonempty");
- expect_ptr_eq(&pageslab, edata_ps_get(&alloc[HUGEPAGE_PAGES - 1]),
- "Removal/reinsertion shouldn't change ordering");
- /*
- * After deallocating and removing both, allocations should fail.
- */
- ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
- expect_ptr_null(ps, "Incorrect eviction");
- psset_update_begin(&psset, &pageslab);
- psset_update_begin(&psset, &worse_pageslab);
- err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
- expect_true(err, "psset should be empty, but an alloc succeeded");
- }
- TEST_END
- TEST_BEGIN(test_purge_prefers_nonhuge) {
- /*
- * All else being equal, we should prefer purging non-huge pages over
- * huge ones for non-empty extents.
- */
- /* Nothing magic about this constant. */
- enum {
- NHP = 23,
- };
- hpdata_t *hpdata;
- psset_t psset;
- psset_init(&psset);
- hpdata_t hpdata_huge[NHP];
- uintptr_t huge_begin = (uintptr_t)&hpdata_huge[0];
- uintptr_t huge_end = (uintptr_t)&hpdata_huge[NHP];
- hpdata_t hpdata_nonhuge[NHP];
- uintptr_t nonhuge_begin = (uintptr_t)&hpdata_nonhuge[0];
- uintptr_t nonhuge_end = (uintptr_t)&hpdata_nonhuge[NHP];
- for (size_t i = 0; i < NHP; i++) {
- hpdata_init(&hpdata_huge[i], (void *)((10 + i) * HUGEPAGE),
- 123 + i);
- psset_insert(&psset, &hpdata_huge[i]);
- hpdata_init(&hpdata_nonhuge[i],
- (void *)((10 + NHP + i) * HUGEPAGE),
- 456 + i);
- psset_insert(&psset, &hpdata_nonhuge[i]);
- }
- for (int i = 0; i < 2 * NHP; i++) {
- hpdata = psset_pick_alloc(&psset, HUGEPAGE * 3 / 4);
- psset_update_begin(&psset, hpdata);
- void *ptr;
- ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE * 3 / 4);
- /* Ignore the first alloc, which will stick around. */
- (void)ptr;
- /*
- * The second alloc is to dirty the pages; free it immediately
- * after allocating.
- */
- ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE / 4);
- hpdata_unreserve(hpdata, ptr, HUGEPAGE / 4);
- if (huge_begin <= (uintptr_t)hpdata
- && (uintptr_t)hpdata < huge_end) {
- hpdata_hugify(hpdata);
- }
- hpdata_purge_allowed_set(hpdata, true);
- psset_update_end(&psset, hpdata);
- }
- /*
- * We've got a bunch of 1/8th dirty hpdatas. It should give us all the
- * non-huge ones to purge, then all the huge ones, then refuse to purge
- * further.
- */
- for (int i = 0; i < NHP; i++) {
- hpdata = psset_pick_purge(&psset);
- assert_true(nonhuge_begin <= (uintptr_t)hpdata
- && (uintptr_t)hpdata < nonhuge_end, "");
- psset_update_begin(&psset, hpdata);
- test_psset_fake_purge(hpdata);
- hpdata_purge_allowed_set(hpdata, false);
- psset_update_end(&psset, hpdata);
- }
- for (int i = 0; i < NHP; i++) {
- hpdata = psset_pick_purge(&psset);
- expect_true(huge_begin <= (uintptr_t)hpdata
- && (uintptr_t)hpdata < huge_end, "");
- psset_update_begin(&psset, hpdata);
- hpdata_dehugify(hpdata);
- test_psset_fake_purge(hpdata);
- hpdata_purge_allowed_set(hpdata, false);
- psset_update_end(&psset, hpdata);
- }
- }
- TEST_END
- TEST_BEGIN(test_purge_prefers_empty) {
- void *ptr;
- psset_t psset;
- psset_init(&psset);
- hpdata_t hpdata_empty;
- hpdata_t hpdata_nonempty;
- hpdata_init(&hpdata_empty, (void *)(10 * HUGEPAGE), 123);
- psset_insert(&psset, &hpdata_empty);
- hpdata_init(&hpdata_nonempty, (void *)(11 * HUGEPAGE), 456);
- psset_insert(&psset, &hpdata_nonempty);
- psset_update_begin(&psset, &hpdata_empty);
- ptr = hpdata_reserve_alloc(&hpdata_empty, PAGE);
- expect_ptr_eq(hpdata_addr_get(&hpdata_empty), ptr, "");
- hpdata_unreserve(&hpdata_empty, ptr, PAGE);
- hpdata_purge_allowed_set(&hpdata_empty, true);
- psset_update_end(&psset, &hpdata_empty);
- psset_update_begin(&psset, &hpdata_nonempty);
- ptr = hpdata_reserve_alloc(&hpdata_nonempty, 10 * PAGE);
- expect_ptr_eq(hpdata_addr_get(&hpdata_nonempty), ptr, "");
- hpdata_unreserve(&hpdata_nonempty, ptr, 9 * PAGE);
- hpdata_purge_allowed_set(&hpdata_nonempty, true);
- psset_update_end(&psset, &hpdata_nonempty);
- /*
- * The nonempty slab has 9 dirty pages, while the empty one has only 1.
- * We should still pick the empty one for purging.
- */
- hpdata_t *to_purge = psset_pick_purge(&psset);
- expect_ptr_eq(&hpdata_empty, to_purge, "");
- }
- TEST_END
- TEST_BEGIN(test_purge_prefers_empty_huge) {
- void *ptr;
- psset_t psset;
- psset_init(&psset);
- enum {NHP = 10 };
- hpdata_t hpdata_huge[NHP];
- hpdata_t hpdata_nonhuge[NHP];
- uintptr_t cur_addr = 100 * HUGEPAGE;
- uint64_t cur_age = 123;
- for (int i = 0; i < NHP; i++) {
- hpdata_init(&hpdata_huge[i], (void *)cur_addr, cur_age);
- cur_addr += HUGEPAGE;
- cur_age++;
- psset_insert(&psset, &hpdata_huge[i]);
- hpdata_init(&hpdata_nonhuge[i], (void *)cur_addr, cur_age);
- cur_addr += HUGEPAGE;
- cur_age++;
- psset_insert(&psset, &hpdata_nonhuge[i]);
- /*
- * Make the hpdata_huge[i] fully dirty, empty, purgable, and
- * huge.
- */
- psset_update_begin(&psset, &hpdata_huge[i]);
- ptr = hpdata_reserve_alloc(&hpdata_huge[i], HUGEPAGE);
- expect_ptr_eq(hpdata_addr_get(&hpdata_huge[i]), ptr, "");
- hpdata_hugify(&hpdata_huge[i]);
- hpdata_unreserve(&hpdata_huge[i], ptr, HUGEPAGE);
- hpdata_purge_allowed_set(&hpdata_huge[i], true);
- psset_update_end(&psset, &hpdata_huge[i]);
- /*
- * Make hpdata_nonhuge[i] fully dirty, empty, purgable, and
- * non-huge.
- */
- psset_update_begin(&psset, &hpdata_nonhuge[i]);
- ptr = hpdata_reserve_alloc(&hpdata_nonhuge[i], HUGEPAGE);
- expect_ptr_eq(hpdata_addr_get(&hpdata_nonhuge[i]), ptr, "");
- hpdata_unreserve(&hpdata_nonhuge[i], ptr, HUGEPAGE);
- hpdata_purge_allowed_set(&hpdata_nonhuge[i], true);
- psset_update_end(&psset, &hpdata_nonhuge[i]);
- }
- /*
- * We have a bunch of empty slabs, half huge, half nonhuge, inserted in
- * alternating order. We should pop all the huge ones before popping
- * any of the non-huge ones for purging.
- */
- for (int i = 0; i < NHP; i++) {
- hpdata_t *to_purge = psset_pick_purge(&psset);
- expect_ptr_eq(&hpdata_huge[i], to_purge, "");
- psset_update_begin(&psset, to_purge);
- hpdata_purge_allowed_set(to_purge, false);
- psset_update_end(&psset, to_purge);
- }
- for (int i = 0; i < NHP; i++) {
- hpdata_t *to_purge = psset_pick_purge(&psset);
- expect_ptr_eq(&hpdata_nonhuge[i], to_purge, "");
- psset_update_begin(&psset, to_purge);
- hpdata_purge_allowed_set(to_purge, false);
- psset_update_end(&psset, to_purge);
- }
- }
- TEST_END
- int
- main(void) {
- return test_no_reentrancy(
- test_empty,
- test_fill,
- test_reuse,
- test_evict,
- test_multi_pageslab,
- test_stats,
- test_oldest_fit,
- test_insert_remove,
- test_purge_prefers_nonhuge,
- test_purge_prefers_empty,
- test_purge_prefers_empty_huge);
- }
|