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