Add psset: a set of pageslabs.
This introduces a new sort of edata_t; a pageslab, and a set to manage them. This is part of a series of a commits to implement a hugepage allocator; the pageset will be per-arena, and track small page allocations requests within a larger extent allocated from a centralized hugepage allocator.
This commit is contained in:
committed by
David Goldblatt
parent
ed99d300b9
commit
018b162d67
306
test/unit/psset.c
Normal file
306
test/unit/psset.c
Normal file
@@ -0,0 +1,306 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#include "jemalloc/internal/psset.h"
|
||||
|
||||
#define PAGESLAB_PAGES 64
|
||||
#define PAGESLAB_SIZE (PAGESLAB_PAGES << LG_PAGE)
|
||||
#define PAGESLAB_SN 123
|
||||
#define PAGESLAB_ADDR ((void *)(1234 << LG_PAGE))
|
||||
|
||||
#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
|
||||
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_zu_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;
|
||||
edata_t pageslab;
|
||||
memset(&pageslab, 0, sizeof(pageslab));
|
||||
edata_t alloc;
|
||||
|
||||
edata_init(&pageslab, /* arena_ind */ 0, PAGESLAB_ADDR, PAGESLAB_SIZE,
|
||||
/* slab */ true, SC_NSIZES, PAGESLAB_SN, extent_state_active,
|
||||
/* zeroed */ false, /* comitted */ true, EXTENT_PAI_HPA,
|
||||
EXTENT_IS_HEAD);
|
||||
edata_init_test(&alloc);
|
||||
|
||||
psset_t psset;
|
||||
psset_init(&psset);
|
||||
|
||||
/* Empty psset should return fail allocations. */
|
||||
err = psset_alloc_reuse(&psset, &alloc, PAGE);
|
||||
expect_true(err, "Empty psset succeeded in an allocation.");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_fill) {
|
||||
bool err;
|
||||
edata_t pageslab;
|
||||
memset(&pageslab, 0, sizeof(pageslab));
|
||||
edata_t alloc[PAGESLAB_PAGES];
|
||||
|
||||
edata_init(&pageslab, /* arena_ind */ 0, PAGESLAB_ADDR, PAGESLAB_SIZE,
|
||||
/* slab */ true, SC_NSIZES, PAGESLAB_SN, extent_state_active,
|
||||
/* zeroed */ false, /* comitted */ true, EXTENT_PAI_HPA,
|
||||
EXTENT_IS_HEAD);
|
||||
|
||||
psset_t psset;
|
||||
psset_init(&psset);
|
||||
|
||||
edata_init_test(&alloc[0]);
|
||||
psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
||||
for (size_t i = 1; i < PAGESLAB_PAGES; i++) {
|
||||
edata_init_test(&alloc[i]);
|
||||
err = psset_alloc_reuse(&psset, &alloc[i], PAGE);
|
||||
expect_false(err, "Nonempty psset failed page allocation.");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < PAGESLAB_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 = 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;
|
||||
edata_t *ps;
|
||||
|
||||
edata_t pageslab;
|
||||
memset(&pageslab, 0, sizeof(pageslab));
|
||||
edata_t alloc[PAGESLAB_PAGES];
|
||||
|
||||
edata_init(&pageslab, /* arena_ind */ 0, PAGESLAB_ADDR, PAGESLAB_SIZE,
|
||||
/* slab */ true, SC_NSIZES, PAGESLAB_SN, extent_state_active,
|
||||
/* zeroed */ false, /* comitted */ true, EXTENT_PAI_HPA,
|
||||
EXTENT_IS_HEAD);
|
||||
|
||||
psset_t psset;
|
||||
psset_init(&psset);
|
||||
|
||||
edata_init_test(&alloc[0]);
|
||||
psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
||||
for (size_t i = 1; i < PAGESLAB_PAGES; i++) {
|
||||
edata_init_test(&alloc[i]);
|
||||
err = psset_alloc_reuse(&psset, &alloc[i], PAGE);
|
||||
expect_false(err, "Nonempty psset failed page allocation.");
|
||||
}
|
||||
|
||||
/* Free odd indices. */
|
||||
for (size_t i = 0; i < PAGESLAB_PAGES; i ++) {
|
||||
if (i % 2 == 0) {
|
||||
continue;
|
||||
}
|
||||
ps = psset_dalloc(&psset, &alloc[i]);
|
||||
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
||||
}
|
||||
/* Realloc into them. */
|
||||
for (size_t i = 0; i < PAGESLAB_PAGES; i++) {
|
||||
if (i % 2 == 0) {
|
||||
continue;
|
||||
}
|
||||
err = 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 < PAGESLAB_PAGES; i++) {
|
||||
if (i % 4 > 1) {
|
||||
continue;
|
||||
}
|
||||
ps = 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 < PAGESLAB_PAGES; i++) {
|
||||
if (i % 4 != 0) {
|
||||
continue;
|
||||
}
|
||||
err = 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 < PAGESLAB_PAGES; i++) {
|
||||
if (i % 4 != 0) {
|
||||
continue;
|
||||
}
|
||||
ps = 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 = psset_dalloc(&psset, &alloc[index_of_3]);
|
||||
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
||||
err = 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. */
|
||||
ps = psset_dalloc(&psset, &alloc[PAGESLAB_PAGES - 1]);
|
||||
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
||||
ps = psset_dalloc(&psset, &alloc[PAGESLAB_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 = PAGESLAB_PAGES - 4;
|
||||
ps = psset_dalloc(&psset, &alloc[index_of_4]);
|
||||
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
||||
err = 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;
|
||||
edata_t *ps;
|
||||
edata_t pageslab;
|
||||
memset(&pageslab, 0, sizeof(pageslab));
|
||||
edata_t alloc[PAGESLAB_PAGES];
|
||||
|
||||
edata_init(&pageslab, /* arena_ind */ 0, PAGESLAB_ADDR, PAGESLAB_SIZE,
|
||||
/* slab */ true, SC_NSIZES, PAGESLAB_SN, extent_state_active,
|
||||
/* zeroed */ false, /* comitted */ true, EXTENT_PAI_HPA,
|
||||
EXTENT_IS_HEAD);
|
||||
psset_t psset;
|
||||
psset_init(&psset);
|
||||
|
||||
/* Alloc the whole slab. */
|
||||
edata_init_test(&alloc[0]);
|
||||
psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
|
||||
for (size_t i = 1; i < PAGESLAB_PAGES; i++) {
|
||||
edata_init_test(&alloc[i]);
|
||||
err = 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 < PAGESLAB_PAGES - 1; i++) {
|
||||
ps = psset_dalloc(&psset, &alloc[i]);
|
||||
expect_ptr_null(ps, "Nonempty pageslab evicted");
|
||||
}
|
||||
ps = psset_dalloc(&psset, &alloc[PAGESLAB_PAGES - 1]);
|
||||
expect_ptr_eq(&pageslab, ps, "Empty pageslab not evicted.");
|
||||
|
||||
err = psset_alloc_reuse(&psset, &alloc[0], PAGE);
|
||||
expect_true(err, "psset should be empty.");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_multi_pageslab) {
|
||||
bool err;
|
||||
edata_t *ps;
|
||||
edata_t pageslab[2];
|
||||
memset(&pageslab, 0, sizeof(pageslab));
|
||||
edata_t alloc[2][PAGESLAB_PAGES];
|
||||
|
||||
edata_init(&pageslab[0], /* arena_ind */ 0, PAGESLAB_ADDR, PAGESLAB_SIZE,
|
||||
/* slab */ true, SC_NSIZES, PAGESLAB_SN, extent_state_active,
|
||||
/* zeroed */ false, /* comitted */ true, EXTENT_PAI_HPA,
|
||||
EXTENT_IS_HEAD);
|
||||
edata_init(&pageslab[1], /* arena_ind */ 0,
|
||||
(void *)((uintptr_t)PAGESLAB_ADDR + PAGESLAB_SIZE), PAGESLAB_SIZE,
|
||||
/* slab */ true, SC_NSIZES, PAGESLAB_SN, extent_state_active,
|
||||
/* zeroed */ false, /* comitted */ true, EXTENT_PAI_HPA,
|
||||
EXTENT_IS_HEAD);
|
||||
|
||||
psset_t psset;
|
||||
psset_init(&psset);
|
||||
|
||||
/* Insert both slabs. */
|
||||
edata_init_test(&alloc[0][0]);
|
||||
psset_alloc_new(&psset, &pageslab[0], &alloc[0][0], PAGE);
|
||||
edata_init_test(&alloc[1][0]);
|
||||
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 < PAGESLAB_PAGES; j++) {
|
||||
edata_init_test(&alloc[i][j]);
|
||||
err = 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 earlier slab for a 1-page
|
||||
* allocation.
|
||||
*/
|
||||
ps = psset_dalloc(&psset, &alloc[0][0]);
|
||||
expect_ptr_null(ps, "Unexpected eviction");
|
||||
ps = psset_dalloc(&psset, &alloc[0][1]);
|
||||
expect_ptr_null(ps, "Unexpected eviction");
|
||||
ps = psset_dalloc(&psset, &alloc[1][0]);
|
||||
expect_ptr_null(ps, "Unexpected eviction");
|
||||
err = psset_alloc_reuse(&psset, &alloc[0][0], PAGE);
|
||||
expect_ptr_eq(&pageslab[0], edata_ps_get(&alloc[0][0]),
|
||||
"Should have picked first pageslab");
|
||||
|
||||
/*
|
||||
* Now both slabs have 1-page holes. Free up a second one in the later
|
||||
* slab.
|
||||
*/
|
||||
ps = 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 = psset_alloc_reuse(&psset, &alloc[1][0], 2 * PAGE);
|
||||
expect_false(err, "Allocation should have succeeded");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void) {
|
||||
return test_no_reentrancy(
|
||||
test_empty,
|
||||
test_fill,
|
||||
test_reuse,
|
||||
test_evict,
|
||||
test_multi_pageslab);
|
||||
}
|
Reference in New Issue
Block a user