xref: /freebsd/contrib/jemalloc/src/psset.c (revision c43cad87172039ccf38172129c79755ea79e6102)
1*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_preamble.h"
2*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_internal_includes.h"
3*c43cad87SWarner Losh 
4*c43cad87SWarner Losh #include "jemalloc/internal/psset.h"
5*c43cad87SWarner Losh 
6*c43cad87SWarner Losh #include "jemalloc/internal/fb.h"
7*c43cad87SWarner Losh 
8*c43cad87SWarner Losh void
9*c43cad87SWarner Losh psset_init(psset_t *psset) {
10*c43cad87SWarner Losh 	for (unsigned i = 0; i < PSSET_NPSIZES; i++) {
11*c43cad87SWarner Losh 		hpdata_age_heap_new(&psset->pageslabs[i]);
12*c43cad87SWarner Losh 	}
13*c43cad87SWarner Losh 	fb_init(psset->pageslab_bitmap, PSSET_NPSIZES);
14*c43cad87SWarner Losh 	memset(&psset->merged_stats, 0, sizeof(psset->merged_stats));
15*c43cad87SWarner Losh 	memset(&psset->stats, 0, sizeof(psset->stats));
16*c43cad87SWarner Losh 	hpdata_empty_list_init(&psset->empty);
17*c43cad87SWarner Losh 	for (int i = 0; i < PSSET_NPURGE_LISTS; i++) {
18*c43cad87SWarner Losh 		hpdata_purge_list_init(&psset->to_purge[i]);
19*c43cad87SWarner Losh 	}
20*c43cad87SWarner Losh 	fb_init(psset->purge_bitmap, PSSET_NPURGE_LISTS);
21*c43cad87SWarner Losh 	hpdata_hugify_list_init(&psset->to_hugify);
22*c43cad87SWarner Losh }
23*c43cad87SWarner Losh 
24*c43cad87SWarner Losh static void
25*c43cad87SWarner Losh psset_bin_stats_accum(psset_bin_stats_t *dst, psset_bin_stats_t *src) {
26*c43cad87SWarner Losh 	dst->npageslabs += src->npageslabs;
27*c43cad87SWarner Losh 	dst->nactive += src->nactive;
28*c43cad87SWarner Losh 	dst->ndirty += src->ndirty;
29*c43cad87SWarner Losh }
30*c43cad87SWarner Losh 
31*c43cad87SWarner Losh void
32*c43cad87SWarner Losh psset_stats_accum(psset_stats_t *dst, psset_stats_t *src) {
33*c43cad87SWarner Losh 	psset_bin_stats_accum(&dst->full_slabs[0], &src->full_slabs[0]);
34*c43cad87SWarner Losh 	psset_bin_stats_accum(&dst->full_slabs[1], &src->full_slabs[1]);
35*c43cad87SWarner Losh 	psset_bin_stats_accum(&dst->empty_slabs[0], &src->empty_slabs[0]);
36*c43cad87SWarner Losh 	psset_bin_stats_accum(&dst->empty_slabs[1], &src->empty_slabs[1]);
37*c43cad87SWarner Losh 	for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
38*c43cad87SWarner Losh 		psset_bin_stats_accum(&dst->nonfull_slabs[i][0],
39*c43cad87SWarner Losh 		    &src->nonfull_slabs[i][0]);
40*c43cad87SWarner Losh 		psset_bin_stats_accum(&dst->nonfull_slabs[i][1],
41*c43cad87SWarner Losh 		    &src->nonfull_slabs[i][1]);
42*c43cad87SWarner Losh 	}
43*c43cad87SWarner Losh }
44*c43cad87SWarner Losh 
45*c43cad87SWarner Losh /*
46*c43cad87SWarner Losh  * The stats maintenance strategy is to remove a pageslab's contribution to the
47*c43cad87SWarner Losh  * stats when we call psset_update_begin, and re-add it (to a potentially new
48*c43cad87SWarner Losh  * bin) when we call psset_update_end.
49*c43cad87SWarner Losh  */
50*c43cad87SWarner Losh JEMALLOC_ALWAYS_INLINE void
51*c43cad87SWarner Losh psset_bin_stats_insert_remove(psset_t *psset, psset_bin_stats_t *binstats,
52*c43cad87SWarner Losh     hpdata_t *ps, bool insert) {
53*c43cad87SWarner Losh 	size_t mul = insert ? (size_t)1 : (size_t)-1;
54*c43cad87SWarner Losh 	size_t huge_idx = (size_t)hpdata_huge_get(ps);
55*c43cad87SWarner Losh 
56*c43cad87SWarner Losh 	binstats[huge_idx].npageslabs += mul * 1;
57*c43cad87SWarner Losh 	binstats[huge_idx].nactive += mul * hpdata_nactive_get(ps);
58*c43cad87SWarner Losh 	binstats[huge_idx].ndirty += mul * hpdata_ndirty_get(ps);
59*c43cad87SWarner Losh 
60*c43cad87SWarner Losh 	psset->merged_stats.npageslabs += mul * 1;
61*c43cad87SWarner Losh 	psset->merged_stats.nactive += mul * hpdata_nactive_get(ps);
62*c43cad87SWarner Losh 	psset->merged_stats.ndirty += mul * hpdata_ndirty_get(ps);
63*c43cad87SWarner Losh 
64*c43cad87SWarner Losh 	if (config_debug) {
65*c43cad87SWarner Losh 		psset_bin_stats_t check_stats = {0};
66*c43cad87SWarner Losh 		for (size_t huge = 0; huge <= 1; huge++) {
67*c43cad87SWarner Losh 			psset_bin_stats_accum(&check_stats,
68*c43cad87SWarner Losh 			    &psset->stats.full_slabs[huge]);
69*c43cad87SWarner Losh 			psset_bin_stats_accum(&check_stats,
70*c43cad87SWarner Losh 			    &psset->stats.empty_slabs[huge]);
71*c43cad87SWarner Losh 			for (pszind_t pind = 0; pind < PSSET_NPSIZES; pind++) {
72*c43cad87SWarner Losh 				psset_bin_stats_accum(&check_stats,
73*c43cad87SWarner Losh 				    &psset->stats.nonfull_slabs[pind][huge]);
74*c43cad87SWarner Losh 			}
75*c43cad87SWarner Losh 		}
76*c43cad87SWarner Losh 		assert(psset->merged_stats.npageslabs
77*c43cad87SWarner Losh 		    == check_stats.npageslabs);
78*c43cad87SWarner Losh 		assert(psset->merged_stats.nactive == check_stats.nactive);
79*c43cad87SWarner Losh 		assert(psset->merged_stats.ndirty == check_stats.ndirty);
80*c43cad87SWarner Losh 	}
81*c43cad87SWarner Losh }
82*c43cad87SWarner Losh 
83*c43cad87SWarner Losh static void
84*c43cad87SWarner Losh psset_bin_stats_insert(psset_t *psset, psset_bin_stats_t *binstats,
85*c43cad87SWarner Losh     hpdata_t *ps) {
86*c43cad87SWarner Losh 	psset_bin_stats_insert_remove(psset, binstats, ps, true);
87*c43cad87SWarner Losh }
88*c43cad87SWarner Losh 
89*c43cad87SWarner Losh static void
90*c43cad87SWarner Losh psset_bin_stats_remove(psset_t *psset, psset_bin_stats_t *binstats,
91*c43cad87SWarner Losh     hpdata_t *ps) {
92*c43cad87SWarner Losh 	psset_bin_stats_insert_remove(psset, binstats, ps, false);
93*c43cad87SWarner Losh }
94*c43cad87SWarner Losh 
95*c43cad87SWarner Losh static void
96*c43cad87SWarner Losh psset_hpdata_heap_remove(psset_t *psset, pszind_t pind, hpdata_t *ps) {
97*c43cad87SWarner Losh 	hpdata_age_heap_remove(&psset->pageslabs[pind], ps);
98*c43cad87SWarner Losh 	if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
99*c43cad87SWarner Losh 		fb_unset(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind);
100*c43cad87SWarner Losh 	}
101*c43cad87SWarner Losh }
102*c43cad87SWarner Losh 
103*c43cad87SWarner Losh static void
104*c43cad87SWarner Losh psset_hpdata_heap_insert(psset_t *psset, pszind_t pind, hpdata_t *ps) {
105*c43cad87SWarner Losh 	if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
106*c43cad87SWarner Losh 		fb_set(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind);
107*c43cad87SWarner Losh 	}
108*c43cad87SWarner Losh 	hpdata_age_heap_insert(&psset->pageslabs[pind], ps);
109*c43cad87SWarner Losh }
110*c43cad87SWarner Losh 
111*c43cad87SWarner Losh static void
112*c43cad87SWarner Losh psset_stats_insert(psset_t* psset, hpdata_t *ps) {
113*c43cad87SWarner Losh 	if (hpdata_empty(ps)) {
114*c43cad87SWarner Losh 		psset_bin_stats_insert(psset, psset->stats.empty_slabs, ps);
115*c43cad87SWarner Losh 	} else if (hpdata_full(ps)) {
116*c43cad87SWarner Losh 		psset_bin_stats_insert(psset, psset->stats.full_slabs, ps);
117*c43cad87SWarner Losh 	} else {
118*c43cad87SWarner Losh 		size_t longest_free_range = hpdata_longest_free_range_get(ps);
119*c43cad87SWarner Losh 
120*c43cad87SWarner Losh 		pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
121*c43cad87SWarner Losh 		    longest_free_range << LG_PAGE));
122*c43cad87SWarner Losh 		assert(pind < PSSET_NPSIZES);
123*c43cad87SWarner Losh 
124*c43cad87SWarner Losh 		psset_bin_stats_insert(psset, psset->stats.nonfull_slabs[pind],
125*c43cad87SWarner Losh 		    ps);
126*c43cad87SWarner Losh 	}
127*c43cad87SWarner Losh }
128*c43cad87SWarner Losh 
129*c43cad87SWarner Losh static void
130*c43cad87SWarner Losh psset_stats_remove(psset_t *psset, hpdata_t *ps) {
131*c43cad87SWarner Losh 	if (hpdata_empty(ps)) {
132*c43cad87SWarner Losh 		psset_bin_stats_remove(psset, psset->stats.empty_slabs, ps);
133*c43cad87SWarner Losh 	} else if (hpdata_full(ps)) {
134*c43cad87SWarner Losh 		psset_bin_stats_remove(psset, psset->stats.full_slabs, ps);
135*c43cad87SWarner Losh 	} else {
136*c43cad87SWarner Losh 		size_t longest_free_range = hpdata_longest_free_range_get(ps);
137*c43cad87SWarner Losh 
138*c43cad87SWarner Losh 		pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
139*c43cad87SWarner Losh 		    longest_free_range << LG_PAGE));
140*c43cad87SWarner Losh 		assert(pind < PSSET_NPSIZES);
141*c43cad87SWarner Losh 
142*c43cad87SWarner Losh 		psset_bin_stats_remove(psset, psset->stats.nonfull_slabs[pind],
143*c43cad87SWarner Losh 		    ps);
144*c43cad87SWarner Losh 	}
145*c43cad87SWarner Losh }
146*c43cad87SWarner Losh 
147*c43cad87SWarner Losh /*
148*c43cad87SWarner Losh  * Put ps into some container so that it can be found during future allocation
149*c43cad87SWarner Losh  * requests.
150*c43cad87SWarner Losh  */
151*c43cad87SWarner Losh static void
152*c43cad87SWarner Losh psset_alloc_container_insert(psset_t *psset, hpdata_t *ps) {
153*c43cad87SWarner Losh 	assert(!hpdata_in_psset_alloc_container_get(ps));
154*c43cad87SWarner Losh 	hpdata_in_psset_alloc_container_set(ps, true);
155*c43cad87SWarner Losh 	if (hpdata_empty(ps)) {
156*c43cad87SWarner Losh 		/*
157*c43cad87SWarner Losh 		 * This prepend, paired with popping the head in psset_fit,
158*c43cad87SWarner Losh 		 * means we implement LIFO ordering for the empty slabs set,
159*c43cad87SWarner Losh 		 * which seems reasonable.
160*c43cad87SWarner Losh 		 */
161*c43cad87SWarner Losh 		hpdata_empty_list_prepend(&psset->empty, ps);
162*c43cad87SWarner Losh 	} else if (hpdata_full(ps)) {
163*c43cad87SWarner Losh 		/*
164*c43cad87SWarner Losh 		 * We don't need to keep track of the full slabs; we're never
165*c43cad87SWarner Losh 		 * going to return them from a psset_pick_alloc call.
166*c43cad87SWarner Losh 		 */
167*c43cad87SWarner Losh 	} else {
168*c43cad87SWarner Losh 		size_t longest_free_range = hpdata_longest_free_range_get(ps);
169*c43cad87SWarner Losh 
170*c43cad87SWarner Losh 		pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
171*c43cad87SWarner Losh 		    longest_free_range << LG_PAGE));
172*c43cad87SWarner Losh 		assert(pind < PSSET_NPSIZES);
173*c43cad87SWarner Losh 
174*c43cad87SWarner Losh 		psset_hpdata_heap_insert(psset, pind, ps);
175*c43cad87SWarner Losh 	}
176*c43cad87SWarner Losh }
177*c43cad87SWarner Losh 
178*c43cad87SWarner Losh /* Remove ps from those collections. */
179*c43cad87SWarner Losh static void
180*c43cad87SWarner Losh psset_alloc_container_remove(psset_t *psset, hpdata_t *ps) {
181*c43cad87SWarner Losh 	assert(hpdata_in_psset_alloc_container_get(ps));
182*c43cad87SWarner Losh 	hpdata_in_psset_alloc_container_set(ps, false);
183*c43cad87SWarner Losh 
184*c43cad87SWarner Losh 	if (hpdata_empty(ps)) {
185*c43cad87SWarner Losh 		hpdata_empty_list_remove(&psset->empty, ps);
186*c43cad87SWarner Losh 	} else if (hpdata_full(ps)) {
187*c43cad87SWarner Losh 		/* Same as above -- do nothing in this case. */
188*c43cad87SWarner Losh 	} else {
189*c43cad87SWarner Losh 		size_t longest_free_range = hpdata_longest_free_range_get(ps);
190*c43cad87SWarner Losh 
191*c43cad87SWarner Losh 		pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
192*c43cad87SWarner Losh 		    longest_free_range << LG_PAGE));
193*c43cad87SWarner Losh 		assert(pind < PSSET_NPSIZES);
194*c43cad87SWarner Losh 
195*c43cad87SWarner Losh 		psset_hpdata_heap_remove(psset, pind, ps);
196*c43cad87SWarner Losh 	}
197*c43cad87SWarner Losh }
198*c43cad87SWarner Losh 
199*c43cad87SWarner Losh static size_t
200*c43cad87SWarner Losh psset_purge_list_ind(hpdata_t *ps) {
201*c43cad87SWarner Losh 	size_t ndirty = hpdata_ndirty_get(ps);
202*c43cad87SWarner Losh 	/* Shouldn't have something with no dirty pages purgeable. */
203*c43cad87SWarner Losh 	assert(ndirty > 0);
204*c43cad87SWarner Losh 	/*
205*c43cad87SWarner Losh 	 * Higher indices correspond to lists we'd like to purge earlier; make
206*c43cad87SWarner Losh 	 * the two highest indices correspond to empty lists, which we attempt
207*c43cad87SWarner Losh 	 * to purge before purging any non-empty list.  This has two advantages:
208*c43cad87SWarner Losh 	 * - Empty page slabs are the least likely to get reused (we'll only
209*c43cad87SWarner Losh 	 *   pick them for an allocation if we have no other choice).
210*c43cad87SWarner Losh 	 * - Empty page slabs can purge every dirty page they contain in a
211*c43cad87SWarner Losh 	 *   single call, which is not usually the case.
212*c43cad87SWarner Losh 	 *
213*c43cad87SWarner Losh 	 * We purge hugeified empty slabs before nonhugeified ones, on the basis
214*c43cad87SWarner Losh 	 * that they are fully dirty, while nonhugified slabs might not be, so
215*c43cad87SWarner Losh 	 * we free up more pages more easily.
216*c43cad87SWarner Losh 	 */
217*c43cad87SWarner Losh 	if (hpdata_nactive_get(ps) == 0) {
218*c43cad87SWarner Losh 		if (hpdata_huge_get(ps)) {
219*c43cad87SWarner Losh 			return PSSET_NPURGE_LISTS - 1;
220*c43cad87SWarner Losh 		} else {
221*c43cad87SWarner Losh 			return PSSET_NPURGE_LISTS - 2;
222*c43cad87SWarner Losh 		}
223*c43cad87SWarner Losh 	}
224*c43cad87SWarner Losh 
225*c43cad87SWarner Losh 	pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(ndirty << LG_PAGE));
226*c43cad87SWarner Losh 	/*
227*c43cad87SWarner Losh 	 * For non-empty slabs, we may reuse them again.  Prefer purging
228*c43cad87SWarner Losh 	 * non-hugeified slabs before hugeified ones then, among pages of
229*c43cad87SWarner Losh 	 * similar dirtiness.  We still get some benefit from the hugification.
230*c43cad87SWarner Losh 	 */
231*c43cad87SWarner Losh 	return (size_t)pind * 2 + (hpdata_huge_get(ps) ? 0 : 1);
232*c43cad87SWarner Losh }
233*c43cad87SWarner Losh 
234*c43cad87SWarner Losh static void
235*c43cad87SWarner Losh psset_maybe_remove_purge_list(psset_t *psset, hpdata_t *ps) {
236*c43cad87SWarner Losh 	/*
237*c43cad87SWarner Losh 	 * Remove the hpdata from its purge list (if it's in one).  Even if it's
238*c43cad87SWarner Losh 	 * going to stay in the same one, by appending it during
239*c43cad87SWarner Losh 	 * psset_update_end, we move it to the end of its queue, so that we
240*c43cad87SWarner Losh 	 * purge LRU within a given dirtiness bucket.
241*c43cad87SWarner Losh 	 */
242*c43cad87SWarner Losh 	if (hpdata_purge_allowed_get(ps)) {
243*c43cad87SWarner Losh 		size_t ind = psset_purge_list_ind(ps);
244*c43cad87SWarner Losh 		hpdata_purge_list_t *purge_list = &psset->to_purge[ind];
245*c43cad87SWarner Losh 		hpdata_purge_list_remove(purge_list, ps);
246*c43cad87SWarner Losh 		if (hpdata_purge_list_empty(purge_list)) {
247*c43cad87SWarner Losh 			fb_unset(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind);
248*c43cad87SWarner Losh 		}
249*c43cad87SWarner Losh 	}
250*c43cad87SWarner Losh }
251*c43cad87SWarner Losh 
252*c43cad87SWarner Losh static void
253*c43cad87SWarner Losh psset_maybe_insert_purge_list(psset_t *psset, hpdata_t *ps) {
254*c43cad87SWarner Losh 	if (hpdata_purge_allowed_get(ps)) {
255*c43cad87SWarner Losh 		size_t ind = psset_purge_list_ind(ps);
256*c43cad87SWarner Losh 		hpdata_purge_list_t *purge_list = &psset->to_purge[ind];
257*c43cad87SWarner Losh 		if (hpdata_purge_list_empty(purge_list)) {
258*c43cad87SWarner Losh 			fb_set(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind);
259*c43cad87SWarner Losh 		}
260*c43cad87SWarner Losh 		hpdata_purge_list_append(purge_list, ps);
261*c43cad87SWarner Losh 	}
262*c43cad87SWarner Losh 
263*c43cad87SWarner Losh }
264*c43cad87SWarner Losh 
265*c43cad87SWarner Losh void
266*c43cad87SWarner Losh psset_update_begin(psset_t *psset, hpdata_t *ps) {
267*c43cad87SWarner Losh 	hpdata_assert_consistent(ps);
268*c43cad87SWarner Losh 	assert(hpdata_in_psset_get(ps));
269*c43cad87SWarner Losh 	hpdata_updating_set(ps, true);
270*c43cad87SWarner Losh 	psset_stats_remove(psset, ps);
271*c43cad87SWarner Losh 	if (hpdata_in_psset_alloc_container_get(ps)) {
272*c43cad87SWarner Losh 		/*
273*c43cad87SWarner Losh 		 * Some metadata updates can break alloc container invariants
274*c43cad87SWarner Losh 		 * (e.g. the longest free range determines the hpdata_heap_t the
275*c43cad87SWarner Losh 		 * pageslab lives in).
276*c43cad87SWarner Losh 		 */
277*c43cad87SWarner Losh 		assert(hpdata_alloc_allowed_get(ps));
278*c43cad87SWarner Losh 		psset_alloc_container_remove(psset, ps);
279*c43cad87SWarner Losh 	}
280*c43cad87SWarner Losh 	psset_maybe_remove_purge_list(psset, ps);
281*c43cad87SWarner Losh 	/*
282*c43cad87SWarner Losh 	 * We don't update presence in the hugify list; we try to keep it FIFO,
283*c43cad87SWarner Losh 	 * even in the presence of other metadata updates.  We'll update
284*c43cad87SWarner Losh 	 * presence at the end of the metadata update if necessary.
285*c43cad87SWarner Losh 	 */
286*c43cad87SWarner Losh }
287*c43cad87SWarner Losh 
288*c43cad87SWarner Losh void
289*c43cad87SWarner Losh psset_update_end(psset_t *psset, hpdata_t *ps) {
290*c43cad87SWarner Losh 	assert(hpdata_in_psset_get(ps));
291*c43cad87SWarner Losh 	hpdata_updating_set(ps, false);
292*c43cad87SWarner Losh 	psset_stats_insert(psset, ps);
293*c43cad87SWarner Losh 
294*c43cad87SWarner Losh 	/*
295*c43cad87SWarner Losh 	 * The update begin should have removed ps from whatever alloc container
296*c43cad87SWarner Losh 	 * it was in.
297*c43cad87SWarner Losh 	 */
298*c43cad87SWarner Losh 	assert(!hpdata_in_psset_alloc_container_get(ps));
299*c43cad87SWarner Losh 	if (hpdata_alloc_allowed_get(ps)) {
300*c43cad87SWarner Losh 		psset_alloc_container_insert(psset, ps);
301*c43cad87SWarner Losh 	}
302*c43cad87SWarner Losh 	psset_maybe_insert_purge_list(psset, ps);
303*c43cad87SWarner Losh 
304*c43cad87SWarner Losh 	if (hpdata_hugify_allowed_get(ps)
305*c43cad87SWarner Losh 	    && !hpdata_in_psset_hugify_container_get(ps)) {
306*c43cad87SWarner Losh 		hpdata_in_psset_hugify_container_set(ps, true);
307*c43cad87SWarner Losh 		hpdata_hugify_list_append(&psset->to_hugify, ps);
308*c43cad87SWarner Losh 	} else if (!hpdata_hugify_allowed_get(ps)
309*c43cad87SWarner Losh 	    && hpdata_in_psset_hugify_container_get(ps)) {
310*c43cad87SWarner Losh 		hpdata_in_psset_hugify_container_set(ps, false);
311*c43cad87SWarner Losh 		hpdata_hugify_list_remove(&psset->to_hugify, ps);
312*c43cad87SWarner Losh 	}
313*c43cad87SWarner Losh 	hpdata_assert_consistent(ps);
314*c43cad87SWarner Losh }
315*c43cad87SWarner Losh 
316*c43cad87SWarner Losh hpdata_t *
317*c43cad87SWarner Losh psset_pick_alloc(psset_t *psset, size_t size) {
318*c43cad87SWarner Losh 	assert((size & PAGE_MASK) == 0);
319*c43cad87SWarner Losh 	assert(size <= HUGEPAGE);
320*c43cad87SWarner Losh 
321*c43cad87SWarner Losh 	pszind_t min_pind = sz_psz2ind(sz_psz_quantize_ceil(size));
322*c43cad87SWarner Losh 	pszind_t pind = (pszind_t)fb_ffs(psset->pageslab_bitmap, PSSET_NPSIZES,
323*c43cad87SWarner Losh 	    (size_t)min_pind);
324*c43cad87SWarner Losh 	if (pind == PSSET_NPSIZES) {
325*c43cad87SWarner Losh 		return hpdata_empty_list_first(&psset->empty);
326*c43cad87SWarner Losh 	}
327*c43cad87SWarner Losh 	hpdata_t *ps = hpdata_age_heap_first(&psset->pageslabs[pind]);
328*c43cad87SWarner Losh 	if (ps == NULL) {
329*c43cad87SWarner Losh 		return NULL;
330*c43cad87SWarner Losh 	}
331*c43cad87SWarner Losh 
332*c43cad87SWarner Losh 	hpdata_assert_consistent(ps);
333*c43cad87SWarner Losh 
334*c43cad87SWarner Losh 	return ps;
335*c43cad87SWarner Losh }
336*c43cad87SWarner Losh 
337*c43cad87SWarner Losh hpdata_t *
338*c43cad87SWarner Losh psset_pick_purge(psset_t *psset) {
339*c43cad87SWarner Losh 	ssize_t ind_ssz = fb_fls(psset->purge_bitmap, PSSET_NPURGE_LISTS,
340*c43cad87SWarner Losh 	    PSSET_NPURGE_LISTS - 1);
341*c43cad87SWarner Losh 	if (ind_ssz < 0) {
342*c43cad87SWarner Losh 		return NULL;
343*c43cad87SWarner Losh 	}
344*c43cad87SWarner Losh 	pszind_t ind = (pszind_t)ind_ssz;
345*c43cad87SWarner Losh 	assert(ind < PSSET_NPURGE_LISTS);
346*c43cad87SWarner Losh 	hpdata_t *ps = hpdata_purge_list_first(&psset->to_purge[ind]);
347*c43cad87SWarner Losh 	assert(ps != NULL);
348*c43cad87SWarner Losh 	return ps;
349*c43cad87SWarner Losh }
350*c43cad87SWarner Losh 
351*c43cad87SWarner Losh hpdata_t *
352*c43cad87SWarner Losh psset_pick_hugify(psset_t *psset) {
353*c43cad87SWarner Losh 	return hpdata_hugify_list_first(&psset->to_hugify);
354*c43cad87SWarner Losh }
355*c43cad87SWarner Losh 
356*c43cad87SWarner Losh void
357*c43cad87SWarner Losh psset_insert(psset_t *psset, hpdata_t *ps) {
358*c43cad87SWarner Losh 	hpdata_in_psset_set(ps, true);
359*c43cad87SWarner Losh 
360*c43cad87SWarner Losh 	psset_stats_insert(psset, ps);
361*c43cad87SWarner Losh 	if (hpdata_alloc_allowed_get(ps)) {
362*c43cad87SWarner Losh 		psset_alloc_container_insert(psset, ps);
363*c43cad87SWarner Losh 	}
364*c43cad87SWarner Losh 	psset_maybe_insert_purge_list(psset, ps);
365*c43cad87SWarner Losh 
366*c43cad87SWarner Losh 	if (hpdata_hugify_allowed_get(ps)) {
367*c43cad87SWarner Losh 		hpdata_in_psset_hugify_container_set(ps, true);
368*c43cad87SWarner Losh 		hpdata_hugify_list_append(&psset->to_hugify, ps);
369*c43cad87SWarner Losh 	}
370*c43cad87SWarner Losh }
371*c43cad87SWarner Losh 
372*c43cad87SWarner Losh void
373*c43cad87SWarner Losh psset_remove(psset_t *psset, hpdata_t *ps) {
374*c43cad87SWarner Losh 	hpdata_in_psset_set(ps, false);
375*c43cad87SWarner Losh 
376*c43cad87SWarner Losh 	psset_stats_remove(psset, ps);
377*c43cad87SWarner Losh 	if (hpdata_in_psset_alloc_container_get(ps)) {
378*c43cad87SWarner Losh 		psset_alloc_container_remove(psset, ps);
379*c43cad87SWarner Losh 	}
380*c43cad87SWarner Losh 	psset_maybe_remove_purge_list(psset, ps);
381*c43cad87SWarner Losh 	if (hpdata_in_psset_hugify_container_get(ps)) {
382*c43cad87SWarner Losh 		hpdata_in_psset_hugify_container_set(ps, false);
383*c43cad87SWarner Losh 		hpdata_hugify_list_remove(&psset->to_hugify, ps);
384*c43cad87SWarner Losh 	}
385*c43cad87SWarner Losh }
386