radixtree.c revision 1.8 1 /* $NetBSD: radixtree.c,v 1.8 2011/10/14 15:15:27 yamt Exp $ */
2
3 /*-
4 * Copyright (c)2011 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * radix tree
31 *
32 * it's designed to work efficiently with dense index distribution.
33 * the memory consumption (number of necessary intermediate nodes)
34 * heavily depends on index distribution. basically, more dense index
35 * distribution consumes less nodes per item.
36 * approximately,
37 * the best case: about RADIX_TREE_PTR_PER_NODE items per node.
38 * the worst case: RADIX_TREE_MAX_HEIGHT nodes per item.
39 */
40
41 #include <sys/cdefs.h>
42
43 #if defined(_KERNEL) || defined(_STANDALONE)
44 __KERNEL_RCSID(0, "$NetBSD: radixtree.c,v 1.8 2011/10/14 15:15:27 yamt Exp $");
45 #include <sys/param.h>
46 #include <sys/errno.h>
47 #include <sys/pool.h>
48 #include <sys/radixtree.h>
49 #include <lib/libkern/libkern.h>
50 #if defined(_STANDALONE)
51 #include <lib/libsa/stand.h>
52 #endif /* defined(_STANDALONE) */
53 #else /* defined(_KERNEL) || defined(_STANDALONE) */
54 __RCSID("$NetBSD: radixtree.c,v 1.8 2011/10/14 15:15:27 yamt Exp $");
55 #include <assert.h>
56 #include <errno.h>
57 #include <stdbool.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #if 1
61 #define KASSERT assert
62 #else
63 #define KASSERT(a) /* nothing */
64 #endif
65 #endif /* defined(_KERNEL) || defined(_STANDALONE) */
66
67 #include <sys/radixtree.h>
68
69 #define RADIX_TREE_BITS_PER_HEIGHT 4 /* XXX tune */
70 #define RADIX_TREE_PTR_PER_NODE (1 << RADIX_TREE_BITS_PER_HEIGHT)
71 #define RADIX_TREE_MAX_HEIGHT (64 / RADIX_TREE_BITS_PER_HEIGHT)
72 __CTASSERT((64 % RADIX_TREE_BITS_PER_HEIGHT) == 0);
73
74 __CTASSERT(((1 << RADIX_TREE_TAG_ID_MAX) & (sizeof(int) - 1)) == 0);
75 #define RADIX_TREE_TAG_MASK ((1 << RADIX_TREE_TAG_ID_MAX) - 1)
76
77 static inline void *
78 entry_ptr(void *p)
79 {
80
81 return (void *)((uintptr_t)p & ~RADIX_TREE_TAG_MASK);
82 }
83
84 static inline unsigned int
85 entry_tagmask(void *p)
86 {
87
88 return (uintptr_t)p & RADIX_TREE_TAG_MASK;
89 }
90
91 static inline void *
92 entry_compose(void *p, unsigned int tagmask)
93 {
94
95 return (void *)((uintptr_t)p | tagmask);
96 }
97
98 static inline bool
99 entry_match_p(void *p, unsigned int tagmask)
100 {
101
102 KASSERT(entry_ptr(p) != NULL || entry_tagmask(p) == 0);
103 if (p == NULL) {
104 return false;
105 }
106 if (tagmask == 0) {
107 return true;
108 }
109 return (entry_tagmask(p) & tagmask) != 0;
110 }
111
112 static inline unsigned int
113 tagid_to_mask(radix_tree_tagid_t id)
114 {
115
116 KASSERT(id >= 0);
117 KASSERT(id < RADIX_TREE_TAG_ID_MAX);
118 return 1U << id;
119 }
120
121 /*
122 * radix_tree_node: an intermediate node
123 *
124 * we don't care the type of leaf nodes. they are just void *.
125 */
126
127 struct radix_tree_node {
128 void *n_ptrs[RADIX_TREE_PTR_PER_NODE];
129 unsigned int n_nptrs; /* # of non-NULL pointers in n_ptrs */
130 };
131
132 /*
133 * any_children_tagmask:
134 *
135 * return OR'ed tagmask of the given node's children.
136 */
137
138 static unsigned int
139 any_children_tagmask(struct radix_tree_node *n)
140 {
141 unsigned int mask;
142 int i;
143
144 mask = 0;
145 for (i = 0; i < RADIX_TREE_PTR_PER_NODE; i++) {
146 mask |= (unsigned int)(uintptr_t)n->n_ptrs[i];
147 }
148 return mask & RADIX_TREE_TAG_MASK;
149 }
150
151 /*
152 * p_refs[0].pptr == &t->t_root
153 * :
154 * p_refs[n].pptr == &(*p_refs[n-1])->n_ptrs[x]
155 * :
156 * :
157 * p_refs[t->t_height].pptr == &leaf_pointer
158 */
159
160 struct radix_tree_path {
161 struct radix_tree_node_ref {
162 void **pptr;
163 } p_refs[RADIX_TREE_MAX_HEIGHT + 1]; /* +1 for the root ptr */
164 int p_lastidx;
165 };
166
167 static inline void **
168 path_pptr(struct radix_tree *t, struct radix_tree_path *p,
169 unsigned int height)
170 {
171
172 KASSERT(height <= t->t_height);
173 return p->p_refs[height].pptr;
174 }
175
176 static inline struct radix_tree_node *
177 path_node(struct radix_tree * t, struct radix_tree_path *p, unsigned int height)
178 {
179
180 KASSERT(height <= t->t_height);
181 return entry_ptr(*path_pptr(t, p, height));
182 }
183
184 static inline unsigned int
185 path_idx(struct radix_tree * t, struct radix_tree_path *p, unsigned int height)
186 {
187
188 KASSERT(height <= t->t_height);
189 return path_pptr(t, p, height + 1) - path_node(t, p, height)->n_ptrs;
190 }
191
192 /*
193 * radix_tree_init_tree:
194 *
195 * initialize a tree.
196 */
197
198 void
199 radix_tree_init_tree(struct radix_tree *t)
200 {
201
202 t->t_height = 0;
203 t->t_root = NULL;
204 }
205
206 /*
207 * radix_tree_init_tree:
208 *
209 * clean up a tree.
210 */
211
212 void
213 radix_tree_fini_tree(struct radix_tree *t)
214 {
215
216 KASSERT(t->t_root == NULL);
217 KASSERT(t->t_height == 0);
218 }
219
220 static void
221 radix_tree_node_init(struct radix_tree_node *n)
222 {
223
224 memset(n, 0, sizeof(*n));
225 }
226
227 #if defined(_KERNEL)
228 pool_cache_t radix_tree_node_cache __read_mostly;
229
230 static int
231 radix_tree_node_ctor(void *dummy, void *item, int flags)
232 {
233 struct radix_tree_node *n = item;
234
235 KASSERT(dummy == NULL);
236 radix_tree_node_init(n);
237 return 0;
238 }
239
240 /*
241 * radix_tree_init:
242 *
243 * initialize the subsystem.
244 */
245
246 void
247 radix_tree_init(void)
248 {
249
250 radix_tree_node_cache = pool_cache_init(sizeof(struct radix_tree_node),
251 0, 0, 0, "radix_tree_node", NULL, IPL_NONE, radix_tree_node_ctor,
252 NULL, NULL);
253 KASSERT(radix_tree_node_cache != NULL);
254 }
255 #endif /* defined(_KERNEL) */
256
257 static bool __unused
258 radix_tree_node_clean_p(const struct radix_tree_node *n)
259 {
260 unsigned int i;
261
262 if (n->n_nptrs != 0) {
263 return false;
264 }
265 for (i = 0; i < RADIX_TREE_PTR_PER_NODE; i++) {
266 if (n->n_ptrs[i] != NULL) {
267 return false;
268 }
269 }
270 return true;
271 }
272
273 static struct radix_tree_node *
274 radix_tree_alloc_node(void)
275 {
276 struct radix_tree_node *n;
277
278 #if defined(_KERNEL)
279 n = pool_cache_get(radix_tree_node_cache, PR_NOWAIT);
280 #else /* defined(_KERNEL) */
281 #if defined(_STANDALONE)
282 n = alloc(sizeof(*n));
283 #else /* defined(_STANDALONE) */
284 n = malloc(sizeof(*n));
285 #endif /* defined(_STANDALONE) */
286 if (n != NULL) {
287 radix_tree_node_init(n);
288 }
289 #endif /* defined(_KERNEL) */
290 KASSERT(n == NULL || radix_tree_node_clean_p(n));
291 return n;
292 }
293
294 static void
295 radix_tree_free_node(struct radix_tree_node *n)
296 {
297
298 KASSERT(radix_tree_node_clean_p(n));
299 #if defined(_KERNEL)
300 pool_cache_put(radix_tree_node_cache, n);
301 #elif defined(_STANDALONE)
302 dealloc(n, sizeof(*n));
303 #else
304 free(n);
305 #endif
306 }
307
308 static int
309 radix_tree_grow(struct radix_tree *t, unsigned int newheight)
310 {
311 const unsigned int tagmask = entry_tagmask(t->t_root);
312
313 KASSERT(newheight <= 64 / RADIX_TREE_BITS_PER_HEIGHT);
314 if (t->t_root == NULL) {
315 t->t_height = newheight;
316 return 0;
317 }
318 while (t->t_height < newheight) {
319 struct radix_tree_node *n;
320
321 n = radix_tree_alloc_node();
322 if (n == NULL) {
323 /*
324 * don't bother to revert our changes.
325 * the caller will likely retry.
326 */
327 return ENOMEM;
328 }
329 n->n_nptrs = 1;
330 n->n_ptrs[0] = t->t_root;
331 t->t_root = entry_compose(n, tagmask);
332 t->t_height++;
333 }
334 return 0;
335 }
336
337 /*
338 * radix_tree_lookup_ptr:
339 *
340 * an internal helper function used for various exported functions.
341 *
342 * return the pointer to store the node for the given index.
343 *
344 * if alloc is true, try to allocate the storage. (note for _KERNEL:
345 * in that case, this function can block.) if the allocation failed or
346 * alloc is false, return NULL.
347 *
348 * if path is not NULL, fill it for the caller's investigation.
349 *
350 * if tagmask is not zero, search only for nodes with the tag set.
351 *
352 * while this function is a bit large, as it's called with some constant
353 * arguments, inlining might have benefits. anyway, a compiler will decide.
354 */
355
356 static inline void **
357 radix_tree_lookup_ptr(struct radix_tree *t, uint64_t idx,
358 struct radix_tree_path *path, bool alloc, const unsigned int tagmask)
359 {
360 struct radix_tree_node *n;
361 int hshift = RADIX_TREE_BITS_PER_HEIGHT * t->t_height;
362 int shift;
363 void **vpp;
364 const uint64_t mask = (UINT64_C(1) << RADIX_TREE_BITS_PER_HEIGHT) - 1;
365 struct radix_tree_node_ref *refs = NULL;
366
367 /*
368 * check unsupported combinations
369 */
370 KASSERT(tagmask == 0 || !alloc);
371 KASSERT(path == NULL || !alloc);
372 vpp = &t->t_root;
373 if (path != NULL) {
374 refs = path->p_refs;
375 refs->pptr = vpp;
376 }
377 n = NULL;
378 for (shift = 64 - RADIX_TREE_BITS_PER_HEIGHT; shift >= 0;) {
379 struct radix_tree_node *c;
380 void *entry;
381 const uint64_t i = (idx >> shift) & mask;
382
383 if (shift >= hshift) {
384 unsigned int newheight;
385
386 KASSERT(vpp == &t->t_root);
387 if (i == 0) {
388 shift -= RADIX_TREE_BITS_PER_HEIGHT;
389 continue;
390 }
391 if (!alloc) {
392 if (path != NULL) {
393 KASSERT((refs - path->p_refs) == 0);
394 path->p_lastidx = 0;
395 }
396 return NULL;
397 }
398 newheight = shift / RADIX_TREE_BITS_PER_HEIGHT + 1;
399 if (radix_tree_grow(t, newheight)) {
400 return NULL;
401 }
402 hshift = RADIX_TREE_BITS_PER_HEIGHT * t->t_height;
403 }
404 entry = *vpp;
405 c = entry_ptr(entry);
406 if (c == NULL ||
407 (tagmask != 0 &&
408 (entry_tagmask(entry) & tagmask) == 0)) {
409 if (!alloc) {
410 if (path != NULL) {
411 path->p_lastidx = refs - path->p_refs;
412 }
413 return NULL;
414 }
415 c = radix_tree_alloc_node();
416 if (c == NULL) {
417 return NULL;
418 }
419 *vpp = c;
420 if (n != NULL) {
421 KASSERT(n->n_nptrs < RADIX_TREE_PTR_PER_NODE);
422 n->n_nptrs++;
423 }
424 }
425 n = c;
426 vpp = &n->n_ptrs[i];
427 if (path != NULL) {
428 refs++;
429 refs->pptr = vpp;
430 }
431 shift -= RADIX_TREE_BITS_PER_HEIGHT;
432 }
433 if (alloc) {
434 KASSERT(*vpp == NULL);
435 if (n != NULL) {
436 KASSERT(n->n_nptrs < RADIX_TREE_PTR_PER_NODE);
437 n->n_nptrs++;
438 }
439 }
440 if (path != NULL) {
441 path->p_lastidx = refs - path->p_refs;
442 }
443 return vpp;
444 }
445
446 /*
447 * radix_tree_insert_node:
448 *
449 * insert the node at idx.
450 * it's illegal to insert NULL.
451 * it's illegal to insert a non-aligned pointer.
452 *
453 * this function returns ENOMEM if necessary memory allocation failed.
454 * otherwise, this function returns 0.
455 *
456 * note that inserting a node can involves memory allocation for intermediate
457 * nodes. if _KERNEL, it's done with non-blocking IPL_NONE memory allocation.
458 *
459 * for the newly inserted node, all tags are cleared.
460 */
461
462 int
463 radix_tree_insert_node(struct radix_tree *t, uint64_t idx, void *p)
464 {
465 void **vpp;
466
467 KASSERT(p != NULL);
468 KASSERT(entry_compose(p, 0) == p);
469 vpp = radix_tree_lookup_ptr(t, idx, NULL, true, 0);
470 if (vpp == NULL) {
471 return ENOMEM;
472 }
473 KASSERT(*vpp == NULL);
474 *vpp = p;
475 return 0;
476 }
477
478 /*
479 * radix_tree_replace_node:
480 *
481 * replace a node at the given index with the given node.
482 * return the old node.
483 * it's illegal to try to replace a node which has not been inserted.
484 *
485 * this function doesn't change tags.
486 */
487
488 void *
489 radix_tree_replace_node(struct radix_tree *t, uint64_t idx, void *p)
490 {
491 void **vpp;
492 void *oldp;
493
494 KASSERT(p != NULL);
495 KASSERT(entry_compose(p, 0) == p);
496 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
497 KASSERT(vpp != NULL);
498 oldp = *vpp;
499 KASSERT(oldp != NULL);
500 *vpp = entry_compose(p, entry_tagmask(*vpp));
501 return entry_ptr(oldp);
502 }
503
504 /*
505 * radix_tree_remove_node:
506 *
507 * remove the node at idx.
508 * it's illegal to try to remove a node which has not been inserted.
509 */
510
511 void *
512 radix_tree_remove_node(struct radix_tree *t, uint64_t idx)
513 {
514 struct radix_tree_path path;
515 void **vpp;
516 void *oldp;
517 int i;
518
519 vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
520 KASSERT(vpp != NULL);
521 oldp = *vpp;
522 KASSERT(oldp != NULL);
523 KASSERT(path.p_lastidx == t->t_height);
524 KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
525 *vpp = NULL;
526 for (i = t->t_height - 1; i >= 0; i--) {
527 void *entry;
528 struct radix_tree_node ** const pptr =
529 (struct radix_tree_node **)path_pptr(t, &path, i);
530 struct radix_tree_node *n;
531
532 KASSERT(pptr != NULL);
533 entry = *pptr;
534 n = entry_ptr(entry);
535 KASSERT(n != NULL);
536 KASSERT(n->n_nptrs > 0);
537 n->n_nptrs--;
538 if (n->n_nptrs > 0) {
539 break;
540 }
541 radix_tree_free_node(n);
542 *pptr = NULL;
543 }
544 /*
545 * fix up height
546 */
547 if (i < 0) {
548 KASSERT(t->t_root == NULL);
549 t->t_height = 0;
550 }
551 /*
552 * update tags
553 */
554 for (; i >= 0; i--) {
555 void *entry;
556 struct radix_tree_node ** const pptr =
557 (struct radix_tree_node **)path_pptr(t, &path, i);
558 struct radix_tree_node *n;
559 unsigned int newmask;
560
561 KASSERT(pptr != NULL);
562 entry = *pptr;
563 n = entry_ptr(entry);
564 KASSERT(n != NULL);
565 KASSERT(n->n_nptrs > 0);
566 newmask = any_children_tagmask(n);
567 if (newmask == entry_tagmask(entry)) {
568 break;
569 }
570 *pptr = entry_compose(n, newmask);
571 }
572 /*
573 * XXX is it worth to try to reduce height?
574 * if we do that, make radix_tree_grow rollback its change as well.
575 */
576 return entry_ptr(oldp);
577 }
578
579 /*
580 * radix_tree_lookup_node:
581 *
582 * returns the node at idx.
583 * returns NULL if nothing is found at idx.
584 */
585
586 void *
587 radix_tree_lookup_node(struct radix_tree *t, uint64_t idx)
588 {
589 void **vpp;
590
591 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
592 if (vpp == NULL) {
593 return NULL;
594 }
595 return entry_ptr(*vpp);
596 }
597
598 static inline void
599 gang_lookup_init(struct radix_tree *t, uint64_t idx,
600 struct radix_tree_path *path, const unsigned int tagmask)
601 {
602 void **vpp;
603
604 vpp = radix_tree_lookup_ptr(t, idx, path, false, tagmask);
605 KASSERT(vpp == NULL ||
606 vpp == path_pptr(t, path, path->p_lastidx));
607 KASSERT(&t->t_root == path_pptr(t, path, 0));
608 }
609
610 static inline unsigned int
611 gang_lookup_scan(struct radix_tree *t, struct radix_tree_path *path,
612 void **results, unsigned int maxresults, const unsigned int tagmask)
613 {
614 void **vpp;
615 int nfound;
616 unsigned int lastidx;
617
618 KASSERT(maxresults > 0);
619 lastidx = path->p_lastidx;
620 if (lastidx == 0) {
621 return 0;
622 }
623 nfound = 0;
624 vpp = path_pptr(t, path, lastidx);
625 while (/*CONSTCOND*/true) {
626 struct radix_tree_node *n;
627 int i;
628
629 if (entry_match_p(*vpp, tagmask)) {
630 KASSERT(lastidx == t->t_height);
631 /*
632 * record the non-NULL leaf.
633 */
634 results[nfound] = entry_ptr(*vpp);
635 nfound++;
636 if (nfound == maxresults) {
637 return nfound;
638 }
639 }
640 scan_siblings:
641 /*
642 * try to find the next non-NULL sibling.
643 */
644 n = path_node(t, path, lastidx - 1);
645 if (*vpp != NULL && n->n_nptrs == 1) {
646 /*
647 * optimization
648 */
649 goto no_siblings;
650 }
651 for (i = path_idx(t, path, lastidx - 1) + 1;
652 i < RADIX_TREE_PTR_PER_NODE;
653 i++) {
654 if (entry_match_p(n->n_ptrs[i], tagmask)) {
655 vpp = &n->n_ptrs[i];
656 path->p_refs[lastidx].pptr = vpp;
657 KASSERT(path_idx(t, path, lastidx - 1)
658 == i);
659 break;
660 }
661 }
662 if (i == RADIX_TREE_PTR_PER_NODE) {
663 no_siblings:
664 /*
665 * not found. go to parent.
666 */
667 lastidx--;
668 if (lastidx == 0) {
669 return nfound;
670 }
671 vpp = path_pptr(t, path, lastidx);
672 goto scan_siblings;
673 }
674 /*
675 * descending the left-most child node, upto the leaf or NULL.
676 */
677 while (entry_match_p(*vpp, tagmask) && lastidx < t->t_height) {
678 n = entry_ptr(*vpp);
679 vpp = &n->n_ptrs[0];
680 lastidx++;
681 path->p_refs[lastidx].pptr = vpp;
682 }
683 }
684 }
685
686 /*
687 * radix_tree_gang_lookup_node:
688 *
689 * search nodes starting from idx in the ascending order.
690 * results should be an array large enough to hold maxresults pointers.
691 * returns the number of nodes found, up to maxresults.
692 * returning less than maxresults means there are no more nodes.
693 *
694 * the result of this function is semantically equivalent to what could be
695 * obtained by repeated calls of radix_tree_lookup_node with increasing index.
696 * but this function is much faster when node indexes are distributed sparsely.
697 *
698 * note that this function doesn't return exact values of node indexes of
699 * found nodes. if they are important for a caller, it's the caller's
700 * responsibility to check them, typically by examinining the returned nodes
701 * using some caller-specific knowledge about them.
702 */
703
704 unsigned int
705 radix_tree_gang_lookup_node(struct radix_tree *t, uint64_t idx,
706 void **results, unsigned int maxresults)
707 {
708 struct radix_tree_path path;
709
710 gang_lookup_init(t, idx, &path, 0);
711 return gang_lookup_scan(t, &path, results, maxresults, 0);
712 }
713
714 /*
715 * radix_tree_gang_lookup_tagged_node:
716 *
717 * same as radix_tree_gang_lookup_node except that this one only returns
718 * nodes tagged with tagid.
719 */
720
721 unsigned int
722 radix_tree_gang_lookup_tagged_node(struct radix_tree *t, uint64_t idx,
723 void **results, unsigned int maxresults, radix_tree_tagid_t tagid)
724 {
725 struct radix_tree_path path;
726 const unsigned int tagmask = tagid_to_mask(tagid);
727
728 gang_lookup_init(t, idx, &path, tagmask);
729 return gang_lookup_scan(t, &path, results, maxresults, tagmask);
730 }
731
732 /*
733 * radix_tree_get_tag:
734 *
735 * return if the tag is set for the node at the given index. (true if set)
736 * it's illegal to call this function for a node which has not been inserted.
737 */
738
739 bool
740 radix_tree_get_tag(struct radix_tree *t, uint64_t idx,
741 radix_tree_tagid_t tagid)
742 {
743 #if 1
744 const unsigned int tagmask = tagid_to_mask(tagid);
745 void **vpp;
746
747 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, tagmask);
748 if (vpp == NULL) {
749 return false;
750 }
751 KASSERT(*vpp != NULL);
752 return (entry_tagmask(*vpp) & tagmask) != 0;
753 #else
754 const unsigned int tagmask = tagid_to_mask(tagid);
755 void **vpp;
756
757 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
758 KASSERT(vpp != NULL);
759 return (entry_tagmask(*vpp) & tagmask) != 0;
760 #endif
761 }
762
763 /*
764 * radix_tree_set_tag:
765 *
766 * set the tag for the node at the given index.
767 * it's illegal to call this function for a node which has not been inserted.
768 */
769
770 void
771 radix_tree_set_tag(struct radix_tree *t, uint64_t idx,
772 radix_tree_tagid_t tagid)
773 {
774 struct radix_tree_path path;
775 const unsigned int tagmask = tagid_to_mask(tagid);
776 void **vpp;
777 int i;
778
779 vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
780 KASSERT(vpp != NULL);
781 KASSERT(*vpp != NULL);
782 KASSERT(path.p_lastidx == t->t_height);
783 KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
784 for (i = t->t_height; i >= 0; i--) {
785 void ** const pptr = (void **)path_pptr(t, &path, i);
786 void *entry;
787
788 KASSERT(pptr != NULL);
789 entry = *pptr;
790 if ((entry_tagmask(entry) & tagmask) != 0) {
791 break;
792 }
793 *pptr = (void *)((uintptr_t)entry | tagmask);
794 }
795 }
796
797 /*
798 * radix_tree_clear_tag:
799 *
800 * clear the tag for the node at the given index.
801 * it's illegal to call this function for a node which has not been inserted.
802 */
803
804 void
805 radix_tree_clear_tag(struct radix_tree *t, uint64_t idx,
806 radix_tree_tagid_t tagid)
807 {
808 struct radix_tree_path path;
809 const unsigned int tagmask = tagid_to_mask(tagid);
810 void **vpp;
811 int i;
812
813 vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
814 KASSERT(vpp != NULL);
815 KASSERT(*vpp != NULL);
816 KASSERT(path.p_lastidx == t->t_height);
817 KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
818 /*
819 * if already cleared, nothing to do
820 */
821 if ((entry_tagmask(*vpp) & tagmask) == 0) {
822 return;
823 }
824 /*
825 * clear the tag only if no children have the tag.
826 */
827 for (i = t->t_height; i >= 0; i--) {
828 void ** const pptr = (void **)path_pptr(t, &path, i);
829 void *entry;
830
831 KASSERT(pptr != NULL);
832 entry = *pptr;
833 KASSERT((entry_tagmask(entry) & tagmask) != 0);
834 *pptr = entry_compose(entry_ptr(entry),
835 entry_tagmask(entry) & ~tagmask);
836 /*
837 * check if we should proceed to process the next level.
838 */
839 if (0 < i) {
840 struct radix_tree_node *n = path_node(t, &path, i - 1);
841
842 if ((any_children_tagmask(n) & tagmask) != 0) {
843 break;
844 }
845 }
846 }
847 }
848
849 #if defined(UNITTEST)
850
851 #include <inttypes.h>
852 #include <stdio.h>
853
854 static void
855 radix_tree_dump_node(const struct radix_tree *t, void *vp,
856 uint64_t offset, unsigned int height)
857 {
858 struct radix_tree_node *n;
859 unsigned int i;
860
861 for (i = 0; i < t->t_height - height; i++) {
862 printf(" ");
863 }
864 if (entry_tagmask(vp) == 0) {
865 printf("[%" PRIu64 "] %p", offset, entry_ptr(vp));
866 } else {
867 printf("[%" PRIu64 "] %p (tagmask=0x%x)", offset, entry_ptr(vp),
868 entry_tagmask(vp));
869 }
870 if (height == 0) {
871 printf(" (leaf)\n");
872 return;
873 }
874 n = entry_ptr(vp);
875 assert(any_children_tagmask(n) == entry_tagmask(vp));
876 printf(" (%u children)\n", n->n_nptrs);
877 for (i = 0; i < __arraycount(n->n_ptrs); i++) {
878 void *c;
879
880 c = n->n_ptrs[i];
881 if (c == NULL) {
882 continue;
883 }
884 radix_tree_dump_node(t, c,
885 offset + i * (UINT64_C(1) <<
886 (RADIX_TREE_BITS_PER_HEIGHT * (height - 1))), height - 1);
887 }
888 }
889
890 void radix_tree_dump(const struct radix_tree *);
891
892 void
893 radix_tree_dump(const struct radix_tree *t)
894 {
895
896 printf("tree %p height=%u\n", t, t->t_height);
897 radix_tree_dump_node(t, t->t_root, 0, t->t_height);
898 }
899
900 static void
901 test1(void)
902 {
903 struct radix_tree s;
904 struct radix_tree *t = &s;
905 void *results[3];
906
907 radix_tree_init_tree(t);
908 radix_tree_dump(t);
909 assert(radix_tree_lookup_node(t, 0) == NULL);
910 assert(radix_tree_lookup_node(t, 1000) == NULL);
911 assert(radix_tree_insert_node(t, 1000, (void *)0xdeadbea0) == 0);
912 radix_tree_dump(t);
913 assert(!radix_tree_get_tag(t, 1000, 0));
914 assert(!radix_tree_get_tag(t, 1000, 1));
915 radix_tree_set_tag(t, 1000, 1);
916 assert(!radix_tree_get_tag(t, 1000, 0));
917 assert(radix_tree_get_tag(t, 1000, 1));
918 radix_tree_dump(t);
919 assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
920 assert(radix_tree_insert_node(t, 0, (void *)0xbea0) == 0);
921 radix_tree_dump(t);
922 assert(radix_tree_lookup_node(t, 0) == (void *)0xbea0);
923 assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
924 assert(radix_tree_insert_node(t, UINT64_C(10000000000), (void *)0xdea0)
925 == 0);
926 radix_tree_dump(t);
927 assert(radix_tree_lookup_node(t, 0) == (void *)0xbea0);
928 assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
929 assert(radix_tree_lookup_node(t, UINT64_C(10000000000)) ==
930 (void *)0xdea0);
931 radix_tree_dump(t);
932 assert(!radix_tree_get_tag(t, 0, 1));
933 assert(radix_tree_get_tag(t, 1000, 1));
934 assert(!radix_tree_get_tag(t, UINT64_C(10000000000), 1));
935 radix_tree_set_tag(t, 0, 1);;
936 radix_tree_set_tag(t, UINT64_C(10000000000), 1);
937 radix_tree_dump(t);
938 assert(radix_tree_get_tag(t, 0, 1));
939 assert(radix_tree_get_tag(t, 1000, 1));
940 assert(radix_tree_get_tag(t, UINT64_C(10000000000), 1));
941 radix_tree_clear_tag(t, 0, 1);;
942 radix_tree_clear_tag(t, UINT64_C(10000000000), 1);
943 radix_tree_dump(t);
944 assert(!radix_tree_get_tag(t, 0, 1));
945 assert(radix_tree_get_tag(t, 1000, 1));
946 assert(!radix_tree_get_tag(t, UINT64_C(10000000000), 1));
947 radix_tree_dump(t);
948 assert(radix_tree_replace_node(t, 1000, (void *)0x12345678) ==
949 (void *)0xdeadbea0);
950 assert(!radix_tree_get_tag(t, 1000, 0));
951 assert(radix_tree_get_tag(t, 1000, 1));
952 assert(radix_tree_gang_lookup_node(t, 0, results, 3) == 3);
953 assert(results[0] == (void *)0xbea0);
954 assert(results[1] == (void *)0x12345678);
955 assert(results[2] == (void *)0xdea0);
956 assert(radix_tree_gang_lookup_node(t, 1, results, 3) == 2);
957 assert(results[0] == (void *)0x12345678);
958 assert(results[1] == (void *)0xdea0);
959 assert(radix_tree_gang_lookup_node(t, 1001, results, 3) == 1);
960 assert(results[0] == (void *)0xdea0);
961 assert(radix_tree_gang_lookup_node(t, UINT64_C(10000000001), results, 3)
962 == 0);
963 assert(radix_tree_gang_lookup_node(t, UINT64_C(1000000000000), results,
964 3) == 0);
965 assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 100, 1) == 1);
966 assert(results[0] == (void *)0x12345678);
967 assert(entry_tagmask(t->t_root) != 0);
968 assert(radix_tree_remove_node(t, 1000) == (void *)0x12345678);
969 assert(entry_tagmask(t->t_root) == 0);
970 radix_tree_dump(t);
971 assert(radix_tree_remove_node(t, UINT64_C(10000000000)) ==
972 (void *)0xdea0);
973 radix_tree_dump(t);
974 assert(radix_tree_remove_node(t, 0) == (void *)0xbea0);
975 radix_tree_dump(t);
976 radix_tree_fini_tree(t);
977 }
978
979 #include <sys/time.h>
980
981 struct testnode {
982 uint64_t idx;
983 };
984
985 static void
986 printops(const char *name, unsigned int n, const struct timeval *stv,
987 const struct timeval *etv)
988 {
989 uint64_t s = stv->tv_sec * 1000000 + stv->tv_usec;
990 uint64_t e = etv->tv_sec * 1000000 + etv->tv_usec;
991
992 printf("%lf %s/s\n", (double)n / (e - s) * 1000000, name);
993 }
994
995 #define TEST2_GANG_LOOKUP_NODES 16
996
997 static bool
998 test2_should_tag(unsigned int i, radix_tree_tagid_t tagid)
999 {
1000
1001 if (tagid == 0) {
1002 return (i & 0x3) == 0;
1003 } else {
1004 return (i % 7) == 0;
1005 }
1006 }
1007
1008 static void
1009 test2(bool dense)
1010 {
1011 struct radix_tree s;
1012 struct radix_tree *t = &s;
1013 struct testnode *n;
1014 unsigned int i;
1015 unsigned int nnodes = 100000;
1016 unsigned int removed;
1017 radix_tree_tagid_t tag;
1018 unsigned int ntagged[RADIX_TREE_TAG_ID_MAX];
1019 struct testnode *nodes;
1020 struct timeval stv;
1021 struct timeval etv;
1022
1023 nodes = malloc(nnodes * sizeof(*nodes));
1024 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1025 ntagged[tag] = 0;
1026 }
1027 radix_tree_init_tree(t);
1028 for (i = 0; i < nnodes; i++) {
1029 n = &nodes[i];
1030 n->idx = random();
1031 if (sizeof(long) == 4) {
1032 n->idx <<= 32;
1033 n->idx |= (uint32_t)random();
1034 }
1035 if (dense) {
1036 n->idx %= nnodes * 2;
1037 }
1038 while (radix_tree_lookup_node(t, n->idx) != NULL) {
1039 n->idx++;
1040 }
1041 radix_tree_insert_node(t, n->idx, n);
1042 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1043 if (test2_should_tag(i, tag)) {
1044 radix_tree_set_tag(t, n->idx, tag);
1045 ntagged[tag]++;
1046 }
1047 assert(test2_should_tag(i, tag) ==
1048 radix_tree_get_tag(t, n->idx, tag));
1049 }
1050 }
1051
1052 gettimeofday(&stv, NULL);
1053 for (i = 0; i < nnodes; i++) {
1054 n = &nodes[i];
1055 assert(radix_tree_lookup_node(t, n->idx) == n);
1056 }
1057 gettimeofday(&etv, NULL);
1058 printops("lookup", nnodes, &stv, &etv);
1059
1060 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1061 gettimeofday(&stv, NULL);
1062 for (i = 0; i < nnodes; i++) {
1063 n = &nodes[i];
1064 assert(test2_should_tag(i, tag) ==
1065 radix_tree_get_tag(t, n->idx, tag));
1066 }
1067 gettimeofday(&etv, NULL);
1068 printops("get_tag", ntagged[tag], &stv, &etv);
1069 }
1070
1071 gettimeofday(&stv, NULL);
1072 for (i = 0; i < nnodes; i++) {
1073 n = &nodes[i];
1074 radix_tree_remove_node(t, n->idx);
1075 }
1076 gettimeofday(&etv, NULL);
1077 printops("remove", nnodes, &stv, &etv);
1078
1079 gettimeofday(&stv, NULL);
1080 for (i = 0; i < nnodes; i++) {
1081 n = &nodes[i];
1082 radix_tree_insert_node(t, n->idx, n);
1083 }
1084 gettimeofday(&etv, NULL);
1085 printops("insert", nnodes, &stv, &etv);
1086
1087 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1088 ntagged[tag] = 0;
1089 gettimeofday(&stv, NULL);
1090 for (i = 0; i < nnodes; i++) {
1091 n = &nodes[i];
1092 if (test2_should_tag(i, tag)) {
1093 radix_tree_set_tag(t, n->idx, tag);
1094 ntagged[tag]++;
1095 }
1096 }
1097 gettimeofday(&etv, NULL);
1098 printops("set_tag", ntagged[tag], &stv, &etv);
1099 }
1100
1101 gettimeofday(&stv, NULL);
1102 {
1103 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1104 uint64_t nextidx;
1105 unsigned int nfound;
1106 unsigned int total;
1107
1108 nextidx = 0;
1109 total = 0;
1110 while ((nfound = radix_tree_gang_lookup_node(t, nextidx,
1111 (void *)results, __arraycount(results))) > 0) {
1112 nextidx = results[nfound - 1]->idx + 1;
1113 total += nfound;
1114 }
1115 assert(total == nnodes);
1116 }
1117 gettimeofday(&etv, NULL);
1118 printops("ganglookup", nnodes, &stv, &etv);
1119
1120 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1121 gettimeofday(&stv, NULL);
1122 {
1123 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1124 uint64_t nextidx;
1125 unsigned int nfound;
1126 unsigned int total;
1127
1128 nextidx = 0;
1129 total = 0;
1130 while ((nfound = radix_tree_gang_lookup_tagged_node(t,
1131 nextidx, (void *)results, __arraycount(results),
1132 tag)) > 0) {
1133 nextidx = results[nfound - 1]->idx + 1;
1134 total += nfound;
1135 }
1136 assert(total == ntagged[tag]);
1137 }
1138 gettimeofday(&etv, NULL);
1139 printops("ganglookup_tag", ntagged[tag], &stv, &etv);
1140 }
1141
1142 removed = 0;
1143 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1144 unsigned int total;
1145
1146 total = 0;
1147 gettimeofday(&stv, NULL);
1148 {
1149 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1150 uint64_t nextidx;
1151 unsigned int nfound;
1152
1153 nextidx = 0;
1154 while ((nfound = radix_tree_gang_lookup_tagged_node(t,
1155 nextidx, (void *)results, __arraycount(results),
1156 tag)) > 0) {
1157 for (i = 0; i < nfound; i++) {
1158 radix_tree_remove_node(t,
1159 results[i]->idx);
1160 }
1161 nextidx = results[nfound - 1]->idx + 1;
1162 total += nfound;
1163 }
1164 assert(tag != 0 || total == ntagged[tag]);
1165 assert(total <= ntagged[tag]);
1166 }
1167 gettimeofday(&etv, NULL);
1168 printops("ganglookup_tag+remove", total, &stv, &etv);
1169 removed += total;
1170 }
1171
1172 gettimeofday(&stv, NULL);
1173 {
1174 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1175 uint64_t nextidx;
1176 unsigned int nfound;
1177 unsigned int total;
1178
1179 nextidx = 0;
1180 total = 0;
1181 while ((nfound = radix_tree_gang_lookup_node(t, nextidx,
1182 (void *)results, __arraycount(results))) > 0) {
1183 for (i = 0; i < nfound; i++) {
1184 assert(results[i] == radix_tree_remove_node(t,
1185 results[i]->idx));
1186 }
1187 nextidx = results[nfound - 1]->idx + 1;
1188 total += nfound;
1189 }
1190 assert(total == nnodes - removed);
1191 }
1192 gettimeofday(&etv, NULL);
1193 printops("ganglookup+remove", nnodes - removed, &stv, &etv);
1194
1195 radix_tree_fini_tree(t);
1196 free(nodes);
1197 }
1198
1199 int
1200 main(int argc, char *argv[])
1201 {
1202
1203 test1();
1204 printf("dense distribution:\n");
1205 test2(true);
1206 printf("sparse distribution:\n");
1207 test2(false);
1208 return 0;
1209 }
1210
1211 #endif /* defined(UNITTEST) */
1212