xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/psset.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1*7bdf38e5Schristos #include "test/jemalloc_test.h"
2*7bdf38e5Schristos 
3*7bdf38e5Schristos #include "jemalloc/internal/psset.h"
4*7bdf38e5Schristos 
5*7bdf38e5Schristos #define PAGESLAB_ADDR ((void *)(1234 * HUGEPAGE))
6*7bdf38e5Schristos #define PAGESLAB_AGE 5678
7*7bdf38e5Schristos 
8*7bdf38e5Schristos #define ALLOC_ARENA_IND 111
9*7bdf38e5Schristos #define ALLOC_ESN 222
10*7bdf38e5Schristos 
11*7bdf38e5Schristos static void
12*7bdf38e5Schristos edata_init_test(edata_t *edata) {
13*7bdf38e5Schristos 	memset(edata, 0, sizeof(*edata));
14*7bdf38e5Schristos 	edata_arena_ind_set(edata, ALLOC_ARENA_IND);
15*7bdf38e5Schristos 	edata_esn_set(edata, ALLOC_ESN);
16*7bdf38e5Schristos }
17*7bdf38e5Schristos 
18*7bdf38e5Schristos static void
19*7bdf38e5Schristos test_psset_fake_purge(hpdata_t *ps) {
20*7bdf38e5Schristos 	hpdata_purge_state_t purge_state;
21*7bdf38e5Schristos 	hpdata_alloc_allowed_set(ps, false);
22*7bdf38e5Schristos 	hpdata_purge_begin(ps, &purge_state);
23*7bdf38e5Schristos 	void *addr;
24*7bdf38e5Schristos 	size_t size;
25*7bdf38e5Schristos 	while (hpdata_purge_next(ps, &purge_state, &addr, &size)) {
26*7bdf38e5Schristos 	}
27*7bdf38e5Schristos 	hpdata_purge_end(ps, &purge_state);
28*7bdf38e5Schristos 	hpdata_alloc_allowed_set(ps, true);
29*7bdf38e5Schristos }
30*7bdf38e5Schristos 
31*7bdf38e5Schristos static void
32*7bdf38e5Schristos test_psset_alloc_new(psset_t *psset, hpdata_t *ps, edata_t *r_edata,
33*7bdf38e5Schristos     size_t size) {
34*7bdf38e5Schristos 	hpdata_assert_empty(ps);
35*7bdf38e5Schristos 
36*7bdf38e5Schristos 	test_psset_fake_purge(ps);
37*7bdf38e5Schristos 
38*7bdf38e5Schristos 	psset_insert(psset, ps);
39*7bdf38e5Schristos 	psset_update_begin(psset, ps);
40*7bdf38e5Schristos 
41*7bdf38e5Schristos         void *addr = hpdata_reserve_alloc(ps, size);
42*7bdf38e5Schristos         edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
43*7bdf38e5Schristos 	    /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
44*7bdf38e5Schristos             /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
45*7bdf38e5Schristos             EXTENT_NOT_HEAD);
46*7bdf38e5Schristos         edata_ps_set(r_edata, ps);
47*7bdf38e5Schristos 	psset_update_end(psset, ps);
48*7bdf38e5Schristos }
49*7bdf38e5Schristos 
50*7bdf38e5Schristos static bool
51*7bdf38e5Schristos test_psset_alloc_reuse(psset_t *psset, edata_t *r_edata, size_t size) {
52*7bdf38e5Schristos 	hpdata_t *ps = psset_pick_alloc(psset, size);
53*7bdf38e5Schristos 	if (ps == NULL) {
54*7bdf38e5Schristos 		return true;
55*7bdf38e5Schristos 	}
56*7bdf38e5Schristos 	psset_update_begin(psset, ps);
57*7bdf38e5Schristos 	void *addr = hpdata_reserve_alloc(ps, size);
58*7bdf38e5Schristos 	edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
59*7bdf38e5Schristos 	    /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
60*7bdf38e5Schristos 	    /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
61*7bdf38e5Schristos 	    EXTENT_NOT_HEAD);
62*7bdf38e5Schristos 	edata_ps_set(r_edata, ps);
63*7bdf38e5Schristos 	psset_update_end(psset, ps);
64*7bdf38e5Schristos 	return false;
65*7bdf38e5Schristos }
66*7bdf38e5Schristos 
67*7bdf38e5Schristos static hpdata_t *
68*7bdf38e5Schristos test_psset_dalloc(psset_t *psset, edata_t *edata) {
69*7bdf38e5Schristos 	hpdata_t *ps = edata_ps_get(edata);
70*7bdf38e5Schristos 	psset_update_begin(psset, ps);
71*7bdf38e5Schristos 	hpdata_unreserve(ps, edata_addr_get(edata), edata_size_get(edata));
72*7bdf38e5Schristos 	psset_update_end(psset, ps);
73*7bdf38e5Schristos 	if (hpdata_empty(ps)) {
74*7bdf38e5Schristos 		psset_remove(psset, ps);
75*7bdf38e5Schristos 		return ps;
76*7bdf38e5Schristos 	} else {
77*7bdf38e5Schristos 		return NULL;
78*7bdf38e5Schristos 	}
79*7bdf38e5Schristos }
80*7bdf38e5Schristos 
81*7bdf38e5Schristos static void
82*7bdf38e5Schristos edata_expect(edata_t *edata, size_t page_offset, size_t page_cnt) {
83*7bdf38e5Schristos 	/*
84*7bdf38e5Schristos 	 * Note that allocations should get the arena ind of their home
85*7bdf38e5Schristos 	 * arena, *not* the arena ind of the pageslab allocator.
86*7bdf38e5Schristos 	 */
87*7bdf38e5Schristos 	expect_u_eq(ALLOC_ARENA_IND, edata_arena_ind_get(edata),
88*7bdf38e5Schristos 	    "Arena ind changed");
89*7bdf38e5Schristos 	expect_ptr_eq(
90*7bdf38e5Schristos 	    (void *)((uintptr_t)PAGESLAB_ADDR + (page_offset << LG_PAGE)),
91*7bdf38e5Schristos 	    edata_addr_get(edata), "Didn't allocate in order");
92*7bdf38e5Schristos 	expect_zu_eq(page_cnt << LG_PAGE, edata_size_get(edata), "");
93*7bdf38e5Schristos 	expect_false(edata_slab_get(edata), "");
94*7bdf38e5Schristos 	expect_u_eq(SC_NSIZES, edata_szind_get_maybe_invalid(edata),
95*7bdf38e5Schristos 	    "");
96*7bdf38e5Schristos 	expect_u64_eq(0, edata_sn_get(edata), "");
97*7bdf38e5Schristos 	expect_d_eq(edata_state_get(edata), extent_state_active, "");
98*7bdf38e5Schristos 	expect_false(edata_zeroed_get(edata), "");
99*7bdf38e5Schristos 	expect_true(edata_committed_get(edata), "");
100*7bdf38e5Schristos 	expect_d_eq(EXTENT_PAI_HPA, edata_pai_get(edata), "");
101*7bdf38e5Schristos 	expect_false(edata_is_head_get(edata), "");
102*7bdf38e5Schristos }
103*7bdf38e5Schristos 
104*7bdf38e5Schristos TEST_BEGIN(test_empty) {
105*7bdf38e5Schristos 	bool err;
106*7bdf38e5Schristos 	hpdata_t pageslab;
107*7bdf38e5Schristos 	hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
108*7bdf38e5Schristos 
109*7bdf38e5Schristos 	edata_t alloc;
110*7bdf38e5Schristos 	edata_init_test(&alloc);
111*7bdf38e5Schristos 
112*7bdf38e5Schristos 	psset_t psset;
113*7bdf38e5Schristos 	psset_init(&psset);
114*7bdf38e5Schristos 
115*7bdf38e5Schristos 	/* Empty psset should return fail allocations. */
116*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc, PAGE);
117*7bdf38e5Schristos 	expect_true(err, "Empty psset succeeded in an allocation.");
118*7bdf38e5Schristos }
119*7bdf38e5Schristos TEST_END
120*7bdf38e5Schristos 
121*7bdf38e5Schristos TEST_BEGIN(test_fill) {
122*7bdf38e5Schristos 	bool err;
123*7bdf38e5Schristos 
124*7bdf38e5Schristos 	hpdata_t pageslab;
125*7bdf38e5Schristos 	hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
126*7bdf38e5Schristos 
127*7bdf38e5Schristos 	edata_t alloc[HUGEPAGE_PAGES];
128*7bdf38e5Schristos 
129*7bdf38e5Schristos 	psset_t psset;
130*7bdf38e5Schristos 	psset_init(&psset);
131*7bdf38e5Schristos 
132*7bdf38e5Schristos 	edata_init_test(&alloc[0]);
133*7bdf38e5Schristos 	test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
134*7bdf38e5Schristos 	for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
135*7bdf38e5Schristos 		edata_init_test(&alloc[i]);
136*7bdf38e5Schristos 		err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
137*7bdf38e5Schristos 		expect_false(err, "Nonempty psset failed page allocation.");
138*7bdf38e5Schristos 	}
139*7bdf38e5Schristos 
140*7bdf38e5Schristos 	for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
141*7bdf38e5Schristos 		edata_t *edata = &alloc[i];
142*7bdf38e5Schristos 		edata_expect(edata, i, 1);
143*7bdf38e5Schristos 	}
144*7bdf38e5Schristos 
145*7bdf38e5Schristos 	/* The pageslab, and thus psset, should now have no allocations. */
146*7bdf38e5Schristos 	edata_t extra_alloc;
147*7bdf38e5Schristos 	edata_init_test(&extra_alloc);
148*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &extra_alloc, PAGE);
149*7bdf38e5Schristos 	expect_true(err, "Alloc succeeded even though psset should be empty");
150*7bdf38e5Schristos }
151*7bdf38e5Schristos TEST_END
152*7bdf38e5Schristos 
153*7bdf38e5Schristos TEST_BEGIN(test_reuse) {
154*7bdf38e5Schristos 	bool err;
155*7bdf38e5Schristos 	hpdata_t *ps;
156*7bdf38e5Schristos 
157*7bdf38e5Schristos 	hpdata_t pageslab;
158*7bdf38e5Schristos 	hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
159*7bdf38e5Schristos 
160*7bdf38e5Schristos 	edata_t alloc[HUGEPAGE_PAGES];
161*7bdf38e5Schristos 
162*7bdf38e5Schristos 	psset_t psset;
163*7bdf38e5Schristos 	psset_init(&psset);
164*7bdf38e5Schristos 
165*7bdf38e5Schristos 	edata_init_test(&alloc[0]);
166*7bdf38e5Schristos 	test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
167*7bdf38e5Schristos 	for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
168*7bdf38e5Schristos 		edata_init_test(&alloc[i]);
169*7bdf38e5Schristos 		err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
170*7bdf38e5Schristos 		expect_false(err, "Nonempty psset failed page allocation.");
171*7bdf38e5Schristos 	}
172*7bdf38e5Schristos 
173*7bdf38e5Schristos 	/* Free odd indices. */
174*7bdf38e5Schristos 	for (size_t i = 0; i < HUGEPAGE_PAGES; i ++) {
175*7bdf38e5Schristos 		if (i % 2 == 0) {
176*7bdf38e5Schristos 			continue;
177*7bdf38e5Schristos 		}
178*7bdf38e5Schristos 		ps = test_psset_dalloc(&psset, &alloc[i]);
179*7bdf38e5Schristos 		expect_ptr_null(ps, "Nonempty pageslab evicted");
180*7bdf38e5Schristos 	}
181*7bdf38e5Schristos 	/* Realloc into them. */
182*7bdf38e5Schristos 	for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
183*7bdf38e5Schristos 		if (i % 2 == 0) {
184*7bdf38e5Schristos 			continue;
185*7bdf38e5Schristos 		}
186*7bdf38e5Schristos 		err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
187*7bdf38e5Schristos 		expect_false(err, "Nonempty psset failed page allocation.");
188*7bdf38e5Schristos 		edata_expect(&alloc[i], i, 1);
189*7bdf38e5Schristos 	}
190*7bdf38e5Schristos 	/* Now, free the pages at indices 0 or 1 mod 2. */
191*7bdf38e5Schristos 	for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
192*7bdf38e5Schristos 		if (i % 4 > 1) {
193*7bdf38e5Schristos 			continue;
194*7bdf38e5Schristos 		}
195*7bdf38e5Schristos 		ps = test_psset_dalloc(&psset, &alloc[i]);
196*7bdf38e5Schristos 		expect_ptr_null(ps, "Nonempty pageslab evicted");
197*7bdf38e5Schristos 	}
198*7bdf38e5Schristos 	/* And realloc 2-page allocations into them. */
199*7bdf38e5Schristos 	for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
200*7bdf38e5Schristos 		if (i % 4 != 0) {
201*7bdf38e5Schristos 			continue;
202*7bdf38e5Schristos 		}
203*7bdf38e5Schristos 		err = test_psset_alloc_reuse(&psset, &alloc[i], 2 * PAGE);
204*7bdf38e5Schristos 		expect_false(err, "Nonempty psset failed page allocation.");
205*7bdf38e5Schristos 		edata_expect(&alloc[i], i, 2);
206*7bdf38e5Schristos 	}
207*7bdf38e5Schristos 	/* Free all the 2-page allocations. */
208*7bdf38e5Schristos 	for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
209*7bdf38e5Schristos 		if (i % 4 != 0) {
210*7bdf38e5Schristos 			continue;
211*7bdf38e5Schristos 		}
212*7bdf38e5Schristos 		ps = test_psset_dalloc(&psset, &alloc[i]);
213*7bdf38e5Schristos 		expect_ptr_null(ps, "Nonempty pageslab evicted");
214*7bdf38e5Schristos 	}
215*7bdf38e5Schristos 	/*
216*7bdf38e5Schristos 	 * Free up a 1-page hole next to a 2-page hole, but somewhere in the
217*7bdf38e5Schristos 	 * middle of the pageslab.  Index 11 should be right before such a hole
218*7bdf38e5Schristos 	 * (since 12 % 4 == 0).
219*7bdf38e5Schristos 	 */
220*7bdf38e5Schristos 	size_t index_of_3 = 11;
221*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[index_of_3]);
222*7bdf38e5Schristos 	expect_ptr_null(ps, "Nonempty pageslab evicted");
223*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc[index_of_3], 3 * PAGE);
224*7bdf38e5Schristos 	expect_false(err, "Should have been able to find alloc.");
225*7bdf38e5Schristos 	edata_expect(&alloc[index_of_3], index_of_3, 3);
226*7bdf38e5Schristos 
227*7bdf38e5Schristos 	/*
228*7bdf38e5Schristos 	 * Free up a 4-page hole at the end.  Recall that the pages at offsets 0
229*7bdf38e5Schristos 	 * and 1 mod 4 were freed above, so we just have to free the last
230*7bdf38e5Schristos 	 * allocations.
231*7bdf38e5Schristos 	 */
232*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
233*7bdf38e5Schristos 	expect_ptr_null(ps, "Nonempty pageslab evicted");
234*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 2]);
235*7bdf38e5Schristos 	expect_ptr_null(ps, "Nonempty pageslab evicted");
236*7bdf38e5Schristos 
237*7bdf38e5Schristos 	/* Make sure we can satisfy an allocation at the very end of a slab. */
238*7bdf38e5Schristos 	size_t index_of_4 = HUGEPAGE_PAGES - 4;
239*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc[index_of_4], 4 * PAGE);
240*7bdf38e5Schristos 	expect_false(err, "Should have been able to find alloc.");
241*7bdf38e5Schristos 	edata_expect(&alloc[index_of_4], index_of_4, 4);
242*7bdf38e5Schristos }
243*7bdf38e5Schristos TEST_END
244*7bdf38e5Schristos 
245*7bdf38e5Schristos TEST_BEGIN(test_evict) {
246*7bdf38e5Schristos 	bool err;
247*7bdf38e5Schristos 	hpdata_t *ps;
248*7bdf38e5Schristos 
249*7bdf38e5Schristos 	hpdata_t pageslab;
250*7bdf38e5Schristos 	hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
251*7bdf38e5Schristos 
252*7bdf38e5Schristos 	edata_t alloc[HUGEPAGE_PAGES];
253*7bdf38e5Schristos 
254*7bdf38e5Schristos 	psset_t psset;
255*7bdf38e5Schristos 	psset_init(&psset);
256*7bdf38e5Schristos 
257*7bdf38e5Schristos 	/* Alloc the whole slab. */
258*7bdf38e5Schristos 	edata_init_test(&alloc[0]);
259*7bdf38e5Schristos 	test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
260*7bdf38e5Schristos 	for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
261*7bdf38e5Schristos 		edata_init_test(&alloc[i]);
262*7bdf38e5Schristos 		err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
263*7bdf38e5Schristos 		expect_false(err, "Unxpected allocation failure");
264*7bdf38e5Schristos 	}
265*7bdf38e5Schristos 
266*7bdf38e5Schristos 	/* Dealloc the whole slab, going forwards. */
267*7bdf38e5Schristos 	for (size_t i = 0; i < HUGEPAGE_PAGES - 1; i++) {
268*7bdf38e5Schristos 		ps = test_psset_dalloc(&psset, &alloc[i]);
269*7bdf38e5Schristos 		expect_ptr_null(ps, "Nonempty pageslab evicted");
270*7bdf38e5Schristos 	}
271*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
272*7bdf38e5Schristos 	expect_ptr_eq(&pageslab, ps, "Empty pageslab not evicted.");
273*7bdf38e5Schristos 
274*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc[0], PAGE);
275*7bdf38e5Schristos 	expect_true(err, "psset should be empty.");
276*7bdf38e5Schristos }
277*7bdf38e5Schristos TEST_END
278*7bdf38e5Schristos 
279*7bdf38e5Schristos TEST_BEGIN(test_multi_pageslab) {
280*7bdf38e5Schristos 	bool err;
281*7bdf38e5Schristos 	hpdata_t *ps;
282*7bdf38e5Schristos 
283*7bdf38e5Schristos 	hpdata_t pageslab[2];
284*7bdf38e5Schristos 	hpdata_init(&pageslab[0], PAGESLAB_ADDR, PAGESLAB_AGE);
285*7bdf38e5Schristos 	hpdata_init(&pageslab[1],
286*7bdf38e5Schristos 	    (void *)((uintptr_t)PAGESLAB_ADDR + HUGEPAGE),
287*7bdf38e5Schristos 	    PAGESLAB_AGE + 1);
288*7bdf38e5Schristos 
289*7bdf38e5Schristos 	edata_t alloc[2][HUGEPAGE_PAGES];
290*7bdf38e5Schristos 
291*7bdf38e5Schristos 	psset_t psset;
292*7bdf38e5Schristos 	psset_init(&psset);
293*7bdf38e5Schristos 
294*7bdf38e5Schristos 	/* Insert both slabs. */
295*7bdf38e5Schristos 	edata_init_test(&alloc[0][0]);
296*7bdf38e5Schristos 	test_psset_alloc_new(&psset, &pageslab[0], &alloc[0][0], PAGE);
297*7bdf38e5Schristos 	edata_init_test(&alloc[1][0]);
298*7bdf38e5Schristos 	test_psset_alloc_new(&psset, &pageslab[1], &alloc[1][0], PAGE);
299*7bdf38e5Schristos 
300*7bdf38e5Schristos 	/* Fill them both up; make sure we do so in first-fit order. */
301*7bdf38e5Schristos 	for (size_t i = 0; i < 2; i++) {
302*7bdf38e5Schristos 		for (size_t j = 1; j < HUGEPAGE_PAGES; j++) {
303*7bdf38e5Schristos 			edata_init_test(&alloc[i][j]);
304*7bdf38e5Schristos 			err = test_psset_alloc_reuse(&psset, &alloc[i][j], PAGE);
305*7bdf38e5Schristos 			expect_false(err,
306*7bdf38e5Schristos 			    "Nonempty psset failed page allocation.");
307*7bdf38e5Schristos 			assert_ptr_eq(&pageslab[i], edata_ps_get(&alloc[i][j]),
308*7bdf38e5Schristos 			    "Didn't pick pageslabs in first-fit");
309*7bdf38e5Schristos 		}
310*7bdf38e5Schristos 	}
311*7bdf38e5Schristos 
312*7bdf38e5Schristos 	/*
313*7bdf38e5Schristos 	 * Free up a 2-page hole in the earlier slab, and a 1-page one in the
314*7bdf38e5Schristos 	 * later one.  We should still pick the later one.
315*7bdf38e5Schristos 	 */
316*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[0][0]);
317*7bdf38e5Schristos 	expect_ptr_null(ps, "Unexpected eviction");
318*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[0][1]);
319*7bdf38e5Schristos 	expect_ptr_null(ps, "Unexpected eviction");
320*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[1][0]);
321*7bdf38e5Schristos 	expect_ptr_null(ps, "Unexpected eviction");
322*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc[0][0], PAGE);
323*7bdf38e5Schristos 	expect_ptr_eq(&pageslab[1], edata_ps_get(&alloc[0][0]),
324*7bdf38e5Schristos 	    "Should have picked the fuller pageslab");
325*7bdf38e5Schristos 
326*7bdf38e5Schristos 	/*
327*7bdf38e5Schristos 	 * Now both slabs have 1-page holes. Free up a second one in the later
328*7bdf38e5Schristos 	 * slab.
329*7bdf38e5Schristos 	 */
330*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[1][1]);
331*7bdf38e5Schristos 	expect_ptr_null(ps, "Unexpected eviction");
332*7bdf38e5Schristos 
333*7bdf38e5Schristos 	/*
334*7bdf38e5Schristos 	 * We should be able to allocate a 2-page object, even though an earlier
335*7bdf38e5Schristos 	 * size class is nonempty.
336*7bdf38e5Schristos 	 */
337*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc[1][0], 2 * PAGE);
338*7bdf38e5Schristos 	expect_false(err, "Allocation should have succeeded");
339*7bdf38e5Schristos }
340*7bdf38e5Schristos TEST_END
341*7bdf38e5Schristos 
342*7bdf38e5Schristos static void
343*7bdf38e5Schristos stats_expect_empty(psset_bin_stats_t *stats) {
344*7bdf38e5Schristos 	assert_zu_eq(0, stats->npageslabs,
345*7bdf38e5Schristos 	    "Supposedly empty bin had positive npageslabs");
346*7bdf38e5Schristos 	expect_zu_eq(0, stats->nactive, "Unexpected nonempty bin"
347*7bdf38e5Schristos 	    "Supposedly empty bin had positive nactive");
348*7bdf38e5Schristos }
349*7bdf38e5Schristos 
350*7bdf38e5Schristos static void
351*7bdf38e5Schristos stats_expect(psset_t *psset, size_t nactive) {
352*7bdf38e5Schristos 	if (nactive == HUGEPAGE_PAGES) {
353*7bdf38e5Schristos 		expect_zu_eq(1, psset->stats.full_slabs[0].npageslabs,
354*7bdf38e5Schristos 		    "Expected a full slab");
355*7bdf38e5Schristos 		expect_zu_eq(HUGEPAGE_PAGES,
356*7bdf38e5Schristos 		    psset->stats.full_slabs[0].nactive,
357*7bdf38e5Schristos 		    "Should have exactly filled the bin");
358*7bdf38e5Schristos 	} else {
359*7bdf38e5Schristos 		stats_expect_empty(&psset->stats.full_slabs[0]);
360*7bdf38e5Schristos 	}
361*7bdf38e5Schristos 	size_t ninactive = HUGEPAGE_PAGES - nactive;
362*7bdf38e5Schristos 	pszind_t nonempty_pind = PSSET_NPSIZES;
363*7bdf38e5Schristos 	if (ninactive != 0 && ninactive < HUGEPAGE_PAGES) {
364*7bdf38e5Schristos 		nonempty_pind = sz_psz2ind(sz_psz_quantize_floor(
365*7bdf38e5Schristos 		    ninactive << LG_PAGE));
366*7bdf38e5Schristos 	}
367*7bdf38e5Schristos 	for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
368*7bdf38e5Schristos 		if (i == nonempty_pind) {
369*7bdf38e5Schristos 			assert_zu_eq(1,
370*7bdf38e5Schristos 			    psset->stats.nonfull_slabs[i][0].npageslabs,
371*7bdf38e5Schristos 			    "Should have found a slab");
372*7bdf38e5Schristos 			expect_zu_eq(nactive,
373*7bdf38e5Schristos 			    psset->stats.nonfull_slabs[i][0].nactive,
374*7bdf38e5Schristos 			    "Mismatch in active pages");
375*7bdf38e5Schristos 		} else {
376*7bdf38e5Schristos 			stats_expect_empty(&psset->stats.nonfull_slabs[i][0]);
377*7bdf38e5Schristos 		}
378*7bdf38e5Schristos 	}
379*7bdf38e5Schristos 	expect_zu_eq(nactive, psset_nactive(psset), "");
380*7bdf38e5Schristos }
381*7bdf38e5Schristos 
382*7bdf38e5Schristos TEST_BEGIN(test_stats) {
383*7bdf38e5Schristos 	bool err;
384*7bdf38e5Schristos 
385*7bdf38e5Schristos 	hpdata_t pageslab;
386*7bdf38e5Schristos 	hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
387*7bdf38e5Schristos 
388*7bdf38e5Schristos 	edata_t alloc[HUGEPAGE_PAGES];
389*7bdf38e5Schristos 
390*7bdf38e5Schristos 	psset_t psset;
391*7bdf38e5Schristos 	psset_init(&psset);
392*7bdf38e5Schristos 	stats_expect(&psset, 0);
393*7bdf38e5Schristos 
394*7bdf38e5Schristos 	edata_init_test(&alloc[0]);
395*7bdf38e5Schristos 	test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
396*7bdf38e5Schristos 	for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
397*7bdf38e5Schristos 		stats_expect(&psset, i);
398*7bdf38e5Schristos 		edata_init_test(&alloc[i]);
399*7bdf38e5Schristos 		err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
400*7bdf38e5Schristos 		expect_false(err, "Nonempty psset failed page allocation.");
401*7bdf38e5Schristos 	}
402*7bdf38e5Schristos 	stats_expect(&psset, HUGEPAGE_PAGES);
403*7bdf38e5Schristos 	hpdata_t *ps;
404*7bdf38e5Schristos 	for (ssize_t i = HUGEPAGE_PAGES - 1; i >= 0; i--) {
405*7bdf38e5Schristos 		ps = test_psset_dalloc(&psset, &alloc[i]);
406*7bdf38e5Schristos 		expect_true((ps == NULL) == (i != 0),
407*7bdf38e5Schristos 		    "test_psset_dalloc should only evict a slab on the last "
408*7bdf38e5Schristos 		    "free");
409*7bdf38e5Schristos 		stats_expect(&psset, i);
410*7bdf38e5Schristos 	}
411*7bdf38e5Schristos 
412*7bdf38e5Schristos 	test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
413*7bdf38e5Schristos 	stats_expect(&psset, 1);
414*7bdf38e5Schristos 	psset_update_begin(&psset, &pageslab);
415*7bdf38e5Schristos 	stats_expect(&psset, 0);
416*7bdf38e5Schristos 	psset_update_end(&psset, &pageslab);
417*7bdf38e5Schristos 	stats_expect(&psset, 1);
418*7bdf38e5Schristos }
419*7bdf38e5Schristos TEST_END
420*7bdf38e5Schristos 
421*7bdf38e5Schristos /*
422*7bdf38e5Schristos  * Fills in and inserts two pageslabs, with the first better than the second,
423*7bdf38e5Schristos  * and each fully allocated (into the allocations in allocs and worse_allocs,
424*7bdf38e5Schristos  * each of which should be HUGEPAGE_PAGES long), except for a single free page
425*7bdf38e5Schristos  * at the end.
426*7bdf38e5Schristos  *
427*7bdf38e5Schristos  * (There's nothing magic about these numbers; it's just useful to share the
428*7bdf38e5Schristos  * setup between the oldest fit and the insert/remove test).
429*7bdf38e5Schristos  */
430*7bdf38e5Schristos static void
431*7bdf38e5Schristos init_test_pageslabs(psset_t *psset, hpdata_t *pageslab,
432*7bdf38e5Schristos     hpdata_t *worse_pageslab, edata_t *alloc, edata_t *worse_alloc) {
433*7bdf38e5Schristos 	bool err;
434*7bdf38e5Schristos 
435*7bdf38e5Schristos 	hpdata_init(pageslab, (void *)(10 * HUGEPAGE), PAGESLAB_AGE);
436*7bdf38e5Schristos 	/*
437*7bdf38e5Schristos 	 * This pageslab would be better from an address-first-fit POV, but
438*7bdf38e5Schristos 	 * worse from an age POV.
439*7bdf38e5Schristos 	 */
440*7bdf38e5Schristos 	hpdata_init(worse_pageslab, (void *)(9 * HUGEPAGE), PAGESLAB_AGE + 1);
441*7bdf38e5Schristos 
442*7bdf38e5Schristos 	psset_init(psset);
443*7bdf38e5Schristos 
444*7bdf38e5Schristos 	edata_init_test(&alloc[0]);
445*7bdf38e5Schristos 	test_psset_alloc_new(psset, pageslab, &alloc[0], PAGE);
446*7bdf38e5Schristos 	for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
447*7bdf38e5Schristos 		edata_init_test(&alloc[i]);
448*7bdf38e5Schristos 		err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
449*7bdf38e5Schristos 		expect_false(err, "Nonempty psset failed page allocation.");
450*7bdf38e5Schristos 		expect_ptr_eq(pageslab, edata_ps_get(&alloc[i]),
451*7bdf38e5Schristos 		    "Allocated from the wrong pageslab");
452*7bdf38e5Schristos 	}
453*7bdf38e5Schristos 
454*7bdf38e5Schristos 	edata_init_test(&worse_alloc[0]);
455*7bdf38e5Schristos 	test_psset_alloc_new(psset, worse_pageslab, &worse_alloc[0], PAGE);
456*7bdf38e5Schristos 	expect_ptr_eq(worse_pageslab, edata_ps_get(&worse_alloc[0]),
457*7bdf38e5Schristos 	    "Allocated from the wrong pageslab");
458*7bdf38e5Schristos 	/*
459*7bdf38e5Schristos 	 * Make the two pssets otherwise indistinguishable; all full except for
460*7bdf38e5Schristos 	 * a single page.
461*7bdf38e5Schristos 	 */
462*7bdf38e5Schristos 	for (size_t i = 1; i < HUGEPAGE_PAGES - 1; i++) {
463*7bdf38e5Schristos 		edata_init_test(&worse_alloc[i]);
464*7bdf38e5Schristos 		err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
465*7bdf38e5Schristos 		expect_false(err, "Nonempty psset failed page allocation.");
466*7bdf38e5Schristos 		expect_ptr_eq(worse_pageslab, edata_ps_get(&alloc[i]),
467*7bdf38e5Schristos 		    "Allocated from the wrong pageslab");
468*7bdf38e5Schristos 	}
469*7bdf38e5Schristos 
470*7bdf38e5Schristos 	/* Deallocate the last page from the older pageslab. */
471*7bdf38e5Schristos 	hpdata_t *evicted = test_psset_dalloc(psset,
472*7bdf38e5Schristos 	    &alloc[HUGEPAGE_PAGES - 1]);
473*7bdf38e5Schristos 	expect_ptr_null(evicted, "Unexpected eviction");
474*7bdf38e5Schristos }
475*7bdf38e5Schristos 
476*7bdf38e5Schristos TEST_BEGIN(test_oldest_fit) {
477*7bdf38e5Schristos 	bool err;
478*7bdf38e5Schristos 	edata_t alloc[HUGEPAGE_PAGES];
479*7bdf38e5Schristos 	edata_t worse_alloc[HUGEPAGE_PAGES];
480*7bdf38e5Schristos 
481*7bdf38e5Schristos 	hpdata_t pageslab;
482*7bdf38e5Schristos 	hpdata_t worse_pageslab;
483*7bdf38e5Schristos 
484*7bdf38e5Schristos 	psset_t psset;
485*7bdf38e5Schristos 
486*7bdf38e5Schristos 	init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
487*7bdf38e5Schristos 	    worse_alloc);
488*7bdf38e5Schristos 
489*7bdf38e5Schristos 	/* The edata should come from the better pageslab. */
490*7bdf38e5Schristos 	edata_t test_edata;
491*7bdf38e5Schristos 	edata_init_test(&test_edata);
492*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &test_edata, PAGE);
493*7bdf38e5Schristos 	expect_false(err, "Nonempty psset failed page allocation");
494*7bdf38e5Schristos 	expect_ptr_eq(&pageslab, edata_ps_get(&test_edata),
495*7bdf38e5Schristos 	    "Allocated from the wrong pageslab");
496*7bdf38e5Schristos }
497*7bdf38e5Schristos TEST_END
498*7bdf38e5Schristos 
499*7bdf38e5Schristos TEST_BEGIN(test_insert_remove) {
500*7bdf38e5Schristos 	bool err;
501*7bdf38e5Schristos 	hpdata_t *ps;
502*7bdf38e5Schristos 	edata_t alloc[HUGEPAGE_PAGES];
503*7bdf38e5Schristos 	edata_t worse_alloc[HUGEPAGE_PAGES];
504*7bdf38e5Schristos 
505*7bdf38e5Schristos 	hpdata_t pageslab;
506*7bdf38e5Schristos 	hpdata_t worse_pageslab;
507*7bdf38e5Schristos 
508*7bdf38e5Schristos 	psset_t psset;
509*7bdf38e5Schristos 
510*7bdf38e5Schristos 	init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
511*7bdf38e5Schristos 	    worse_alloc);
512*7bdf38e5Schristos 
513*7bdf38e5Schristos 	/* Remove better; should still be able to alloc from worse. */
514*7bdf38e5Schristos 	psset_update_begin(&psset, &pageslab);
515*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &worse_alloc[HUGEPAGE_PAGES - 1],
516*7bdf38e5Schristos 	    PAGE);
517*7bdf38e5Schristos 	expect_false(err, "Removal should still leave an empty page");
518*7bdf38e5Schristos 	expect_ptr_eq(&worse_pageslab,
519*7bdf38e5Schristos 	    edata_ps_get(&worse_alloc[HUGEPAGE_PAGES - 1]),
520*7bdf38e5Schristos 	    "Allocated out of wrong ps");
521*7bdf38e5Schristos 
522*7bdf38e5Schristos 	/*
523*7bdf38e5Schristos 	 * After deallocating the previous alloc and reinserting better, it
524*7bdf38e5Schristos 	 * should be preferred for future allocations.
525*7bdf38e5Schristos 	 */
526*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &worse_alloc[HUGEPAGE_PAGES - 1]);
527*7bdf38e5Schristos 	expect_ptr_null(ps, "Incorrect eviction of nonempty pageslab");
528*7bdf38e5Schristos 	psset_update_end(&psset, &pageslab);
529*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
530*7bdf38e5Schristos 	expect_false(err, "psset should be nonempty");
531*7bdf38e5Schristos 	expect_ptr_eq(&pageslab, edata_ps_get(&alloc[HUGEPAGE_PAGES - 1]),
532*7bdf38e5Schristos 	    "Removal/reinsertion shouldn't change ordering");
533*7bdf38e5Schristos 	/*
534*7bdf38e5Schristos 	 * After deallocating and removing both, allocations should fail.
535*7bdf38e5Schristos 	 */
536*7bdf38e5Schristos 	ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
537*7bdf38e5Schristos 	expect_ptr_null(ps, "Incorrect eviction");
538*7bdf38e5Schristos 	psset_update_begin(&psset, &pageslab);
539*7bdf38e5Schristos 	psset_update_begin(&psset, &worse_pageslab);
540*7bdf38e5Schristos 	err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
541*7bdf38e5Schristos 	expect_true(err, "psset should be empty, but an alloc succeeded");
542*7bdf38e5Schristos }
543*7bdf38e5Schristos TEST_END
544*7bdf38e5Schristos 
545*7bdf38e5Schristos TEST_BEGIN(test_purge_prefers_nonhuge) {
546*7bdf38e5Schristos 	/*
547*7bdf38e5Schristos 	 * All else being equal, we should prefer purging non-huge pages over
548*7bdf38e5Schristos 	 * huge ones for non-empty extents.
549*7bdf38e5Schristos 	 */
550*7bdf38e5Schristos 
551*7bdf38e5Schristos 	/* Nothing magic about this constant. */
552*7bdf38e5Schristos 	enum {
553*7bdf38e5Schristos 		NHP = 23,
554*7bdf38e5Schristos 	};
555*7bdf38e5Schristos 	hpdata_t *hpdata;
556*7bdf38e5Schristos 
557*7bdf38e5Schristos 	psset_t psset;
558*7bdf38e5Schristos 	psset_init(&psset);
559*7bdf38e5Schristos 
560*7bdf38e5Schristos 	hpdata_t hpdata_huge[NHP];
561*7bdf38e5Schristos 	uintptr_t huge_begin = (uintptr_t)&hpdata_huge[0];
562*7bdf38e5Schristos 	uintptr_t huge_end = (uintptr_t)&hpdata_huge[NHP];
563*7bdf38e5Schristos 	hpdata_t hpdata_nonhuge[NHP];
564*7bdf38e5Schristos 	uintptr_t nonhuge_begin = (uintptr_t)&hpdata_nonhuge[0];
565*7bdf38e5Schristos 	uintptr_t nonhuge_end = (uintptr_t)&hpdata_nonhuge[NHP];
566*7bdf38e5Schristos 
567*7bdf38e5Schristos 	for (size_t i = 0; i < NHP; i++) {
568*7bdf38e5Schristos 		hpdata_init(&hpdata_huge[i], (void *)((10 + i) * HUGEPAGE),
569*7bdf38e5Schristos 		    123 + i);
570*7bdf38e5Schristos 		psset_insert(&psset, &hpdata_huge[i]);
571*7bdf38e5Schristos 
572*7bdf38e5Schristos 		hpdata_init(&hpdata_nonhuge[i],
573*7bdf38e5Schristos 		    (void *)((10 + NHP + i) * HUGEPAGE),
574*7bdf38e5Schristos 		    456 + i);
575*7bdf38e5Schristos 		psset_insert(&psset, &hpdata_nonhuge[i]);
576*7bdf38e5Schristos 
577*7bdf38e5Schristos 	}
578*7bdf38e5Schristos 	for (int i = 0; i < 2 * NHP; i++) {
579*7bdf38e5Schristos 		hpdata = psset_pick_alloc(&psset, HUGEPAGE * 3 / 4);
580*7bdf38e5Schristos 		psset_update_begin(&psset, hpdata);
581*7bdf38e5Schristos 		void *ptr;
582*7bdf38e5Schristos 		ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE * 3 / 4);
583*7bdf38e5Schristos 		/* Ignore the first alloc, which will stick around. */
584*7bdf38e5Schristos 		(void)ptr;
585*7bdf38e5Schristos 		/*
586*7bdf38e5Schristos 		 * The second alloc is to dirty the pages; free it immediately
587*7bdf38e5Schristos 		 * after allocating.
588*7bdf38e5Schristos 		 */
589*7bdf38e5Schristos 		ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE / 4);
590*7bdf38e5Schristos 		hpdata_unreserve(hpdata, ptr, HUGEPAGE / 4);
591*7bdf38e5Schristos 
592*7bdf38e5Schristos 		if (huge_begin <= (uintptr_t)hpdata
593*7bdf38e5Schristos 		    && (uintptr_t)hpdata < huge_end) {
594*7bdf38e5Schristos 			hpdata_hugify(hpdata);
595*7bdf38e5Schristos 		}
596*7bdf38e5Schristos 
597*7bdf38e5Schristos 		hpdata_purge_allowed_set(hpdata, true);
598*7bdf38e5Schristos 		psset_update_end(&psset, hpdata);
599*7bdf38e5Schristos 	}
600*7bdf38e5Schristos 
601*7bdf38e5Schristos 	/*
602*7bdf38e5Schristos 	 * We've got a bunch of 1/8th dirty hpdatas.  It should give us all the
603*7bdf38e5Schristos 	 * non-huge ones to purge, then all the huge ones, then refuse to purge
604*7bdf38e5Schristos 	 * further.
605*7bdf38e5Schristos 	 */
606*7bdf38e5Schristos 	for (int i = 0; i < NHP; i++) {
607*7bdf38e5Schristos 		hpdata = psset_pick_purge(&psset);
608*7bdf38e5Schristos 		assert_true(nonhuge_begin <= (uintptr_t)hpdata
609*7bdf38e5Schristos 		    && (uintptr_t)hpdata < nonhuge_end, "");
610*7bdf38e5Schristos 		psset_update_begin(&psset, hpdata);
611*7bdf38e5Schristos 		test_psset_fake_purge(hpdata);
612*7bdf38e5Schristos 		hpdata_purge_allowed_set(hpdata, false);
613*7bdf38e5Schristos 		psset_update_end(&psset, hpdata);
614*7bdf38e5Schristos 	}
615*7bdf38e5Schristos 	for (int i = 0; i < NHP; i++) {
616*7bdf38e5Schristos 		hpdata = psset_pick_purge(&psset);
617*7bdf38e5Schristos 		expect_true(huge_begin <= (uintptr_t)hpdata
618*7bdf38e5Schristos 		    && (uintptr_t)hpdata < huge_end, "");
619*7bdf38e5Schristos 		psset_update_begin(&psset, hpdata);
620*7bdf38e5Schristos 		hpdata_dehugify(hpdata);
621*7bdf38e5Schristos 		test_psset_fake_purge(hpdata);
622*7bdf38e5Schristos 		hpdata_purge_allowed_set(hpdata, false);
623*7bdf38e5Schristos 		psset_update_end(&psset, hpdata);
624*7bdf38e5Schristos 	}
625*7bdf38e5Schristos }
626*7bdf38e5Schristos TEST_END
627*7bdf38e5Schristos 
628*7bdf38e5Schristos TEST_BEGIN(test_purge_prefers_empty) {
629*7bdf38e5Schristos 	void *ptr;
630*7bdf38e5Schristos 
631*7bdf38e5Schristos 	psset_t psset;
632*7bdf38e5Schristos 	psset_init(&psset);
633*7bdf38e5Schristos 
634*7bdf38e5Schristos 	hpdata_t hpdata_empty;
635*7bdf38e5Schristos 	hpdata_t hpdata_nonempty;
636*7bdf38e5Schristos 	hpdata_init(&hpdata_empty, (void *)(10 * HUGEPAGE), 123);
637*7bdf38e5Schristos 	psset_insert(&psset, &hpdata_empty);
638*7bdf38e5Schristos 	hpdata_init(&hpdata_nonempty, (void *)(11 * HUGEPAGE), 456);
639*7bdf38e5Schristos 	psset_insert(&psset, &hpdata_nonempty);
640*7bdf38e5Schristos 
641*7bdf38e5Schristos 	psset_update_begin(&psset, &hpdata_empty);
642*7bdf38e5Schristos 	ptr = hpdata_reserve_alloc(&hpdata_empty, PAGE);
643*7bdf38e5Schristos 	expect_ptr_eq(hpdata_addr_get(&hpdata_empty), ptr, "");
644*7bdf38e5Schristos 	hpdata_unreserve(&hpdata_empty, ptr, PAGE);
645*7bdf38e5Schristos 	hpdata_purge_allowed_set(&hpdata_empty, true);
646*7bdf38e5Schristos 	psset_update_end(&psset, &hpdata_empty);
647*7bdf38e5Schristos 
648*7bdf38e5Schristos 	psset_update_begin(&psset, &hpdata_nonempty);
649*7bdf38e5Schristos 	ptr = hpdata_reserve_alloc(&hpdata_nonempty, 10 * PAGE);
650*7bdf38e5Schristos 	expect_ptr_eq(hpdata_addr_get(&hpdata_nonempty), ptr, "");
651*7bdf38e5Schristos 	hpdata_unreserve(&hpdata_nonempty, ptr, 9 * PAGE);
652*7bdf38e5Schristos 	hpdata_purge_allowed_set(&hpdata_nonempty, true);
653*7bdf38e5Schristos 	psset_update_end(&psset, &hpdata_nonempty);
654*7bdf38e5Schristos 
655*7bdf38e5Schristos 	/*
656*7bdf38e5Schristos 	 * The nonempty slab has 9 dirty pages, while the empty one has only 1.
657*7bdf38e5Schristos 	 * We should still pick the empty one for purging.
658*7bdf38e5Schristos 	 */
659*7bdf38e5Schristos 	hpdata_t *to_purge = psset_pick_purge(&psset);
660*7bdf38e5Schristos 	expect_ptr_eq(&hpdata_empty, to_purge, "");
661*7bdf38e5Schristos }
662*7bdf38e5Schristos TEST_END
663*7bdf38e5Schristos 
664*7bdf38e5Schristos TEST_BEGIN(test_purge_prefers_empty_huge) {
665*7bdf38e5Schristos 	void *ptr;
666*7bdf38e5Schristos 
667*7bdf38e5Schristos 	psset_t psset;
668*7bdf38e5Schristos 	psset_init(&psset);
669*7bdf38e5Schristos 
670*7bdf38e5Schristos 	enum {NHP = 10 };
671*7bdf38e5Schristos 
672*7bdf38e5Schristos 	hpdata_t hpdata_huge[NHP];
673*7bdf38e5Schristos 	hpdata_t hpdata_nonhuge[NHP];
674*7bdf38e5Schristos 
675*7bdf38e5Schristos 	uintptr_t cur_addr = 100 * HUGEPAGE;
676*7bdf38e5Schristos 	uint64_t cur_age = 123;
677*7bdf38e5Schristos 	for (int i = 0; i < NHP; i++) {
678*7bdf38e5Schristos 		hpdata_init(&hpdata_huge[i], (void *)cur_addr, cur_age);
679*7bdf38e5Schristos 		cur_addr += HUGEPAGE;
680*7bdf38e5Schristos 		cur_age++;
681*7bdf38e5Schristos 		psset_insert(&psset, &hpdata_huge[i]);
682*7bdf38e5Schristos 
683*7bdf38e5Schristos 		hpdata_init(&hpdata_nonhuge[i], (void *)cur_addr, cur_age);
684*7bdf38e5Schristos 		cur_addr += HUGEPAGE;
685*7bdf38e5Schristos 		cur_age++;
686*7bdf38e5Schristos 		psset_insert(&psset, &hpdata_nonhuge[i]);
687*7bdf38e5Schristos 
688*7bdf38e5Schristos 		/*
689*7bdf38e5Schristos 		 * Make the hpdata_huge[i] fully dirty, empty, purgable, and
690*7bdf38e5Schristos 		 * huge.
691*7bdf38e5Schristos 		 */
692*7bdf38e5Schristos 		psset_update_begin(&psset, &hpdata_huge[i]);
693*7bdf38e5Schristos 		ptr = hpdata_reserve_alloc(&hpdata_huge[i], HUGEPAGE);
694*7bdf38e5Schristos 		expect_ptr_eq(hpdata_addr_get(&hpdata_huge[i]), ptr, "");
695*7bdf38e5Schristos 		hpdata_hugify(&hpdata_huge[i]);
696*7bdf38e5Schristos 		hpdata_unreserve(&hpdata_huge[i], ptr, HUGEPAGE);
697*7bdf38e5Schristos 		hpdata_purge_allowed_set(&hpdata_huge[i], true);
698*7bdf38e5Schristos 		psset_update_end(&psset, &hpdata_huge[i]);
699*7bdf38e5Schristos 
700*7bdf38e5Schristos 		/*
701*7bdf38e5Schristos 		 * Make hpdata_nonhuge[i] fully dirty, empty, purgable, and
702*7bdf38e5Schristos 		 * non-huge.
703*7bdf38e5Schristos 		 */
704*7bdf38e5Schristos 		psset_update_begin(&psset, &hpdata_nonhuge[i]);
705*7bdf38e5Schristos 		ptr = hpdata_reserve_alloc(&hpdata_nonhuge[i], HUGEPAGE);
706*7bdf38e5Schristos 		expect_ptr_eq(hpdata_addr_get(&hpdata_nonhuge[i]), ptr, "");
707*7bdf38e5Schristos 		hpdata_unreserve(&hpdata_nonhuge[i], ptr, HUGEPAGE);
708*7bdf38e5Schristos 		hpdata_purge_allowed_set(&hpdata_nonhuge[i], true);
709*7bdf38e5Schristos 		psset_update_end(&psset, &hpdata_nonhuge[i]);
710*7bdf38e5Schristos 	}
711*7bdf38e5Schristos 
712*7bdf38e5Schristos 	/*
713*7bdf38e5Schristos 	 * We have a bunch of empty slabs, half huge, half nonhuge, inserted in
714*7bdf38e5Schristos 	 * alternating order.  We should pop all the huge ones before popping
715*7bdf38e5Schristos 	 * any of the non-huge ones for purging.
716*7bdf38e5Schristos 	 */
717*7bdf38e5Schristos 	for (int i = 0; i < NHP; i++) {
718*7bdf38e5Schristos 		hpdata_t *to_purge = psset_pick_purge(&psset);
719*7bdf38e5Schristos 		expect_ptr_eq(&hpdata_huge[i], to_purge, "");
720*7bdf38e5Schristos 		psset_update_begin(&psset, to_purge);
721*7bdf38e5Schristos 		hpdata_purge_allowed_set(to_purge, false);
722*7bdf38e5Schristos 		psset_update_end(&psset, to_purge);
723*7bdf38e5Schristos 	}
724*7bdf38e5Schristos 	for (int i = 0; i < NHP; i++) {
725*7bdf38e5Schristos 		hpdata_t *to_purge = psset_pick_purge(&psset);
726*7bdf38e5Schristos 		expect_ptr_eq(&hpdata_nonhuge[i], to_purge, "");
727*7bdf38e5Schristos 		psset_update_begin(&psset, to_purge);
728*7bdf38e5Schristos 		hpdata_purge_allowed_set(to_purge, false);
729*7bdf38e5Schristos 		psset_update_end(&psset, to_purge);
730*7bdf38e5Schristos 	}
731*7bdf38e5Schristos }
732*7bdf38e5Schristos TEST_END
733*7bdf38e5Schristos 
734*7bdf38e5Schristos int
735*7bdf38e5Schristos main(void) {
736*7bdf38e5Schristos 	return test_no_reentrancy(
737*7bdf38e5Schristos 	    test_empty,
738*7bdf38e5Schristos 	    test_fill,
739*7bdf38e5Schristos 	    test_reuse,
740*7bdf38e5Schristos 	    test_evict,
741*7bdf38e5Schristos 	    test_multi_pageslab,
742*7bdf38e5Schristos 	    test_stats,
743*7bdf38e5Schristos 	    test_oldest_fit,
744*7bdf38e5Schristos 	    test_insert_remove,
745*7bdf38e5Schristos 	    test_purge_prefers_nonhuge,
746*7bdf38e5Schristos 	    test_purge_prefers_empty,
747*7bdf38e5Schristos 	    test_purge_prefers_empty_huge);
748*7bdf38e5Schristos }
749