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