xref: /freebsd/sys/vm/vm_radix.c (revision 880659fe819832bf064cf63dba0aae34f83a659e)
1774d251dSAttilio Rao /*
2774d251dSAttilio Rao  * Copyright (c) 2013 EMC Corp.
3774d251dSAttilio Rao  * Copyright (c) 2011 Jeffrey Roberson <jeff@freebsd.org>
4774d251dSAttilio Rao  * Copyright (c) 2008 Mayur Shardul <mayur.shardul@gmail.com>
5774d251dSAttilio Rao  * All rights reserved.
6774d251dSAttilio Rao  *
7774d251dSAttilio Rao  * Redistribution and use in source and binary forms, with or without
8774d251dSAttilio Rao  * modification, are permitted provided that the following conditions
9774d251dSAttilio Rao  * are met:
10774d251dSAttilio Rao  * 1. Redistributions of source code must retain the above copyright
11774d251dSAttilio Rao  *    notice, this list of conditions and the following disclaimer.
12774d251dSAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
13774d251dSAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
14774d251dSAttilio Rao  *    documentation and/or other materials provided with the distribution.
15774d251dSAttilio Rao  *
16774d251dSAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17774d251dSAttilio Rao  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18774d251dSAttilio Rao  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19774d251dSAttilio Rao  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20774d251dSAttilio Rao  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21774d251dSAttilio Rao  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22774d251dSAttilio Rao  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23774d251dSAttilio Rao  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24774d251dSAttilio Rao  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25774d251dSAttilio Rao  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26774d251dSAttilio Rao  * SUCH DAMAGE.
27774d251dSAttilio Rao  *
28774d251dSAttilio Rao  */
29774d251dSAttilio Rao 
30774d251dSAttilio Rao /*
31774d251dSAttilio Rao  * Path-compressed radix trie implementation.
32774d251dSAttilio Rao  * The following code is not generalized into a general purpose library
33774d251dSAttilio Rao  * because there are way too many parameters embedded that should really
34774d251dSAttilio Rao  * be decided by the library consumers.  At the same time, consumers
35774d251dSAttilio Rao  * of this code must achieve highest possible performance.
36774d251dSAttilio Rao  *
37774d251dSAttilio Rao  * The implementation takes into account the following rationale:
38774d251dSAttilio Rao  * - Size of the nodes should be as small as possible but still big enough
39774d251dSAttilio Rao  *   to avoid a large maximum depth for the trie.  This is a balance
40774d251dSAttilio Rao  *   between the necessity to not wire too much physical memory for the nodes
41774d251dSAttilio Rao  *   and the necessity to avoid too much cache pollution during the trie
42774d251dSAttilio Rao  *   operations.
43774d251dSAttilio Rao  * - There is not a huge bias toward the number of lookup operations over
44774d251dSAttilio Rao  *   the number of insert and remove operations.  This basically implies
45774d251dSAttilio Rao  *   that optimizations supposedly helping one operation but hurting the
46774d251dSAttilio Rao  *   other might be carefully evaluated.
47774d251dSAttilio Rao  * - On average not many nodes are expected to be fully populated, hence
48774d251dSAttilio Rao  *   level compression may just complicate things.
49774d251dSAttilio Rao  */
50774d251dSAttilio Rao 
51774d251dSAttilio Rao #include <sys/cdefs.h>
52774d251dSAttilio Rao __FBSDID("$FreeBSD$");
53774d251dSAttilio Rao 
54774d251dSAttilio Rao #include "opt_ddb.h"
55774d251dSAttilio Rao 
56774d251dSAttilio Rao #include <sys/param.h>
57774d251dSAttilio Rao #include <sys/systm.h>
58774d251dSAttilio Rao #include <sys/kernel.h>
59774d251dSAttilio Rao #include <sys/vmmeter.h>
60774d251dSAttilio Rao 
61774d251dSAttilio Rao #include <vm/uma.h>
62774d251dSAttilio Rao #include <vm/vm.h>
63774d251dSAttilio Rao #include <vm/vm_param.h>
64774d251dSAttilio Rao #include <vm/vm_page.h>
65774d251dSAttilio Rao #include <vm/vm_radix.h>
66774d251dSAttilio Rao 
67774d251dSAttilio Rao #ifdef DDB
68774d251dSAttilio Rao #include <ddb/ddb.h>
69774d251dSAttilio Rao #endif
70774d251dSAttilio Rao 
71774d251dSAttilio Rao /*
72774d251dSAttilio Rao  * These widths should allow the pointers to a node's children to fit within
73774d251dSAttilio Rao  * a single cache line.  The extra levels from a narrow width should not be
74774d251dSAttilio Rao  * a problem thanks to path compression.
75774d251dSAttilio Rao  */
76774d251dSAttilio Rao #ifdef __LP64__
77774d251dSAttilio Rao #define	VM_RADIX_WIDTH	4
78774d251dSAttilio Rao #else
79774d251dSAttilio Rao #define	VM_RADIX_WIDTH	3
80774d251dSAttilio Rao #endif
81774d251dSAttilio Rao 
82774d251dSAttilio Rao #define	VM_RADIX_COUNT	(1 << VM_RADIX_WIDTH)
83774d251dSAttilio Rao #define	VM_RADIX_MASK	(VM_RADIX_COUNT - 1)
84774d251dSAttilio Rao #define	VM_RADIX_LIMIT							\
85774d251dSAttilio Rao 	(howmany((sizeof(vm_pindex_t) * NBBY), VM_RADIX_WIDTH) - 1)
86774d251dSAttilio Rao 
87774d251dSAttilio Rao /* Flag bits stored in node pointers. */
88774d251dSAttilio Rao #define	VM_RADIX_ISLEAF	0x1
89774d251dSAttilio Rao #define	VM_RADIX_FLAGS	0x1
90774d251dSAttilio Rao #define	VM_RADIX_PAD	VM_RADIX_FLAGS
91774d251dSAttilio Rao 
92774d251dSAttilio Rao /* Returns one unit associated with specified level. */
93774d251dSAttilio Rao #define	VM_RADIX_UNITLEVEL(lev)						\
94774d251dSAttilio Rao 	((vm_pindex_t)1 << ((VM_RADIX_LIMIT - (lev)) * VM_RADIX_WIDTH))
95774d251dSAttilio Rao 
96774d251dSAttilio Rao struct vm_radix_node {
97774d251dSAttilio Rao 	vm_pindex_t	 rn_owner;			/* Owner of record. */
98774d251dSAttilio Rao 	uint16_t	 rn_count;			/* Valid children. */
99774d251dSAttilio Rao 	uint16_t	 rn_clev;			/* Current level. */
1002c899fedSAlan Cox 	void		*rn_child[VM_RADIX_COUNT];	/* Child nodes. */
101774d251dSAttilio Rao };
102774d251dSAttilio Rao 
103774d251dSAttilio Rao static uma_zone_t vm_radix_node_zone;
104774d251dSAttilio Rao 
105774d251dSAttilio Rao /*
106774d251dSAttilio Rao  * Allocate a radix node.  Pre-allocation should ensure that the request
107774d251dSAttilio Rao  * will always be satisfied.
108774d251dSAttilio Rao  */
109774d251dSAttilio Rao static __inline struct vm_radix_node *
110774d251dSAttilio Rao vm_radix_node_get(vm_pindex_t owner, uint16_t count, uint16_t clevel)
111774d251dSAttilio Rao {
112774d251dSAttilio Rao 	struct vm_radix_node *rnode;
113774d251dSAttilio Rao 
114774d251dSAttilio Rao 	rnode = uma_zalloc(vm_radix_node_zone, M_NOWAIT);
115774d251dSAttilio Rao 
116774d251dSAttilio Rao 	/*
117774d251dSAttilio Rao 	 * The required number of nodes should already be pre-allocated
118774d251dSAttilio Rao 	 * by vm_radix_prealloc().  However, UMA can hold a few nodes
119774d251dSAttilio Rao 	 * in per-CPU buckets, which will not be accessible by the
120774d251dSAttilio Rao 	 * current CPU.  Thus, the allocation could return NULL when
121774d251dSAttilio Rao 	 * the pre-allocated pool is close to exhaustion.  Anyway,
122774d251dSAttilio Rao 	 * in practice this should never occur because a new node
123774d251dSAttilio Rao 	 * is not always required for insert.  Thus, the pre-allocated
124774d251dSAttilio Rao 	 * pool should have some extra pages that prevent this from
125774d251dSAttilio Rao 	 * becoming a problem.
126774d251dSAttilio Rao 	 */
127774d251dSAttilio Rao 	if (rnode == NULL)
128774d251dSAttilio Rao 		panic("%s: uma_zalloc() returned NULL for a new node",
129774d251dSAttilio Rao 		    __func__);
130774d251dSAttilio Rao 	rnode->rn_owner = owner;
131774d251dSAttilio Rao 	rnode->rn_count = count;
132774d251dSAttilio Rao 	rnode->rn_clev = clevel;
133774d251dSAttilio Rao 	return (rnode);
134774d251dSAttilio Rao }
135774d251dSAttilio Rao 
136774d251dSAttilio Rao /*
137774d251dSAttilio Rao  * Free radix node.
138774d251dSAttilio Rao  */
139774d251dSAttilio Rao static __inline void
140774d251dSAttilio Rao vm_radix_node_put(struct vm_radix_node *rnode)
141774d251dSAttilio Rao {
142774d251dSAttilio Rao 
143774d251dSAttilio Rao 	uma_zfree(vm_radix_node_zone, rnode);
144774d251dSAttilio Rao }
145774d251dSAttilio Rao 
146774d251dSAttilio Rao /*
147774d251dSAttilio Rao  * Return the position in the array for a given level.
148774d251dSAttilio Rao  */
149774d251dSAttilio Rao static __inline int
150774d251dSAttilio Rao vm_radix_slot(vm_pindex_t index, uint16_t level)
151774d251dSAttilio Rao {
152774d251dSAttilio Rao 
153774d251dSAttilio Rao 	return ((index >> ((VM_RADIX_LIMIT - level) * VM_RADIX_WIDTH)) &
154774d251dSAttilio Rao 	    VM_RADIX_MASK);
155774d251dSAttilio Rao }
156774d251dSAttilio Rao 
157774d251dSAttilio Rao /* Trims the key after the specified level. */
158774d251dSAttilio Rao static __inline vm_pindex_t
159774d251dSAttilio Rao vm_radix_trimkey(vm_pindex_t index, uint16_t level)
160774d251dSAttilio Rao {
161774d251dSAttilio Rao 	vm_pindex_t ret;
162774d251dSAttilio Rao 
163774d251dSAttilio Rao 	ret = index;
164774d251dSAttilio Rao 	if (level < VM_RADIX_LIMIT) {
165774d251dSAttilio Rao 		ret >>= (VM_RADIX_LIMIT - level) * VM_RADIX_WIDTH;
166774d251dSAttilio Rao 		ret <<= (VM_RADIX_LIMIT - level) * VM_RADIX_WIDTH;
167774d251dSAttilio Rao 	}
168774d251dSAttilio Rao 	return (ret);
169774d251dSAttilio Rao }
170774d251dSAttilio Rao 
171774d251dSAttilio Rao /*
172774d251dSAttilio Rao  * Get the root node for a radix tree.
173774d251dSAttilio Rao  */
174774d251dSAttilio Rao static __inline struct vm_radix_node *
175774d251dSAttilio Rao vm_radix_getroot(struct vm_radix *rtree)
176774d251dSAttilio Rao {
177774d251dSAttilio Rao 
1782c899fedSAlan Cox 	return ((struct vm_radix_node *)rtree->rt_root);
179774d251dSAttilio Rao }
180774d251dSAttilio Rao 
181774d251dSAttilio Rao /*
182774d251dSAttilio Rao  * Set the root node for a radix tree.
183774d251dSAttilio Rao  */
184774d251dSAttilio Rao static __inline void
185774d251dSAttilio Rao vm_radix_setroot(struct vm_radix *rtree, struct vm_radix_node *rnode)
186774d251dSAttilio Rao {
187774d251dSAttilio Rao 
188774d251dSAttilio Rao 	rtree->rt_root = (uintptr_t)rnode;
189774d251dSAttilio Rao }
190774d251dSAttilio Rao 
191774d251dSAttilio Rao /*
1923fc10b73SAlan Cox  * Returns TRUE if the specified radix node is a leaf and FALSE otherwise.
1933fc10b73SAlan Cox  */
1943fc10b73SAlan Cox static __inline boolean_t
1953fc10b73SAlan Cox vm_radix_isleaf(struct vm_radix_node *rnode)
1963fc10b73SAlan Cox {
1973fc10b73SAlan Cox 
1983fc10b73SAlan Cox 	return (((uintptr_t)rnode & VM_RADIX_ISLEAF) != 0);
1993fc10b73SAlan Cox }
2003fc10b73SAlan Cox 
2013fc10b73SAlan Cox /*
20296f1a842SAlan Cox  * Returns the associated page extracted from rnode.
203774d251dSAttilio Rao  */
204774d251dSAttilio Rao static __inline vm_page_t
20596f1a842SAlan Cox vm_radix_topage(struct vm_radix_node *rnode)
206774d251dSAttilio Rao {
207774d251dSAttilio Rao 
20896f1a842SAlan Cox 	return ((vm_page_t)((uintptr_t)rnode & ~VM_RADIX_FLAGS));
209774d251dSAttilio Rao }
210774d251dSAttilio Rao 
211774d251dSAttilio Rao /*
212774d251dSAttilio Rao  * Adds the page as a child of the provided node.
213774d251dSAttilio Rao  */
214774d251dSAttilio Rao static __inline void
215774d251dSAttilio Rao vm_radix_addpage(struct vm_radix_node *rnode, vm_pindex_t index, uint16_t clev,
216774d251dSAttilio Rao     vm_page_t page)
217774d251dSAttilio Rao {
218774d251dSAttilio Rao 	int slot;
219774d251dSAttilio Rao 
220774d251dSAttilio Rao 	slot = vm_radix_slot(index, clev);
221774d251dSAttilio Rao 	rnode->rn_child[slot] = (void *)((uintptr_t)page | VM_RADIX_ISLEAF);
222774d251dSAttilio Rao }
223774d251dSAttilio Rao 
224774d251dSAttilio Rao /*
225774d251dSAttilio Rao  * Returns the slot where two keys differ.
226774d251dSAttilio Rao  * It cannot accept 2 equal keys.
227774d251dSAttilio Rao  */
228774d251dSAttilio Rao static __inline uint16_t
229774d251dSAttilio Rao vm_radix_keydiff(vm_pindex_t index1, vm_pindex_t index2)
230774d251dSAttilio Rao {
231774d251dSAttilio Rao 	uint16_t clev;
232774d251dSAttilio Rao 
233774d251dSAttilio Rao 	KASSERT(index1 != index2, ("%s: passing the same key value %jx",
234774d251dSAttilio Rao 	    __func__, (uintmax_t)index1));
235774d251dSAttilio Rao 
236774d251dSAttilio Rao 	index1 ^= index2;
237774d251dSAttilio Rao 	for (clev = 0; clev <= VM_RADIX_LIMIT ; clev++)
238774d251dSAttilio Rao 		if (vm_radix_slot(index1, clev))
239774d251dSAttilio Rao 			return (clev);
240774d251dSAttilio Rao 	panic("%s: cannot reach this point", __func__);
241774d251dSAttilio Rao 	return (0);
242774d251dSAttilio Rao }
243774d251dSAttilio Rao 
244774d251dSAttilio Rao /*
245774d251dSAttilio Rao  * Returns TRUE if it can be determined that key does not belong to the
246774d251dSAttilio Rao  * specified rnode.  Otherwise, returns FALSE.
247774d251dSAttilio Rao  */
248774d251dSAttilio Rao static __inline boolean_t
249774d251dSAttilio Rao vm_radix_keybarr(struct vm_radix_node *rnode, vm_pindex_t idx)
250774d251dSAttilio Rao {
251774d251dSAttilio Rao 
252774d251dSAttilio Rao 	if (rnode->rn_clev > 0) {
253774d251dSAttilio Rao 		idx = vm_radix_trimkey(idx, rnode->rn_clev - 1);
254c1c82b36SAlan Cox 		return (idx != rnode->rn_owner);
255774d251dSAttilio Rao 	}
256774d251dSAttilio Rao 	return (FALSE);
257774d251dSAttilio Rao }
258774d251dSAttilio Rao 
259774d251dSAttilio Rao /*
260774d251dSAttilio Rao  * Adjusts the idx key to the first upper level available, based on a valid
261774d251dSAttilio Rao  * initial level and map of available levels.
262774d251dSAttilio Rao  * Returns a value bigger than 0 to signal that there are not valid levels
263774d251dSAttilio Rao  * available.
264774d251dSAttilio Rao  */
265774d251dSAttilio Rao static __inline int
266774d251dSAttilio Rao vm_radix_addlev(vm_pindex_t *idx, boolean_t *levels, uint16_t ilev)
267774d251dSAttilio Rao {
268774d251dSAttilio Rao 	vm_pindex_t wrapidx;
269774d251dSAttilio Rao 
270774d251dSAttilio Rao 	for (; levels[ilev] == FALSE ||
271774d251dSAttilio Rao 	    vm_radix_slot(*idx, ilev) == (VM_RADIX_COUNT - 1); ilev--)
272774d251dSAttilio Rao 		if (ilev == 0)
273774d251dSAttilio Rao 			return (1);
274774d251dSAttilio Rao 	wrapidx = *idx;
275774d251dSAttilio Rao 	*idx = vm_radix_trimkey(*idx, ilev);
276774d251dSAttilio Rao 	*idx += VM_RADIX_UNITLEVEL(ilev);
277774d251dSAttilio Rao 	return (*idx < wrapidx);
278774d251dSAttilio Rao }
279774d251dSAttilio Rao 
280774d251dSAttilio Rao /*
281774d251dSAttilio Rao  * Adjusts the idx key to the first lower level available, based on a valid
282774d251dSAttilio Rao  * initial level and map of available levels.
283774d251dSAttilio Rao  * Returns a value bigger than 0 to signal that there are not valid levels
284774d251dSAttilio Rao  * available.
285774d251dSAttilio Rao  */
286774d251dSAttilio Rao static __inline int
287774d251dSAttilio Rao vm_radix_declev(vm_pindex_t *idx, boolean_t *levels, uint16_t ilev)
288774d251dSAttilio Rao {
289774d251dSAttilio Rao 	vm_pindex_t wrapidx;
290774d251dSAttilio Rao 
291774d251dSAttilio Rao 	for (; levels[ilev] == FALSE ||
292774d251dSAttilio Rao 	    vm_radix_slot(*idx, ilev) == 0; ilev--)
293774d251dSAttilio Rao 		if (ilev == 0)
294774d251dSAttilio Rao 			return (1);
295774d251dSAttilio Rao 	wrapidx = *idx;
296774d251dSAttilio Rao 	*idx = vm_radix_trimkey(*idx, ilev);
297774d251dSAttilio Rao 	*idx |= VM_RADIX_UNITLEVEL(ilev) - 1;
298774d251dSAttilio Rao 	*idx -= VM_RADIX_UNITLEVEL(ilev);
299774d251dSAttilio Rao 	return (*idx > wrapidx);
300774d251dSAttilio Rao }
301774d251dSAttilio Rao 
302774d251dSAttilio Rao /*
303774d251dSAttilio Rao  * Internal helper for vm_radix_reclaim_allnodes().
304774d251dSAttilio Rao  * This function is recursive.
305774d251dSAttilio Rao  */
306774d251dSAttilio Rao static void
307774d251dSAttilio Rao vm_radix_reclaim_allnodes_int(struct vm_radix_node *rnode)
308774d251dSAttilio Rao {
309774d251dSAttilio Rao 	int slot;
310774d251dSAttilio Rao 
311652615dcSAlan Cox 	KASSERT(rnode->rn_count <= VM_RADIX_COUNT,
312652615dcSAlan Cox 	    ("vm_radix_reclaim_allnodes_int: bad count in rnode %p", rnode));
313652615dcSAlan Cox 	for (slot = 0; rnode->rn_count != 0; slot++) {
314774d251dSAttilio Rao 		if (rnode->rn_child[slot] == NULL)
315774d251dSAttilio Rao 			continue;
3163fc10b73SAlan Cox 		if (!vm_radix_isleaf(rnode->rn_child[slot]))
317774d251dSAttilio Rao 			vm_radix_reclaim_allnodes_int(rnode->rn_child[slot]);
318774d251dSAttilio Rao 		rnode->rn_child[slot] = NULL;
319774d251dSAttilio Rao 		rnode->rn_count--;
320774d251dSAttilio Rao 	}
321774d251dSAttilio Rao 	vm_radix_node_put(rnode);
322774d251dSAttilio Rao }
323774d251dSAttilio Rao 
324774d251dSAttilio Rao #ifdef INVARIANTS
325774d251dSAttilio Rao /*
326774d251dSAttilio Rao  * Radix node zone destructor.
327774d251dSAttilio Rao  */
328774d251dSAttilio Rao static void
329774d251dSAttilio Rao vm_radix_node_zone_dtor(void *mem, int size __unused, void *arg __unused)
330774d251dSAttilio Rao {
331774d251dSAttilio Rao 	struct vm_radix_node *rnode;
332774d251dSAttilio Rao 	int slot;
333774d251dSAttilio Rao 
334774d251dSAttilio Rao 	rnode = mem;
335774d251dSAttilio Rao 	KASSERT(rnode->rn_count == 0,
336774d251dSAttilio Rao 	    ("vm_radix_node_put: rnode %p has %d children", rnode,
337774d251dSAttilio Rao 	    rnode->rn_count));
338774d251dSAttilio Rao 	for (slot = 0; slot < VM_RADIX_COUNT; slot++)
339774d251dSAttilio Rao 		KASSERT(rnode->rn_child[slot] == NULL,
340774d251dSAttilio Rao 		    ("vm_radix_node_put: rnode %p has a child", rnode));
341774d251dSAttilio Rao }
342774d251dSAttilio Rao #endif
343774d251dSAttilio Rao 
344774d251dSAttilio Rao /*
345774d251dSAttilio Rao  * Radix node zone initializer.
346774d251dSAttilio Rao  */
347774d251dSAttilio Rao static int
348774d251dSAttilio Rao vm_radix_node_zone_init(void *mem, int size __unused, int flags __unused)
349774d251dSAttilio Rao {
350774d251dSAttilio Rao 	struct vm_radix_node *rnode;
351774d251dSAttilio Rao 
352774d251dSAttilio Rao 	rnode = mem;
353774d251dSAttilio Rao 	memset(rnode->rn_child, 0, sizeof(rnode->rn_child));
354774d251dSAttilio Rao 	return (0);
355774d251dSAttilio Rao }
356774d251dSAttilio Rao 
357774d251dSAttilio Rao /*
358774d251dSAttilio Rao  * Pre-allocate intermediate nodes from the UMA slab zone.
359774d251dSAttilio Rao  */
360774d251dSAttilio Rao static void
361774d251dSAttilio Rao vm_radix_prealloc(void *arg __unused)
362774d251dSAttilio Rao {
363*880659feSAlan Cox 	int nodes;
364774d251dSAttilio Rao 
365*880659feSAlan Cox 	/*
366*880659feSAlan Cox 	 * Calculate the number of reserved nodes, discounting the pages that
367*880659feSAlan Cox 	 * are needed to store them.
368*880659feSAlan Cox 	 */
369*880659feSAlan Cox 	nodes = ((vm_paddr_t)cnt.v_page_count * PAGE_SIZE) / (PAGE_SIZE +
370*880659feSAlan Cox 	    sizeof(struct vm_radix_node));
371*880659feSAlan Cox 	if (!uma_zone_reserve_kva(vm_radix_node_zone, nodes))
372774d251dSAttilio Rao 		panic("%s: unable to create new zone", __func__);
373*880659feSAlan Cox 	uma_prealloc(vm_radix_node_zone, nodes);
374774d251dSAttilio Rao }
375774d251dSAttilio Rao SYSINIT(vm_radix_prealloc, SI_SUB_KMEM, SI_ORDER_SECOND, vm_radix_prealloc,
376774d251dSAttilio Rao     NULL);
377774d251dSAttilio Rao 
378774d251dSAttilio Rao /*
379774d251dSAttilio Rao  * Initialize the UMA slab zone.
380774d251dSAttilio Rao  * Until vm_radix_prealloc() is called, the zone will be served by the
381774d251dSAttilio Rao  * UMA boot-time pre-allocated pool of pages.
382774d251dSAttilio Rao  */
383774d251dSAttilio Rao void
384774d251dSAttilio Rao vm_radix_init(void)
385774d251dSAttilio Rao {
386774d251dSAttilio Rao 
387774d251dSAttilio Rao 	vm_radix_node_zone = uma_zcreate("RADIX NODE",
388774d251dSAttilio Rao 	    sizeof(struct vm_radix_node), NULL,
389774d251dSAttilio Rao #ifdef INVARIANTS
390774d251dSAttilio Rao 	    vm_radix_node_zone_dtor,
391774d251dSAttilio Rao #else
392774d251dSAttilio Rao 	    NULL,
393774d251dSAttilio Rao #endif
394774d251dSAttilio Rao 	    vm_radix_node_zone_init, NULL, VM_RADIX_PAD, UMA_ZONE_VM |
395774d251dSAttilio Rao 	    UMA_ZONE_NOFREE);
396774d251dSAttilio Rao }
397774d251dSAttilio Rao 
398774d251dSAttilio Rao /*
399774d251dSAttilio Rao  * Inserts the key-value pair into the trie.
400774d251dSAttilio Rao  * Panics if the key already exists.
401774d251dSAttilio Rao  */
402774d251dSAttilio Rao void
403774d251dSAttilio Rao vm_radix_insert(struct vm_radix *rtree, vm_page_t page)
404774d251dSAttilio Rao {
405774d251dSAttilio Rao 	vm_pindex_t index, newind;
406a08f2cf6SAlan Cox 	void **parentp;
407a08f2cf6SAlan Cox 	struct vm_radix_node *rnode, *tmp;
408774d251dSAttilio Rao 	vm_page_t m;
409774d251dSAttilio Rao 	int slot;
410774d251dSAttilio Rao 	uint16_t clev;
411774d251dSAttilio Rao 
412774d251dSAttilio Rao 	index = page->pindex;
413774d251dSAttilio Rao 
414774d251dSAttilio Rao 	/*
415774d251dSAttilio Rao 	 * The owner of record for root is not really important because it
416774d251dSAttilio Rao 	 * will never be used.
417774d251dSAttilio Rao 	 */
418774d251dSAttilio Rao 	rnode = vm_radix_getroot(rtree);
419774d251dSAttilio Rao 	if (rnode == NULL) {
420a08f2cf6SAlan Cox 		rtree->rt_root = (uintptr_t)page | VM_RADIX_ISLEAF;
421774d251dSAttilio Rao 		return;
422774d251dSAttilio Rao 	}
423a08f2cf6SAlan Cox 	parentp = (void **)&rtree->rt_root;
424a08f2cf6SAlan Cox 	for (;;) {
425a08f2cf6SAlan Cox 		if (vm_radix_isleaf(rnode)) {
426a08f2cf6SAlan Cox 			m = vm_radix_topage(rnode);
427774d251dSAttilio Rao 			if (m->pindex == index)
428774d251dSAttilio Rao 				panic("%s: key %jx is already present",
429774d251dSAttilio Rao 				    __func__, (uintmax_t)index);
430774d251dSAttilio Rao 			clev = vm_radix_keydiff(m->pindex, index);
431774d251dSAttilio Rao 			tmp = vm_radix_node_get(vm_radix_trimkey(index,
432774d251dSAttilio Rao 			    clev - 1), 2, clev);
433a08f2cf6SAlan Cox 			*parentp = tmp;
434774d251dSAttilio Rao 			vm_radix_addpage(tmp, index, clev, page);
435774d251dSAttilio Rao 			vm_radix_addpage(tmp, m->pindex, clev, m);
436774d251dSAttilio Rao 			return;
437a08f2cf6SAlan Cox 		} else if (vm_radix_keybarr(rnode, index))
438a08f2cf6SAlan Cox 			break;
439a08f2cf6SAlan Cox 		slot = vm_radix_slot(index, rnode->rn_clev);
440774d251dSAttilio Rao 		if (rnode->rn_child[slot] == NULL) {
441774d251dSAttilio Rao 			rnode->rn_count++;
442774d251dSAttilio Rao 			vm_radix_addpage(rnode, index, rnode->rn_clev, page);
443774d251dSAttilio Rao 			return;
444774d251dSAttilio Rao 		}
445a08f2cf6SAlan Cox 		parentp = &rnode->rn_child[slot];
446774d251dSAttilio Rao 		rnode = rnode->rn_child[slot];
447a08f2cf6SAlan Cox 	}
448774d251dSAttilio Rao 
449774d251dSAttilio Rao 	/*
450774d251dSAttilio Rao 	 * A new node is needed because the right insertion level is reached.
451774d251dSAttilio Rao 	 * Setup the new intermediate node and add the 2 children: the
452774d251dSAttilio Rao 	 * new object and the older edge.
453774d251dSAttilio Rao 	 */
45472abda64SAlan Cox 	newind = rnode->rn_owner;
45572abda64SAlan Cox 	clev = vm_radix_keydiff(newind, index);
45672abda64SAlan Cox 	tmp = vm_radix_node_get(vm_radix_trimkey(index, clev - 1), 2,
457774d251dSAttilio Rao 	    clev);
458a08f2cf6SAlan Cox 	*parentp = tmp;
45972abda64SAlan Cox 	vm_radix_addpage(tmp, index, clev, page);
460774d251dSAttilio Rao 	slot = vm_radix_slot(newind, clev);
46172abda64SAlan Cox 	tmp->rn_child[slot] = rnode;
462774d251dSAttilio Rao }
463774d251dSAttilio Rao 
464774d251dSAttilio Rao /*
465774d251dSAttilio Rao  * Returns the value stored at the index.  If the index is not present,
466774d251dSAttilio Rao  * NULL is returned.
467774d251dSAttilio Rao  */
468774d251dSAttilio Rao vm_page_t
469774d251dSAttilio Rao vm_radix_lookup(struct vm_radix *rtree, vm_pindex_t index)
470774d251dSAttilio Rao {
471774d251dSAttilio Rao 	struct vm_radix_node *rnode;
472774d251dSAttilio Rao 	vm_page_t m;
473774d251dSAttilio Rao 	int slot;
474774d251dSAttilio Rao 
475774d251dSAttilio Rao 	rnode = vm_radix_getroot(rtree);
476774d251dSAttilio Rao 	while (rnode != NULL) {
47796f1a842SAlan Cox 		if (vm_radix_isleaf(rnode)) {
47896f1a842SAlan Cox 			m = vm_radix_topage(rnode);
479774d251dSAttilio Rao 			if (m->pindex == index)
480774d251dSAttilio Rao 				return (m);
481774d251dSAttilio Rao 			else
4826f9c0b15SAlan Cox 				break;
4836f9c0b15SAlan Cox 		} else if (vm_radix_keybarr(rnode, index))
4846f9c0b15SAlan Cox 			break;
4856f9c0b15SAlan Cox 		slot = vm_radix_slot(index, rnode->rn_clev);
4866f9c0b15SAlan Cox 		rnode = rnode->rn_child[slot];
487774d251dSAttilio Rao 	}
488774d251dSAttilio Rao 	return (NULL);
489774d251dSAttilio Rao }
490774d251dSAttilio Rao 
491774d251dSAttilio Rao /*
492774d251dSAttilio Rao  * Look up the nearest entry at a position bigger than or equal to index.
493774d251dSAttilio Rao  */
494774d251dSAttilio Rao vm_page_t
495774d251dSAttilio Rao vm_radix_lookup_ge(struct vm_radix *rtree, vm_pindex_t index)
496774d251dSAttilio Rao {
497774d251dSAttilio Rao 	vm_pindex_t inc;
498774d251dSAttilio Rao 	vm_page_t m;
49996f1a842SAlan Cox 	struct vm_radix_node *child, *rnode;
500774d251dSAttilio Rao 	int slot;
501774d251dSAttilio Rao 	uint16_t difflev;
502774d251dSAttilio Rao 	boolean_t maplevels[VM_RADIX_LIMIT + 1];
503774d251dSAttilio Rao #ifdef INVARIANTS
504774d251dSAttilio Rao 	int loops = 0;
505774d251dSAttilio Rao #endif
506774d251dSAttilio Rao 
5076f9c0b15SAlan Cox 	rnode = vm_radix_getroot(rtree);
5086f9c0b15SAlan Cox 	if (rnode == NULL)
5096f9c0b15SAlan Cox 		return (NULL);
5106f9c0b15SAlan Cox 	else if (vm_radix_isleaf(rnode)) {
5116f9c0b15SAlan Cox 		m = vm_radix_topage(rnode);
5126f9c0b15SAlan Cox 		if (m->pindex >= index)
5136f9c0b15SAlan Cox 			return (m);
5146f9c0b15SAlan Cox 		else
5156f9c0b15SAlan Cox 			return (NULL);
5166f9c0b15SAlan Cox 	}
517774d251dSAttilio Rao restart:
518774d251dSAttilio Rao 	KASSERT(++loops < 1000, ("%s: too many loops", __func__));
519774d251dSAttilio Rao 	for (difflev = 0; difflev < (VM_RADIX_LIMIT + 1); difflev++)
520774d251dSAttilio Rao 		maplevels[difflev] = FALSE;
5216f9c0b15SAlan Cox 	for (;;) {
522774d251dSAttilio Rao 		maplevels[rnode->rn_clev] = TRUE;
523774d251dSAttilio Rao 
524774d251dSAttilio Rao 		/*
525774d251dSAttilio Rao 		 * If the keys differ before the current bisection node
526774d251dSAttilio Rao 		 * the search key might rollback to the earliest
527774d251dSAttilio Rao 		 * available bisection node, or to the smaller value
528774d251dSAttilio Rao 		 * in the current domain (if the owner is bigger than the
529774d251dSAttilio Rao 		 * search key).
530774d251dSAttilio Rao 		 * The maplevels array records any node has been seen
531774d251dSAttilio Rao 		 * at a given level.  This aids the search for a valid
532774d251dSAttilio Rao 		 * bisection node.
533774d251dSAttilio Rao 		 */
534774d251dSAttilio Rao 		if (vm_radix_keybarr(rnode, index)) {
535774d251dSAttilio Rao 			difflev = vm_radix_keydiff(index, rnode->rn_owner);
536774d251dSAttilio Rao 			if (index > rnode->rn_owner) {
537774d251dSAttilio Rao 				if (vm_radix_addlev(&index, maplevels,
538774d251dSAttilio Rao 				    difflev) > 0)
539774d251dSAttilio Rao 					break;
540774d251dSAttilio Rao 			} else
541774d251dSAttilio Rao 				index = vm_radix_trimkey(rnode->rn_owner,
542774d251dSAttilio Rao 				    difflev);
5436f9c0b15SAlan Cox 			rnode = vm_radix_getroot(rtree);
544774d251dSAttilio Rao 			goto restart;
545774d251dSAttilio Rao 		}
546774d251dSAttilio Rao 		slot = vm_radix_slot(index, rnode->rn_clev);
54796f1a842SAlan Cox 		child = rnode->rn_child[slot];
54896f1a842SAlan Cox 		if (vm_radix_isleaf(child)) {
54996f1a842SAlan Cox 			m = vm_radix_topage(child);
55096f1a842SAlan Cox 			if (m->pindex >= index)
551774d251dSAttilio Rao 				return (m);
55296f1a842SAlan Cox 		} else if (child != NULL)
55396f1a842SAlan Cox 			goto descend;
554774d251dSAttilio Rao 
555774d251dSAttilio Rao 		/*
556774d251dSAttilio Rao 		 * Look for an available edge or page within the current
557774d251dSAttilio Rao 		 * bisection node.
558774d251dSAttilio Rao 		 */
559774d251dSAttilio Rao                 if (slot < (VM_RADIX_COUNT - 1)) {
560774d251dSAttilio Rao 			inc = VM_RADIX_UNITLEVEL(rnode->rn_clev);
561774d251dSAttilio Rao 			index = vm_radix_trimkey(index, rnode->rn_clev);
56296f1a842SAlan Cox 			do {
563774d251dSAttilio Rao 				index += inc;
564774d251dSAttilio Rao 				slot++;
56596f1a842SAlan Cox 				child = rnode->rn_child[slot];
56696f1a842SAlan Cox 				if (vm_radix_isleaf(child)) {
56796f1a842SAlan Cox 					m = vm_radix_topage(child);
56896f1a842SAlan Cox 					if (m->pindex >= index)
569774d251dSAttilio Rao 						return (m);
57096f1a842SAlan Cox 				} else if (child != NULL)
57196f1a842SAlan Cox 					goto descend;
57296f1a842SAlan Cox 			} while (slot < (VM_RADIX_COUNT - 1));
573774d251dSAttilio Rao 		}
57496f1a842SAlan Cox 		KASSERT(child == NULL || vm_radix_isleaf(child),
57596f1a842SAlan Cox 		    ("vm_radix_lookup_ge: child is radix node"));
576774d251dSAttilio Rao 
577774d251dSAttilio Rao 		/*
578774d251dSAttilio Rao 		 * If a valid page or edge bigger than the search slot is
579774d251dSAttilio Rao 		 * found in the traversal, skip to the next higher-level key.
580774d251dSAttilio Rao 		 */
58196f1a842SAlan Cox 		if (rnode->rn_clev == 0 || vm_radix_addlev(&index, maplevels,
58296f1a842SAlan Cox 		    rnode->rn_clev - 1) > 0)
583774d251dSAttilio Rao 			break;
5846f9c0b15SAlan Cox 		rnode = vm_radix_getroot(rtree);
585774d251dSAttilio Rao 		goto restart;
58696f1a842SAlan Cox descend:
58796f1a842SAlan Cox 		rnode = child;
588774d251dSAttilio Rao 	}
589774d251dSAttilio Rao 	return (NULL);
590774d251dSAttilio Rao }
591774d251dSAttilio Rao 
592774d251dSAttilio Rao /*
593774d251dSAttilio Rao  * Look up the nearest entry at a position less than or equal to index.
594774d251dSAttilio Rao  */
595774d251dSAttilio Rao vm_page_t
596774d251dSAttilio Rao vm_radix_lookup_le(struct vm_radix *rtree, vm_pindex_t index)
597774d251dSAttilio Rao {
598774d251dSAttilio Rao 	vm_pindex_t inc;
599774d251dSAttilio Rao 	vm_page_t m;
60096f1a842SAlan Cox 	struct vm_radix_node *child, *rnode;
601774d251dSAttilio Rao 	int slot;
602774d251dSAttilio Rao 	uint16_t difflev;
603774d251dSAttilio Rao 	boolean_t maplevels[VM_RADIX_LIMIT + 1];
604774d251dSAttilio Rao #ifdef INVARIANTS
605774d251dSAttilio Rao 	int loops = 0;
606774d251dSAttilio Rao #endif
607774d251dSAttilio Rao 
6086f9c0b15SAlan Cox 	rnode = vm_radix_getroot(rtree);
6096f9c0b15SAlan Cox 	if (rnode == NULL)
6106f9c0b15SAlan Cox 		return (NULL);
6116f9c0b15SAlan Cox 	else if (vm_radix_isleaf(rnode)) {
6126f9c0b15SAlan Cox 		m = vm_radix_topage(rnode);
6136f9c0b15SAlan Cox 		if (m->pindex <= index)
6146f9c0b15SAlan Cox 			return (m);
6156f9c0b15SAlan Cox 		else
6166f9c0b15SAlan Cox 			return (NULL);
6176f9c0b15SAlan Cox 	}
618774d251dSAttilio Rao restart:
619774d251dSAttilio Rao 	KASSERT(++loops < 1000, ("%s: too many loops", __func__));
620774d251dSAttilio Rao 	for (difflev = 0; difflev < (VM_RADIX_LIMIT + 1); difflev++)
621774d251dSAttilio Rao 		maplevels[difflev] = FALSE;
6226f9c0b15SAlan Cox 	for (;;) {
623774d251dSAttilio Rao 		maplevels[rnode->rn_clev] = TRUE;
624774d251dSAttilio Rao 
625774d251dSAttilio Rao 		/*
626774d251dSAttilio Rao 		 * If the keys differ before the current bisection node
627774d251dSAttilio Rao 		 * the search key might rollback to the earliest
628774d251dSAttilio Rao 		 * available bisection node, or to the higher value
629774d251dSAttilio Rao 		 * in the current domain (if the owner is smaller than the
630774d251dSAttilio Rao 		 * search key).
631774d251dSAttilio Rao 		 * The maplevels array records any node has been seen
632774d251dSAttilio Rao 		 * at a given level.  This aids the search for a valid
633774d251dSAttilio Rao 		 * bisection node.
634774d251dSAttilio Rao 		 */
635774d251dSAttilio Rao 		if (vm_radix_keybarr(rnode, index)) {
636774d251dSAttilio Rao 			difflev = vm_radix_keydiff(index, rnode->rn_owner);
637774d251dSAttilio Rao 			if (index > rnode->rn_owner) {
638774d251dSAttilio Rao 				index = vm_radix_trimkey(rnode->rn_owner,
639774d251dSAttilio Rao 				    difflev);
640774d251dSAttilio Rao 				index |= VM_RADIX_UNITLEVEL(difflev) - 1;
641774d251dSAttilio Rao 			} else if (vm_radix_declev(&index, maplevels,
642774d251dSAttilio Rao 			    difflev) > 0)
643774d251dSAttilio Rao 				break;
6446f9c0b15SAlan Cox 			rnode = vm_radix_getroot(rtree);
645774d251dSAttilio Rao 			goto restart;
646774d251dSAttilio Rao 		}
647774d251dSAttilio Rao 		slot = vm_radix_slot(index, rnode->rn_clev);
64896f1a842SAlan Cox 		child = rnode->rn_child[slot];
64996f1a842SAlan Cox 		if (vm_radix_isleaf(child)) {
65096f1a842SAlan Cox 			m = vm_radix_topage(child);
65196f1a842SAlan Cox 			if (m->pindex <= index)
652774d251dSAttilio Rao 				return (m);
65396f1a842SAlan Cox 		} else if (child != NULL)
65496f1a842SAlan Cox 			goto descend;
655774d251dSAttilio Rao 
656774d251dSAttilio Rao 		/*
657774d251dSAttilio Rao 		 * Look for an available edge or page within the current
658774d251dSAttilio Rao 		 * bisection node.
659774d251dSAttilio Rao 		 */
660774d251dSAttilio Rao 		if (slot > 0) {
661774d251dSAttilio Rao 			inc = VM_RADIX_UNITLEVEL(rnode->rn_clev);
662774d251dSAttilio Rao 			index = vm_radix_trimkey(index, rnode->rn_clev);
663774d251dSAttilio Rao 			index |= inc - 1;
66496f1a842SAlan Cox 			do {
665774d251dSAttilio Rao 				index -= inc;
666774d251dSAttilio Rao 				slot--;
66796f1a842SAlan Cox 				child = rnode->rn_child[slot];
66896f1a842SAlan Cox 				if (vm_radix_isleaf(child)) {
66996f1a842SAlan Cox 					m = vm_radix_topage(child);
67096f1a842SAlan Cox 					if (m->pindex <= index)
671774d251dSAttilio Rao 						return (m);
67296f1a842SAlan Cox 				} else if (child != NULL)
67396f1a842SAlan Cox 					goto descend;
67496f1a842SAlan Cox 			} while (slot > 0);
675774d251dSAttilio Rao 		}
67696f1a842SAlan Cox 		KASSERT(child == NULL || vm_radix_isleaf(child),
67796f1a842SAlan Cox 		    ("vm_radix_lookup_le: child is radix node"));
678774d251dSAttilio Rao 
679774d251dSAttilio Rao 		/*
680774d251dSAttilio Rao 		 * If a valid page or edge smaller than the search slot is
681774d251dSAttilio Rao 		 * found in the traversal, skip to the next higher-level key.
682774d251dSAttilio Rao 		 */
68396f1a842SAlan Cox 		if (rnode->rn_clev == 0 || vm_radix_declev(&index, maplevels,
68496f1a842SAlan Cox 		    rnode->rn_clev - 1) > 0)
685774d251dSAttilio Rao 			break;
6866f9c0b15SAlan Cox 		rnode = vm_radix_getroot(rtree);
687774d251dSAttilio Rao 		goto restart;
68896f1a842SAlan Cox descend:
68996f1a842SAlan Cox 		rnode = child;
690774d251dSAttilio Rao 	}
691774d251dSAttilio Rao 	return (NULL);
692774d251dSAttilio Rao }
693774d251dSAttilio Rao 
694774d251dSAttilio Rao /*
695774d251dSAttilio Rao  * Remove the specified index from the tree.
696774d251dSAttilio Rao  * Panics if the key is not present.
697774d251dSAttilio Rao  */
698774d251dSAttilio Rao void
699774d251dSAttilio Rao vm_radix_remove(struct vm_radix *rtree, vm_pindex_t index)
700774d251dSAttilio Rao {
701774d251dSAttilio Rao 	struct vm_radix_node *rnode, *parent;
702774d251dSAttilio Rao 	vm_page_t m;
703774d251dSAttilio Rao 	int i, slot;
704774d251dSAttilio Rao 
705774d251dSAttilio Rao 	rnode = vm_radix_getroot(rtree);
706a08f2cf6SAlan Cox 	if (vm_radix_isleaf(rnode)) {
707a08f2cf6SAlan Cox 		m = vm_radix_topage(rnode);
708a08f2cf6SAlan Cox 		if (m->pindex != index)
709a08f2cf6SAlan Cox 			panic("%s: invalid key found", __func__);
710a08f2cf6SAlan Cox 		vm_radix_setroot(rtree, NULL);
711a08f2cf6SAlan Cox 		return;
712a08f2cf6SAlan Cox 	}
713a08f2cf6SAlan Cox 	parent = NULL;
714774d251dSAttilio Rao 	for (;;) {
715774d251dSAttilio Rao 		if (rnode == NULL)
716774d251dSAttilio Rao 			panic("vm_radix_remove: impossible to locate the key");
717774d251dSAttilio Rao 		slot = vm_radix_slot(index, rnode->rn_clev);
71896f1a842SAlan Cox 		if (vm_radix_isleaf(rnode->rn_child[slot])) {
71996f1a842SAlan Cox 			m = vm_radix_topage(rnode->rn_child[slot]);
72096f1a842SAlan Cox 			if (m->pindex != index)
72196f1a842SAlan Cox 				panic("%s: invalid key found", __func__);
722774d251dSAttilio Rao 			rnode->rn_child[slot] = NULL;
723774d251dSAttilio Rao 			rnode->rn_count--;
724774d251dSAttilio Rao 			if (rnode->rn_count > 1)
725774d251dSAttilio Rao 				break;
726774d251dSAttilio Rao 			for (i = 0; i < VM_RADIX_COUNT; i++)
727774d251dSAttilio Rao 				if (rnode->rn_child[i] != NULL)
728774d251dSAttilio Rao 					break;
729774d251dSAttilio Rao 			KASSERT(i != VM_RADIX_COUNT,
730774d251dSAttilio Rao 			    ("%s: invalid node configuration", __func__));
731a08f2cf6SAlan Cox 			if (parent == NULL)
732a08f2cf6SAlan Cox 				vm_radix_setroot(rtree, rnode->rn_child[i]);
733a08f2cf6SAlan Cox 			else {
734774d251dSAttilio Rao 				slot = vm_radix_slot(index, parent->rn_clev);
735774d251dSAttilio Rao 				KASSERT(parent->rn_child[slot] == rnode,
736774d251dSAttilio Rao 				    ("%s: invalid child value", __func__));
737774d251dSAttilio Rao 				parent->rn_child[slot] = rnode->rn_child[i];
738a08f2cf6SAlan Cox 			}
739774d251dSAttilio Rao 			rnode->rn_count--;
740774d251dSAttilio Rao 			rnode->rn_child[i] = NULL;
741774d251dSAttilio Rao 			vm_radix_node_put(rnode);
742774d251dSAttilio Rao 			break;
743774d251dSAttilio Rao 		}
744774d251dSAttilio Rao 		parent = rnode;
745774d251dSAttilio Rao 		rnode = rnode->rn_child[slot];
746774d251dSAttilio Rao 	}
747774d251dSAttilio Rao }
748774d251dSAttilio Rao 
749774d251dSAttilio Rao /*
750774d251dSAttilio Rao  * Remove and free all the nodes from the radix tree.
751774d251dSAttilio Rao  * This function is recursive but there is a tight control on it as the
752774d251dSAttilio Rao  * maximum depth of the tree is fixed.
753774d251dSAttilio Rao  */
754774d251dSAttilio Rao void
755774d251dSAttilio Rao vm_radix_reclaim_allnodes(struct vm_radix *rtree)
756774d251dSAttilio Rao {
757774d251dSAttilio Rao 	struct vm_radix_node *root;
758774d251dSAttilio Rao 
759774d251dSAttilio Rao 	root = vm_radix_getroot(rtree);
760774d251dSAttilio Rao 	if (root == NULL)
761774d251dSAttilio Rao 		return;
762774d251dSAttilio Rao 	vm_radix_setroot(rtree, NULL);
763a08f2cf6SAlan Cox 	if (!vm_radix_isleaf(root))
764652615dcSAlan Cox 		vm_radix_reclaim_allnodes_int(root);
765774d251dSAttilio Rao }
766774d251dSAttilio Rao 
767774d251dSAttilio Rao #ifdef DDB
768774d251dSAttilio Rao /*
769774d251dSAttilio Rao  * Show details about the given radix node.
770774d251dSAttilio Rao  */
771774d251dSAttilio Rao DB_SHOW_COMMAND(radixnode, db_show_radixnode)
772774d251dSAttilio Rao {
773774d251dSAttilio Rao 	struct vm_radix_node *rnode;
774774d251dSAttilio Rao 	int i;
775774d251dSAttilio Rao 
776774d251dSAttilio Rao         if (!have_addr)
777774d251dSAttilio Rao                 return;
778774d251dSAttilio Rao 	rnode = (struct vm_radix_node *)addr;
779774d251dSAttilio Rao 	db_printf("radixnode %p, owner %jx, children count %u, level %u:\n",
780774d251dSAttilio Rao 	    (void *)rnode, (uintmax_t)rnode->rn_owner, rnode->rn_count,
781774d251dSAttilio Rao 	    rnode->rn_clev);
782774d251dSAttilio Rao 	for (i = 0; i < VM_RADIX_COUNT; i++)
783774d251dSAttilio Rao 		if (rnode->rn_child[i] != NULL)
784774d251dSAttilio Rao 			db_printf("slot: %d, val: %p, page: %p, clev: %d\n",
785774d251dSAttilio Rao 			    i, (void *)rnode->rn_child[i],
78696f1a842SAlan Cox 			    vm_radix_isleaf(rnode->rn_child[i]) ?
78796f1a842SAlan Cox 			    vm_radix_topage(rnode->rn_child[i]) : NULL,
788774d251dSAttilio Rao 			    rnode->rn_clev);
789774d251dSAttilio Rao }
790774d251dSAttilio Rao #endif /* DDB */
791