linux_radix.c revision 271127
1/*-
2 * Copyright (c) 2010 Isilon Systems, Inc.
3 * Copyright (c) 2010 iX Systems, Inc.
4 * Copyright (c) 2010 Panasas, Inc.
5 * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice unmodified, this list of conditions, and the following
13 *    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 ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/malloc.h>
33#include <sys/kernel.h>
34#include <sys/sysctl.h>
35
36#include <linux/slab.h>
37#include <linux/kernel.h>
38#include <linux/radix-tree.h>
39#include <linux/err.h>
40
41static MALLOC_DEFINE(M_RADIX, "radix", "Linux radix compat");
42
43static inline int
44radix_max(struct radix_tree_root *root)
45{
46	return (1 << (root->height * RADIX_TREE_MAP_SHIFT)) - 1;
47}
48
49static inline int
50radix_pos(long id, int height)
51{
52	return (id >> (RADIX_TREE_MAP_SHIFT * height)) & RADIX_TREE_MAP_MASK;
53}
54
55void *
56radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
57{
58	struct radix_tree_node *node;
59	void *item;
60	int height;
61
62	item = NULL;
63	node = root->rnode;
64	height = root->height - 1;
65	if (index > radix_max(root))
66		goto out;
67	while (height && node)
68		node = node->slots[radix_pos(index, height--)];
69	if (node)
70		item = node->slots[radix_pos(index, 0)];
71
72out:
73	return (item);
74}
75
76void *
77radix_tree_delete(struct radix_tree_root *root, unsigned long index)
78{
79	struct radix_tree_node *stack[RADIX_TREE_MAX_HEIGHT];
80	struct radix_tree_node *node;
81	void *item;
82	int height;
83	int idx;
84
85	item = NULL;
86	node = root->rnode;
87	height = root->height - 1;
88	if (index > radix_max(root))
89		goto out;
90	/*
91	 * Find the node and record the path in stack.
92	 */
93	while (height && node) {
94		stack[height] = node;
95		node = node->slots[radix_pos(index, height--)];
96	}
97	idx = radix_pos(index, 0);
98	if (node)
99		item = node->slots[idx];
100	/*
101	 * If we removed something reduce the height of the tree.
102	 */
103	if (item)
104		for (;;) {
105			node->slots[idx] = NULL;
106			node->count--;
107			if (node->count > 0)
108				break;
109			free(node, M_RADIX);
110			if (node == root->rnode) {
111				root->rnode = NULL;
112				root->height = 0;
113				break;
114			}
115			height++;
116			node = stack[height];
117			idx = radix_pos(index, height);
118		}
119out:
120	return (item);
121}
122
123int
124radix_tree_insert(struct radix_tree_root *root, unsigned long index, void *item)
125{
126	struct radix_tree_node *node;
127	struct radix_tree_node *temp[RADIX_TREE_MAX_HEIGHT - 1];
128	int height;
129	int idx;
130
131	/* bail out upon insertion of a NULL item */
132	if (item == NULL)
133		return (-EINVAL);
134
135	/* get root node, if any */
136	node = root->rnode;
137
138	/* allocate root node, if any */
139	if (node == NULL) {
140		node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
141		if (node == NULL)
142			return (-ENOMEM);
143		root->rnode = node;
144		root->height++;
145	}
146
147	/* expand radix tree as needed */
148	while (radix_max(root) < index) {
149
150		/* check if the radix tree is getting too big */
151		if (root->height == RADIX_TREE_MAX_HEIGHT)
152			return (-E2BIG);
153
154		/*
155		 * If the root radix level is not empty, we need to
156		 * allocate a new radix level:
157		 */
158		if (node->count != 0) {
159			node = malloc(sizeof(*node), M_RADIX, root->gfp_mask | M_ZERO);
160			if (node == NULL)
161				return (-ENOMEM);
162			node->slots[0] = root->rnode;
163			node->count++;
164			root->rnode = node;
165		}
166		root->height++;
167	}
168
169	/* get radix tree height index */
170	height = root->height - 1;
171
172	/* walk down the tree until the first missing node, if any */
173	for ( ; height != 0; height--) {
174		idx = radix_pos(index, height);
175		if (node->slots[idx] == NULL)
176			break;
177		node = node->slots[idx];
178	}
179
180	/* allocate the missing radix levels, if any */
181	for (idx = 0; idx != height; idx++) {
182		temp[idx] = malloc(sizeof(*node), M_RADIX,
183		    root->gfp_mask | M_ZERO);
184		if (temp[idx] == NULL) {
185			while(idx--)
186				free(temp[idx], M_RADIX);
187			/* check if we should free the root node aswell */
188			if (root->rnode->count == 0) {
189				free(root->rnode, M_RADIX);
190				root->rnode = NULL;
191				root->height = 0;
192			}
193			return (-ENOMEM);
194		}
195	}
196
197	/* setup new radix levels, if any */
198	for ( ; height != 0; height--) {
199		idx = radix_pos(index, height);
200		node->slots[idx] = temp[height - 1];
201		node->count++;
202		node = node->slots[idx];
203	}
204
205	/*
206	 * Insert and adjust count if the item does not already exist.
207	 */
208	idx = radix_pos(index, 0);
209	if (node->slots[idx])
210		return (-EEXIST);
211	node->slots[idx] = item;
212	node->count++;
213
214	return (0);
215}
216