xref: /freebsd/sys/compat/linuxkpi/common/src/linux_radix.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
18d59ecb2SHans Petter Selasky /*-
28d59ecb2SHans Petter Selasky  * Copyright (c) 2010 Isilon Systems, Inc.
38d59ecb2SHans Petter Selasky  * Copyright (c) 2010 iX Systems, Inc.
48d59ecb2SHans Petter Selasky  * Copyright (c) 2010 Panasas, Inc.
5*6b839ff4SHans Petter Selasky  * Copyright (c) 2013-2020 Mellanox Technologies, Ltd.
68d59ecb2SHans Petter Selasky  * All rights reserved.
78d59ecb2SHans Petter Selasky  *
88d59ecb2SHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
98d59ecb2SHans Petter Selasky  * modification, are permitted provided that the following conditions
108d59ecb2SHans Petter Selasky  * are met:
118d59ecb2SHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
128d59ecb2SHans Petter Selasky  *    notice unmodified, this list of conditions, and the following
138d59ecb2SHans Petter Selasky  *    disclaimer.
148d59ecb2SHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
158d59ecb2SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
168d59ecb2SHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
178d59ecb2SHans Petter Selasky  *
188d59ecb2SHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
198d59ecb2SHans Petter Selasky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
208d59ecb2SHans Petter Selasky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
218d59ecb2SHans Petter Selasky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
228d59ecb2SHans Petter Selasky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
238d59ecb2SHans Petter Selasky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
248d59ecb2SHans Petter Selasky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
258d59ecb2SHans Petter Selasky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
268d59ecb2SHans Petter Selasky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
278d59ecb2SHans Petter Selasky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
288d59ecb2SHans Petter Selasky  */
298d59ecb2SHans Petter Selasky 
308d59ecb2SHans Petter Selasky #include <sys/param.h>
318d59ecb2SHans Petter Selasky #include <sys/systm.h>
328d59ecb2SHans Petter Selasky #include <sys/malloc.h>
338d59ecb2SHans Petter Selasky #include <sys/kernel.h>
348d59ecb2SHans Petter Selasky #include <sys/sysctl.h>
358d59ecb2SHans Petter Selasky 
368d59ecb2SHans Petter Selasky #include <linux/slab.h>
378d59ecb2SHans Petter Selasky #include <linux/kernel.h>
388d59ecb2SHans Petter Selasky #include <linux/radix-tree.h>
398d59ecb2SHans Petter Selasky #include <linux/err.h>
408d59ecb2SHans Petter Selasky 
418d59ecb2SHans Petter Selasky static MALLOC_DEFINE(M_RADIX, "radix", "Linux radix compat");
428d59ecb2SHans Petter Selasky 
43ead15282SHans Petter Selasky static inline unsigned long
radix_max(struct radix_tree_root * root)448d59ecb2SHans Petter Selasky radix_max(struct radix_tree_root *root)
458d59ecb2SHans Petter Selasky {
46ead15282SHans Petter Selasky 	return ((1UL << (root->height * RADIX_TREE_MAP_SHIFT)) - 1UL);
478d59ecb2SHans Petter Selasky }
488d59ecb2SHans Petter Selasky 
498d59ecb2SHans Petter Selasky static inline int
radix_pos(long id,int height)508d59ecb2SHans Petter Selasky radix_pos(long id, int height)
518d59ecb2SHans Petter Selasky {
528d59ecb2SHans Petter Selasky 	return (id >> (RADIX_TREE_MAP_SHIFT * height)) & RADIX_TREE_MAP_MASK;
538d59ecb2SHans Petter Selasky }
548d59ecb2SHans Petter Selasky 
55*6b839ff4SHans Petter Selasky static void
radix_tree_clean_root_node(struct radix_tree_root * root)56*6b839ff4SHans Petter Selasky radix_tree_clean_root_node(struct radix_tree_root *root)
57*6b839ff4SHans Petter Selasky {
58*6b839ff4SHans Petter Selasky 	/* Check if the root node should be freed */
59*6b839ff4SHans Petter Selasky 	if (root->rnode->count == 0) {
60*6b839ff4SHans Petter Selasky 		free(root->rnode, M_RADIX);
61*6b839ff4SHans Petter Selasky 		root->rnode = NULL;
62*6b839ff4SHans Petter Selasky 		root->height = 0;
63*6b839ff4SHans Petter Selasky 	}
64*6b839ff4SHans Petter Selasky }
65*6b839ff4SHans Petter Selasky 
668d59ecb2SHans Petter Selasky void *
radix_tree_lookup(struct radix_tree_root * root,unsigned long index)678d59ecb2SHans Petter Selasky radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
688d59ecb2SHans Petter Selasky {
698d59ecb2SHans Petter Selasky 	struct radix_tree_node *node;
708d59ecb2SHans Petter Selasky 	void *item;
718d59ecb2SHans Petter Selasky 	int height;
728d59ecb2SHans Petter Selasky 
738d59ecb2SHans Petter Selasky 	item = NULL;
748d59ecb2SHans Petter Selasky 	node = root->rnode;
758d59ecb2SHans Petter Selasky 	height = root->height - 1;
768d59ecb2SHans Petter Selasky 	if (index > radix_max(root))
778d59ecb2SHans Petter Selasky 		goto out;
788d59ecb2SHans Petter Selasky 	while (height && node)
798d59ecb2SHans Petter Selasky 		node = node->slots[radix_pos(index, height--)];
808d59ecb2SHans Petter Selasky 	if (node)
818d59ecb2SHans Petter Selasky 		item = node->slots[radix_pos(index, 0)];
828d59ecb2SHans Petter Selasky 
838d59ecb2SHans Petter Selasky out:
848d59ecb2SHans Petter Selasky 	return (item);
858d59ecb2SHans Petter Selasky }
868d59ecb2SHans Petter Selasky 
87ead15282SHans Petter Selasky bool
radix_tree_iter_find(struct radix_tree_root * root,struct radix_tree_iter * iter,void *** pppslot)88ead15282SHans Petter Selasky radix_tree_iter_find(struct radix_tree_root *root, struct radix_tree_iter *iter,
89ead15282SHans Petter Selasky     void ***pppslot)
90ead15282SHans Petter Selasky {
91ead15282SHans Petter Selasky 	struct radix_tree_node *node;
92ead15282SHans Petter Selasky 	unsigned long index = iter->index;
93ead15282SHans Petter Selasky 	int height;
94ead15282SHans Petter Selasky 
95ead15282SHans Petter Selasky restart:
96ead15282SHans Petter Selasky 	node = root->rnode;
97ead15282SHans Petter Selasky 	if (node == NULL)
98ead15282SHans Petter Selasky 		return (false);
99ead15282SHans Petter Selasky 	height = root->height - 1;
100ead15282SHans Petter Selasky 	if (height == -1 || index > radix_max(root))
101ead15282SHans Petter Selasky 		return (false);
102ead15282SHans Petter Selasky 	do {
103ead15282SHans Petter Selasky 		unsigned long mask = RADIX_TREE_MAP_MASK << (RADIX_TREE_MAP_SHIFT * height);
104ead15282SHans Petter Selasky 		unsigned long step = 1UL << (RADIX_TREE_MAP_SHIFT * height);
105ead15282SHans Petter Selasky 		int pos = radix_pos(index, height);
106ead15282SHans Petter Selasky 		struct radix_tree_node *next;
107ead15282SHans Petter Selasky 
108ead15282SHans Petter Selasky 		/* track last slot */
109ead15282SHans Petter Selasky 		*pppslot = node->slots + pos;
110ead15282SHans Petter Selasky 
111ead15282SHans Petter Selasky 		next = node->slots[pos];
112ead15282SHans Petter Selasky 		if (next == NULL) {
113ead15282SHans Petter Selasky 			index += step;
1140f839f3aSHans Petter Selasky 			index &= -step;
115ead15282SHans Petter Selasky 			if ((index & mask) == 0)
116ead15282SHans Petter Selasky 				goto restart;
117ead15282SHans Petter Selasky 		} else {
118ead15282SHans Petter Selasky 			node = next;
119ead15282SHans Petter Selasky 			height--;
120ead15282SHans Petter Selasky 		}
121ead15282SHans Petter Selasky 	} while (height != -1);
122ead15282SHans Petter Selasky 	iter->index = index;
123ead15282SHans Petter Selasky 	return (true);
124ead15282SHans Petter Selasky }
125ead15282SHans Petter Selasky 
1268d59ecb2SHans Petter Selasky void *
radix_tree_delete(struct radix_tree_root * root,unsigned long index)1278d59ecb2SHans Petter Selasky radix_tree_delete(struct radix_tree_root *root, unsigned long index)
1288d59ecb2SHans Petter Selasky {
1298d59ecb2SHans Petter Selasky 	struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];
1308d59ecb2SHans Petter Selasky 	struct radix_tree_node *node;
1318d59ecb2SHans Petter Selasky 	void *item;
1328d59ecb2SHans Petter Selasky 	int height;
1338d59ecb2SHans Petter Selasky 	int idx;
1348d59ecb2SHans Petter Selasky 
1358d59ecb2SHans Petter Selasky 	item = NULL;
1368d59ecb2SHans Petter Selasky 	node = root->rnode;
1378d59ecb2SHans Petter Selasky 	height = root->height - 1;
1388d59ecb2SHans Petter Selasky 	if (index > radix_max(root))
1398d59ecb2SHans Petter Selasky 		goto out;
1408d59ecb2SHans Petter Selasky 	/*
1418d59ecb2SHans Petter Selasky 	 * Find the node and record the path in stack.
1428d59ecb2SHans Petter Selasky 	 */
1438d59ecb2SHans Petter Selasky 	while (height && node) {
1448d59ecb2SHans Petter Selasky 		stack[height] = node;
1458d59ecb2SHans Petter Selasky 		node = node->slots[radix_pos(index, height--)];
1468d59ecb2SHans Petter Selasky 	}
1478d59ecb2SHans Petter Selasky 	idx = radix_pos(index, 0);
1488d59ecb2SHans Petter Selasky 	if (node)
1498d59ecb2SHans Petter Selasky 		item = node->slots[idx];
1508d59ecb2SHans Petter Selasky 	/*
1518d59ecb2SHans Petter Selasky 	 * If we removed something reduce the height of the tree.
1528d59ecb2SHans Petter Selasky 	 */
1538d59ecb2SHans Petter Selasky 	if (item)
1548d59ecb2SHans Petter Selasky 		for (;;) {
1558d59ecb2SHans Petter Selasky 			node->slots[idx] = NULL;
1568d59ecb2SHans Petter Selasky 			node->count--;
1578d59ecb2SHans Petter Selasky 			if (node->count > 0)
1588d59ecb2SHans Petter Selasky 				break;
1598d59ecb2SHans Petter Selasky 			free(node, M_RADIX);
1608d59ecb2SHans Petter Selasky 			if (node == root->rnode) {
1618d59ecb2SHans Petter Selasky 				root->rnode = NULL;
1628d59ecb2SHans Petter Selasky 				root->height = 0;
1638d59ecb2SHans Petter Selasky 				break;
1648d59ecb2SHans Petter Selasky 			}
1658d59ecb2SHans Petter Selasky 			height++;
1668d59ecb2SHans Petter Selasky 			node = stack[height];
1678d59ecb2SHans Petter Selasky 			idx = radix_pos(index, height);
1688d59ecb2SHans Petter Selasky 		}
1698d59ecb2SHans Petter Selasky out:
1708d59ecb2SHans Petter Selasky 	return (item);
1718d59ecb2SHans Petter Selasky }
1728d59ecb2SHans Petter Selasky 
1736fad8d17SHans Petter Selasky void
radix_tree_iter_delete(struct radix_tree_root * root,struct radix_tree_iter * iter,void ** slot)1746fad8d17SHans Petter Selasky radix_tree_iter_delete(struct radix_tree_root *root,
1756fad8d17SHans Petter Selasky     struct radix_tree_iter *iter, void **slot)
1766fad8d17SHans Petter Selasky {
1776fad8d17SHans Petter Selasky 	radix_tree_delete(root, iter->index);
1786fad8d17SHans Petter Selasky }
1796fad8d17SHans Petter Selasky 
1808d59ecb2SHans Petter Selasky int
radix_tree_insert(struct radix_tree_root * root,unsigned long index,void * item)1818d59ecb2SHans Petter Selasky radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item)
1828d59ecb2SHans Petter Selasky {
1838d59ecb2SHans Petter Selasky 	struct radix_tree_node *node;
1848d59ecb2SHans Petter Selasky 	struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];
1858d59ecb2SHans Petter Selasky 	int height;
1868d59ecb2SHans Petter Selasky 	int idx;
1878d59ecb2SHans Petter Selasky 
1888d59ecb2SHans Petter Selasky 	/* bail out upon insertion of a NULL item */
1898d59ecb2SHans Petter Selasky 	if (item == NULL)
1908d59ecb2SHans Petter Selasky 		return (-EINVAL);
1918d59ecb2SHans Petter Selasky 
1928d59ecb2SHans Petter Selasky 	/* get root node, if any */
1938d59ecb2SHans Petter Selasky 	node = root->rnode;
1948d59ecb2SHans Petter Selasky 
1958d59ecb2SHans Petter Selasky 	/* allocate root node, if any */
1968d59ecb2SHans Petter Selasky 	if (node == NULL) {
1978d59ecb2SHans Petter Selasky 		node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
1988d59ecb2SHans Petter Selasky 		if (node == NULL)
1998d59ecb2SHans Petter Selasky 			return (-ENOMEM);
2008d59ecb2SHans Petter Selasky 		root->rnode = node;
2018d59ecb2SHans Petter Selasky 		root->height++;
2028d59ecb2SHans Petter Selasky 	}
2038d59ecb2SHans Petter Selasky 
2048d59ecb2SHans Petter Selasky 	/* expand radix tree as needed */
2058d59ecb2SHans Petter Selasky 	while (radix_max(root) < index) {
2068d59ecb2SHans Petter Selasky 		/* check if the radix tree is getting too big */
207*6b839ff4SHans Petter Selasky 		if (root->height == RADIX_TREE_MAX_HEIGHT) {
208*6b839ff4SHans Petter Selasky 			radix_tree_clean_root_node(root);
2098d59ecb2SHans Petter Selasky 			return (-E2BIG);
210*6b839ff4SHans Petter Selasky 		}
2118d59ecb2SHans Petter Selasky 
2128d59ecb2SHans Petter Selasky 		/*
2138d59ecb2SHans Petter Selasky 		 * If the root radix level is not empty, we need to
2148d59ecb2SHans Petter Selasky 		 * allocate a new radix level:
2158d59ecb2SHans Petter Selasky 		 */
2168d59ecb2SHans Petter Selasky 		if (node->count != 0) {
2178d59ecb2SHans Petter Selasky 			node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
218*6b839ff4SHans Petter Selasky 			if (node == NULL) {
219*6b839ff4SHans Petter Selasky 				/*
220*6b839ff4SHans Petter Selasky 				 * Freeing the already allocated radix
221*6b839ff4SHans Petter Selasky 				 * levels, if any, will be handled by
222*6b839ff4SHans Petter Selasky 				 * the radix_tree_delete() function.
223*6b839ff4SHans Petter Selasky 				 * This code path can only happen when
224*6b839ff4SHans Petter Selasky 				 * the tree is not empty.
225*6b839ff4SHans Petter Selasky 				 */
2268d59ecb2SHans Petter Selasky 				return (-ENOMEM);
227*6b839ff4SHans Petter Selasky 			}
2288d59ecb2SHans Petter Selasky 			node->slots[0] = root->rnode;
2298d59ecb2SHans Petter Selasky 			node->count++;
2308d59ecb2SHans Petter Selasky 			root->rnode = node;
2318d59ecb2SHans Petter Selasky 		}
2328d59ecb2SHans Petter Selasky 		root->height++;
2338d59ecb2SHans Petter Selasky 	}
2348d59ecb2SHans Petter Selasky 
2358d59ecb2SHans Petter Selasky 	/* get radix tree height index */
2368d59ecb2SHans Petter Selasky 	height = root->height - 1;
2378d59ecb2SHans Petter Selasky 
2388d59ecb2SHans Petter Selasky 	/* walk down the tree until the first missing node, if any */
2398d59ecb2SHans Petter Selasky 	for ( ; height != 0; height--) {
2408d59ecb2SHans Petter Selasky 		idx = radix_pos(index, height);
2418d59ecb2SHans Petter Selasky 		if (node->slots[idx] == NULL)
2428d59ecb2SHans Petter Selasky 			break;
2438d59ecb2SHans Petter Selasky 		node = node->slots[idx];
2448d59ecb2SHans Petter Selasky 	}
2458d59ecb2SHans Petter Selasky 
2468d59ecb2SHans Petter Selasky 	/* allocate the missing radix levels, if any */
2478d59ecb2SHans Petter Selasky 	for (idx = 0; idx != height; idx++) {
2488d59ecb2SHans Petter Selasky 		temp[idx] = malloc(sizeof(*node), M_RADIX,
2498d59ecb2SHans Petter Selasky 		    root->gfp_mask | M_ZERO);
2508d59ecb2SHans Petter Selasky 		if (temp[idx] == NULL) {
2518d59ecb2SHans Petter Selasky 			while (idx--)
2528d59ecb2SHans Petter Selasky 				free(temp[idx], M_RADIX);
253*6b839ff4SHans Petter Selasky 			radix_tree_clean_root_node(root);
2548d59ecb2SHans Petter Selasky 			return (-ENOMEM);
2558d59ecb2SHans Petter Selasky 		}
2568d59ecb2SHans Petter Selasky 	}
2578d59ecb2SHans Petter Selasky 
2588d59ecb2SHans Petter Selasky 	/* setup new radix levels, if any */
2598d59ecb2SHans Petter Selasky 	for ( ; height != 0; height--) {
2608d59ecb2SHans Petter Selasky 		idx = radix_pos(index, height);
2618d59ecb2SHans Petter Selasky 		node->slots[idx] = temp[height - 1];
2628d59ecb2SHans Petter Selasky 		node->count++;
2638d59ecb2SHans Petter Selasky 		node = node->slots[idx];
2648d59ecb2SHans Petter Selasky 	}
2658d59ecb2SHans Petter Selasky 
2668d59ecb2SHans Petter Selasky 	/*
2678d59ecb2SHans Petter Selasky 	 * Insert and adjust count if the item does not already exist.
2688d59ecb2SHans Petter Selasky 	 */
2698d59ecb2SHans Petter Selasky 	idx = radix_pos(index, 0);
2708d59ecb2SHans Petter Selasky 	if (node->slots[idx])
2718d59ecb2SHans Petter Selasky 		return (-EEXIST);
2728d59ecb2SHans Petter Selasky 	node->slots[idx] = item;
2738d59ecb2SHans Petter Selasky 	node->count++;
2748d59ecb2SHans Petter Selasky 
2758d59ecb2SHans Petter Selasky 	return (0);
2768d59ecb2SHans Petter Selasky }
277*6b839ff4SHans Petter Selasky 
278*6b839ff4SHans Petter Selasky int
radix_tree_store(struct radix_tree_root * root,unsigned long index,void ** ppitem)279*6b839ff4SHans Petter Selasky radix_tree_store(struct radix_tree_root *root, unsigned long index, void **ppitem)
280*6b839ff4SHans Petter Selasky {
281*6b839ff4SHans Petter Selasky 	struct radix_tree_node *node;
282*6b839ff4SHans Petter Selasky 	struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];
283*6b839ff4SHans Petter Selasky 	void *pitem;
284*6b839ff4SHans Petter Selasky 	int height;
285*6b839ff4SHans Petter Selasky 	int idx;
286*6b839ff4SHans Petter Selasky 
287*6b839ff4SHans Petter Selasky 	/*
288*6b839ff4SHans Petter Selasky 	 * Inserting a NULL item means delete it. The old pointer is
289*6b839ff4SHans Petter Selasky 	 * stored at the location pointed to by "ppitem".
290*6b839ff4SHans Petter Selasky 	 */
291*6b839ff4SHans Petter Selasky 	if (*ppitem == NULL) {
292*6b839ff4SHans Petter Selasky 		*ppitem = radix_tree_delete(root, index);
293*6b839ff4SHans Petter Selasky 		return (0);
294*6b839ff4SHans Petter Selasky 	}
295*6b839ff4SHans Petter Selasky 
296*6b839ff4SHans Petter Selasky 	/* get root node, if any */
297*6b839ff4SHans Petter Selasky 	node = root->rnode;
298*6b839ff4SHans Petter Selasky 
299*6b839ff4SHans Petter Selasky 	/* allocate root node, if any */
300*6b839ff4SHans Petter Selasky 	if (node == NULL) {
301*6b839ff4SHans Petter Selasky 		node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
302*6b839ff4SHans Petter Selasky 		if (node == NULL)
303*6b839ff4SHans Petter Selasky 			return (-ENOMEM);
304*6b839ff4SHans Petter Selasky 		root->rnode = node;
305*6b839ff4SHans Petter Selasky 		root->height++;
306*6b839ff4SHans Petter Selasky 	}
307*6b839ff4SHans Petter Selasky 
308*6b839ff4SHans Petter Selasky 	/* expand radix tree as needed */
309*6b839ff4SHans Petter Selasky 	while (radix_max(root) < index) {
310*6b839ff4SHans Petter Selasky 		/* check if the radix tree is getting too big */
311*6b839ff4SHans Petter Selasky 		if (root->height == RADIX_TREE_MAX_HEIGHT) {
312*6b839ff4SHans Petter Selasky 			radix_tree_clean_root_node(root);
313*6b839ff4SHans Petter Selasky 			return (-E2BIG);
314*6b839ff4SHans Petter Selasky 		}
315*6b839ff4SHans Petter Selasky 
316*6b839ff4SHans Petter Selasky 		/*
317*6b839ff4SHans Petter Selasky 		 * If the root radix level is not empty, we need to
318*6b839ff4SHans Petter Selasky 		 * allocate a new radix level:
319*6b839ff4SHans Petter Selasky 		 */
320*6b839ff4SHans Petter Selasky 		if (node->count != 0) {
321*6b839ff4SHans Petter Selasky 			node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
322*6b839ff4SHans Petter Selasky 			if (node == NULL) {
323*6b839ff4SHans Petter Selasky 				/*
324*6b839ff4SHans Petter Selasky 				 * Freeing the already allocated radix
325*6b839ff4SHans Petter Selasky 				 * levels, if any, will be handled by
326*6b839ff4SHans Petter Selasky 				 * the radix_tree_delete() function.
327*6b839ff4SHans Petter Selasky 				 * This code path can only happen when
328*6b839ff4SHans Petter Selasky 				 * the tree is not empty.
329*6b839ff4SHans Petter Selasky 				 */
330*6b839ff4SHans Petter Selasky 				return (-ENOMEM);
331*6b839ff4SHans Petter Selasky 			}
332*6b839ff4SHans Petter Selasky 			node->slots[0] = root->rnode;
333*6b839ff4SHans Petter Selasky 			node->count++;
334*6b839ff4SHans Petter Selasky 			root->rnode = node;
335*6b839ff4SHans Petter Selasky 		}
336*6b839ff4SHans Petter Selasky 		root->height++;
337*6b839ff4SHans Petter Selasky 	}
338*6b839ff4SHans Petter Selasky 
339*6b839ff4SHans Petter Selasky 	/* get radix tree height index */
340*6b839ff4SHans Petter Selasky 	height = root->height - 1;
341*6b839ff4SHans Petter Selasky 
342*6b839ff4SHans Petter Selasky 	/* walk down the tree until the first missing node, if any */
343*6b839ff4SHans Petter Selasky 	for ( ; height != 0; height--) {
344*6b839ff4SHans Petter Selasky 		idx = radix_pos(index, height);
345*6b839ff4SHans Petter Selasky 		if (node->slots[idx] == NULL)
346*6b839ff4SHans Petter Selasky 			break;
347*6b839ff4SHans Petter Selasky 		node = node->slots[idx];
348*6b839ff4SHans Petter Selasky 	}
349*6b839ff4SHans Petter Selasky 
350*6b839ff4SHans Petter Selasky 	/* allocate the missing radix levels, if any */
351*6b839ff4SHans Petter Selasky 	for (idx = 0; idx != height; idx++) {
352*6b839ff4SHans Petter Selasky 		temp[idx] = malloc(sizeof(*node), M_RADIX,
353*6b839ff4SHans Petter Selasky 		    root->gfp_mask | M_ZERO);
354*6b839ff4SHans Petter Selasky 		if (temp[idx] == NULL) {
355*6b839ff4SHans Petter Selasky 			while (idx--)
356*6b839ff4SHans Petter Selasky 				free(temp[idx], M_RADIX);
357*6b839ff4SHans Petter Selasky 			radix_tree_clean_root_node(root);
358*6b839ff4SHans Petter Selasky 			return (-ENOMEM);
359*6b839ff4SHans Petter Selasky 		}
360*6b839ff4SHans Petter Selasky 	}
361*6b839ff4SHans Petter Selasky 
362*6b839ff4SHans Petter Selasky 	/* setup new radix levels, if any */
363*6b839ff4SHans Petter Selasky 	for ( ; height != 0; height--) {
364*6b839ff4SHans Petter Selasky 		idx = radix_pos(index, height);
365*6b839ff4SHans Petter Selasky 		node->slots[idx] = temp[height - 1];
366*6b839ff4SHans Petter Selasky 		node->count++;
367*6b839ff4SHans Petter Selasky 		node = node->slots[idx];
368*6b839ff4SHans Petter Selasky 	}
369*6b839ff4SHans Petter Selasky 
370*6b839ff4SHans Petter Selasky 	/*
371*6b839ff4SHans Petter Selasky 	 * Insert and adjust count if the item does not already exist.
372*6b839ff4SHans Petter Selasky 	 */
373*6b839ff4SHans Petter Selasky 	idx = radix_pos(index, 0);
374*6b839ff4SHans Petter Selasky 	/* swap */
375*6b839ff4SHans Petter Selasky 	pitem = node->slots[idx];
376*6b839ff4SHans Petter Selasky 	node->slots[idx] = *ppitem;
377*6b839ff4SHans Petter Selasky 	*ppitem = pitem;
378*6b839ff4SHans Petter Selasky 
379*6b839ff4SHans Petter Selasky 	if (pitem == NULL)
380*6b839ff4SHans Petter Selasky 		node->count++;
381*6b839ff4SHans Petter Selasky 	return (0);
382*6b839ff4SHans Petter Selasky }
383