2020-07-11 08:40:13 +08:00
|
|
|
#include "test/jemalloc_test.h"
|
|
|
|
|
|
|
|
#include "jemalloc/internal/psset.h"
|
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
#define PAGESLAB_ADDR ((void *)(1234 * HUGEPAGE))
|
|
|
|
#define PAGESLAB_AGE 5678
|
2020-07-11 08:40:13 +08:00
|
|
|
|
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
static void
|
|
|
|
test_psset_fake_purge(hpdata_t *ps) {
|
|
|
|
hpdata_purge_state_t purge_state;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-12-01 08:10:56 +08:00
|
|
|
static void
|
|
|
|
test_psset_alloc_new(psset_t *psset, hpdata_t *ps, edata_t *r_edata,
|
|
|
|
size_t size) {
|
|
|
|
hpdata_assert_empty(ps);
|
2020-12-06 07:58:31 +08:00
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
test_psset_fake_purge(ps);
|
|
|
|
|
|
|
|
psset_insert(psset, ps);
|
|
|
|
psset_update_begin(psset, ps);
|
2020-12-06 07:58:31 +08:00
|
|
|
|
2020-12-01 08:10:56 +08:00
|
|
|
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);
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_end(psset, ps);
|
2020-12-01 08:10:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
test_psset_alloc_reuse(psset_t *psset, edata_t *r_edata, size_t size) {
|
2020-12-06 07:58:31 +08:00
|
|
|
hpdata_t *ps = psset_pick_alloc(psset, size);
|
2020-12-01 08:10:56 +08:00
|
|
|
if (ps == NULL) {
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_begin(psset, ps);
|
2020-12-01 08:10:56 +08:00
|
|
|
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);
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_end(psset, ps);
|
2020-12-01 08:10:56 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static hpdata_t *
|
|
|
|
test_psset_dalloc(psset_t *psset, edata_t *edata) {
|
|
|
|
hpdata_t *ps = edata_ps_get(edata);
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_begin(psset, ps);
|
2020-12-01 08:10:56 +08:00
|
|
|
hpdata_unreserve(ps, edata_addr_get(edata), edata_size_get(edata));
|
2020-12-06 09:42:04 +08:00
|
|
|
psset_update_end(psset, ps);
|
2020-12-01 08:10:56 +08:00
|
|
|
if (hpdata_empty(ps)) {
|
2020-12-06 09:42:04 +08:00
|
|
|
psset_remove(psset, ps);
|
2020-12-01 08:10:56 +08:00
|
|
|
return ps;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-11 08:40:13 +08:00
|
|
|
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),
|
|
|
|
"");
|
2021-02-07 01:29:01 +08:00
|
|
|
expect_u64_eq(0, edata_sn_get(edata), "");
|
2020-07-11 08:40:13 +08:00
|
|
|
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;
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t pageslab;
|
|
|
|
hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
edata_t alloc;
|
2020-07-11 08:40:13 +08:00
|
|
|
edata_init_test(&alloc);
|
|
|
|
|
|
|
|
psset_t psset;
|
|
|
|
psset_init(&psset);
|
|
|
|
|
|
|
|
/* Empty psset should return fail allocations. */
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc, PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_true(err, "Empty psset succeeded in an allocation.");
|
|
|
|
}
|
|
|
|
TEST_END
|
|
|
|
|
|
|
|
TEST_BEGIN(test_fill) {
|
|
|
|
bool err;
|
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t pageslab;
|
|
|
|
hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
|
|
|
|
|
|
|
|
edata_t alloc[HUGEPAGE_PAGES];
|
2020-07-11 08:40:13 +08:00
|
|
|
|
|
|
|
psset_t psset;
|
|
|
|
psset_init(&psset);
|
|
|
|
|
|
|
|
edata_init_test(&alloc[0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
edata_init_test(&alloc[i]);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_false(err, "Nonempty psset failed page allocation.");
|
|
|
|
}
|
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
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);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &extra_alloc, PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_true(err, "Alloc succeeded even though psset should be empty");
|
|
|
|
}
|
|
|
|
TEST_END
|
|
|
|
|
|
|
|
TEST_BEGIN(test_reuse) {
|
|
|
|
bool err;
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t *ps;
|
2020-07-11 08:40:13 +08:00
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t pageslab;
|
|
|
|
hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
edata_t alloc[HUGEPAGE_PAGES];
|
2020-07-11 08:40:13 +08:00
|
|
|
|
|
|
|
psset_t psset;
|
|
|
|
psset_init(&psset);
|
|
|
|
|
|
|
|
edata_init_test(&alloc[0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
edata_init_test(&alloc[i]);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_false(err, "Nonempty psset failed page allocation.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free odd indices. */
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 0; i < HUGEPAGE_PAGES; i ++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
if (i % 2 == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[i]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
|
|
|
}
|
|
|
|
/* Realloc into them. */
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
if (i % 2 == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
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. */
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
if (i % 4 > 1) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[i]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
|
|
|
}
|
|
|
|
/* And realloc 2-page allocations into them. */
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
if (i % 4 != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[i], 2 * PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_false(err, "Nonempty psset failed page allocation.");
|
|
|
|
edata_expect(&alloc[i], i, 2);
|
|
|
|
}
|
|
|
|
/* Free all the 2-page allocations. */
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
if (i % 4 != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[i]);
|
2020-07-11 08:40:13 +08:00
|
|
|
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;
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[index_of_3]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[index_of_3], 3 * PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_false(err, "Should have been able to find alloc.");
|
|
|
|
edata_expect(&alloc[index_of_3], index_of_3, 3);
|
|
|
|
|
2020-12-03 06:20:45 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 2]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
|
|
|
|
|
|
|
/* Make sure we can satisfy an allocation at the very end of a slab. */
|
2020-11-18 08:32:45 +08:00
|
|
|
size_t index_of_4 = HUGEPAGE_PAGES - 4;
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[index_of_4], 4 * PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
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;
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t *ps;
|
|
|
|
|
|
|
|
hpdata_t pageslab;
|
|
|
|
hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
|
|
|
|
|
|
|
|
edata_t alloc[HUGEPAGE_PAGES];
|
|
|
|
|
2020-07-11 08:40:13 +08:00
|
|
|
psset_t psset;
|
|
|
|
psset_init(&psset);
|
|
|
|
|
|
|
|
/* Alloc the whole slab. */
|
|
|
|
edata_init_test(&alloc[0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
edata_init_test(&alloc[i]);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_false(err, "Unxpected allocation failure");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dealloc the whole slab, going forwards. */
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 0; i < HUGEPAGE_PAGES - 1; i++) {
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[i]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
|
|
|
}
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_eq(&pageslab, ps, "Empty pageslab not evicted.");
|
|
|
|
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[0], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_true(err, "psset should be empty.");
|
|
|
|
}
|
|
|
|
TEST_END
|
|
|
|
|
|
|
|
TEST_BEGIN(test_multi_pageslab) {
|
|
|
|
bool err;
|
2020-11-18 08:32:45 +08:00
|
|
|
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];
|
2020-07-11 08:40:13 +08:00
|
|
|
|
|
|
|
psset_t psset;
|
|
|
|
psset_init(&psset);
|
|
|
|
|
|
|
|
/* Insert both slabs. */
|
|
|
|
edata_init_test(&alloc[0][0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(&psset, &pageslab[0], &alloc[0][0], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
edata_init_test(&alloc[1][0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(&psset, &pageslab[1], &alloc[1][0], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
|
|
|
|
/* Fill them both up; make sure we do so in first-fit order. */
|
|
|
|
for (size_t i = 0; i < 2; i++) {
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t j = 1; j < HUGEPAGE_PAGES; j++) {
|
2020-07-11 08:40:13 +08:00
|
|
|
edata_init_test(&alloc[i][j]);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[i][j], PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
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
|
2020-09-19 07:36:40 +08:00
|
|
|
* later one. We should still pick the later one.
|
2020-07-11 08:40:13 +08:00
|
|
|
*/
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[0][0]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Unexpected eviction");
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[0][1]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Unexpected eviction");
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[1][0]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Unexpected eviction");
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[0][0], PAGE);
|
2020-09-19 07:36:40 +08:00
|
|
|
expect_ptr_eq(&pageslab[1], edata_ps_get(&alloc[0][0]),
|
|
|
|
"Should have picked the fuller pageslab");
|
2020-07-11 08:40:13 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now both slabs have 1-page holes. Free up a second one in the later
|
|
|
|
* slab.
|
|
|
|
*/
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[1][1]);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_ptr_null(ps, "Unexpected eviction");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We should be able to allocate a 2-page object, even though an earlier
|
|
|
|
* size class is nonempty.
|
|
|
|
*/
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[1][0], 2 * PAGE);
|
2020-07-11 08:40:13 +08:00
|
|
|
expect_false(err, "Allocation should have succeeded");
|
|
|
|
}
|
|
|
|
TEST_END
|
|
|
|
|
2020-09-03 03:59:10 +08:00
|
|
|
static void
|
|
|
|
stats_expect_empty(psset_bin_stats_t *stats) {
|
2020-12-04 10:32:42 +08:00
|
|
|
assert_zu_eq(0, stats->npageslabs,
|
2020-09-03 03:59:10 +08:00
|
|
|
"Supposedly empty bin had positive npageslabs");
|
2020-12-04 10:32:42 +08:00
|
|
|
expect_zu_eq(0, stats->nactive, "Unexpected nonempty bin"
|
2020-09-03 03:59:10 +08:00
|
|
|
"Supposedly empty bin had positive nactive");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stats_expect(psset_t *psset, size_t nactive) {
|
2020-11-18 08:32:45 +08:00
|
|
|
if (nactive == HUGEPAGE_PAGES) {
|
2020-12-04 10:32:42 +08:00
|
|
|
expect_zu_eq(1, psset->stats.full_slabs[0].npageslabs,
|
2020-09-03 03:59:10 +08:00
|
|
|
"Expected a full slab");
|
2020-11-18 08:32:45 +08:00
|
|
|
expect_zu_eq(HUGEPAGE_PAGES,
|
2020-12-04 10:32:42 +08:00
|
|
|
psset->stats.full_slabs[0].nactive,
|
2020-09-03 03:59:10 +08:00
|
|
|
"Should have exactly filled the bin");
|
|
|
|
} else {
|
2020-12-04 10:32:42 +08:00
|
|
|
stats_expect_empty(&psset->stats.full_slabs[0]);
|
2020-09-03 03:59:10 +08:00
|
|
|
}
|
2020-11-18 08:32:45 +08:00
|
|
|
size_t ninactive = HUGEPAGE_PAGES - nactive;
|
2020-09-03 03:59:10 +08:00
|
|
|
pszind_t nonempty_pind = PSSET_NPSIZES;
|
2020-11-18 08:32:45 +08:00
|
|
|
if (ninactive != 0 && ninactive < HUGEPAGE_PAGES) {
|
2020-09-03 03:59:10 +08:00
|
|
|
nonempty_pind = sz_psz2ind(sz_psz_quantize_floor(
|
|
|
|
ninactive << LG_PAGE));
|
|
|
|
}
|
|
|
|
for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
|
|
|
|
if (i == nonempty_pind) {
|
2020-11-11 08:23:03 +08:00
|
|
|
assert_zu_eq(1,
|
2020-12-04 10:32:42 +08:00
|
|
|
psset->stats.nonfull_slabs[i][0].npageslabs,
|
2020-09-03 03:59:10 +08:00
|
|
|
"Should have found a slab");
|
2020-11-11 08:23:03 +08:00
|
|
|
expect_zu_eq(nactive,
|
2020-12-04 10:32:42 +08:00
|
|
|
psset->stats.nonfull_slabs[i][0].nactive,
|
2020-09-03 03:59:10 +08:00
|
|
|
"Mismatch in active pages");
|
|
|
|
} else {
|
2020-12-04 10:32:42 +08:00
|
|
|
stats_expect_empty(&psset->stats.nonfull_slabs[i][0]);
|
2020-09-03 03:59:10 +08:00
|
|
|
}
|
|
|
|
}
|
2020-12-07 04:49:03 +08:00
|
|
|
expect_zu_eq(nactive, psset_nactive(psset), "");
|
2020-09-03 03:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_BEGIN(test_stats) {
|
|
|
|
bool err;
|
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t pageslab;
|
|
|
|
hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
|
|
|
|
|
|
|
|
edata_t alloc[HUGEPAGE_PAGES];
|
2020-09-03 03:59:10 +08:00
|
|
|
|
|
|
|
psset_t psset;
|
|
|
|
psset_init(&psset);
|
|
|
|
stats_expect(&psset, 0);
|
|
|
|
|
|
|
|
edata_init_test(&alloc[0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
|
2020-09-03 03:59:10 +08:00
|
|
|
stats_expect(&psset, i);
|
|
|
|
edata_init_test(&alloc[i]);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
|
2020-09-03 03:59:10 +08:00
|
|
|
expect_false(err, "Nonempty psset failed page allocation.");
|
|
|
|
}
|
2020-11-18 08:32:45 +08:00
|
|
|
stats_expect(&psset, HUGEPAGE_PAGES);
|
|
|
|
hpdata_t *ps;
|
|
|
|
for (ssize_t i = HUGEPAGE_PAGES - 1; i >= 0; i--) {
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[i]);
|
2020-09-03 03:59:10 +08:00
|
|
|
expect_true((ps == NULL) == (i != 0),
|
2020-12-01 08:10:56 +08:00
|
|
|
"test_psset_dalloc should only evict a slab on the last "
|
|
|
|
"free");
|
2020-09-03 03:59:10 +08:00
|
|
|
stats_expect(&psset, i);
|
|
|
|
}
|
2020-11-10 09:24:31 +08:00
|
|
|
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
2020-11-10 09:24:31 +08:00
|
|
|
stats_expect(&psset, 1);
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_begin(&psset, &pageslab);
|
2020-11-10 09:24:31 +08:00
|
|
|
stats_expect(&psset, 0);
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_end(&psset, &pageslab);
|
2020-11-10 09:24:31 +08:00
|
|
|
stats_expect(&psset, 1);
|
2020-09-03 03:59:10 +08:00
|
|
|
}
|
|
|
|
TEST_END
|
|
|
|
|
2020-11-10 09:24:31 +08:00
|
|
|
/*
|
|
|
|
* 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,
|
2020-12-03 06:20:45 +08:00
|
|
|
* each of which should be HUGEPAGE_PAGES long), except for a single free page
|
|
|
|
* at the end.
|
2020-11-10 09:24:31 +08:00
|
|
|
*
|
|
|
|
* (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
|
2020-11-18 08:32:45 +08:00
|
|
|
init_test_pageslabs(psset_t *psset, hpdata_t *pageslab,
|
|
|
|
hpdata_t *worse_pageslab, edata_t *alloc, edata_t *worse_alloc) {
|
2020-09-19 07:36:40 +08:00
|
|
|
bool err;
|
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_init(pageslab, (void *)(10 * HUGEPAGE), PAGESLAB_AGE);
|
2020-09-19 07:36:40 +08:00
|
|
|
/*
|
2020-11-18 08:32:45 +08:00
|
|
|
* This pageslab would be better from an address-first-fit POV, but
|
2020-12-03 06:20:45 +08:00
|
|
|
* worse from an age POV.
|
2020-09-19 07:36:40 +08:00
|
|
|
*/
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_init(worse_pageslab, (void *)(9 * HUGEPAGE), PAGESLAB_AGE + 1);
|
2020-09-19 07:36:40 +08:00
|
|
|
|
2020-11-10 09:24:31 +08:00
|
|
|
psset_init(psset);
|
2020-09-19 07:36:40 +08:00
|
|
|
|
|
|
|
edata_init_test(&alloc[0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(psset, pageslab, &alloc[0], PAGE);
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
|
2020-09-19 07:36:40 +08:00
|
|
|
edata_init_test(&alloc[i]);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
|
2020-09-19 07:36:40 +08:00
|
|
|
expect_false(err, "Nonempty psset failed page allocation.");
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_ptr_eq(pageslab, edata_ps_get(&alloc[i]),
|
2020-09-19 07:36:40 +08:00
|
|
|
"Allocated from the wrong pageslab");
|
|
|
|
}
|
|
|
|
|
|
|
|
edata_init_test(&worse_alloc[0]);
|
2020-12-01 08:10:56 +08:00
|
|
|
test_psset_alloc_new(psset, worse_pageslab, &worse_alloc[0], PAGE);
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_ptr_eq(worse_pageslab, edata_ps_get(&worse_alloc[0]),
|
2020-09-19 07:36:40 +08:00
|
|
|
"Allocated from the wrong pageslab");
|
|
|
|
/*
|
|
|
|
* Make the two pssets otherwise indistinguishable; all full except for
|
|
|
|
* a single page.
|
|
|
|
*/
|
2020-11-18 08:32:45 +08:00
|
|
|
for (size_t i = 1; i < HUGEPAGE_PAGES - 1; i++) {
|
2020-09-19 07:36:40 +08:00
|
|
|
edata_init_test(&worse_alloc[i]);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
|
2020-09-19 07:36:40 +08:00
|
|
|
expect_false(err, "Nonempty psset failed page allocation.");
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_ptr_eq(worse_pageslab, edata_ps_get(&alloc[i]),
|
2020-09-19 07:36:40 +08:00
|
|
|
"Allocated from the wrong pageslab");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deallocate the last page from the older pageslab. */
|
2020-12-01 08:10:56 +08:00
|
|
|
hpdata_t *evicted = test_psset_dalloc(psset,
|
|
|
|
&alloc[HUGEPAGE_PAGES - 1]);
|
2020-09-19 07:36:40 +08:00
|
|
|
expect_ptr_null(evicted, "Unexpected eviction");
|
2020-11-10 09:24:31 +08:00
|
|
|
}
|
2020-09-19 07:36:40 +08:00
|
|
|
|
2020-11-10 09:24:31 +08:00
|
|
|
TEST_BEGIN(test_oldest_fit) {
|
|
|
|
bool err;
|
2020-11-18 08:32:45 +08:00
|
|
|
edata_t alloc[HUGEPAGE_PAGES];
|
|
|
|
edata_t worse_alloc[HUGEPAGE_PAGES];
|
2020-11-10 09:24:31 +08:00
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t pageslab;
|
|
|
|
hpdata_t worse_pageslab;
|
2020-11-10 09:24:31 +08:00
|
|
|
|
|
|
|
psset_t psset;
|
|
|
|
|
|
|
|
init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
|
|
|
|
worse_alloc);
|
|
|
|
|
|
|
|
/* The edata should come from the better pageslab. */
|
2020-09-19 07:36:40 +08:00
|
|
|
edata_t test_edata;
|
|
|
|
edata_init_test(&test_edata);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &test_edata, PAGE);
|
2020-09-19 07:36:40 +08:00
|
|
|
expect_false(err, "Nonempty psset failed page allocation");
|
|
|
|
expect_ptr_eq(&pageslab, edata_ps_get(&test_edata),
|
|
|
|
"Allocated from the wrong pageslab");
|
|
|
|
}
|
|
|
|
TEST_END
|
|
|
|
|
2020-11-10 09:24:31 +08:00
|
|
|
TEST_BEGIN(test_insert_remove) {
|
|
|
|
bool err;
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t *ps;
|
|
|
|
edata_t alloc[HUGEPAGE_PAGES];
|
|
|
|
edata_t worse_alloc[HUGEPAGE_PAGES];
|
2020-11-10 09:24:31 +08:00
|
|
|
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t pageslab;
|
|
|
|
hpdata_t worse_pageslab;
|
2020-11-10 09:24:31 +08:00
|
|
|
|
|
|
|
psset_t psset;
|
|
|
|
|
|
|
|
init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
|
|
|
|
worse_alloc);
|
|
|
|
|
|
|
|
/* Remove better; should still be able to alloc from worse. */
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_begin(&psset, &pageslab);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &worse_alloc[HUGEPAGE_PAGES - 1],
|
|
|
|
PAGE);
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_false(err, "Removal should still leave an empty page");
|
|
|
|
expect_ptr_eq(&worse_pageslab,
|
2020-11-18 08:32:45 +08:00
|
|
|
edata_ps_get(&worse_alloc[HUGEPAGE_PAGES - 1]),
|
2020-11-10 09:24:31 +08:00
|
|
|
"Allocated out of wrong ps");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After deallocating the previous alloc and reinserting better, it
|
|
|
|
* should be preferred for future allocations.
|
|
|
|
*/
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &worse_alloc[HUGEPAGE_PAGES - 1]);
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_ptr_null(ps, "Incorrect eviction of nonempty pageslab");
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_end(&psset, &pageslab);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_false(err, "psset should be nonempty");
|
2020-11-18 08:32:45 +08:00
|
|
|
expect_ptr_eq(&pageslab, edata_ps_get(&alloc[HUGEPAGE_PAGES - 1]),
|
2020-11-10 09:24:31 +08:00
|
|
|
"Removal/reinsertion shouldn't change ordering");
|
|
|
|
/*
|
|
|
|
* After deallocating and removing both, allocations should fail.
|
|
|
|
*/
|
2020-12-01 08:10:56 +08:00
|
|
|
ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_ptr_null(ps, "Incorrect eviction");
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_begin(&psset, &pageslab);
|
|
|
|
psset_update_begin(&psset, &worse_pageslab);
|
2020-12-01 08:10:56 +08:00
|
|
|
err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
|
2020-11-10 09:24:31 +08:00
|
|
|
expect_true(err, "psset should be empty, but an alloc succeeded");
|
|
|
|
}
|
|
|
|
TEST_END
|
|
|
|
|
2021-02-09 06:11:37 +08:00
|
|
|
TEST_BEGIN(test_purge_prefers_nonhuge) {
|
|
|
|
/*
|
|
|
|
* All else being equal, we should prefer purging non-huge pages over
|
|
|
|
* huge ones.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
2020-07-11 08:40:13 +08:00
|
|
|
int
|
|
|
|
main(void) {
|
|
|
|
return test_no_reentrancy(
|
|
|
|
test_empty,
|
|
|
|
test_fill,
|
|
|
|
test_reuse,
|
|
|
|
test_evict,
|
2020-09-03 03:59:10 +08:00
|
|
|
test_multi_pageslab,
|
2020-09-19 07:36:40 +08:00
|
|
|
test_stats,
|
2020-11-10 09:24:31 +08:00
|
|
|
test_oldest_fit,
|
2021-02-09 06:11:37 +08:00
|
|
|
test_insert_remove,
|
|
|
|
test_purge_prefers_nonhuge);
|
2020-07-11 08:40:13 +08:00
|
|
|
}
|