aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/radix-tree/idr-test.c
diff options
context:
space:
mode:
authorMatthew Wilcox <mawilcox@microsoft.com>2016-12-20 10:27:56 -0500
committerMatthew Wilcox <mawilcox@microsoft.com>2017-02-13 21:44:01 -0500
commit0a835c4f090af2c76fc2932c539c3b32fd21fbbb (patch)
tree729c24514309afc323ee08e6d8336eb1e558406e /tools/testing/radix-tree/idr-test.c
parent0ac398ef391b53122976325ab6953456ce8e8310 (diff)
Reimplement IDR and IDA using the radix tree
The IDR is very similar to the radix tree. It has some functionality that the radix tree did not have (alloc next free, cyclic allocation, a callback-based for_each, destroy tree), which is readily implementable on top of the radix tree. A few small changes were needed in order to use a tag to represent nodes with free space below them. More extensive changes were needed to support storing NULL as a valid entry in an IDR. Plain radix trees still interpret NULL as a not-present entry. The IDA is reimplemented as a client of the newly enhanced radix tree. As in the current implementation, it uses a bitmap at the last level of the tree. Signed-off-by: Matthew Wilcox <willy@infradead.org> Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com> Tested-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Konstantin Khlebnikov <koct9i@gmail.com> Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Cc: Tejun Heo <tj@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'tools/testing/radix-tree/idr-test.c')
-rw-r--r--tools/testing/radix-tree/idr-test.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c
new file mode 100644
index 000000000000..4dffad9284e0
--- /dev/null
+++ b/tools/testing/radix-tree/idr-test.c
@@ -0,0 +1,342 @@
1/*
2 * idr-test.c: Test the IDR API
3 * Copyright (c) 2016 Matthew Wilcox <willy@infradead.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 */
14#include <linux/idr.h>
15#include <linux/slab.h>
16#include <linux/kernel.h>
17#include <linux/errno.h>
18
19#include "test.h"
20
21#define DUMMY_PTR ((void *)0x12)
22
23int item_idr_free(int id, void *p, void *data)
24{
25 struct item *item = p;
26 assert(item->index == id);
27 free(p);
28
29 return 0;
30}
31
32void item_idr_remove(struct idr *idr, int id)
33{
34 struct item *item = idr_find(idr, id);
35 assert(item->index == id);
36 idr_remove(idr, id);
37 free(item);
38}
39
40void idr_alloc_test(void)
41{
42 unsigned long i;
43 DEFINE_IDR(idr);
44
45 assert(idr_alloc_cyclic(&idr, DUMMY_PTR, 0, 0x4000, GFP_KERNEL) == 0);
46 assert(idr_alloc_cyclic(&idr, DUMMY_PTR, 0x3ffd, 0x4000, GFP_KERNEL) == 0x3ffd);
47 idr_remove(&idr, 0x3ffd);
48 idr_remove(&idr, 0);
49
50 for (i = 0x3ffe; i < 0x4003; i++) {
51 int id;
52 struct item *item;
53
54 if (i < 0x4000)
55 item = item_create(i, 0);
56 else
57 item = item_create(i - 0x3fff, 0);
58
59 id = idr_alloc_cyclic(&idr, item, 1, 0x4000, GFP_KERNEL);
60 assert(id == item->index);
61 }
62
63 idr_for_each(&idr, item_idr_free, &idr);
64 idr_destroy(&idr);
65}
66
67void idr_replace_test(void)
68{
69 DEFINE_IDR(idr);
70
71 idr_alloc(&idr, (void *)-1, 10, 11, GFP_KERNEL);
72 idr_replace(&idr, &idr, 10);
73
74 idr_destroy(&idr);
75}
76
77/*
78 * Unlike the radix tree, you can put a NULL pointer -- with care -- into
79 * the IDR. Some interfaces, like idr_find() do not distinguish between
80 * "present, value is NULL" and "not present", but that's exactly what some
81 * users want.
82 */
83void idr_null_test(void)
84{
85 int i;
86 DEFINE_IDR(idr);
87
88 assert(idr_is_empty(&idr));
89
90 assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 0);
91 assert(!idr_is_empty(&idr));
92 idr_remove(&idr, 0);
93 assert(idr_is_empty(&idr));
94
95 assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 0);
96 assert(!idr_is_empty(&idr));
97 idr_destroy(&idr);
98 assert(idr_is_empty(&idr));
99
100 for (i = 0; i < 10; i++) {
101 assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == i);
102 }
103
104 assert(idr_replace(&idr, DUMMY_PTR, 3) == NULL);
105 assert(idr_replace(&idr, DUMMY_PTR, 4) == NULL);
106 assert(idr_replace(&idr, NULL, 4) == DUMMY_PTR);
107 assert(idr_replace(&idr, DUMMY_PTR, 11) == ERR_PTR(-ENOENT));
108 idr_remove(&idr, 5);
109 assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 5);
110 idr_remove(&idr, 5);
111
112 for (i = 0; i < 9; i++) {
113 idr_remove(&idr, i);
114 assert(!idr_is_empty(&idr));
115 }
116 idr_remove(&idr, 8);
117 assert(!idr_is_empty(&idr));
118 idr_remove(&idr, 9);
119 assert(idr_is_empty(&idr));
120
121 assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 0);
122 assert(idr_replace(&idr, DUMMY_PTR, 3) == ERR_PTR(-ENOENT));
123 assert(idr_replace(&idr, DUMMY_PTR, 0) == NULL);
124 assert(idr_replace(&idr, NULL, 0) == DUMMY_PTR);
125
126 idr_destroy(&idr);
127 assert(idr_is_empty(&idr));
128
129 for (i = 1; i < 10; i++) {
130 assert(idr_alloc(&idr, NULL, 1, 0, GFP_KERNEL) == i);
131 }
132
133 idr_destroy(&idr);
134 assert(idr_is_empty(&idr));
135}
136
137void idr_nowait_test(void)
138{
139 unsigned int i;
140 DEFINE_IDR(idr);
141
142 idr_preload(GFP_KERNEL);
143
144 for (i = 0; i < 3; i++) {
145 struct item *item = item_create(i, 0);
146 assert(idr_alloc(&idr, item, i, i + 1, GFP_NOWAIT) == i);
147 }
148
149 idr_preload_end();
150
151 idr_for_each(&idr, item_idr_free, &idr);
152 idr_destroy(&idr);
153}
154
155void idr_checks(void)
156{
157 unsigned long i;
158 DEFINE_IDR(idr);
159
160 for (i = 0; i < 10000; i++) {
161 struct item *item = item_create(i, 0);
162 assert(idr_alloc(&idr, item, 0, 20000, GFP_KERNEL) == i);
163 }
164
165 assert(idr_alloc(&idr, DUMMY_PTR, 5, 30, GFP_KERNEL) < 0);
166
167 for (i = 0; i < 5000; i++)
168 item_idr_remove(&idr, i);
169
170 idr_remove(&idr, 3);
171
172 idr_for_each(&idr, item_idr_free, &idr);
173 idr_destroy(&idr);
174
175 assert(idr_is_empty(&idr));
176
177 idr_remove(&idr, 3);
178 idr_remove(&idr, 0);
179
180 for (i = INT_MAX - 3UL; i < INT_MAX + 1UL; i++) {
181 struct item *item = item_create(i, 0);
182 assert(idr_alloc(&idr, item, i, i + 10, GFP_KERNEL) == i);
183 }
184 assert(idr_alloc(&idr, DUMMY_PTR, i - 2, i, GFP_KERNEL) == -ENOSPC);
185
186 idr_for_each(&idr, item_idr_free, &idr);
187 idr_destroy(&idr);
188 idr_destroy(&idr);
189
190 assert(idr_is_empty(&idr));
191
192 for (i = 1; i < 10000; i++) {
193 struct item *item = item_create(i, 0);
194 assert(idr_alloc(&idr, item, 1, 20000, GFP_KERNEL) == i);
195 }
196
197 idr_for_each(&idr, item_idr_free, &idr);
198 idr_destroy(&idr);
199
200 idr_replace_test();
201 idr_alloc_test();
202 idr_null_test();
203 idr_nowait_test();
204}
205
206/*
207 * Check that we get the correct error when we run out of memory doing
208 * allocations. To ensure we run out of memory, just "forget" to preload.
209 * The first test is for not having a bitmap available, and the second test
210 * is for not being able to allocate a level of the radix tree.
211 */
212void ida_check_nomem(void)
213{
214 DEFINE_IDA(ida);
215 int id, err;
216
217 err = ida_get_new(&ida, &id);
218 assert(err == -EAGAIN);
219 err = ida_get_new_above(&ida, 1UL << 30, &id);
220 assert(err == -EAGAIN);
221}
222
223/*
224 * Check what happens when we fill a leaf and then delete it. This may
225 * discover mishandling of IDR_FREE.
226 */
227void ida_check_leaf(void)
228{
229 DEFINE_IDA(ida);
230 int id;
231 unsigned long i;
232
233 for (i = 0; i < IDA_BITMAP_BITS; i++) {
234 assert(ida_pre_get(&ida, GFP_KERNEL));
235 assert(!ida_get_new(&ida, &id));
236 assert(id == i);
237 }
238
239 ida_destroy(&ida);
240 assert(ida_is_empty(&ida));
241
242 assert(ida_pre_get(&ida, GFP_KERNEL));
243 assert(!ida_get_new(&ida, &id));
244 assert(id == 0);
245 ida_destroy(&ida);
246 assert(ida_is_empty(&ida));
247}
248
249/*
250 * Check allocations up to and slightly above the maximum allowed (2^31-1) ID.
251 * Allocating up to 2^31-1 should succeed, and then allocating the next one
252 * should fail.
253 */
254void ida_check_max(void)
255{
256 DEFINE_IDA(ida);
257 int id, err;
258 unsigned long i, j;
259
260 for (j = 1; j < 65537; j *= 2) {
261 unsigned long base = (1UL << 31) - j;
262 for (i = 0; i < j; i++) {
263 assert(ida_pre_get(&ida, GFP_KERNEL));
264 assert(!ida_get_new_above(&ida, base, &id));
265 assert(id == base + i);
266 }
267 assert(ida_pre_get(&ida, GFP_KERNEL));
268 err = ida_get_new_above(&ida, base, &id);
269 assert(err == -ENOSPC);
270 ida_destroy(&ida);
271 assert(ida_is_empty(&ida));
272 rcu_barrier();
273 }
274}
275
276void ida_checks(void)
277{
278 DEFINE_IDA(ida);
279 int id;
280 unsigned long i;
281
282 radix_tree_cpu_dead(1);
283 ida_check_nomem();
284
285 for (i = 0; i < 10000; i++) {
286 assert(ida_pre_get(&ida, GFP_KERNEL));
287 assert(!ida_get_new(&ida, &id));
288 assert(id == i);
289 }
290
291 ida_remove(&ida, 20);
292 ida_remove(&ida, 21);
293 for (i = 0; i < 3; i++) {
294 assert(ida_pre_get(&ida, GFP_KERNEL));
295 assert(!ida_get_new(&ida, &id));
296 if (i == 2)
297 assert(id == 10000);
298 }
299
300 for (i = 0; i < 5000; i++)
301 ida_remove(&ida, i);
302
303 assert(ida_pre_get(&ida, GFP_KERNEL));
304 assert(!ida_get_new_above(&ida, 5000, &id));
305 assert(id == 10001);
306
307 ida_destroy(&ida);
308
309 assert(ida_is_empty(&ida));
310
311 assert(ida_pre_get(&ida, GFP_KERNEL));
312 assert(!ida_get_new_above(&ida, 1, &id));
313 assert(id == 1);
314
315 ida_remove(&ida, id);
316 assert(ida_is_empty(&ida));
317 ida_destroy(&ida);
318 assert(ida_is_empty(&ida));
319
320 assert(ida_pre_get(&ida, GFP_KERNEL));
321 assert(!ida_get_new_above(&ida, 1, &id));
322 ida_destroy(&ida);
323 assert(ida_is_empty(&ida));
324
325 assert(ida_pre_get(&ida, GFP_KERNEL));
326 assert(!ida_get_new_above(&ida, 1, &id));
327 assert(id == 1);
328 assert(ida_pre_get(&ida, GFP_KERNEL));
329 assert(!ida_get_new_above(&ida, 1025, &id));
330 assert(id == 1025);
331 assert(ida_pre_get(&ida, GFP_KERNEL));
332 assert(!ida_get_new_above(&ida, 10000, &id));
333 assert(id == 10000);
334 ida_remove(&ida, 1025);
335 ida_destroy(&ida);
336 assert(ida_is_empty(&ida));
337
338 ida_check_leaf();
339 ida_check_max();
340
341 radix_tree_cpu_dead(1);
342}