xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/hpdata.c (revision 4439cfd0acf9c7dc90625e5cd83b2317a9ab8967)
1 #include "test/jemalloc_test.h"
2 
3 #define HPDATA_ADDR ((void *)(10 * HUGEPAGE))
4 #define HPDATA_AGE 123
5 
6 TEST_BEGIN(test_reserve_alloc) {
7 	hpdata_t hpdata;
8 	hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
9 
10 	/* Allocating a page at a time, we should do first fit. */
11 	for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
12 		expect_true(hpdata_consistent(&hpdata), "");
13 		expect_zu_eq(HUGEPAGE_PAGES - i,
14 		    hpdata_longest_free_range_get(&hpdata), "");
15 		void *alloc = hpdata_reserve_alloc(&hpdata, PAGE);
16 		expect_ptr_eq((char *)HPDATA_ADDR + i * PAGE, alloc, "");
17 		expect_true(hpdata_consistent(&hpdata), "");
18 	}
19 	expect_true(hpdata_consistent(&hpdata), "");
20 	expect_zu_eq(0, hpdata_longest_free_range_get(&hpdata), "");
21 
22 	/*
23 	 * Build up a bigger free-range, 2 pages at a time, until we've got 6
24 	 * adjacent free pages total.  Pages 8-13 should be unreserved after
25 	 * this.
26 	 */
27 	hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 10 * PAGE, 2 * PAGE);
28 	expect_true(hpdata_consistent(&hpdata), "");
29 	expect_zu_eq(2, hpdata_longest_free_range_get(&hpdata), "");
30 
31 	hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 12 * PAGE, 2 * PAGE);
32 	expect_true(hpdata_consistent(&hpdata), "");
33 	expect_zu_eq(4, hpdata_longest_free_range_get(&hpdata), "");
34 
35 	hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 8 * PAGE, 2 * PAGE);
36 	expect_true(hpdata_consistent(&hpdata), "");
37 	expect_zu_eq(6, hpdata_longest_free_range_get(&hpdata), "");
38 
39 	/*
40 	 * Leave page 14 reserved, but free page 15 (this test the case where
41 	 * unreserving combines two ranges).
42 	 */
43 	hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 15 * PAGE, PAGE);
44 	/*
45 	 * Longest free range shouldn't change; we've got a free range of size
46 	 * 6, then a reserved page, then another free range.
47 	 */
48 	expect_true(hpdata_consistent(&hpdata), "");
49 	expect_zu_eq(6, hpdata_longest_free_range_get(&hpdata), "");
50 
51 	/* After freeing page 14, the two ranges get combined. */
52 	hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 14 * PAGE, PAGE);
53 	expect_true(hpdata_consistent(&hpdata), "");
54 	expect_zu_eq(8, hpdata_longest_free_range_get(&hpdata), "");
55 }
56 TEST_END
57 
58 TEST_BEGIN(test_purge_simple) {
59 	hpdata_t hpdata;
60 	hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
61 
62 	void *alloc = hpdata_reserve_alloc(&hpdata, HUGEPAGE_PAGES / 2 * PAGE);
63 	expect_ptr_eq(alloc, HPDATA_ADDR, "");
64 
65 	/* Create HUGEPAGE_PAGES / 4 dirty inactive pages at the beginning. */
66 	hpdata_unreserve(&hpdata, alloc, HUGEPAGE_PAGES / 4 * PAGE);
67 
68 	expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 2, "");
69 
70 	hpdata_alloc_allowed_set(&hpdata, false);
71 	hpdata_purge_state_t purge_state;
72 	size_t to_purge = hpdata_purge_begin(&hpdata, &purge_state);
73 	expect_zu_eq(HUGEPAGE_PAGES / 4, to_purge, "");
74 
75 	void *purge_addr;
76 	size_t purge_size;
77 	bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
78 	    &purge_size);
79 	expect_true(got_result, "");
80 	expect_ptr_eq(HPDATA_ADDR, purge_addr, "");
81 	expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
82 
83 	got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
84 	    &purge_size);
85 	expect_false(got_result, "Unexpected additional purge range: "
86 	    "extent at %p of size %zu", purge_addr, purge_size);
87 
88 	hpdata_purge_end(&hpdata, &purge_state);
89 	expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 4, "");
90 }
91 TEST_END
92 
93 /*
94  * We only test intervening dalloc's not intervening allocs; the latter are
95  * disallowed as a purging precondition (because they interfere with purging
96  * across a retained extent, saving a purge call).
97  */
98 TEST_BEGIN(test_purge_intervening_dalloc) {
99 	hpdata_t hpdata;
100 	hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
101 
102 	/* Allocate the first 3/4 of the pages. */
103 	void *alloc = hpdata_reserve_alloc(&hpdata, 3 * HUGEPAGE_PAGES / 4  * PAGE);
104 	expect_ptr_eq(alloc, HPDATA_ADDR, "");
105 
106 	/* Free the first 1/4 and the third 1/4 of the pages. */
107 	hpdata_unreserve(&hpdata, alloc, HUGEPAGE_PAGES / 4 * PAGE);
108 	hpdata_unreserve(&hpdata,
109 	    (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE),
110 	    HUGEPAGE_PAGES / 4 * PAGE);
111 
112 	expect_zu_eq(hpdata_ntouched_get(&hpdata), 3 * HUGEPAGE_PAGES / 4, "");
113 
114 	hpdata_alloc_allowed_set(&hpdata, false);
115 	hpdata_purge_state_t purge_state;
116 	size_t to_purge = hpdata_purge_begin(&hpdata, &purge_state);
117 	expect_zu_eq(HUGEPAGE_PAGES / 2, to_purge, "");
118 
119 	void *purge_addr;
120 	size_t purge_size;
121 	/* First purge. */
122 	bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
123 	    &purge_size);
124 	expect_true(got_result, "");
125 	expect_ptr_eq(HPDATA_ADDR, purge_addr, "");
126 	expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
127 
128 	/* Deallocate the second 1/4 before the second purge occurs. */
129 	hpdata_unreserve(&hpdata,
130 	    (void *)((uintptr_t)alloc + 1 * HUGEPAGE_PAGES / 4 * PAGE),
131 	    HUGEPAGE_PAGES / 4 * PAGE);
132 
133 	/* Now continue purging. */
134 	got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
135 	    &purge_size);
136 	expect_true(got_result, "");
137 	expect_ptr_eq(
138 	    (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE),
139 	    purge_addr, "");
140 	expect_zu_ge(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
141 
142 	got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
143 	    &purge_size);
144 	expect_false(got_result, "Unexpected additional purge range: "
145 	    "extent at %p of size %zu", purge_addr, purge_size);
146 
147 	hpdata_purge_end(&hpdata, &purge_state);
148 
149 	expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 4, "");
150 }
151 TEST_END
152 
153 TEST_BEGIN(test_purge_over_retained) {
154 	void *purge_addr;
155 	size_t purge_size;
156 
157 	hpdata_t hpdata;
158 	hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
159 
160 	/* Allocate the first 3/4 of the pages. */
161 	void *alloc = hpdata_reserve_alloc(&hpdata, 3 * HUGEPAGE_PAGES / 4  * PAGE);
162 	expect_ptr_eq(alloc, HPDATA_ADDR, "");
163 
164 	/* Free the second quarter. */
165 	void *second_quarter =
166 	    (void *)((uintptr_t)alloc + HUGEPAGE_PAGES / 4 * PAGE);
167 	hpdata_unreserve(&hpdata, second_quarter, HUGEPAGE_PAGES / 4 * PAGE);
168 
169 	expect_zu_eq(hpdata_ntouched_get(&hpdata), 3 * HUGEPAGE_PAGES / 4, "");
170 
171 	/* Purge the second quarter. */
172 	hpdata_alloc_allowed_set(&hpdata, false);
173 	hpdata_purge_state_t purge_state;
174 	size_t to_purge_dirty = hpdata_purge_begin(&hpdata, &purge_state);
175 	expect_zu_eq(HUGEPAGE_PAGES / 4, to_purge_dirty, "");
176 
177 	bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
178 	    &purge_size);
179 	expect_true(got_result, "");
180 	expect_ptr_eq(second_quarter, purge_addr, "");
181 	expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
182 
183 	got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
184 	    &purge_size);
185 	expect_false(got_result, "Unexpected additional purge range: "
186 	    "extent at %p of size %zu", purge_addr, purge_size);
187 	hpdata_purge_end(&hpdata, &purge_state);
188 
189 	expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 2, "");
190 
191 	/* Free the first and third quarter. */
192 	hpdata_unreserve(&hpdata, HPDATA_ADDR, HUGEPAGE_PAGES / 4 * PAGE);
193 	hpdata_unreserve(&hpdata,
194 	    (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE),
195 	    HUGEPAGE_PAGES / 4 * PAGE);
196 
197 	/*
198 	 * Purge again.  The second quarter is retained, so we can safely
199 	 * re-purge it.  We expect a single purge of 3/4 of the hugepage,
200 	 * purging half its pages.
201 	 */
202 	to_purge_dirty = hpdata_purge_begin(&hpdata, &purge_state);
203 	expect_zu_eq(HUGEPAGE_PAGES / 2, to_purge_dirty, "");
204 
205 	got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
206 	    &purge_size);
207 	expect_true(got_result, "");
208 	expect_ptr_eq(HPDATA_ADDR, purge_addr, "");
209 	expect_zu_eq(3 * HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
210 
211 	got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
212 	    &purge_size);
213 	expect_false(got_result, "Unexpected additional purge range: "
214 	    "extent at %p of size %zu", purge_addr, purge_size);
215 	hpdata_purge_end(&hpdata, &purge_state);
216 
217 	expect_zu_eq(hpdata_ntouched_get(&hpdata), 0, "");
218 }
219 TEST_END
220 
221 TEST_BEGIN(test_hugify) {
222 	hpdata_t hpdata;
223 	hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
224 
225 	void *alloc = hpdata_reserve_alloc(&hpdata, HUGEPAGE / 2);
226 	expect_ptr_eq(alloc, HPDATA_ADDR, "");
227 
228 	expect_zu_eq(HUGEPAGE_PAGES / 2, hpdata_ntouched_get(&hpdata), "");
229 
230 	hpdata_hugify(&hpdata);
231 
232 	/* Hugeifying should have increased the dirty page count. */
233 	expect_zu_eq(HUGEPAGE_PAGES, hpdata_ntouched_get(&hpdata), "");
234 }
235 TEST_END
236 
237 int main(void) {
238 	return test_no_reentrancy(
239 	    test_reserve_alloc,
240 	    test_purge_simple,
241 	    test_purge_intervening_dalloc,
242 	    test_purge_over_retained,
243 	    test_hugify);
244 }
245