xref: /netbsd-src/common/lib/libc/gen/radixtree.c (revision 779666e6a046a896eec03d9f90a6ee45b68c472f)
1*779666e6Schs /*	$NetBSD: radixtree.c,v 1.34 2024/05/04 17:58:24 chs Exp $	*/
262e2ded6Syamt 
362e2ded6Syamt /*-
40558f521Sad  * Copyright (c)2011,2012,2013 YAMAMOTO Takashi,
562e2ded6Syamt  * All rights reserved.
662e2ded6Syamt  *
762e2ded6Syamt  * Redistribution and use in source and binary forms, with or without
862e2ded6Syamt  * modification, are permitted provided that the following conditions
962e2ded6Syamt  * are met:
1062e2ded6Syamt  * 1. Redistributions of source code must retain the above copyright
1162e2ded6Syamt  *    notice, this list of conditions and the following disclaimer.
1262e2ded6Syamt  * 2. Redistributions in binary form must reproduce the above copyright
1362e2ded6Syamt  *    notice, this list of conditions and the following disclaimer in the
1462e2ded6Syamt  *    documentation and/or other materials provided with the distribution.
1562e2ded6Syamt  *
1662e2ded6Syamt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1762e2ded6Syamt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1862e2ded6Syamt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1962e2ded6Syamt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2062e2ded6Syamt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2162e2ded6Syamt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2262e2ded6Syamt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2362e2ded6Syamt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2462e2ded6Syamt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2562e2ded6Syamt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2662e2ded6Syamt  * SUCH DAMAGE.
2762e2ded6Syamt  */
2862e2ded6Syamt 
2962e2ded6Syamt /*
30d837abefSyamt  * radixtree.c
3162e2ded6Syamt  *
320558f521Sad  * Overview:
330558f521Sad  *
340558f521Sad  * This is an implementation of radix tree, whose keys are uint64_t and leafs
35d837abefSyamt  * are user provided pointers.
36d837abefSyamt  *
370558f521Sad  * Leaf nodes are just void * and this implementation doesn't care about
380558f521Sad  * what they actually point to.  However, this implementation has an assumption
390558f521Sad  * about their alignment.  Specifically, this implementation assumes that their
400558f521Sad  * 2 LSBs are always zero and uses them for internal accounting.
41d837abefSyamt  *
420558f521Sad  * Intermediate nodes and memory allocation:
43d837abefSyamt  *
440558f521Sad  * Intermediate nodes are automatically allocated and freed internally and
450558f521Sad  * basically users don't need to care about them.  The allocation is done via
4659e0001fSad  * kmem_zalloc(9) for _KERNEL, malloc(3) for userland, and alloc() for
470558f521Sad  * _STANDALONE environment.  Only radix_tree_insert_node function can allocate
480558f521Sad  * memory for intermediate nodes and thus can fail for ENOMEM.
49d837abefSyamt  *
500558f521Sad  * Memory Efficiency:
510558f521Sad  *
520558f521Sad  * It's designed to work efficiently with dense index distribution.
530558f521Sad  * The memory consumption (number of necessary intermediate nodes) heavily
540558f521Sad  * depends on the index distribution.  Basically, more dense index distribution
550558f521Sad  * consumes less nodes per item.  Approximately,
560558f521Sad  *
570558f521Sad  *  - the best case: about RADIX_TREE_PTR_PER_NODE items per intermediate node.
580558f521Sad  *    it would look like the following.
590558f521Sad  *
600558f521Sad  *     root (t_height=1)
610558f521Sad  *      |
620558f521Sad  *      v
630558f521Sad  *      [ | | | ]   (intermediate node.  RADIX_TREE_PTR_PER_NODE=4 in this fig)
640558f521Sad  *       | | | |
650558f521Sad  *       v v v v
660558f521Sad  *       p p p p    (items)
670558f521Sad  *
680558f521Sad  *  - the worst case: RADIX_TREE_MAX_HEIGHT intermediate nodes per item.
690558f521Sad  *    it would look like the following if RADIX_TREE_MAX_HEIGHT=3.
700558f521Sad  *
710558f521Sad  *     root (t_height=3)
720558f521Sad  *      |
730558f521Sad  *      v
740558f521Sad  *      [ | | | ]
750558f521Sad  *           |
760558f521Sad  *           v
770558f521Sad  *           [ | | | ]
780558f521Sad  *                |
790558f521Sad  *                v
800558f521Sad  *                [ | | | ]
810558f521Sad  *                   |
820558f521Sad  *                   v
830558f521Sad  *                   p
840558f521Sad  *
850558f521Sad  * The height of tree (t_height) is dynamic.  It's smaller if only small
860558f521Sad  * index values are used.  As an extreme case, if only index 0 is used,
870558f521Sad  * the corresponding value is directly stored in the root of the tree
880558f521Sad  * (struct radix_tree) without allocating any intermediate nodes.  In that
890558f521Sad  * case, t_height=0.
900558f521Sad  *
910558f521Sad  * Gang lookup:
920558f521Sad  *
930558f521Sad  * This implementation provides a way to scan many nodes quickly via
94d837abefSyamt  * radix_tree_gang_lookup_node function and its varients.
95d837abefSyamt  *
960558f521Sad  * Tags:
970558f521Sad  *
980558f521Sad  * This implementation provides tagging functionality, which allows quick
990558f521Sad  * scanning of a subset of leaf nodes.  Leaf nodes are untagged when inserted
1000558f521Sad  * into the tree and can be tagged by radix_tree_set_tag function.
1010558f521Sad  * radix_tree_gang_lookup_tagged_node function and its variants returns only
1020558f521Sad  * leaf nodes with the given tag.  To reduce amount of nodes to visit for
103d837abefSyamt  * these functions, this implementation keeps tagging information in internal
104d837abefSyamt  * intermediate nodes and quickly skips uninterested parts of a tree.
1050558f521Sad  *
1060558f521Sad  * A tree has RADIX_TREE_TAG_ID_MAX independent tag spaces, each of which are
107c9b3c34dSandvar  * identified by a zero-origin numbers, tagid.  For the current implementation,
1080558f521Sad  * RADIX_TREE_TAG_ID_MAX is 2.  A set of tags is described as a bitmask tagmask,
1090558f521Sad  * which is a bitwise OR of (1 << tagid).
11062e2ded6Syamt  */
11162e2ded6Syamt 
11262e2ded6Syamt #include <sys/cdefs.h>
11362e2ded6Syamt 
114714ba23eSyamt #if defined(_KERNEL) || defined(_STANDALONE)
115*779666e6Schs __KERNEL_RCSID(0, "$NetBSD: radixtree.c,v 1.34 2024/05/04 17:58:24 chs Exp $");
11662e2ded6Syamt #include <sys/param.h>
11725dcdd54Syamt #include <sys/errno.h>
11859e0001fSad #include <sys/kmem.h>
11962e2ded6Syamt #include <sys/radixtree.h>
12025dcdd54Syamt #include <lib/libkern/libkern.h>
12125dcdd54Syamt #if defined(_STANDALONE)
12225dcdd54Syamt #include <lib/libsa/stand.h>
12325dcdd54Syamt #endif /* defined(_STANDALONE) */
124714ba23eSyamt #else /* defined(_KERNEL) || defined(_STANDALONE) */
125*779666e6Schs __RCSID("$NetBSD: radixtree.c,v 1.34 2024/05/04 17:58:24 chs Exp $");
12662e2ded6Syamt #include <assert.h>
12762e2ded6Syamt #include <errno.h>
12862e2ded6Syamt #include <stdbool.h>
12962e2ded6Syamt #include <stdlib.h>
130765c8495Syamt #include <string.h>
13162e2ded6Syamt #if 1
13262e2ded6Syamt #define KASSERT assert
13362e2ded6Syamt #else
13462e2ded6Syamt #define KASSERT(a)	/* nothing */
13562e2ded6Syamt #endif
136714ba23eSyamt #endif /* defined(_KERNEL) || defined(_STANDALONE) */
13762e2ded6Syamt 
13862e2ded6Syamt #include <sys/radixtree.h>
13962e2ded6Syamt 
14062e2ded6Syamt #define	RADIX_TREE_BITS_PER_HEIGHT	4	/* XXX tune */
14162e2ded6Syamt #define	RADIX_TREE_PTR_PER_NODE		(1 << RADIX_TREE_BITS_PER_HEIGHT)
14262e2ded6Syamt #define	RADIX_TREE_MAX_HEIGHT		(64 / RADIX_TREE_BITS_PER_HEIGHT)
143759124c5Syamt #define	RADIX_TREE_INVALID_HEIGHT	(RADIX_TREE_MAX_HEIGHT + 1)
144714ba23eSyamt __CTASSERT((64 % RADIX_TREE_BITS_PER_HEIGHT) == 0);
14562e2ded6Syamt 
146714ba23eSyamt __CTASSERT(((1 << RADIX_TREE_TAG_ID_MAX) & (sizeof(int) - 1)) == 0);
14762e2ded6Syamt #define	RADIX_TREE_TAG_MASK	((1 << RADIX_TREE_TAG_ID_MAX) - 1)
14862e2ded6Syamt 
14962e2ded6Syamt static inline void *
entry_ptr(void * p)15062e2ded6Syamt entry_ptr(void *p)
15162e2ded6Syamt {
15262e2ded6Syamt 
15362e2ded6Syamt 	return (void *)((uintptr_t)p & ~RADIX_TREE_TAG_MASK);
15462e2ded6Syamt }
15562e2ded6Syamt 
15662e2ded6Syamt static inline unsigned int
entry_tagmask(void * p)15762e2ded6Syamt entry_tagmask(void *p)
15862e2ded6Syamt {
15962e2ded6Syamt 
16062e2ded6Syamt 	return (uintptr_t)p & RADIX_TREE_TAG_MASK;
16162e2ded6Syamt }
16262e2ded6Syamt 
16362e2ded6Syamt static inline void *
entry_compose(void * p,unsigned int tagmask)16462e2ded6Syamt entry_compose(void *p, unsigned int tagmask)
16562e2ded6Syamt {
16662e2ded6Syamt 
16762e2ded6Syamt 	return (void *)((uintptr_t)p | tagmask);
16862e2ded6Syamt }
16962e2ded6Syamt 
17062e2ded6Syamt static inline bool
entry_match_p(void * p,unsigned int tagmask)17162e2ded6Syamt entry_match_p(void *p, unsigned int tagmask)
17262e2ded6Syamt {
17362e2ded6Syamt 
17462e2ded6Syamt 	KASSERT(entry_ptr(p) != NULL || entry_tagmask(p) == 0);
17562e2ded6Syamt 	if (p == NULL) {
17662e2ded6Syamt 		return false;
17762e2ded6Syamt 	}
17862e2ded6Syamt 	if (tagmask == 0) {
17962e2ded6Syamt 		return true;
18062e2ded6Syamt 	}
18162e2ded6Syamt 	return (entry_tagmask(p) & tagmask) != 0;
18262e2ded6Syamt }
18362e2ded6Syamt 
18462e2ded6Syamt /*
18562e2ded6Syamt  * radix_tree_node: an intermediate node
18662e2ded6Syamt  *
18762e2ded6Syamt  * we don't care the type of leaf nodes.  they are just void *.
1889afd1ce3Sad  *
1899afd1ce3Sad  * we used to maintain a count of non-NULL nodes in this structure, but it
1909afd1ce3Sad  * prevented it from being aligned to a cache line boundary; the performance
1919afd1ce3Sad  * benefit from being cache friendly is greater than the benefit of having
1929afd1ce3Sad  * a dedicated count value, especially in multi-processor situations where
1939afd1ce3Sad  * we need to avoid intra-pool-page false sharing.
19462e2ded6Syamt  */
19562e2ded6Syamt 
19662e2ded6Syamt struct radix_tree_node {
19762e2ded6Syamt 	void *n_ptrs[RADIX_TREE_PTR_PER_NODE];
19862e2ded6Syamt };
19962e2ded6Syamt 
200d9389408Syamt /*
20162e2ded6Syamt  * p_refs[0].pptr == &t->t_root
20262e2ded6Syamt  *	:
20362e2ded6Syamt  * p_refs[n].pptr == &(*p_refs[n-1])->n_ptrs[x]
20462e2ded6Syamt  *	:
20562e2ded6Syamt  *	:
20662e2ded6Syamt  * p_refs[t->t_height].pptr == &leaf_pointer
20762e2ded6Syamt  */
20862e2ded6Syamt 
20962e2ded6Syamt struct radix_tree_path {
21062e2ded6Syamt 	struct radix_tree_node_ref {
21162e2ded6Syamt 		void **pptr;
21262e2ded6Syamt 	} p_refs[RADIX_TREE_MAX_HEIGHT + 1]; /* +1 for the root ptr */
213759124c5Syamt 	/*
214759124c5Syamt 	 * p_lastidx is either the index of the last valid element of p_refs[]
215759124c5Syamt 	 * or RADIX_TREE_INVALID_HEIGHT.
216759124c5Syamt 	 * RADIX_TREE_INVALID_HEIGHT means that radix_tree_lookup_ptr found
217759124c5Syamt 	 * that the height of the tree is not enough to cover the given index.
218759124c5Syamt 	 */
2191414ffd1Syamt 	unsigned int p_lastidx;
22062e2ded6Syamt };
22162e2ded6Syamt 
22262e2ded6Syamt static inline void **
path_pptr(const struct radix_tree * t,const struct radix_tree_path * p,unsigned int height)223ccdce53cSyamt path_pptr(const struct radix_tree *t, const struct radix_tree_path *p,
22462e2ded6Syamt     unsigned int height)
22562e2ded6Syamt {
22662e2ded6Syamt 
22762e2ded6Syamt 	KASSERT(height <= t->t_height);
22862e2ded6Syamt 	return p->p_refs[height].pptr;
22962e2ded6Syamt }
23062e2ded6Syamt 
23162e2ded6Syamt static inline struct radix_tree_node *
path_node(const struct radix_tree * t,const struct radix_tree_path * p,unsigned int height)232ccdce53cSyamt path_node(const struct radix_tree * t, const struct radix_tree_path *p,
233ccdce53cSyamt     unsigned int height)
23462e2ded6Syamt {
23562e2ded6Syamt 
23662e2ded6Syamt 	KASSERT(height <= t->t_height);
23762e2ded6Syamt 	return entry_ptr(*path_pptr(t, p, height));
23862e2ded6Syamt }
23962e2ded6Syamt 
24062e2ded6Syamt /*
24162e2ded6Syamt  * radix_tree_init_tree:
24262e2ded6Syamt  *
2430558f521Sad  * Initialize a tree.
24462e2ded6Syamt  */
24562e2ded6Syamt 
24662e2ded6Syamt void
radix_tree_init_tree(struct radix_tree * t)24762e2ded6Syamt radix_tree_init_tree(struct radix_tree *t)
24862e2ded6Syamt {
24962e2ded6Syamt 
25062e2ded6Syamt 	t->t_height = 0;
25162e2ded6Syamt 	t->t_root = NULL;
25262e2ded6Syamt }
25362e2ded6Syamt 
25462e2ded6Syamt /*
2550558f521Sad  * radix_tree_fini_tree:
25662e2ded6Syamt  *
2570558f521Sad  * Finish using a tree.
25862e2ded6Syamt  */
25962e2ded6Syamt 
26062e2ded6Syamt void
radix_tree_fini_tree(struct radix_tree * t)26162e2ded6Syamt radix_tree_fini_tree(struct radix_tree *t)
26262e2ded6Syamt {
26362e2ded6Syamt 
26462e2ded6Syamt 	KASSERT(t->t_root == NULL);
26562e2ded6Syamt 	KASSERT(t->t_height == 0);
26662e2ded6Syamt }
26762e2ded6Syamt 
2680558f521Sad /*
2690558f521Sad  * radix_tree_empty_tree_p:
2700558f521Sad  *
2710558f521Sad  * Return if the tree is empty.
2720558f521Sad  */
2730558f521Sad 
274c6656027Syamt bool
radix_tree_empty_tree_p(struct radix_tree * t)275c6656027Syamt radix_tree_empty_tree_p(struct radix_tree *t)
276c6656027Syamt {
277c6656027Syamt 
278c6656027Syamt 	return t->t_root == NULL;
279c6656027Syamt }
280c6656027Syamt 
2810558f521Sad /*
2820558f521Sad  * radix_tree_empty_tree_p:
2830558f521Sad  *
2840558f521Sad  * Return true if the tree has any nodes with the given tag.  Otherwise
2850558f521Sad  * return false.
2860558f521Sad  *
2870558f521Sad  * It's illegal to call this function with tagmask 0.
2880558f521Sad  */
289d1036328Syamt 
2900558f521Sad bool
radix_tree_empty_tagged_tree_p(struct radix_tree * t,unsigned int tagmask)2910558f521Sad radix_tree_empty_tagged_tree_p(struct radix_tree *t, unsigned int tagmask)
2920558f521Sad {
2930558f521Sad 
2940558f521Sad 	KASSERT(tagmask != 0);
295d1036328Syamt 	return (entry_tagmask(t->t_root) & tagmask) == 0;
296d1036328Syamt }
297d1036328Syamt 
29825dcdd54Syamt static void
radix_tree_node_init(struct radix_tree_node * n)29925dcdd54Syamt radix_tree_node_init(struct radix_tree_node *n)
30025dcdd54Syamt {
30125dcdd54Syamt 
30225dcdd54Syamt 	memset(n, 0, sizeof(*n));
30325dcdd54Syamt }
30425dcdd54Syamt 
30562e2ded6Syamt #if defined(_KERNEL)
30662e2ded6Syamt /*
30762e2ded6Syamt  * radix_tree_init:
30862e2ded6Syamt  *
30962e2ded6Syamt  * initialize the subsystem.
31062e2ded6Syamt  */
31162e2ded6Syamt 
31262e2ded6Syamt void
radix_tree_init(void)31362e2ded6Syamt radix_tree_init(void)
31462e2ded6Syamt {
31562e2ded6Syamt 
31659e0001fSad 	/* nothing right now */
31762e2ded6Syamt }
318e4d889e5Sad 
319e4d889e5Sad /*
320e4d889e5Sad  * radix_tree_await_memory:
321e4d889e5Sad  *
322e4d889e5Sad  * after an insert has failed with ENOMEM, wait for memory to become
323f1ed81b8Sad  * available, so the caller can retry.  this needs to ensure that the
324f1ed81b8Sad  * maximum possible required number of nodes is available.
325e4d889e5Sad  */
326e4d889e5Sad 
327e4d889e5Sad void
radix_tree_await_memory(void)328e4d889e5Sad radix_tree_await_memory(void)
329e4d889e5Sad {
330f1ed81b8Sad 	struct radix_tree_node *nodes[RADIX_TREE_MAX_HEIGHT];
331f1ed81b8Sad 	int i;
332e4d889e5Sad 
333f1ed81b8Sad 	for (i = 0; i < __arraycount(nodes); i++) {
33459e0001fSad 		nodes[i] = kmem_intr_alloc(sizeof(struct radix_tree_node),
33559e0001fSad 		    KM_SLEEP);
336f1ed81b8Sad 	}
337f1ed81b8Sad 	while (--i >= 0) {
338996c09cdSad 		kmem_intr_free(nodes[i], sizeof(struct radix_tree_node));
339f1ed81b8Sad 	}
340e4d889e5Sad }
341e4d889e5Sad 
34262e2ded6Syamt #endif /* defined(_KERNEL) */
34362e2ded6Syamt 
3445e0de1ecSad /*
3455ff779feSad  * radix_tree_sum_node:
3465e0de1ecSad  *
3475e0de1ecSad  * return the logical sum of all entries in the given node.  used to quickly
3485e0de1ecSad  * check for tag masks or empty nodes.
3495e0de1ecSad  */
3505e0de1ecSad 
3515e0de1ecSad static uintptr_t
radix_tree_sum_node(const struct radix_tree_node * n)3525ff779feSad radix_tree_sum_node(const struct radix_tree_node *n)
35362e2ded6Syamt {
3549afd1ce3Sad #if RADIX_TREE_PTR_PER_NODE > 16
355f1ed81b8Sad 	unsigned int i;
3565e0de1ecSad 	uintptr_t sum;
35762e2ded6Syamt 
3585e0de1ecSad 	for (i = 0, sum = 0; i < RADIX_TREE_PTR_PER_NODE; i++) {
3595e0de1ecSad 		sum |= (uintptr_t)n->n_ptrs[i];
36062e2ded6Syamt 	}
3615e0de1ecSad 	return sum;
3629afd1ce3Sad #else /* RADIX_TREE_PTR_PER_NODE > 16 */
3639afd1ce3Sad 	uintptr_t sum;
3649afd1ce3Sad 
3659afd1ce3Sad 	/*
3669afd1ce3Sad 	 * Unrolling the above is much better than a tight loop with two
3679afd1ce3Sad 	 * test+branch pairs.  On x86 with gcc 5.5.0 this compiles into 19
3689afd1ce3Sad 	 * deterministic instructions including the "return" and prologue &
3699afd1ce3Sad 	 * epilogue.
3709afd1ce3Sad 	 */
3719afd1ce3Sad 	sum = (uintptr_t)n->n_ptrs[0];
3729afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[1];
3739afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[2];
3749afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[3];
3759afd1ce3Sad #if RADIX_TREE_PTR_PER_NODE > 4
3769afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[4];
3779afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[5];
3789afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[6];
3799afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[7];
3809afd1ce3Sad #endif
3819afd1ce3Sad #if RADIX_TREE_PTR_PER_NODE > 8
3829afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[8];
3839afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[9];
3849afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[10];
3859afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[11];
3869afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[12];
3879afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[13];
3889afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[14];
3899afd1ce3Sad 	sum |= (uintptr_t)n->n_ptrs[15];
3909afd1ce3Sad #endif
3915e0de1ecSad 	return sum;
3929afd1ce3Sad #endif /* RADIX_TREE_PTR_PER_NODE > 16 */
3939afd1ce3Sad }
3949afd1ce3Sad 
3959afd1ce3Sad static int __unused
radix_tree_node_count_ptrs(const struct radix_tree_node * n)3969afd1ce3Sad radix_tree_node_count_ptrs(const struct radix_tree_node *n)
3979afd1ce3Sad {
3989afd1ce3Sad 	unsigned int i, c;
3999afd1ce3Sad 
4009afd1ce3Sad 	for (i = c = 0; i < RADIX_TREE_PTR_PER_NODE; i++) {
4019afd1ce3Sad 		c += (n->n_ptrs[i] != NULL);
4029afd1ce3Sad 	}
4039afd1ce3Sad 	return c;
40462e2ded6Syamt }
40562e2ded6Syamt 
40662e2ded6Syamt static struct radix_tree_node *
radix_tree_alloc_node(void)40762e2ded6Syamt radix_tree_alloc_node(void)
40862e2ded6Syamt {
40962e2ded6Syamt 	struct radix_tree_node *n;
41062e2ded6Syamt 
41162e2ded6Syamt #if defined(_KERNEL)
4120558f521Sad 	/*
413*779666e6Schs 	 * We must not block waiting for memory because this function
414*779666e6Schs 	 * can be called in contexts where waiting for memory is illegal.
4150558f521Sad 	 */
416*779666e6Schs 	n = kmem_intr_alloc(sizeof(struct radix_tree_node), KM_NOSLEEP);
41759e0001fSad #elif defined(_STANDALONE)
41825dcdd54Syamt 	n = alloc(sizeof(*n));
41925dcdd54Syamt #else /* defined(_STANDALONE) */
42025dcdd54Syamt 	n = malloc(sizeof(*n));
42125dcdd54Syamt #endif /* defined(_STANDALONE) */
42225dcdd54Syamt 	if (n != NULL) {
42325dcdd54Syamt 		radix_tree_node_init(n);
42425dcdd54Syamt 	}
4255ff779feSad 	KASSERT(n == NULL || radix_tree_sum_node(n) == 0);
42662e2ded6Syamt 	return n;
42762e2ded6Syamt }
42862e2ded6Syamt 
42962e2ded6Syamt static void
radix_tree_free_node(struct radix_tree_node * n)43062e2ded6Syamt radix_tree_free_node(struct radix_tree_node *n)
43162e2ded6Syamt {
43262e2ded6Syamt 
4335ff779feSad 	KASSERT(radix_tree_sum_node(n) == 0);
43462e2ded6Syamt #if defined(_KERNEL)
43559e0001fSad 	kmem_intr_free(n, sizeof(struct radix_tree_node));
43625dcdd54Syamt #elif defined(_STANDALONE)
43725dcdd54Syamt 	dealloc(n, sizeof(*n));
43825dcdd54Syamt #else
43962e2ded6Syamt 	free(n);
44025dcdd54Syamt #endif
44162e2ded6Syamt }
44262e2ded6Syamt 
443f1ed81b8Sad /*
444f1ed81b8Sad  * radix_tree_grow:
445f1ed81b8Sad  *
446f1ed81b8Sad  * increase the height of the tree.
447f1ed81b8Sad  */
448f1ed81b8Sad 
449f1ed81b8Sad static __noinline int
radix_tree_grow(struct radix_tree * t,unsigned int newheight)45062e2ded6Syamt radix_tree_grow(struct radix_tree *t, unsigned int newheight)
45162e2ded6Syamt {
45262e2ded6Syamt 	const unsigned int tagmask = entry_tagmask(t->t_root);
453f1ed81b8Sad 	struct radix_tree_node *newnodes[RADIX_TREE_MAX_HEIGHT];
454f1ed81b8Sad 	void *root;
455f1ed81b8Sad 	int h;
45662e2ded6Syamt 
457f1ed81b8Sad 	KASSERT(newheight <= RADIX_TREE_MAX_HEIGHT);
458f1ed81b8Sad 	if ((root = t->t_root) == NULL) {
45962e2ded6Syamt 		t->t_height = newheight;
46062e2ded6Syamt 		return 0;
46162e2ded6Syamt 	}
462f1ed81b8Sad 	for (h = t->t_height; h < newheight; h++) {
463f1ed81b8Sad 		newnodes[h] = radix_tree_alloc_node();
464f1ed81b8Sad 		if (__predict_false(newnodes[h] == NULL)) {
465f1ed81b8Sad 			while (--h >= (int)t->t_height) {
466f1ed81b8Sad 				newnodes[h]->n_ptrs[0] = NULL;
467f1ed81b8Sad 				radix_tree_free_node(newnodes[h]);
468f1ed81b8Sad 			}
46962e2ded6Syamt 			return ENOMEM;
47062e2ded6Syamt 		}
471f1ed81b8Sad 		newnodes[h]->n_ptrs[0] = root;
472f1ed81b8Sad 		root = entry_compose(newnodes[h], tagmask);
47362e2ded6Syamt 	}
474f1ed81b8Sad 	t->t_root = root;
475f1ed81b8Sad 	t->t_height = h;
47662e2ded6Syamt 	return 0;
47762e2ded6Syamt }
47862e2ded6Syamt 
47959a4821fSyamt /*
48059a4821fSyamt  * radix_tree_lookup_ptr:
48159a4821fSyamt  *
48259a4821fSyamt  * an internal helper function used for various exported functions.
48359a4821fSyamt  *
48459a4821fSyamt  * return the pointer to store the node for the given index.
48559a4821fSyamt  *
48659a4821fSyamt  * if alloc is true, try to allocate the storage.  (note for _KERNEL:
48759a4821fSyamt  * in that case, this function can block.)  if the allocation failed or
48859a4821fSyamt  * alloc is false, return NULL.
48959a4821fSyamt  *
49059a4821fSyamt  * if path is not NULL, fill it for the caller's investigation.
49159a4821fSyamt  *
49259a4821fSyamt  * if tagmask is not zero, search only for nodes with the tag set.
493759124c5Syamt  * note that, however, this function doesn't check the tagmask for the leaf
494759124c5Syamt  * pointer.  it's a caller's responsibility to investigate the value which
495759124c5Syamt  * is pointed by the returned pointer if necessary.
49659a4821fSyamt  *
49759a4821fSyamt  * while this function is a bit large, as it's called with some constant
49859a4821fSyamt  * arguments, inlining might have benefits.  anyway, a compiler will decide.
49959a4821fSyamt  */
50059a4821fSyamt 
50162e2ded6Syamt static inline void **
radix_tree_lookup_ptr(struct radix_tree * t,uint64_t idx,struct radix_tree_path * path,bool alloc,const unsigned int tagmask)50262e2ded6Syamt radix_tree_lookup_ptr(struct radix_tree *t, uint64_t idx,
50362e2ded6Syamt     struct radix_tree_path *path, bool alloc, const unsigned int tagmask)
50462e2ded6Syamt {
50562e2ded6Syamt 	struct radix_tree_node *n;
50662e2ded6Syamt 	int hshift = RADIX_TREE_BITS_PER_HEIGHT * t->t_height;
50762e2ded6Syamt 	int shift;
50862e2ded6Syamt 	void **vpp;
50962e2ded6Syamt 	const uint64_t mask = (UINT64_C(1) << RADIX_TREE_BITS_PER_HEIGHT) - 1;
51062e2ded6Syamt 	struct radix_tree_node_ref *refs = NULL;
51162e2ded6Syamt 
51259a4821fSyamt 	/*
51359a4821fSyamt 	 * check unsupported combinations
51459a4821fSyamt 	 */
51562e2ded6Syamt 	KASSERT(tagmask == 0 || !alloc);
51662e2ded6Syamt 	KASSERT(path == NULL || !alloc);
51762e2ded6Syamt 	vpp = &t->t_root;
51862e2ded6Syamt 	if (path != NULL) {
51962e2ded6Syamt 		refs = path->p_refs;
52062e2ded6Syamt 		refs->pptr = vpp;
52162e2ded6Syamt 	}
52262e2ded6Syamt 	n = NULL;
52362e2ded6Syamt 	for (shift = 64 - RADIX_TREE_BITS_PER_HEIGHT; shift >= 0;) {
52462e2ded6Syamt 		struct radix_tree_node *c;
52562e2ded6Syamt 		void *entry;
52662e2ded6Syamt 		const uint64_t i = (idx >> shift) & mask;
52762e2ded6Syamt 
52862e2ded6Syamt 		if (shift >= hshift) {
52962e2ded6Syamt 			unsigned int newheight;
53062e2ded6Syamt 
53162e2ded6Syamt 			KASSERT(vpp == &t->t_root);
53262e2ded6Syamt 			if (i == 0) {
53362e2ded6Syamt 				shift -= RADIX_TREE_BITS_PER_HEIGHT;
53462e2ded6Syamt 				continue;
53562e2ded6Syamt 			}
53662e2ded6Syamt 			if (!alloc) {
53762e2ded6Syamt 				if (path != NULL) {
53862e2ded6Syamt 					KASSERT((refs - path->p_refs) == 0);
539759124c5Syamt 					path->p_lastidx =
540759124c5Syamt 					    RADIX_TREE_INVALID_HEIGHT;
54162e2ded6Syamt 				}
54262e2ded6Syamt 				return NULL;
54362e2ded6Syamt 			}
54462e2ded6Syamt 			newheight = shift / RADIX_TREE_BITS_PER_HEIGHT + 1;
54562e2ded6Syamt 			if (radix_tree_grow(t, newheight)) {
54662e2ded6Syamt 				return NULL;
54762e2ded6Syamt 			}
54862e2ded6Syamt 			hshift = RADIX_TREE_BITS_PER_HEIGHT * t->t_height;
54962e2ded6Syamt 		}
55062e2ded6Syamt 		entry = *vpp;
55162e2ded6Syamt 		c = entry_ptr(entry);
55262e2ded6Syamt 		if (c == NULL ||
55362e2ded6Syamt 		    (tagmask != 0 &&
55462e2ded6Syamt 		    (entry_tagmask(entry) & tagmask) == 0)) {
55562e2ded6Syamt 			if (!alloc) {
55662e2ded6Syamt 				if (path != NULL) {
55762e2ded6Syamt 					path->p_lastidx = refs - path->p_refs;
55862e2ded6Syamt 				}
55962e2ded6Syamt 				return NULL;
56062e2ded6Syamt 			}
56162e2ded6Syamt 			c = radix_tree_alloc_node();
56262e2ded6Syamt 			if (c == NULL) {
56362e2ded6Syamt 				return NULL;
56462e2ded6Syamt 			}
56562e2ded6Syamt 			*vpp = c;
56662e2ded6Syamt 		}
56762e2ded6Syamt 		n = c;
56862e2ded6Syamt 		vpp = &n->n_ptrs[i];
56962e2ded6Syamt 		if (path != NULL) {
57062e2ded6Syamt 			refs++;
57162e2ded6Syamt 			refs->pptr = vpp;
57262e2ded6Syamt 		}
57362e2ded6Syamt 		shift -= RADIX_TREE_BITS_PER_HEIGHT;
57462e2ded6Syamt 	}
57562e2ded6Syamt 	if (alloc) {
57662e2ded6Syamt 		KASSERT(*vpp == NULL);
57762e2ded6Syamt 	}
57862e2ded6Syamt 	if (path != NULL) {
57962e2ded6Syamt 		path->p_lastidx = refs - path->p_refs;
58062e2ded6Syamt 	}
58162e2ded6Syamt 	return vpp;
58262e2ded6Syamt }
58362e2ded6Syamt 
58462e2ded6Syamt /*
585f1ed81b8Sad  * radix_tree_undo_insert_node:
586f1ed81b8Sad  *
587f1ed81b8Sad  * Undo the effects of a failed insert.  The conditions that led to the
588f1ed81b8Sad  * insert may change and it may not be retried.  If the insert is not
589f1ed81b8Sad  * retried, there will be no corresponding radix_tree_remove_node() for
590f1ed81b8Sad  * this index in the future.  Therefore any adjustments made to the tree
591f1ed81b8Sad  * before memory was exhausted must be reverted.
592f1ed81b8Sad  */
593f1ed81b8Sad 
594f1ed81b8Sad static __noinline void
radix_tree_undo_insert_node(struct radix_tree * t,uint64_t idx)595f1ed81b8Sad radix_tree_undo_insert_node(struct radix_tree *t, uint64_t idx)
596f1ed81b8Sad {
597f1ed81b8Sad 	struct radix_tree_path path;
598f1ed81b8Sad 	int i;
599f1ed81b8Sad 
600f1ed81b8Sad 	(void)radix_tree_lookup_ptr(t, idx, &path, false, 0);
601f1ed81b8Sad 	if (path.p_lastidx == RADIX_TREE_INVALID_HEIGHT) {
602f1ed81b8Sad 		/*
603f1ed81b8Sad 		 * no nodes were inserted.
604f1ed81b8Sad 		 */
605f1ed81b8Sad 		return;
606f1ed81b8Sad 	}
607f1ed81b8Sad 	for (i = path.p_lastidx - 1; i >= 0; i--) {
608f1ed81b8Sad 		struct radix_tree_node ** const pptr =
609f1ed81b8Sad 		    (struct radix_tree_node **)path_pptr(t, &path, i);
610f1ed81b8Sad 		struct radix_tree_node *n;
611f1ed81b8Sad 
612f1ed81b8Sad 		KASSERT(pptr != NULL);
613f1ed81b8Sad 		n = entry_ptr(*pptr);
614f1ed81b8Sad 		KASSERT(n != NULL);
6155ff779feSad 		if (radix_tree_sum_node(n) != 0) {
616f1ed81b8Sad 			break;
617f1ed81b8Sad 		}
618f1ed81b8Sad 		radix_tree_free_node(n);
619f1ed81b8Sad 		*pptr = NULL;
620f1ed81b8Sad 	}
621f1ed81b8Sad 	/*
622f1ed81b8Sad 	 * fix up height
623f1ed81b8Sad 	 */
624f1ed81b8Sad 	if (i < 0) {
625f1ed81b8Sad 		KASSERT(t->t_root == NULL);
626f1ed81b8Sad 		t->t_height = 0;
627f1ed81b8Sad 	}
628f1ed81b8Sad }
629f1ed81b8Sad 
630f1ed81b8Sad /*
63162e2ded6Syamt  * radix_tree_insert_node:
63262e2ded6Syamt  *
6330558f521Sad  * Insert the node at the given index.
63462e2ded6Syamt  *
6350558f521Sad  * It's illegal to insert NULL.  It's illegal to insert a non-aligned pointer.
63662e2ded6Syamt  *
6370558f521Sad  * This function returns ENOMEM if necessary memory allocation failed.
6380558f521Sad  * Otherwise, this function returns 0.
639a8d2a6deSyamt  *
6400558f521Sad  * Note that inserting a node can involves memory allocation for intermediate
6410558f521Sad  * nodes.  If _KERNEL, it's done with no-sleep IPL_NONE memory allocation.
6420558f521Sad  *
6430558f521Sad  * For the newly inserted node, all tags are cleared.
64462e2ded6Syamt  */
64562e2ded6Syamt 
64662e2ded6Syamt int
radix_tree_insert_node(struct radix_tree * t,uint64_t idx,void * p)64762e2ded6Syamt radix_tree_insert_node(struct radix_tree *t, uint64_t idx, void *p)
64862e2ded6Syamt {
64962e2ded6Syamt 	void **vpp;
65062e2ded6Syamt 
65162e2ded6Syamt 	KASSERT(p != NULL);
6520558f521Sad 	KASSERT(entry_tagmask(entry_compose(p, 0)) == 0);
65362e2ded6Syamt 	vpp = radix_tree_lookup_ptr(t, idx, NULL, true, 0);
654f1ed81b8Sad 	if (__predict_false(vpp == NULL)) {
655f1ed81b8Sad 		radix_tree_undo_insert_node(t, idx);
65662e2ded6Syamt 		return ENOMEM;
65762e2ded6Syamt 	}
65862e2ded6Syamt 	KASSERT(*vpp == NULL);
65962e2ded6Syamt 	*vpp = p;
66062e2ded6Syamt 	return 0;
66162e2ded6Syamt }
66262e2ded6Syamt 
663a8d2a6deSyamt /*
664a8d2a6deSyamt  * radix_tree_replace_node:
665a8d2a6deSyamt  *
6660558f521Sad  * Replace a node at the given index with the given node and return the
6670558f521Sad  * replaced one.
668a8d2a6deSyamt  *
6690558f521Sad  * It's illegal to try to replace a node which has not been inserted.
6700558f521Sad  *
6710558f521Sad  * This function keeps tags intact.
672a8d2a6deSyamt  */
673a8d2a6deSyamt 
67462e2ded6Syamt void *
radix_tree_replace_node(struct radix_tree * t,uint64_t idx,void * p)67562e2ded6Syamt radix_tree_replace_node(struct radix_tree *t, uint64_t idx, void *p)
67662e2ded6Syamt {
67762e2ded6Syamt 	void **vpp;
67862e2ded6Syamt 	void *oldp;
67962e2ded6Syamt 
68062e2ded6Syamt 	KASSERT(p != NULL);
6810558f521Sad 	KASSERT(entry_tagmask(entry_compose(p, 0)) == 0);
68262e2ded6Syamt 	vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
68362e2ded6Syamt 	KASSERT(vpp != NULL);
68462e2ded6Syamt 	oldp = *vpp;
68562e2ded6Syamt 	KASSERT(oldp != NULL);
68662e2ded6Syamt 	*vpp = entry_compose(p, entry_tagmask(*vpp));
68762e2ded6Syamt 	return entry_ptr(oldp);
68862e2ded6Syamt }
68962e2ded6Syamt 
69062e2ded6Syamt /*
69162e2ded6Syamt  * radix_tree_remove_node:
69262e2ded6Syamt  *
6930558f521Sad  * Remove the node at the given index.
6940558f521Sad  *
6950558f521Sad  * It's illegal to try to remove a node which has not been inserted.
69662e2ded6Syamt  */
69762e2ded6Syamt 
69862e2ded6Syamt void *
radix_tree_remove_node(struct radix_tree * t,uint64_t idx)69962e2ded6Syamt radix_tree_remove_node(struct radix_tree *t, uint64_t idx)
70062e2ded6Syamt {
70162e2ded6Syamt 	struct radix_tree_path path;
70262e2ded6Syamt 	void **vpp;
70362e2ded6Syamt 	void *oldp;
70462e2ded6Syamt 	int i;
70562e2ded6Syamt 
70662e2ded6Syamt 	vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
70762e2ded6Syamt 	KASSERT(vpp != NULL);
70862e2ded6Syamt 	oldp = *vpp;
70962e2ded6Syamt 	KASSERT(oldp != NULL);
71062e2ded6Syamt 	KASSERT(path.p_lastidx == t->t_height);
71162e2ded6Syamt 	KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
71262e2ded6Syamt 	*vpp = NULL;
71362e2ded6Syamt 	for (i = t->t_height - 1; i >= 0; i--) {
71462e2ded6Syamt 		void *entry;
71562e2ded6Syamt 		struct radix_tree_node ** const pptr =
71662e2ded6Syamt 		    (struct radix_tree_node **)path_pptr(t, &path, i);
71762e2ded6Syamt 		struct radix_tree_node *n;
71862e2ded6Syamt 
71962e2ded6Syamt 		KASSERT(pptr != NULL);
72062e2ded6Syamt 		entry = *pptr;
72162e2ded6Syamt 		n = entry_ptr(entry);
72262e2ded6Syamt 		KASSERT(n != NULL);
7235ff779feSad 		if (radix_tree_sum_node(n) != 0) {
72462e2ded6Syamt 			break;
72562e2ded6Syamt 		}
72662e2ded6Syamt 		radix_tree_free_node(n);
72762e2ded6Syamt 		*pptr = NULL;
72862e2ded6Syamt 	}
72962e2ded6Syamt 	/*
73062e2ded6Syamt 	 * fix up height
73162e2ded6Syamt 	 */
73262e2ded6Syamt 	if (i < 0) {
73362e2ded6Syamt 		KASSERT(t->t_root == NULL);
73462e2ded6Syamt 		t->t_height = 0;
73562e2ded6Syamt 	}
73662e2ded6Syamt 	/*
73762e2ded6Syamt 	 * update tags
73862e2ded6Syamt 	 */
73962e2ded6Syamt 	for (; i >= 0; i--) {
74062e2ded6Syamt 		void *entry;
74162e2ded6Syamt 		struct radix_tree_node ** const pptr =
74262e2ded6Syamt 		    (struct radix_tree_node **)path_pptr(t, &path, i);
74362e2ded6Syamt 		struct radix_tree_node *n;
74462e2ded6Syamt 		unsigned int newmask;
74562e2ded6Syamt 
74662e2ded6Syamt 		KASSERT(pptr != NULL);
74762e2ded6Syamt 		entry = *pptr;
74862e2ded6Syamt 		n = entry_ptr(entry);
74962e2ded6Syamt 		KASSERT(n != NULL);
7505ff779feSad 		KASSERT(radix_tree_sum_node(n) != 0);
7515ff779feSad 		newmask = radix_tree_sum_node(n) & RADIX_TREE_TAG_MASK;
75262e2ded6Syamt 		if (newmask == entry_tagmask(entry)) {
75362e2ded6Syamt 			break;
75462e2ded6Syamt 		}
75562e2ded6Syamt 		*pptr = entry_compose(n, newmask);
75662e2ded6Syamt 	}
75762e2ded6Syamt 	/*
75862e2ded6Syamt 	 * XXX is it worth to try to reduce height?
75962e2ded6Syamt 	 * if we do that, make radix_tree_grow rollback its change as well.
76062e2ded6Syamt 	 */
76162e2ded6Syamt 	return entry_ptr(oldp);
76262e2ded6Syamt }
76362e2ded6Syamt 
76462e2ded6Syamt /*
76562e2ded6Syamt  * radix_tree_lookup_node:
76662e2ded6Syamt  *
7670558f521Sad  * Returns the node at the given index.
7680558f521Sad  * Returns NULL if nothing is found at the given index.
76962e2ded6Syamt  */
77062e2ded6Syamt 
77162e2ded6Syamt void *
radix_tree_lookup_node(struct radix_tree * t,uint64_t idx)77262e2ded6Syamt radix_tree_lookup_node(struct radix_tree *t, uint64_t idx)
77362e2ded6Syamt {
77462e2ded6Syamt 	void **vpp;
77562e2ded6Syamt 
77662e2ded6Syamt 	vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
77762e2ded6Syamt 	if (vpp == NULL) {
77862e2ded6Syamt 		return NULL;
77962e2ded6Syamt 	}
78062e2ded6Syamt 	return entry_ptr(*vpp);
78162e2ded6Syamt }
78262e2ded6Syamt 
78362e2ded6Syamt static inline void
gang_lookup_init(struct radix_tree * t,uint64_t idx,struct radix_tree_path * path,const unsigned int tagmask)78462e2ded6Syamt gang_lookup_init(struct radix_tree *t, uint64_t idx,
78562e2ded6Syamt     struct radix_tree_path *path, const unsigned int tagmask)
78662e2ded6Syamt {
7870558f521Sad 	void **vpp __unused;
78862e2ded6Syamt 
7899afd1ce3Sad 	vpp = radix_tree_lookup_ptr(t, idx, path, false, tagmask);
79062e2ded6Syamt 	KASSERT(vpp == NULL ||
79162e2ded6Syamt 	    vpp == path_pptr(t, path, path->p_lastidx));
79262e2ded6Syamt 	KASSERT(&t->t_root == path_pptr(t, path, 0));
793759124c5Syamt 	KASSERT(path->p_lastidx == RADIX_TREE_INVALID_HEIGHT ||
794759124c5Syamt 	   path->p_lastidx == t->t_height ||
795759124c5Syamt 	   !entry_match_p(*path_pptr(t, path, path->p_lastidx), tagmask));
79662e2ded6Syamt }
79762e2ded6Syamt 
798759124c5Syamt /*
799759124c5Syamt  * gang_lookup_scan:
800759124c5Syamt  *
801759124c5Syamt  * a helper routine for radix_tree_gang_lookup_node and its variants.
802759124c5Syamt  */
803759124c5Syamt 
80462e2ded6Syamt static inline unsigned int
805759124c5Syamt __attribute__((__always_inline__))
gang_lookup_scan(struct radix_tree * t,struct radix_tree_path * path,void ** results,const unsigned int maxresults,const unsigned int tagmask,const bool reverse,const bool dense)80662e2ded6Syamt gang_lookup_scan(struct radix_tree *t, struct radix_tree_path *path,
8070558f521Sad     void **results, const unsigned int maxresults, const unsigned int tagmask,
8080558f521Sad     const bool reverse, const bool dense)
80962e2ded6Syamt {
810759124c5Syamt 
811759124c5Syamt 	/*
812759124c5Syamt 	 * we keep the path updated only for lastidx-1.
813759124c5Syamt 	 * vpp is what path_pptr(t, path, lastidx) would be.
814759124c5Syamt 	 */
81562e2ded6Syamt 	void **vpp;
8161414ffd1Syamt 	unsigned int nfound;
81762e2ded6Syamt 	unsigned int lastidx;
818759124c5Syamt 	/*
819759124c5Syamt 	 * set up scan direction dependant constants so that we can iterate
820759124c5Syamt 	 * n_ptrs as the following.
821759124c5Syamt 	 *
822759124c5Syamt 	 *	for (i = first; i != guard; i += step)
823759124c5Syamt 	 *		visit n->n_ptrs[i];
824759124c5Syamt 	 */
825759124c5Syamt 	const int step = reverse ? -1 : 1;
826759124c5Syamt 	const unsigned int first = reverse ? RADIX_TREE_PTR_PER_NODE - 1 : 0;
827759124c5Syamt 	const unsigned int last = reverse ? 0 : RADIX_TREE_PTR_PER_NODE - 1;
828759124c5Syamt 	const unsigned int guard = last + step;
82962e2ded6Syamt 
83062e2ded6Syamt 	KASSERT(maxresults > 0);
831759124c5Syamt 	KASSERT(&t->t_root == path_pptr(t, path, 0));
83262e2ded6Syamt 	lastidx = path->p_lastidx;
833759124c5Syamt 	KASSERT(lastidx == RADIX_TREE_INVALID_HEIGHT ||
834759124c5Syamt 	   lastidx == t->t_height ||
835759124c5Syamt 	   !entry_match_p(*path_pptr(t, path, lastidx), tagmask));
836759124c5Syamt 	nfound = 0;
837759124c5Syamt 	if (lastidx == RADIX_TREE_INVALID_HEIGHT) {
8380558f521Sad 		/*
8390558f521Sad 		 * requested idx is beyond the right-most node.
8400558f521Sad 		 */
8410558f521Sad 		if (reverse && !dense) {
842759124c5Syamt 			lastidx = 0;
843759124c5Syamt 			vpp = path_pptr(t, path, lastidx);
844759124c5Syamt 			goto descend;
845759124c5Syamt 		}
84662e2ded6Syamt 		return 0;
84762e2ded6Syamt 	}
84862e2ded6Syamt 	vpp = path_pptr(t, path, lastidx);
84962e2ded6Syamt 	while (/*CONSTCOND*/true) {
85062e2ded6Syamt 		struct radix_tree_node *n;
8511414ffd1Syamt 		unsigned int i;
85262e2ded6Syamt 
85362e2ded6Syamt 		if (entry_match_p(*vpp, tagmask)) {
85462e2ded6Syamt 			KASSERT(lastidx == t->t_height);
85562e2ded6Syamt 			/*
856759124c5Syamt 			 * record the matching non-NULL leaf.
85762e2ded6Syamt 			 */
85862e2ded6Syamt 			results[nfound] = entry_ptr(*vpp);
85962e2ded6Syamt 			nfound++;
86062e2ded6Syamt 			if (nfound == maxresults) {
86162e2ded6Syamt 				return nfound;
86262e2ded6Syamt 			}
8630558f521Sad 		} else if (dense) {
8640558f521Sad 			return nfound;
86562e2ded6Syamt 		}
86662e2ded6Syamt scan_siblings:
86762e2ded6Syamt 		/*
868759124c5Syamt 		 * try to find the next matching non-NULL sibling.
86962e2ded6Syamt 		 */
870759124c5Syamt 		if (lastidx == 0) {
871759124c5Syamt 			/*
872759124c5Syamt 			 * the root has no siblings.
873759124c5Syamt 			 * we've done.
874759124c5Syamt 			 */
875759124c5Syamt 			KASSERT(vpp == &t->t_root);
876759124c5Syamt 			break;
877759124c5Syamt 		}
87862e2ded6Syamt 		n = path_node(t, path, lastidx - 1);
879759124c5Syamt 		for (i = vpp - n->n_ptrs + step; i != guard; i += step) {
880759124c5Syamt 			KASSERT(i < RADIX_TREE_PTR_PER_NODE);
88162e2ded6Syamt 			if (entry_match_p(n->n_ptrs[i], tagmask)) {
88262e2ded6Syamt 				vpp = &n->n_ptrs[i];
88362e2ded6Syamt 				break;
88481d0e040Sad 			} else if (dense) {
88581d0e040Sad 				return nfound;
88662e2ded6Syamt 			}
88762e2ded6Syamt 		}
888759124c5Syamt 		if (i == guard) {
88962e2ded6Syamt 			/*
89062e2ded6Syamt 			 * not found.  go to parent.
89162e2ded6Syamt 			 */
89262e2ded6Syamt 			lastidx--;
89362e2ded6Syamt 			vpp = path_pptr(t, path, lastidx);
89462e2ded6Syamt 			goto scan_siblings;
89562e2ded6Syamt 		}
896759124c5Syamt descend:
89762e2ded6Syamt 		/*
898759124c5Syamt 		 * following the left-most (or right-most in the case of
899cdc507f0Sandvar 		 * reverse scan) child node, descend until reaching the leaf or
900c9b3c34dSandvar 		 * a non-matching entry.
90162e2ded6Syamt 		 */
90262e2ded6Syamt 		while (entry_match_p(*vpp, tagmask) && lastidx < t->t_height) {
903759124c5Syamt 			/*
904759124c5Syamt 			 * save vpp in the path so that we can come back to this
905759124c5Syamt 			 * node after finishing visiting children.
906759124c5Syamt 			 */
90762e2ded6Syamt 			path->p_refs[lastidx].pptr = vpp;
908759124c5Syamt 			n = entry_ptr(*vpp);
909759124c5Syamt 			vpp = &n->n_ptrs[first];
910759124c5Syamt 			lastidx++;
91162e2ded6Syamt 		}
91262e2ded6Syamt 	}
913759124c5Syamt 	return nfound;
91462e2ded6Syamt }
91562e2ded6Syamt 
91662e2ded6Syamt /*
91762e2ded6Syamt  * radix_tree_gang_lookup_node:
91862e2ded6Syamt  *
9190558f521Sad  * Scan the tree starting from the given index in the ascending order and
9200558f521Sad  * return found nodes.
9210558f521Sad  *
92262e2ded6Syamt  * results should be an array large enough to hold maxresults pointers.
9230558f521Sad  * This function returns the number of nodes found, up to maxresults.
9240558f521Sad  * Returning less than maxresults means there are no more nodes in the tree.
92562e2ded6Syamt  *
9260558f521Sad  * If dense == true, this function stops scanning when it founds a hole of
9270558f521Sad  * indexes.  I.e. an index for which radix_tree_lookup_node would returns NULL.
9280558f521Sad  * If dense == false, this function skips holes and continue scanning until
9290558f521Sad  * maxresults nodes are found or it reaches the limit of the index range.
9300558f521Sad  *
9310558f521Sad  * The result of this function is semantically equivalent to what could be
93262e2ded6Syamt  * obtained by repeated calls of radix_tree_lookup_node with increasing index.
9330558f521Sad  * but this function is expected to be computationally cheaper when looking up
9340558f521Sad  * multiple nodes at once.  Especially, it's expected to be much cheaper when
9350558f521Sad  * node indexes are distributed sparsely.
93662e2ded6Syamt  *
9370558f521Sad  * Note that this function doesn't return index values of found nodes.
9380558f521Sad  * Thus, in the case of dense == false, if index values are important for
9390558f521Sad  * a caller, it's the caller's responsibility to check them, typically
940c9b3c34dSandvar  * by examining the returned nodes using some caller-specific knowledge
9410558f521Sad  * about them.
9420558f521Sad  * In the case of dense == true, a node returned via results[N] is always for
9430558f521Sad  * the index (idx + N).
94462e2ded6Syamt  */
94562e2ded6Syamt 
94662e2ded6Syamt unsigned int
radix_tree_gang_lookup_node(struct radix_tree * t,uint64_t idx,void ** results,unsigned int maxresults,bool dense)94762e2ded6Syamt radix_tree_gang_lookup_node(struct radix_tree *t, uint64_t idx,
9480558f521Sad     void **results, unsigned int maxresults, bool dense)
94962e2ded6Syamt {
95062e2ded6Syamt 	struct radix_tree_path path;
95162e2ded6Syamt 
95262e2ded6Syamt 	gang_lookup_init(t, idx, &path, 0);
9530558f521Sad 	return gang_lookup_scan(t, &path, results, maxresults, 0, false, dense);
954759124c5Syamt }
955759124c5Syamt 
956759124c5Syamt /*
957759124c5Syamt  * radix_tree_gang_lookup_node_reverse:
958759124c5Syamt  *
9590558f521Sad  * Same as radix_tree_gang_lookup_node except that this one scans the
9600558f521Sad  * tree in the reverse order.  I.e. descending index values.
961759124c5Syamt  */
962759124c5Syamt 
963759124c5Syamt unsigned int
radix_tree_gang_lookup_node_reverse(struct radix_tree * t,uint64_t idx,void ** results,unsigned int maxresults,bool dense)964759124c5Syamt radix_tree_gang_lookup_node_reverse(struct radix_tree *t, uint64_t idx,
9650558f521Sad     void **results, unsigned int maxresults, bool dense)
966759124c5Syamt {
967759124c5Syamt 	struct radix_tree_path path;
968759124c5Syamt 
969759124c5Syamt 	gang_lookup_init(t, idx, &path, 0);
9700558f521Sad 	return gang_lookup_scan(t, &path, results, maxresults, 0, true, dense);
97162e2ded6Syamt }
97262e2ded6Syamt 
97362e2ded6Syamt /*
97462e2ded6Syamt  * radix_tree_gang_lookup_tagged_node:
97562e2ded6Syamt  *
9760558f521Sad  * Same as radix_tree_gang_lookup_node except that this one only returns
97762e2ded6Syamt  * nodes tagged with tagid.
9780558f521Sad  *
9790558f521Sad  * It's illegal to call this function with tagmask 0.
98062e2ded6Syamt  */
98162e2ded6Syamt 
98262e2ded6Syamt unsigned int
radix_tree_gang_lookup_tagged_node(struct radix_tree * t,uint64_t idx,void ** results,unsigned int maxresults,bool dense,unsigned int tagmask)98362e2ded6Syamt radix_tree_gang_lookup_tagged_node(struct radix_tree *t, uint64_t idx,
9840558f521Sad     void **results, unsigned int maxresults, bool dense, unsigned int tagmask)
98562e2ded6Syamt {
98662e2ded6Syamt 	struct radix_tree_path path;
98762e2ded6Syamt 
9880558f521Sad 	KASSERT(tagmask != 0);
98962e2ded6Syamt 	gang_lookup_init(t, idx, &path, tagmask);
9900558f521Sad 	return gang_lookup_scan(t, &path, results, maxresults, tagmask, false,
9910558f521Sad 	    dense);
992759124c5Syamt }
993759124c5Syamt 
994759124c5Syamt /*
995759124c5Syamt  * radix_tree_gang_lookup_tagged_node_reverse:
996759124c5Syamt  *
9970558f521Sad  * Same as radix_tree_gang_lookup_tagged_node except that this one scans the
9980558f521Sad  * tree in the reverse order.  I.e. descending index values.
999759124c5Syamt  */
1000759124c5Syamt 
1001759124c5Syamt unsigned int
radix_tree_gang_lookup_tagged_node_reverse(struct radix_tree * t,uint64_t idx,void ** results,unsigned int maxresults,bool dense,unsigned int tagmask)1002759124c5Syamt radix_tree_gang_lookup_tagged_node_reverse(struct radix_tree *t, uint64_t idx,
10030558f521Sad     void **results, unsigned int maxresults, bool dense, unsigned int tagmask)
1004759124c5Syamt {
1005759124c5Syamt 	struct radix_tree_path path;
1006759124c5Syamt 
10070558f521Sad 	KASSERT(tagmask != 0);
1008759124c5Syamt 	gang_lookup_init(t, idx, &path, tagmask);
10090558f521Sad 	return gang_lookup_scan(t, &path, results, maxresults, tagmask, true,
10100558f521Sad 	    dense);
101162e2ded6Syamt }
101262e2ded6Syamt 
1013a8d2a6deSyamt /*
1014a8d2a6deSyamt  * radix_tree_get_tag:
1015a8d2a6deSyamt  *
10160558f521Sad  * Return the tagmask for the node at the given index.
10170558f521Sad  *
10180558f521Sad  * It's illegal to call this function for a node which has not been inserted.
1019a8d2a6deSyamt  */
1020a8d2a6deSyamt 
10210558f521Sad unsigned int
radix_tree_get_tag(struct radix_tree * t,uint64_t idx,unsigned int tagmask)10220558f521Sad radix_tree_get_tag(struct radix_tree *t, uint64_t idx, unsigned int tagmask)
102362e2ded6Syamt {
10240558f521Sad 	/*
10250558f521Sad 	 * the following two implementations should behave same.
10260558f521Sad 	 * the former one was chosen because it seems faster.
10270558f521Sad 	 */
102862e2ded6Syamt #if 1
102962e2ded6Syamt 	void **vpp;
103062e2ded6Syamt 
103162e2ded6Syamt 	vpp = radix_tree_lookup_ptr(t, idx, NULL, false, tagmask);
103262e2ded6Syamt 	if (vpp == NULL) {
103362e2ded6Syamt 		return false;
103462e2ded6Syamt 	}
103562e2ded6Syamt 	KASSERT(*vpp != NULL);
10360558f521Sad 	return (entry_tagmask(*vpp) & tagmask);
103762e2ded6Syamt #else
103862e2ded6Syamt 	void **vpp;
103962e2ded6Syamt 
104062e2ded6Syamt 	vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
104162e2ded6Syamt 	KASSERT(vpp != NULL);
10420558f521Sad 	return (entry_tagmask(*vpp) & tagmask);
104362e2ded6Syamt #endif
104462e2ded6Syamt }
104562e2ded6Syamt 
1046a8d2a6deSyamt /*
1047a8d2a6deSyamt  * radix_tree_set_tag:
1048a8d2a6deSyamt  *
10490558f521Sad  * Set the tag for the node at the given index.
10500558f521Sad  *
10510558f521Sad  * It's illegal to call this function for a node which has not been inserted.
10520558f521Sad  * It's illegal to call this function with tagmask 0.
1053a8d2a6deSyamt  */
1054a8d2a6deSyamt 
105562e2ded6Syamt void
radix_tree_set_tag(struct radix_tree * t,uint64_t idx,unsigned int tagmask)10560558f521Sad radix_tree_set_tag(struct radix_tree *t, uint64_t idx, unsigned int tagmask)
105762e2ded6Syamt {
105862e2ded6Syamt 	struct radix_tree_path path;
10590558f521Sad 	void **vpp __unused;
106062e2ded6Syamt 	int i;
106162e2ded6Syamt 
10620558f521Sad 	KASSERT(tagmask != 0);
10639afd1ce3Sad 	vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
106462e2ded6Syamt 	KASSERT(vpp != NULL);
106562e2ded6Syamt 	KASSERT(*vpp != NULL);
106662e2ded6Syamt 	KASSERT(path.p_lastidx == t->t_height);
106762e2ded6Syamt 	KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
106862e2ded6Syamt 	for (i = t->t_height; i >= 0; i--) {
106962e2ded6Syamt 		void ** const pptr = (void **)path_pptr(t, &path, i);
107062e2ded6Syamt 		void *entry;
107162e2ded6Syamt 
107262e2ded6Syamt 		KASSERT(pptr != NULL);
107362e2ded6Syamt 		entry = *pptr;
107462e2ded6Syamt 		if ((entry_tagmask(entry) & tagmask) != 0) {
107562e2ded6Syamt 			break;
107662e2ded6Syamt 		}
107762e2ded6Syamt 		*pptr = (void *)((uintptr_t)entry | tagmask);
107862e2ded6Syamt 	}
107962e2ded6Syamt }
108062e2ded6Syamt 
1081a8d2a6deSyamt /*
1082a8d2a6deSyamt  * radix_tree_clear_tag:
1083a8d2a6deSyamt  *
10840558f521Sad  * Clear the tag for the node at the given index.
10850558f521Sad  *
10860558f521Sad  * It's illegal to call this function for a node which has not been inserted.
10870558f521Sad  * It's illegal to call this function with tagmask 0.
1088a8d2a6deSyamt  */
1089a8d2a6deSyamt 
109062e2ded6Syamt void
radix_tree_clear_tag(struct radix_tree * t,uint64_t idx,unsigned int tagmask)10910558f521Sad radix_tree_clear_tag(struct radix_tree *t, uint64_t idx, unsigned int tagmask)
109262e2ded6Syamt {
109362e2ded6Syamt 	struct radix_tree_path path;
109462e2ded6Syamt 	void **vpp;
109562e2ded6Syamt 	int i;
109662e2ded6Syamt 
10970558f521Sad 	KASSERT(tagmask != 0);
109862e2ded6Syamt 	vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
109962e2ded6Syamt 	KASSERT(vpp != NULL);
110062e2ded6Syamt 	KASSERT(*vpp != NULL);
110162e2ded6Syamt 	KASSERT(path.p_lastidx == t->t_height);
110262e2ded6Syamt 	KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
1103d9389408Syamt 	/*
1104d9389408Syamt 	 * if already cleared, nothing to do
1105d9389408Syamt 	 */
110662e2ded6Syamt 	if ((entry_tagmask(*vpp) & tagmask) == 0) {
110762e2ded6Syamt 		return;
110862e2ded6Syamt 	}
1109d9389408Syamt 	/*
1110d9389408Syamt 	 * clear the tag only if no children have the tag.
1111d9389408Syamt 	 */
111262e2ded6Syamt 	for (i = t->t_height; i >= 0; i--) {
111362e2ded6Syamt 		void ** const pptr = (void **)path_pptr(t, &path, i);
111462e2ded6Syamt 		void *entry;
111562e2ded6Syamt 
111662e2ded6Syamt 		KASSERT(pptr != NULL);
111762e2ded6Syamt 		entry = *pptr;
111862e2ded6Syamt 		KASSERT((entry_tagmask(entry) & tagmask) != 0);
111962e2ded6Syamt 		*pptr = entry_compose(entry_ptr(entry),
112062e2ded6Syamt 		    entry_tagmask(entry) & ~tagmask);
1121d9389408Syamt 		/*
1122d9389408Syamt 		 * check if we should proceed to process the next level.
1123d9389408Syamt 		 */
1124d9389408Syamt 		if (0 < i) {
112562e2ded6Syamt 			struct radix_tree_node *n = path_node(t, &path, i - 1);
112662e2ded6Syamt 
11275ff779feSad 			if ((radix_tree_sum_node(n) & tagmask) != 0) {
112862e2ded6Syamt 				break;
112962e2ded6Syamt 			}
113062e2ded6Syamt 		}
113162e2ded6Syamt 	}
113262e2ded6Syamt }
113362e2ded6Syamt 
113462e2ded6Syamt #if defined(UNITTEST)
113562e2ded6Syamt 
113662e2ded6Syamt #include <inttypes.h>
113762e2ded6Syamt #include <stdio.h>
113862e2ded6Syamt 
113962e2ded6Syamt static void
radix_tree_dump_node(const struct radix_tree * t,void * vp,uint64_t offset,unsigned int height)114062e2ded6Syamt radix_tree_dump_node(const struct radix_tree *t, void *vp,
114162e2ded6Syamt     uint64_t offset, unsigned int height)
114262e2ded6Syamt {
114362e2ded6Syamt 	struct radix_tree_node *n;
114462e2ded6Syamt 	unsigned int i;
114562e2ded6Syamt 
114662e2ded6Syamt 	for (i = 0; i < t->t_height - height; i++) {
114762e2ded6Syamt 		printf(" ");
114862e2ded6Syamt 	}
114962e2ded6Syamt 	if (entry_tagmask(vp) == 0) {
115062e2ded6Syamt 		printf("[%" PRIu64 "] %p", offset, entry_ptr(vp));
115162e2ded6Syamt 	} else {
115262e2ded6Syamt 		printf("[%" PRIu64 "] %p (tagmask=0x%x)", offset, entry_ptr(vp),
115362e2ded6Syamt 		    entry_tagmask(vp));
115462e2ded6Syamt 	}
115562e2ded6Syamt 	if (height == 0) {
115662e2ded6Syamt 		printf(" (leaf)\n");
115762e2ded6Syamt 		return;
115862e2ded6Syamt 	}
115962e2ded6Syamt 	n = entry_ptr(vp);
11605ff779feSad 	assert((radix_tree_sum_node(n) & RADIX_TREE_TAG_MASK) ==
11615e0de1ecSad 	    entry_tagmask(vp));
11629afd1ce3Sad 	printf(" (%u children)\n", radix_tree_node_count_ptrs(n));
116362e2ded6Syamt 	for (i = 0; i < __arraycount(n->n_ptrs); i++) {
116462e2ded6Syamt 		void *c;
116562e2ded6Syamt 
116662e2ded6Syamt 		c = n->n_ptrs[i];
116762e2ded6Syamt 		if (c == NULL) {
116862e2ded6Syamt 			continue;
116962e2ded6Syamt 		}
117062e2ded6Syamt 		radix_tree_dump_node(t, c,
117162e2ded6Syamt 		    offset + i * (UINT64_C(1) <<
117262e2ded6Syamt 		    (RADIX_TREE_BITS_PER_HEIGHT * (height - 1))), height - 1);
117362e2ded6Syamt 	}
117462e2ded6Syamt }
117562e2ded6Syamt 
117662e2ded6Syamt void radix_tree_dump(const struct radix_tree *);
117762e2ded6Syamt 
117862e2ded6Syamt void
radix_tree_dump(const struct radix_tree * t)117962e2ded6Syamt radix_tree_dump(const struct radix_tree *t)
118062e2ded6Syamt {
118162e2ded6Syamt 
118262e2ded6Syamt 	printf("tree %p height=%u\n", t, t->t_height);
118362e2ded6Syamt 	radix_tree_dump_node(t, t->t_root, 0, t->t_height);
118462e2ded6Syamt }
118562e2ded6Syamt 
118662e2ded6Syamt static void
test1(void)118762e2ded6Syamt test1(void)
118862e2ded6Syamt {
118962e2ded6Syamt 	struct radix_tree s;
119062e2ded6Syamt 	struct radix_tree *t = &s;
119162e2ded6Syamt 	void *results[3];
119262e2ded6Syamt 
119362e2ded6Syamt 	radix_tree_init_tree(t);
119462e2ded6Syamt 	radix_tree_dump(t);
119562e2ded6Syamt 	assert(radix_tree_lookup_node(t, 0) == NULL);
119662e2ded6Syamt 	assert(radix_tree_lookup_node(t, 1000) == NULL);
11970558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, false) == 0);
11980558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, true) == 0);
11990558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1000, results, 3, false) == 0);
12000558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1000, results, 3, true) == 0);
12010558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3, false) ==
12020558f521Sad 	    0);
12030558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3, true) ==
12040558f521Sad 	    0);
12050558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3, false)
1206759124c5Syamt 	    == 0);
12070558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3, true)
12080558f521Sad 	    == 0);
12090558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, false, 1)
12100558f521Sad 	    == 0);
12110558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, true, 1)
12120558f521Sad 	    == 0);
12130558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 1000, results, 3, false, 1)
12140558f521Sad 	    == 0);
12150558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 1000, results, 3, true, 1)
12160558f521Sad 	    == 0);
12170558f521Sad 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3,
12180558f521Sad 	    false, 1) == 0);
12190558f521Sad 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3,
12200558f521Sad 	    true, 1) == 0);
1221759124c5Syamt 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 1000, results, 3,
12220558f521Sad 	    false, 1) == 0);
12230558f521Sad 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 1000, results, 3,
12240558f521Sad 	    true, 1) == 0);
1225759124c5Syamt 	assert(radix_tree_empty_tree_p(t));
1226d1036328Syamt 	assert(radix_tree_empty_tagged_tree_p(t, 1));
12270558f521Sad 	assert(radix_tree_empty_tagged_tree_p(t, 2));
1228759124c5Syamt 	assert(radix_tree_insert_node(t, 0, (void *)0xdeadbea0) == 0);
1229759124c5Syamt 	assert(!radix_tree_empty_tree_p(t));
1230d1036328Syamt 	assert(radix_tree_empty_tagged_tree_p(t, 1));
12310558f521Sad 	assert(radix_tree_empty_tagged_tree_p(t, 2));
1232759124c5Syamt 	assert(radix_tree_lookup_node(t, 0) == (void *)0xdeadbea0);
1233759124c5Syamt 	assert(radix_tree_lookup_node(t, 1000) == NULL);
1234759124c5Syamt 	memset(results, 0, sizeof(results));
12350558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, false) == 1);
1236759124c5Syamt 	assert(results[0] == (void *)0xdeadbea0);
1237759124c5Syamt 	memset(results, 0, sizeof(results));
12380558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, true) == 1);
1239759124c5Syamt 	assert(results[0] == (void *)0xdeadbea0);
12400558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1000, results, 3, false) == 0);
12410558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1000, results, 3, true) == 0);
12420558f521Sad 	memset(results, 0, sizeof(results));
12430558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3, false) ==
12440558f521Sad 	    1);
12450558f521Sad 	assert(results[0] == (void *)0xdeadbea0);
12460558f521Sad 	memset(results, 0, sizeof(results));
12470558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3, true) ==
12480558f521Sad 	    1);
12490558f521Sad 	assert(results[0] == (void *)0xdeadbea0);
12500558f521Sad 	memset(results, 0, sizeof(results));
12510558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3, false)
12520558f521Sad 	    == 1);
12530558f521Sad 	assert(results[0] == (void *)0xdeadbea0);
12540558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3, true)
1255759124c5Syamt 	    == 0);
12560558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, false, 1)
1257759124c5Syamt 	    == 0);
12580558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, true, 1)
12590558f521Sad 	    == 0);
12600558f521Sad 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3,
12610558f521Sad 	    false, 1) == 0);
12620558f521Sad 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3,
12630558f521Sad 	    true, 1) == 0);
126462e2ded6Syamt 	assert(radix_tree_insert_node(t, 1000, (void *)0xdeadbea0) == 0);
1265759124c5Syamt 	assert(radix_tree_remove_node(t, 0) == (void *)0xdeadbea0);
1266759124c5Syamt 	assert(!radix_tree_empty_tree_p(t));
126762e2ded6Syamt 	radix_tree_dump(t);
1268759124c5Syamt 	assert(radix_tree_lookup_node(t, 0) == NULL);
1269759124c5Syamt 	assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
1270759124c5Syamt 	memset(results, 0, sizeof(results));
12710558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, false) == 1);
12720558f521Sad 	assert(results[0] == (void *)0xdeadbea0);
12730558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, true) == 0);
12740558f521Sad 	memset(results, 0, sizeof(results));
12750558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1000, results, 3, false) == 1);
1276759124c5Syamt 	assert(results[0] == (void *)0xdeadbea0);
1277759124c5Syamt 	memset(results, 0, sizeof(results));
12780558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1000, results, 3, true) == 1);
1279759124c5Syamt 	assert(results[0] == (void *)0xdeadbea0);
12800558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3, false)
12810558f521Sad 	    == 0);
12820558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3, true)
12830558f521Sad 	    == 0);
1284759124c5Syamt 	memset(results, 0, sizeof(results));
12850558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3, false)
12860558f521Sad 	    == 1);
12870558f521Sad 	memset(results, 0, sizeof(results));
12880558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3, true)
12890558f521Sad 	    == 1);
1290759124c5Syamt 	assert(results[0] == (void *)0xdeadbea0);
12910558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, false, 1)
1292759124c5Syamt 	    == 0);
12930558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, true, 1)
1294759124c5Syamt 	    == 0);
12950558f521Sad 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3,
12960558f521Sad 	    false, 1) == 0);
12970558f521Sad 	assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3,
12980558f521Sad 	    true, 1) == 0);
129962e2ded6Syamt 	assert(!radix_tree_get_tag(t, 1000, 1));
13000558f521Sad 	assert(!radix_tree_get_tag(t, 1000, 2));
13010558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2 | 1) == 0);
1302d1036328Syamt 	assert(radix_tree_empty_tagged_tree_p(t, 1));
13030558f521Sad 	assert(radix_tree_empty_tagged_tree_p(t, 2));
13040558f521Sad 	radix_tree_set_tag(t, 1000, 2);
13050558f521Sad 	assert(!radix_tree_get_tag(t, 1000, 1));
13060558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2));
13070558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2 | 1) == 2);
13080558f521Sad 	assert(radix_tree_empty_tagged_tree_p(t, 1));
13090558f521Sad 	assert(!radix_tree_empty_tagged_tree_p(t, 2));
131062e2ded6Syamt 	radix_tree_dump(t);
131162e2ded6Syamt 	assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
131262e2ded6Syamt 	assert(radix_tree_insert_node(t, 0, (void *)0xbea0) == 0);
131362e2ded6Syamt 	radix_tree_dump(t);
131462e2ded6Syamt 	assert(radix_tree_lookup_node(t, 0) == (void *)0xbea0);
131562e2ded6Syamt 	assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
131662e2ded6Syamt 	assert(radix_tree_insert_node(t, UINT64_C(10000000000), (void *)0xdea0)
131762e2ded6Syamt 	    == 0);
131862e2ded6Syamt 	radix_tree_dump(t);
131962e2ded6Syamt 	assert(radix_tree_lookup_node(t, 0) == (void *)0xbea0);
132062e2ded6Syamt 	assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
132162e2ded6Syamt 	assert(radix_tree_lookup_node(t, UINT64_C(10000000000)) ==
132262e2ded6Syamt 	    (void *)0xdea0);
132362e2ded6Syamt 	radix_tree_dump(t);
13240558f521Sad 	assert(!radix_tree_get_tag(t, 0, 2));
13250558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2));
132662e2ded6Syamt 	assert(!radix_tree_get_tag(t, UINT64_C(10000000000), 1));
13278012ca3fSmsaitoh 	radix_tree_set_tag(t, 0, 2);
13280558f521Sad 	radix_tree_set_tag(t, UINT64_C(10000000000), 2);
132962e2ded6Syamt 	radix_tree_dump(t);
13300558f521Sad 	assert(radix_tree_get_tag(t, 0, 2));
13310558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2));
13320558f521Sad 	assert(radix_tree_get_tag(t, UINT64_C(10000000000), 2));
13338012ca3fSmsaitoh 	radix_tree_clear_tag(t, 0, 2);
13340558f521Sad 	radix_tree_clear_tag(t, UINT64_C(10000000000), 2);
133562e2ded6Syamt 	radix_tree_dump(t);
13360558f521Sad 	assert(!radix_tree_get_tag(t, 0, 2));
13370558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2));
13380558f521Sad 	assert(!radix_tree_get_tag(t, UINT64_C(10000000000), 2));
133962e2ded6Syamt 	radix_tree_dump(t);
134062e2ded6Syamt 	assert(radix_tree_replace_node(t, 1000, (void *)0x12345678) ==
134162e2ded6Syamt 	    (void *)0xdeadbea0);
13420558f521Sad 	assert(!radix_tree_get_tag(t, 1000, 1));
13430558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2));
13440558f521Sad 	assert(radix_tree_get_tag(t, 1000, 2 | 1) == 2);
13450558f521Sad 	memset(results, 0, sizeof(results));
13460558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, false) == 3);
134762e2ded6Syamt 	assert(results[0] == (void *)0xbea0);
134862e2ded6Syamt 	assert(results[1] == (void *)0x12345678);
134962e2ded6Syamt 	assert(results[2] == (void *)0xdea0);
13500558f521Sad 	memset(results, 0, sizeof(results));
13510558f521Sad 	assert(radix_tree_gang_lookup_node(t, 0, results, 3, true) == 1);
13520558f521Sad 	assert(results[0] == (void *)0xbea0);
13530558f521Sad 	memset(results, 0, sizeof(results));
13540558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1, results, 3, false) == 2);
135562e2ded6Syamt 	assert(results[0] == (void *)0x12345678);
135662e2ded6Syamt 	assert(results[1] == (void *)0xdea0);
13570558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1, results, 3, true) == 0);
13580558f521Sad 	memset(results, 0, sizeof(results));
13590558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1001, results, 3, false) == 1);
136062e2ded6Syamt 	assert(results[0] == (void *)0xdea0);
13610558f521Sad 	assert(radix_tree_gang_lookup_node(t, 1001, results, 3, true) == 0);
13620558f521Sad 	assert(radix_tree_gang_lookup_node(t, UINT64_C(10000000001), results, 3,
13630558f521Sad 	    false) == 0);
13640558f521Sad 	assert(radix_tree_gang_lookup_node(t, UINT64_C(10000000001), results, 3,
13650558f521Sad 	    true) == 0);
136662e2ded6Syamt 	assert(radix_tree_gang_lookup_node(t, UINT64_C(1000000000000), results,
13670558f521Sad 	    3, false) == 0);
13680558f521Sad 	assert(radix_tree_gang_lookup_node(t, UINT64_C(1000000000000), results,
13690558f521Sad 	    3, true) == 0);
13700558f521Sad 	memset(results, 0, sizeof(results));
13710558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 100, false, 2)
13720558f521Sad 	    == 1);
137362e2ded6Syamt 	assert(results[0] == (void *)0x12345678);
13740558f521Sad 	assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 100, true, 2)
13750558f521Sad 	    == 0);
137662e2ded6Syamt 	assert(entry_tagmask(t->t_root) != 0);
137762e2ded6Syamt 	assert(radix_tree_remove_node(t, 1000) == (void *)0x12345678);
137862e2ded6Syamt 	assert(entry_tagmask(t->t_root) == 0);
137962e2ded6Syamt 	radix_tree_dump(t);
13800558f521Sad 	assert(radix_tree_insert_node(t, UINT64_C(10000000001), (void *)0xfff0)
13810558f521Sad 	    == 0);
13820558f521Sad 	memset(results, 0, sizeof(results));
13830558f521Sad 	assert(radix_tree_gang_lookup_node(t, UINT64_C(10000000000), results, 3,
13840558f521Sad 	    false) == 2);
13850558f521Sad 	assert(results[0] == (void *)0xdea0);
13860558f521Sad 	assert(results[1] == (void *)0xfff0);
13870558f521Sad 	memset(results, 0, sizeof(results));
13880558f521Sad 	assert(radix_tree_gang_lookup_node(t, UINT64_C(10000000000), results, 3,
13890558f521Sad 	    true) == 2);
13900558f521Sad 	assert(results[0] == (void *)0xdea0);
13910558f521Sad 	assert(results[1] == (void *)0xfff0);
13920558f521Sad 	memset(results, 0, sizeof(results));
13930558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, UINT64_C(10000000001),
13940558f521Sad 	    results, 3, false) == 3);
13950558f521Sad 	assert(results[0] == (void *)0xfff0);
13960558f521Sad 	assert(results[1] == (void *)0xdea0);
13970558f521Sad 	assert(results[2] == (void *)0xbea0);
13980558f521Sad 	memset(results, 0, sizeof(results));
13990558f521Sad 	assert(radix_tree_gang_lookup_node_reverse(t, UINT64_C(10000000001),
14000558f521Sad 	    results, 3, true) == 2);
14010558f521Sad 	assert(results[0] == (void *)0xfff0);
14020558f521Sad 	assert(results[1] == (void *)0xdea0);
140362e2ded6Syamt 	assert(radix_tree_remove_node(t, UINT64_C(10000000000)) ==
140462e2ded6Syamt 	    (void *)0xdea0);
14050558f521Sad 	assert(radix_tree_remove_node(t, UINT64_C(10000000001)) ==
14060558f521Sad 	    (void *)0xfff0);
140762e2ded6Syamt 	radix_tree_dump(t);
140862e2ded6Syamt 	assert(radix_tree_remove_node(t, 0) == (void *)0xbea0);
140962e2ded6Syamt 	radix_tree_dump(t);
141062e2ded6Syamt 	radix_tree_fini_tree(t);
141162e2ded6Syamt }
141262e2ded6Syamt 
141362e2ded6Syamt #include <sys/time.h>
141462e2ded6Syamt 
141562e2ded6Syamt struct testnode {
141662e2ded6Syamt 	uint64_t idx;
141782698115Syamt 	bool tagged[RADIX_TREE_TAG_ID_MAX];
141862e2ded6Syamt };
141962e2ded6Syamt 
142062e2ded6Syamt static void
printops(const char * title,const char * name,int tag,unsigned int n,const struct timeval * stv,const struct timeval * etv)1421e4944d44Syamt printops(const char *title, const char *name, int tag, unsigned int n,
1422e4944d44Syamt     const struct timeval *stv, const struct timeval *etv)
142362e2ded6Syamt {
142462e2ded6Syamt 	uint64_t s = stv->tv_sec * 1000000 + stv->tv_usec;
142562e2ded6Syamt 	uint64_t e = etv->tv_sec * 1000000 + etv->tv_usec;
142662e2ded6Syamt 
1427e4944d44Syamt 	printf("RESULT %s %s %d %lf op/s\n", title, name, tag,
1428e4944d44Syamt 	    (double)n / (e - s) * 1000000);
142962e2ded6Syamt }
143062e2ded6Syamt 
143162e2ded6Syamt #define	TEST2_GANG_LOOKUP_NODES	16
143262e2ded6Syamt 
143362e2ded6Syamt static bool
test2_should_tag(unsigned int i,unsigned int tagid)14340558f521Sad test2_should_tag(unsigned int i, unsigned int tagid)
143562e2ded6Syamt {
143662e2ded6Syamt 
143762e2ded6Syamt 	if (tagid == 0) {
14380558f521Sad 		return (i % 4) == 0;	/* 25% */
143962e2ded6Syamt 	} else {
1440e4944d44Syamt 		return (i % 7) == 0;	/* 14% */
144162e2ded6Syamt 	}
14420558f521Sad 	return 1;
14430558f521Sad }
14440558f521Sad 
14450558f521Sad static void
check_tag_count(const unsigned int * ntagged,unsigned int tagmask,unsigned int count)14460558f521Sad check_tag_count(const unsigned int *ntagged, unsigned int tagmask,
14470558f521Sad     unsigned int count)
14480558f521Sad {
14490558f521Sad 	unsigned int tag;
14500558f521Sad 
14510558f521Sad 	for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
14520558f521Sad 		if ((tagmask & (1 << tag)) == 0) {
14530558f521Sad 			continue;
14540558f521Sad 		}
14550558f521Sad 		if (((tagmask - 1) & tagmask) == 0) {
14560558f521Sad 			assert(count == ntagged[tag]);
14570558f521Sad 		} else {
14580558f521Sad 			assert(count >= ntagged[tag]);
14590558f521Sad 		}
14600558f521Sad 	}
146162e2ded6Syamt }
146262e2ded6Syamt 
146362e2ded6Syamt static void
test2(const char * title,bool dense)1464e4944d44Syamt test2(const char *title, bool dense)
146562e2ded6Syamt {
146662e2ded6Syamt 	struct radix_tree s;
146762e2ded6Syamt 	struct radix_tree *t = &s;
146862e2ded6Syamt 	struct testnode *n;
146962e2ded6Syamt 	unsigned int i;
147062e2ded6Syamt 	unsigned int nnodes = 100000;
147162e2ded6Syamt 	unsigned int removed;
14720558f521Sad 	unsigned int tag;
14730558f521Sad 	unsigned int tagmask;
147462e2ded6Syamt 	unsigned int ntagged[RADIX_TREE_TAG_ID_MAX];
147562e2ded6Syamt 	struct testnode *nodes;
147662e2ded6Syamt 	struct timeval stv;
147762e2ded6Syamt 	struct timeval etv;
147862e2ded6Syamt 
147962e2ded6Syamt 	nodes = malloc(nnodes * sizeof(*nodes));
148062e2ded6Syamt 	for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
148162e2ded6Syamt 		ntagged[tag] = 0;
148262e2ded6Syamt 	}
148362e2ded6Syamt 	radix_tree_init_tree(t);
148462e2ded6Syamt 	for (i = 0; i < nnodes; i++) {
148562e2ded6Syamt 		n = &nodes[i];
148662e2ded6Syamt 		n->idx = random();
148762e2ded6Syamt 		if (sizeof(long) == 4) {
148862e2ded6Syamt 			n->idx <<= 32;
148962e2ded6Syamt 			n->idx |= (uint32_t)random();
149062e2ded6Syamt 		}
149162e2ded6Syamt 		if (dense) {
149262e2ded6Syamt 			n->idx %= nnodes * 2;
149362e2ded6Syamt 		}
149462e2ded6Syamt 		while (radix_tree_lookup_node(t, n->idx) != NULL) {
149562e2ded6Syamt 			n->idx++;
149662e2ded6Syamt 		}
149762e2ded6Syamt 		radix_tree_insert_node(t, n->idx, n);
149862e2ded6Syamt 		for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
14990558f521Sad 			tagmask = 1 << tag;
15000558f521Sad 
150182698115Syamt 			n->tagged[tag] = test2_should_tag(i, tag);
150282698115Syamt 			if (n->tagged[tag]) {
15030558f521Sad 				radix_tree_set_tag(t, n->idx, tagmask);
150462e2ded6Syamt 				ntagged[tag]++;
150562e2ded6Syamt 			}
15060558f521Sad 			assert((n->tagged[tag] ? tagmask : 0) ==
15070558f521Sad 			    radix_tree_get_tag(t, n->idx, tagmask));
150862e2ded6Syamt 		}
150962e2ded6Syamt 	}
151062e2ded6Syamt 
151162e2ded6Syamt 	gettimeofday(&stv, NULL);
151262e2ded6Syamt 	for (i = 0; i < nnodes; i++) {
151362e2ded6Syamt 		n = &nodes[i];
151462e2ded6Syamt 		assert(radix_tree_lookup_node(t, n->idx) == n);
151562e2ded6Syamt 	}
151662e2ded6Syamt 	gettimeofday(&etv, NULL);
1517e4944d44Syamt 	printops(title, "lookup", 0, nnodes, &stv, &etv);
151862e2ded6Syamt 
15190558f521Sad 	for (tagmask = 1; tagmask <= RADIX_TREE_TAG_MASK; tagmask ++) {
152082698115Syamt 		unsigned int count = 0;
152182698115Syamt 
152262e2ded6Syamt 		gettimeofday(&stv, NULL);
152362e2ded6Syamt 		for (i = 0; i < nnodes; i++) {
15240558f521Sad 			unsigned int tagged;
152582698115Syamt 
152662e2ded6Syamt 			n = &nodes[i];
15270558f521Sad 			tagged = radix_tree_get_tag(t, n->idx, tagmask);
15280558f521Sad 			assert((tagged & ~tagmask) == 0);
15290558f521Sad 			for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
15300558f521Sad 				assert((tagmask & (1 << tag)) == 0 ||
15310558f521Sad 				    n->tagged[tag] == !!(tagged & (1 << tag)));
15320558f521Sad 			}
153382698115Syamt 			if (tagged) {
153482698115Syamt 				count++;
153582698115Syamt 			}
153662e2ded6Syamt 		}
153762e2ded6Syamt 		gettimeofday(&etv, NULL);
15380558f521Sad 		check_tag_count(ntagged, tagmask, count);
15390558f521Sad 		printops(title, "get_tag", tagmask, nnodes, &stv, &etv);
154062e2ded6Syamt 	}
154162e2ded6Syamt 
154262e2ded6Syamt 	gettimeofday(&stv, NULL);
154362e2ded6Syamt 	for (i = 0; i < nnodes; i++) {
154462e2ded6Syamt 		n = &nodes[i];
154562e2ded6Syamt 		radix_tree_remove_node(t, n->idx);
154662e2ded6Syamt 	}
154762e2ded6Syamt 	gettimeofday(&etv, NULL);
1548e4944d44Syamt 	printops(title, "remove", 0, nnodes, &stv, &etv);
154962e2ded6Syamt 
155062e2ded6Syamt 	gettimeofday(&stv, NULL);
155162e2ded6Syamt 	for (i = 0; i < nnodes; i++) {
155262e2ded6Syamt 		n = &nodes[i];
155362e2ded6Syamt 		radix_tree_insert_node(t, n->idx, n);
155462e2ded6Syamt 	}
155562e2ded6Syamt 	gettimeofday(&etv, NULL);
1556e4944d44Syamt 	printops(title, "insert", 0, nnodes, &stv, &etv);
155762e2ded6Syamt 
155862e2ded6Syamt 	for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
15590558f521Sad 		tagmask = 1 << tag;
15600558f521Sad 
156162e2ded6Syamt 		ntagged[tag] = 0;
156262e2ded6Syamt 		gettimeofday(&stv, NULL);
156362e2ded6Syamt 		for (i = 0; i < nnodes; i++) {
156462e2ded6Syamt 			n = &nodes[i];
156582698115Syamt 			if (n->tagged[tag]) {
15660558f521Sad 				radix_tree_set_tag(t, n->idx, tagmask);
156762e2ded6Syamt 				ntagged[tag]++;
156862e2ded6Syamt 			}
156962e2ded6Syamt 		}
157062e2ded6Syamt 		gettimeofday(&etv, NULL);
1571e4944d44Syamt 		printops(title, "set_tag", tag, ntagged[tag], &stv, &etv);
157262e2ded6Syamt 	}
157362e2ded6Syamt 
157462e2ded6Syamt 	gettimeofday(&stv, NULL);
157562e2ded6Syamt 	{
157662e2ded6Syamt 		struct testnode *results[TEST2_GANG_LOOKUP_NODES];
157762e2ded6Syamt 		uint64_t nextidx;
157862e2ded6Syamt 		unsigned int nfound;
157962e2ded6Syamt 		unsigned int total;
158062e2ded6Syamt 
158162e2ded6Syamt 		nextidx = 0;
158262e2ded6Syamt 		total = 0;
158362e2ded6Syamt 		while ((nfound = radix_tree_gang_lookup_node(t, nextidx,
15840558f521Sad 		    (void *)results, __arraycount(results), false)) > 0) {
158562e2ded6Syamt 			nextidx = results[nfound - 1]->idx + 1;
158662e2ded6Syamt 			total += nfound;
1587759124c5Syamt 			if (nextidx == 0) {
1588759124c5Syamt 				break;
1589759124c5Syamt 			}
159062e2ded6Syamt 		}
159162e2ded6Syamt 		assert(total == nnodes);
159262e2ded6Syamt 	}
159362e2ded6Syamt 	gettimeofday(&etv, NULL);
1594e4944d44Syamt 	printops(title, "ganglookup", 0, nnodes, &stv, &etv);
159562e2ded6Syamt 
1596759124c5Syamt 	gettimeofday(&stv, NULL);
1597759124c5Syamt 	{
1598759124c5Syamt 		struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1599759124c5Syamt 		uint64_t nextidx;
1600759124c5Syamt 		unsigned int nfound;
1601759124c5Syamt 		unsigned int total;
1602759124c5Syamt 
1603759124c5Syamt 		nextidx = UINT64_MAX;
1604759124c5Syamt 		total = 0;
1605759124c5Syamt 		while ((nfound = radix_tree_gang_lookup_node_reverse(t, nextidx,
16060558f521Sad 		    (void *)results, __arraycount(results), false)) > 0) {
1607759124c5Syamt 			nextidx = results[nfound - 1]->idx - 1;
1608759124c5Syamt 			total += nfound;
1609759124c5Syamt 			if (nextidx == UINT64_MAX) {
1610759124c5Syamt 				break;
1611759124c5Syamt 			}
1612759124c5Syamt 		}
1613759124c5Syamt 		assert(total == nnodes);
1614759124c5Syamt 	}
1615759124c5Syamt 	gettimeofday(&etv, NULL);
1616759124c5Syamt 	printops(title, "ganglookup_reverse", 0, nnodes, &stv, &etv);
1617759124c5Syamt 
16180558f521Sad 	for (tagmask = 1; tagmask <= RADIX_TREE_TAG_MASK; tagmask ++) {
16190558f521Sad 		unsigned int total = 0;
16200558f521Sad 
162162e2ded6Syamt 		gettimeofday(&stv, NULL);
162262e2ded6Syamt 		{
162362e2ded6Syamt 			struct testnode *results[TEST2_GANG_LOOKUP_NODES];
162462e2ded6Syamt 			uint64_t nextidx;
162562e2ded6Syamt 			unsigned int nfound;
162662e2ded6Syamt 
162762e2ded6Syamt 			nextidx = 0;
162862e2ded6Syamt 			while ((nfound = radix_tree_gang_lookup_tagged_node(t,
162962e2ded6Syamt 			    nextidx, (void *)results, __arraycount(results),
16300558f521Sad 			    false, tagmask)) > 0) {
163162e2ded6Syamt 				nextidx = results[nfound - 1]->idx + 1;
163262e2ded6Syamt 				total += nfound;
163362e2ded6Syamt 			}
163462e2ded6Syamt 		}
163562e2ded6Syamt 		gettimeofday(&etv, NULL);
16360558f521Sad 		check_tag_count(ntagged, tagmask, total);
16370558f521Sad 		assert(tagmask != 0 || total == 0);
16380558f521Sad 		printops(title, "ganglookup_tag", tagmask, total, &stv, &etv);
163962e2ded6Syamt 	}
164062e2ded6Syamt 
16410558f521Sad 	for (tagmask = 1; tagmask <= RADIX_TREE_TAG_MASK; tagmask ++) {
16420558f521Sad 		unsigned int total = 0;
16430558f521Sad 
1644759124c5Syamt 		gettimeofday(&stv, NULL);
1645759124c5Syamt 		{
1646759124c5Syamt 			struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1647759124c5Syamt 			uint64_t nextidx;
1648759124c5Syamt 			unsigned int nfound;
1649759124c5Syamt 
1650759124c5Syamt 			nextidx = UINT64_MAX;
1651759124c5Syamt 			while ((nfound =
1652759124c5Syamt 			    radix_tree_gang_lookup_tagged_node_reverse(t,
1653759124c5Syamt 			    nextidx, (void *)results, __arraycount(results),
16540558f521Sad 			    false, tagmask)) > 0) {
1655759124c5Syamt 				nextidx = results[nfound - 1]->idx - 1;
1656759124c5Syamt 				total += nfound;
1657759124c5Syamt 				if (nextidx == UINT64_MAX) {
1658759124c5Syamt 					break;
1659759124c5Syamt 				}
1660759124c5Syamt 			}
1661759124c5Syamt 		}
1662759124c5Syamt 		gettimeofday(&etv, NULL);
16630558f521Sad 		check_tag_count(ntagged, tagmask, total);
16640558f521Sad 		assert(tagmask != 0 || total == 0);
16650558f521Sad 		printops(title, "ganglookup_tag_reverse", tagmask, total,
1666759124c5Syamt 		    &stv, &etv);
1667759124c5Syamt 	}
1668759124c5Syamt 
166962e2ded6Syamt 	removed = 0;
167062e2ded6Syamt 	for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
167162e2ded6Syamt 		unsigned int total;
167262e2ded6Syamt 
167362e2ded6Syamt 		total = 0;
16740558f521Sad 		tagmask = 1 << tag;
167562e2ded6Syamt 		gettimeofday(&stv, NULL);
167662e2ded6Syamt 		{
167762e2ded6Syamt 			struct testnode *results[TEST2_GANG_LOOKUP_NODES];
167862e2ded6Syamt 			uint64_t nextidx;
167962e2ded6Syamt 			unsigned int nfound;
168062e2ded6Syamt 
168162e2ded6Syamt 			nextidx = 0;
168262e2ded6Syamt 			while ((nfound = radix_tree_gang_lookup_tagged_node(t,
168362e2ded6Syamt 			    nextidx, (void *)results, __arraycount(results),
16840558f521Sad 			    false, tagmask)) > 0) {
168562e2ded6Syamt 				for (i = 0; i < nfound; i++) {
168662e2ded6Syamt 					radix_tree_remove_node(t,
168762e2ded6Syamt 					    results[i]->idx);
168862e2ded6Syamt 				}
168962e2ded6Syamt 				nextidx = results[nfound - 1]->idx + 1;
169062e2ded6Syamt 				total += nfound;
1691759124c5Syamt 				if (nextidx == 0) {
1692759124c5Syamt 					break;
1693759124c5Syamt 				}
169462e2ded6Syamt 			}
169562e2ded6Syamt 		}
169662e2ded6Syamt 		gettimeofday(&etv, NULL);
16970558f521Sad 		if (tag == 0) {
16980558f521Sad 			check_tag_count(ntagged, tagmask, total);
16990558f521Sad 		} else {
17000558f521Sad 			assert(total <= ntagged[tag]);
17010558f521Sad 		}
17020558f521Sad 		printops(title, "ganglookup_tag+remove", tagmask, total, &stv,
1703e4944d44Syamt 		    &etv);
170462e2ded6Syamt 		removed += total;
170562e2ded6Syamt 	}
170662e2ded6Syamt 
170762e2ded6Syamt 	gettimeofday(&stv, NULL);
170862e2ded6Syamt 	{
170962e2ded6Syamt 		struct testnode *results[TEST2_GANG_LOOKUP_NODES];
171062e2ded6Syamt 		uint64_t nextidx;
171162e2ded6Syamt 		unsigned int nfound;
171262e2ded6Syamt 		unsigned int total;
171362e2ded6Syamt 
171462e2ded6Syamt 		nextidx = 0;
171562e2ded6Syamt 		total = 0;
171662e2ded6Syamt 		while ((nfound = radix_tree_gang_lookup_node(t, nextidx,
17170558f521Sad 		    (void *)results, __arraycount(results), false)) > 0) {
171862e2ded6Syamt 			for (i = 0; i < nfound; i++) {
171962e2ded6Syamt 				assert(results[i] == radix_tree_remove_node(t,
172062e2ded6Syamt 				    results[i]->idx));
172162e2ded6Syamt 			}
172262e2ded6Syamt 			nextidx = results[nfound - 1]->idx + 1;
172362e2ded6Syamt 			total += nfound;
1724759124c5Syamt 			if (nextidx == 0) {
1725759124c5Syamt 				break;
1726759124c5Syamt 			}
172762e2ded6Syamt 		}
172862e2ded6Syamt 		assert(total == nnodes - removed);
172962e2ded6Syamt 	}
173062e2ded6Syamt 	gettimeofday(&etv, NULL);
1731e4944d44Syamt 	printops(title, "ganglookup+remove", 0, nnodes - removed, &stv, &etv);
173262e2ded6Syamt 
1733d1036328Syamt 	assert(radix_tree_empty_tree_p(t));
17340558f521Sad 	for (tagmask = 1; tagmask <= RADIX_TREE_TAG_MASK; tagmask ++) {
17350558f521Sad 		assert(radix_tree_empty_tagged_tree_p(t, tagmask));
17360558f521Sad 	}
173762e2ded6Syamt 	radix_tree_fini_tree(t);
173862e2ded6Syamt 	free(nodes);
173962e2ded6Syamt }
174062e2ded6Syamt 
174162e2ded6Syamt int
main(int argc,char * argv[])174262e2ded6Syamt main(int argc, char *argv[])
174362e2ded6Syamt {
174462e2ded6Syamt 
174562e2ded6Syamt 	test1();
1746e4944d44Syamt 	test2("dense", true);
1747e4944d44Syamt 	test2("sparse", false);
174862e2ded6Syamt 	return 0;
174962e2ded6Syamt }
175062e2ded6Syamt 
175162e2ded6Syamt #endif /* defined(UNITTEST) */
1752