xref: /freebsd/sys/kern/subr_pctrie.c (revision 51e235148a4becba94e824a44bd69687644a7f56)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 EMC Corp.
5  * Copyright (c) 2011 Jeffrey Roberson <jeff@freebsd.org>
6  * Copyright (c) 2008 Mayur Shardul <mayur.shardul@gmail.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31 
32 /*
33  * Path-compressed radix trie implementation.
34  *
35  * The implementation takes into account the following rationale:
36  * - Size of the nodes should be as small as possible but still big enough
37  *   to avoid a large maximum depth for the trie.  This is a balance
38  *   between the necessity to not wire too much physical memory for the nodes
39  *   and the necessity to avoid too much cache pollution during the trie
40  *   operations.
41  * - There is not a huge bias toward the number of lookup operations over
42  *   the number of insert and remove operations.  This basically implies
43  *   that optimizations supposedly helping one operation but hurting the
44  *   other might be carefully evaluated.
45  * - On average not many nodes are expected to be fully populated, hence
46  *   level compression may just complicate things.
47  */
48 
49 #include <sys/cdefs.h>
50 #include "opt_ddb.h"
51 
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/kernel.h>
55 #include <sys/libkern.h>
56 #include <sys/pctrie.h>
57 #include <sys/proc.h>	/* smr.h depends on struct thread. */
58 #include <sys/smr.h>
59 #include <sys/smr_types.h>
60 
61 #ifdef DDB
62 #include <ddb/ddb.h>
63 #endif
64 
65 #define	PCTRIE_MASK	(PCTRIE_COUNT - 1)
66 #define	PCTRIE_LIMIT	(howmany(sizeof(uint64_t) * NBBY, PCTRIE_WIDTH) - 1)
67 
68 #if PCTRIE_WIDTH == 3
69 typedef uint8_t pn_popmap_t;
70 #elif PCTRIE_WIDTH == 4
71 typedef uint16_t pn_popmap_t;
72 #elif PCTRIE_WIDTH == 5
73 typedef uint32_t pn_popmap_t;
74 #else
75 #error Unsupported width
76 #endif
77 _Static_assert(sizeof(pn_popmap_t) <= sizeof(int),
78     "pn_popmap_t too wide");
79 
80 /* Set of all flag bits stored in node pointers. */
81 #define	PCTRIE_FLAGS	(PCTRIE_ISLEAF)
82 #define	PCTRIE_PAD	PCTRIE_FLAGS
83 
84 struct pctrie_node;
85 typedef SMR_POINTER(struct pctrie_node *) smr_pctnode_t;
86 
87 struct pctrie_node {
88 	uint64_t	pn_owner;			/* Owner of record. */
89 	pn_popmap_t	pn_popmap;			/* Valid children. */
90 	uint8_t		pn_clev;			/* Level * WIDTH. */
91 	smr_pctnode_t	pn_child[PCTRIE_COUNT];		/* Child nodes. */
92 };
93 
94 enum pctrie_access { PCTRIE_SMR, PCTRIE_LOCKED, PCTRIE_UNSERIALIZED };
95 
96 static __inline void pctrie_node_store(smr_pctnode_t *p, void *val,
97     enum pctrie_access access);
98 
99 /*
100  * Map index to an array position for the children of node,
101  */
102 static __inline int
103 pctrie_slot(struct pctrie_node *node, uint64_t index)
104 {
105 	return ((index >> node->pn_clev) & PCTRIE_MASK);
106 }
107 
108 /*
109  * Returns true if index does not belong to the specified node.  Otherwise,
110  * sets slot value, and returns false.
111  */
112 static __inline bool
113 pctrie_keybarr(struct pctrie_node *node, uint64_t index, int *slot)
114 {
115 	index = (index - node->pn_owner) >> node->pn_clev;
116 	if (index >= PCTRIE_COUNT)
117 		return (true);
118 	*slot = index;
119 	return (false);
120 }
121 
122 /*
123  * Allocate a node.  Pre-allocation should ensure that the request
124  * will always be satisfied.
125  */
126 static struct pctrie_node *
127 pctrie_node_get(struct pctrie *ptree, pctrie_alloc_t allocfn, uint64_t index,
128     uint64_t newind)
129 {
130 	struct pctrie_node *node;
131 
132 	node = allocfn(ptree);
133 	if (node == NULL)
134 		return (NULL);
135 
136 	/*
137 	 * We want to clear the last child pointer after the final section
138 	 * has exited so lookup can not return false negatives.  It is done
139 	 * here because it will be cache-cold in the dtor callback.
140 	 */
141 	if (node->pn_popmap != 0) {
142 		pctrie_node_store(&node->pn_child[ffs(node->pn_popmap) - 1],
143 		    PCTRIE_NULL, PCTRIE_UNSERIALIZED);
144 		node->pn_popmap = 0;
145 	}
146 
147 	/*
148 	 * From the highest-order bit where the indexes differ,
149 	 * compute the highest level in the trie where they differ.  Then,
150 	 * compute the least index of this subtrie.
151 	 */
152 	KASSERT(index != newind, ("%s: passing the same key value %jx",
153 	    __func__, (uintmax_t)index));
154 	_Static_assert(sizeof(long long) >= sizeof(uint64_t),
155 	    "uint64 too wide");
156 	_Static_assert(sizeof(uint64_t) * NBBY <=
157 	    (1 << (sizeof(node->pn_clev) * NBBY)), "pn_clev too narrow");
158 	node->pn_clev = rounddown(flsll(index ^ newind) - 1, PCTRIE_WIDTH);
159 	node->pn_owner = PCTRIE_COUNT;
160 	node->pn_owner = index & -(node->pn_owner << node->pn_clev);
161 	return (node);
162 }
163 
164 /*
165  * Free radix node.
166  */
167 static __inline void
168 pctrie_node_put(struct pctrie *ptree, struct pctrie_node *node,
169     pctrie_free_t freefn)
170 {
171 #ifdef INVARIANTS
172 	int slot;
173 
174 	KASSERT(powerof2(node->pn_popmap),
175 	    ("pctrie_node_put: node %p has too many children %04x", node,
176 	    node->pn_popmap));
177 	for (slot = 0; slot < PCTRIE_COUNT; slot++) {
178 		if ((node->pn_popmap & (1 << slot)) != 0)
179 			continue;
180 		KASSERT(smr_unserialized_load(&node->pn_child[slot], true) ==
181 		    PCTRIE_NULL,
182 		    ("pctrie_node_put: node %p has a child", node));
183 	}
184 #endif
185 	freefn(ptree, node);
186 }
187 
188 /*
189  * Fetch a node pointer from a slot.
190  */
191 static __inline struct pctrie_node *
192 pctrie_node_load(smr_pctnode_t *p, smr_t smr, enum pctrie_access access)
193 {
194 	switch (access) {
195 	case PCTRIE_UNSERIALIZED:
196 		return (smr_unserialized_load(p, true));
197 	case PCTRIE_LOCKED:
198 		return (smr_serialized_load(p, true));
199 	case PCTRIE_SMR:
200 		return (smr_entered_load(p, smr));
201 	}
202 	__assert_unreachable();
203 }
204 
205 static __inline void
206 pctrie_node_store(smr_pctnode_t *p, void *v, enum pctrie_access access)
207 {
208 	switch (access) {
209 	case PCTRIE_UNSERIALIZED:
210 		smr_unserialized_store(p, v, true);
211 		break;
212 	case PCTRIE_LOCKED:
213 		smr_serialized_store(p, v, true);
214 		break;
215 	case PCTRIE_SMR:
216 		panic("%s: Not supported in SMR section.", __func__);
217 		break;
218 	default:
219 		__assert_unreachable();
220 		break;
221 	}
222 }
223 
224 /*
225  * Get the root node for a tree.
226  */
227 static __inline struct pctrie_node *
228 pctrie_root_load(struct pctrie *ptree, smr_t smr, enum pctrie_access access)
229 {
230 	return (pctrie_node_load((smr_pctnode_t *)&ptree->pt_root, smr, access));
231 }
232 
233 /*
234  * Set the root node for a tree.
235  */
236 static __inline void
237 pctrie_root_store(struct pctrie *ptree, struct pctrie_node *node,
238     enum pctrie_access access)
239 {
240 	pctrie_node_store((smr_pctnode_t *)&ptree->pt_root, node, access);
241 }
242 
243 /*
244  * Returns TRUE if the specified node is a leaf and FALSE otherwise.
245  */
246 static __inline bool
247 pctrie_isleaf(struct pctrie_node *node)
248 {
249 
250 	return (((uintptr_t)node & PCTRIE_ISLEAF) != 0);
251 }
252 
253 /*
254  * Returns val with leaf bit set.
255  */
256 static __inline void *
257 pctrie_toleaf(uint64_t *val)
258 {
259 	return ((void *)((uintptr_t)val | PCTRIE_ISLEAF));
260 }
261 
262 /*
263  * Returns the associated val extracted from node.
264  */
265 static __inline uint64_t *
266 pctrie_toval(struct pctrie_node *node)
267 {
268 
269 	return ((uint64_t *)((uintptr_t)node & ~PCTRIE_FLAGS));
270 }
271 
272 /*
273  * Make 'child' a child of 'node'.
274  */
275 static __inline void
276 pctrie_addnode(struct pctrie_node *node, uint64_t index,
277     struct pctrie_node *child, enum pctrie_access access)
278 {
279 	int slot;
280 
281 	slot = pctrie_slot(node, index);
282 	pctrie_node_store(&node->pn_child[slot], child, access);
283 	node->pn_popmap ^= 1 << slot;
284 	KASSERT((node->pn_popmap & (1 << slot)) != 0,
285 	    ("%s: bad popmap slot %d in node %p", __func__, slot, node));
286 }
287 
288 /*
289  * Internal helper for pctrie_reclaim_allnodes().
290  * This function is recursive.
291  */
292 static void
293 pctrie_reclaim_allnodes_int(struct pctrie *ptree, struct pctrie_node *node,
294     pctrie_free_t freefn)
295 {
296 	struct pctrie_node *child;
297 	int slot;
298 
299 	while (node->pn_popmap != 0) {
300 		slot = ffs(node->pn_popmap) - 1;
301 		child = pctrie_node_load(&node->pn_child[slot], NULL,
302 		    PCTRIE_UNSERIALIZED);
303 		KASSERT(child != PCTRIE_NULL,
304 		    ("%s: bad popmap slot %d in node %p",
305 		    __func__, slot, node));
306 		if (!pctrie_isleaf(child))
307 			pctrie_reclaim_allnodes_int(ptree, child, freefn);
308 		node->pn_popmap ^= 1 << slot;
309 		pctrie_node_store(&node->pn_child[slot], PCTRIE_NULL,
310 		    PCTRIE_UNSERIALIZED);
311 	}
312 	pctrie_node_put(ptree, node, freefn);
313 }
314 
315 /*
316  * pctrie node zone initializer.
317  */
318 int
319 pctrie_zone_init(void *mem, int size __unused, int flags __unused)
320 {
321 	struct pctrie_node *node;
322 
323 	node = mem;
324 	node->pn_popmap = 0;
325 	for (int i = 0; i < nitems(node->pn_child); i++)
326 		pctrie_node_store(&node->pn_child[i], PCTRIE_NULL,
327 		    PCTRIE_UNSERIALIZED);
328 	return (0);
329 }
330 
331 size_t
332 pctrie_node_size(void)
333 {
334 
335 	return (sizeof(struct pctrie_node));
336 }
337 
338 /*
339  * Inserts the key-value pair into the trie.
340  * Panics if the key already exists.
341  */
342 int
343 pctrie_insert(struct pctrie *ptree, uint64_t *val, pctrie_alloc_t allocfn)
344 {
345 	uint64_t index, newind;
346 	struct pctrie_node *leaf, *node, *parent;
347 	smr_pctnode_t *parentp;
348 	int slot;
349 
350 	index = *val;
351 	leaf = pctrie_toleaf(val);
352 
353 	/*
354 	 * The owner of record for root is not really important because it
355 	 * will never be used.
356 	 */
357 	node = pctrie_root_load(ptree, NULL, PCTRIE_LOCKED);
358 	parent = NULL;
359 	for (;;) {
360 		if (pctrie_isleaf(node)) {
361 			if (node == PCTRIE_NULL) {
362 				if (parent == NULL)
363 					ptree->pt_root = leaf;
364 				else
365 					pctrie_addnode(parent, index, leaf,
366 					    PCTRIE_LOCKED);
367 				return (0);
368 			}
369 			newind = *pctrie_toval(node);
370 			if (newind == index)
371 				panic("%s: key %jx is already present",
372 				    __func__, (uintmax_t)index);
373 			break;
374 		}
375 		if (pctrie_keybarr(node, index, &slot)) {
376 			newind = node->pn_owner;
377 			break;
378 		}
379 		parent = node;
380 		node = pctrie_node_load(&node->pn_child[slot], NULL,
381 		    PCTRIE_LOCKED);
382 	}
383 
384 	/*
385 	 * A new node is needed because the right insertion level is reached.
386 	 * Setup the new intermediate node and add the 2 children: the
387 	 * new object and the older edge or object.
388 	 */
389 	parentp = (parent != NULL) ? &parent->pn_child[slot]:
390 	    (smr_pctnode_t *)&ptree->pt_root;
391 	parent = pctrie_node_get(ptree, allocfn, index, newind);
392 	if (parent == NULL)
393 		return (ENOMEM);
394 	/* These writes are not yet visible due to ordering. */
395 	pctrie_addnode(parent, index, leaf, PCTRIE_UNSERIALIZED);
396 	pctrie_addnode(parent, newind, node, PCTRIE_UNSERIALIZED);
397 	/* Synchronize to make the above visible. */
398 	pctrie_node_store(parentp, parent, PCTRIE_LOCKED);
399 	return (0);
400 }
401 
402 /*
403  * Returns the value stored at the index.  If the index is not present,
404  * NULL is returned.
405  */
406 static __always_inline uint64_t *
407 _pctrie_lookup(struct pctrie *ptree, uint64_t index, smr_t smr,
408     enum pctrie_access access)
409 {
410 	struct pctrie_node *node;
411 	uint64_t *m;
412 	int slot;
413 
414 	node = pctrie_root_load(ptree, smr, access);
415 	for (;;) {
416 		if (pctrie_isleaf(node)) {
417 			if ((m = pctrie_toval(node)) != NULL && *m == index)
418 				return (m);
419 			break;
420 		}
421 		if (pctrie_keybarr(node, index, &slot))
422 			break;
423 		node = pctrie_node_load(&node->pn_child[slot], smr, access);
424 	}
425 	return (NULL);
426 }
427 
428 /*
429  * Returns the value stored at the index, assuming access is externally
430  * synchronized by a lock.
431  *
432  * If the index is not present, NULL is returned.
433  */
434 uint64_t *
435 pctrie_lookup(struct pctrie *ptree, uint64_t index)
436 {
437 	return (_pctrie_lookup(ptree, index, NULL, PCTRIE_LOCKED));
438 }
439 
440 /*
441  * Returns the value stored at the index without requiring an external lock.
442  *
443  * If the index is not present, NULL is returned.
444  */
445 uint64_t *
446 pctrie_lookup_unlocked(struct pctrie *ptree, uint64_t index, smr_t smr)
447 {
448 	uint64_t *res;
449 
450 	smr_enter(smr);
451 	res = _pctrie_lookup(ptree, index, smr, PCTRIE_SMR);
452 	smr_exit(smr);
453 	return (res);
454 }
455 
456 /*
457  * Returns the value with the least index that is greater than or equal to the
458  * specified index, or NULL if there are no such values.
459  *
460  * Requires that access be externally synchronized by a lock.
461  */
462 uint64_t *
463 pctrie_lookup_ge(struct pctrie *ptree, uint64_t index)
464 {
465 	struct pctrie_node *node, *succ;
466 	uint64_t *m;
467 	int slot;
468 
469 	/*
470 	 * Descend the trie as if performing an ordinary lookup for the
471 	 * specified value.  However, unlike an ordinary lookup, as we descend
472 	 * the trie, we use "succ" to remember the last branching-off point,
473 	 * that is, the interior node under which the least value that is both
474 	 * outside our current path down the trie and greater than the specified
475 	 * index resides.  (The node's popmap makes it fast and easy to
476 	 * recognize a branching-off point.)  If our ordinary lookup fails to
477 	 * yield a value that is greater than or equal to the specified index,
478 	 * then we will exit this loop and perform a lookup starting from
479 	 * "succ".  If "succ" is not NULL, then that lookup is guaranteed to
480 	 * succeed.
481 	 */
482 	node = pctrie_root_load(ptree, NULL, PCTRIE_LOCKED);
483 	succ = NULL;
484 	for (;;) {
485 		if (pctrie_isleaf(node)) {
486 			if ((m = pctrie_toval(node)) != NULL && *m >= index)
487 				return (m);
488 			break;
489 		}
490 		if (pctrie_keybarr(node, index, &slot)) {
491 			/*
492 			 * If all values in this subtree are > index, then the
493 			 * least value in this subtree is the answer.
494 			 */
495 			if (node->pn_owner > index)
496 				succ = node;
497 			break;
498 		}
499 
500 		/*
501 		 * Just in case the next search step leads to a subtree of all
502 		 * values < index, check popmap to see if a next bigger step, to
503 		 * a subtree of all pages with values > index, is available.  If
504 		 * so, remember to restart the search here.
505 		 */
506 		if ((node->pn_popmap >> slot) > 1)
507 			succ = node;
508 		node = pctrie_node_load(&node->pn_child[slot], NULL,
509 		    PCTRIE_LOCKED);
510 	}
511 
512 	/*
513 	 * Restart the search from the last place visited in the subtree that
514 	 * included some values > index, if there was such a place.
515 	 */
516 	if (succ == NULL)
517 		return (NULL);
518 	if (succ != node) {
519 		/*
520 		 * Take a step to the next bigger sibling of the node chosen
521 		 * last time.  In that subtree, all values > index.
522 		 */
523 		slot = pctrie_slot(succ, index) + 1;
524 		KASSERT((succ->pn_popmap >> slot) != 0,
525 		    ("%s: no popmap siblings past slot %d in node %p",
526 		    __func__, slot, succ));
527 		slot += ffs(succ->pn_popmap >> slot) - 1;
528 		succ = pctrie_node_load(&succ->pn_child[slot], NULL,
529 		    PCTRIE_LOCKED);
530 	}
531 
532 	/*
533 	 * Find the value in the subtree rooted at "succ" with the least index.
534 	 */
535 	while (!pctrie_isleaf(succ)) {
536 		KASSERT(succ->pn_popmap != 0,
537 		    ("%s: no popmap children in node %p",  __func__, succ));
538 		slot = ffs(succ->pn_popmap) - 1;
539 		succ = pctrie_node_load(&succ->pn_child[slot], NULL,
540 		    PCTRIE_LOCKED);
541 	}
542 	return (pctrie_toval(succ));
543 }
544 
545 /*
546  * Returns the value with the greatest index that is less than or equal to the
547  * specified index, or NULL if there are no such values.
548  *
549  * Requires that access be externally synchronized by a lock.
550  */
551 uint64_t *
552 pctrie_lookup_le(struct pctrie *ptree, uint64_t index)
553 {
554 	struct pctrie_node *node, *pred;
555 	uint64_t *m;
556 	int slot;
557 
558 	/*
559 	 * Mirror the implementation of pctrie_lookup_ge, described above.
560 	 */
561 	node = pctrie_root_load(ptree, NULL, PCTRIE_LOCKED);
562 	pred = NULL;
563 	for (;;) {
564 		if (pctrie_isleaf(node)) {
565 			if ((m = pctrie_toval(node)) != NULL && *m <= index)
566 				return (m);
567 			break;
568 		}
569 		if (pctrie_keybarr(node, index, &slot)) {
570 			if (node->pn_owner < index)
571 				pred = node;
572 			break;
573 		}
574 		if ((node->pn_popmap & ((1 << slot) - 1)) != 0)
575 			pred = node;
576 		node = pctrie_node_load(&node->pn_child[slot], NULL,
577 		    PCTRIE_LOCKED);
578 	}
579 	if (pred == NULL)
580 		return (NULL);
581 	if (pred != node) {
582 		slot = pctrie_slot(pred, index);
583 		KASSERT((pred->pn_popmap & ((1 << slot) - 1)) != 0,
584 		    ("%s: no popmap siblings before slot %d in node %p",
585 		    __func__, slot, pred));
586 		slot = fls(pred->pn_popmap & ((1 << slot) - 1)) - 1;
587 		pred = pctrie_node_load(&pred->pn_child[slot], NULL,
588 		    PCTRIE_LOCKED);
589 	}
590 	while (!pctrie_isleaf(pred)) {
591 		KASSERT(pred->pn_popmap != 0,
592 		    ("%s: no popmap children in node %p",  __func__, pred));
593 		slot = fls(pred->pn_popmap) - 1;
594 		pred = pctrie_node_load(&pred->pn_child[slot], NULL,
595 		    PCTRIE_LOCKED);
596 	}
597 	return (pctrie_toval(pred));
598 }
599 
600 /*
601  * Remove the specified index from the tree.
602  * Panics if the key is not present.
603  */
604 void
605 pctrie_remove(struct pctrie *ptree, uint64_t index, pctrie_free_t freefn)
606 {
607 	struct pctrie_node *child, *node, *parent;
608 	uint64_t *m;
609 	int slot;
610 
611 	node = NULL;
612 	child = pctrie_root_load(ptree, NULL, PCTRIE_LOCKED);
613 	for (;;) {
614 		if (pctrie_isleaf(child))
615 			break;
616 		parent = node;
617 		node = child;
618 		slot = pctrie_slot(node, index);
619 		child = pctrie_node_load(&node->pn_child[slot], NULL,
620 		    PCTRIE_LOCKED);
621 	}
622 	if ((m = pctrie_toval(child)) == NULL || *m != index)
623 		panic("%s: key not found", __func__);
624 	if (node == NULL) {
625 		pctrie_root_store(ptree, PCTRIE_NULL, PCTRIE_LOCKED);
626 		return;
627 	}
628 	KASSERT((node->pn_popmap & (1 << slot)) != 0,
629 	    ("%s: bad popmap slot %d in node %p",
630 	    __func__, slot, node));
631 	node->pn_popmap ^= 1 << slot;
632 	pctrie_node_store(&node->pn_child[slot], PCTRIE_NULL, PCTRIE_LOCKED);
633 	if (!powerof2(node->pn_popmap))
634 		return;
635 	KASSERT(node->pn_popmap != 0, ("%s: bad popmap all zeroes", __func__));
636 	slot = ffs(node->pn_popmap) - 1;
637 	child = pctrie_node_load(&node->pn_child[slot], NULL, PCTRIE_LOCKED);
638 	KASSERT(child != PCTRIE_NULL,
639 	    ("%s: bad popmap slot %d in node %p", __func__, slot, node));
640 	if (parent == NULL)
641 		pctrie_root_store(ptree, child, PCTRIE_LOCKED);
642 	else {
643 		slot = pctrie_slot(parent, index);
644 		KASSERT(node ==
645 		    pctrie_node_load(&parent->pn_child[slot], NULL,
646 		    PCTRIE_LOCKED), ("%s: invalid child value", __func__));
647 		pctrie_node_store(&parent->pn_child[slot], child,
648 		    PCTRIE_LOCKED);
649 	}
650 	/*
651 	 * The child is still valid and we can not zero the
652 	 * pointer until all SMR references are gone.
653 	 */
654 	pctrie_node_put(ptree, node, freefn);
655 }
656 
657 /*
658  * Remove and free all the nodes from the tree.
659  * This function is recursive but there is a tight control on it as the
660  * maximum depth of the tree is fixed.
661  */
662 void
663 pctrie_reclaim_allnodes(struct pctrie *ptree, pctrie_free_t freefn)
664 {
665 	struct pctrie_node *root;
666 
667 	root = pctrie_root_load(ptree, NULL, PCTRIE_LOCKED);
668 	if (root == PCTRIE_NULL)
669 		return;
670 	pctrie_root_store(ptree, PCTRIE_NULL, PCTRIE_UNSERIALIZED);
671 	if (!pctrie_isleaf(root))
672 		pctrie_reclaim_allnodes_int(ptree, root, freefn);
673 }
674 
675 #ifdef DDB
676 /*
677  * Show details about the given node.
678  */
679 DB_SHOW_COMMAND(pctrienode, db_show_pctrienode)
680 {
681 	struct pctrie_node *node, *tmp;
682 	int slot;
683 	pn_popmap_t popmap;
684 
685         if (!have_addr)
686                 return;
687 	node = (struct pctrie_node *)addr;
688 	db_printf("node %p, owner %jx, children popmap %04x, level %u:\n",
689 	    (void *)node, (uintmax_t)node->pn_owner, node->pn_popmap,
690 	    node->pn_clev / PCTRIE_WIDTH);
691 	for (popmap = node->pn_popmap; popmap != 0; popmap ^= 1 << slot) {
692 		slot = ffs(popmap) - 1;
693 		tmp = pctrie_node_load(&node->pn_child[slot], NULL,
694 		    PCTRIE_UNSERIALIZED);
695 		db_printf("slot: %d, val: %p, value: %p, clev: %d\n",
696 		    slot, (void *)tmp,
697 		    pctrie_isleaf(tmp) ? pctrie_toval(tmp) : NULL,
698 		    node->pn_clev / PCTRIE_WIDTH);
699 	}
700 }
701 #endif /* DDB */
702