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