]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1366c37e MW |
2 | #include <stdio.h> |
3 | #include <stdlib.h> | |
4 | #include <unistd.h> | |
5 | #include <time.h> | |
6 | #include <assert.h> | |
0a835c4f | 7 | #include <limits.h> |
1366c37e MW |
8 | |
9 | #include <linux/slab.h> | |
10 | #include <linux/radix-tree.h> | |
11 | ||
12 | #include "test.h" | |
13 | #include "regression.h" | |
14 | ||
15 | void __gang_check(unsigned long middle, long down, long up, int chunk, int hop) | |
16 | { | |
17 | long idx; | |
18 | RADIX_TREE(tree, GFP_KERNEL); | |
19 | ||
20 | middle = 1 << 30; | |
21 | ||
22 | for (idx = -down; idx < up; idx++) | |
23 | item_insert(&tree, middle + idx); | |
24 | ||
25 | item_check_absent(&tree, middle - down - 1); | |
26 | for (idx = -down; idx < up; idx++) | |
27 | item_check_present(&tree, middle + idx); | |
28 | item_check_absent(&tree, middle + up); | |
29 | ||
30 | item_gang_check_present(&tree, middle - down, | |
31 | up + down, chunk, hop); | |
32 | item_full_scan(&tree, middle - down, down + up, chunk); | |
33 | item_kill_tree(&tree); | |
34 | } | |
35 | ||
36 | void gang_check(void) | |
37 | { | |
38 | __gang_check(1 << 30, 128, 128, 35, 2); | |
39 | __gang_check(1 << 31, 128, 128, 32, 32); | |
40 | __gang_check(1 << 31, 128, 128, 32, 100); | |
41 | __gang_check(1 << 31, 128, 128, 17, 7); | |
42 | __gang_check(0xffff0000, 0, 65536, 17, 7); | |
43 | __gang_check(0xfffffffe, 1, 1, 17, 7); | |
44 | } | |
45 | ||
46 | void __big_gang_check(void) | |
47 | { | |
48 | unsigned long start; | |
49 | int wrapped = 0; | |
50 | ||
51 | start = 0; | |
52 | do { | |
53 | unsigned long old_start; | |
54 | ||
55 | // printf("0x%08lx\n", start); | |
56 | __gang_check(start, rand() % 113 + 1, rand() % 71, | |
57 | rand() % 157, rand() % 91 + 1); | |
58 | old_start = start; | |
59 | start += rand() % 1000000; | |
60 | start %= 1ULL << 33; | |
61 | if (start < old_start) | |
62 | wrapped = 1; | |
63 | } while (!wrapped); | |
64 | } | |
65 | ||
aa1d62d8 | 66 | void big_gang_check(bool long_run) |
1366c37e MW |
67 | { |
68 | int i; | |
69 | ||
aa1d62d8 | 70 | for (i = 0; i < (long_run ? 1000 : 3); i++) { |
1366c37e | 71 | __big_gang_check(); |
73bc029b | 72 | printv(2, "%d ", i); |
1366c37e MW |
73 | fflush(stdout); |
74 | } | |
75 | } | |
76 | ||
77 | void add_and_check(void) | |
78 | { | |
79 | RADIX_TREE(tree, GFP_KERNEL); | |
80 | ||
81 | item_insert(&tree, 44); | |
82 | item_check_present(&tree, 44); | |
83 | item_check_absent(&tree, 43); | |
84 | item_kill_tree(&tree); | |
85 | } | |
86 | ||
87 | void dynamic_height_check(void) | |
88 | { | |
89 | int i; | |
90 | RADIX_TREE(tree, GFP_KERNEL); | |
91 | tree_verify_min_height(&tree, 0); | |
92 | ||
93 | item_insert(&tree, 42); | |
94 | tree_verify_min_height(&tree, 42); | |
95 | ||
96 | item_insert(&tree, 1000000); | |
97 | tree_verify_min_height(&tree, 1000000); | |
98 | ||
99 | assert(item_delete(&tree, 1000000)); | |
100 | tree_verify_min_height(&tree, 42); | |
101 | ||
102 | assert(item_delete(&tree, 42)); | |
103 | tree_verify_min_height(&tree, 0); | |
104 | ||
105 | for (i = 0; i < 1000; i++) { | |
106 | item_insert(&tree, i); | |
107 | tree_verify_min_height(&tree, i); | |
108 | } | |
109 | ||
110 | i--; | |
111 | for (;;) { | |
112 | assert(item_delete(&tree, i)); | |
113 | if (i == 0) { | |
114 | tree_verify_min_height(&tree, 0); | |
115 | break; | |
116 | } | |
117 | i--; | |
118 | tree_verify_min_height(&tree, i); | |
119 | } | |
120 | ||
121 | item_kill_tree(&tree); | |
122 | } | |
123 | ||
124 | void check_copied_tags(struct radix_tree_root *tree, unsigned long start, unsigned long end, unsigned long *idx, int count, int fromtag, int totag) | |
125 | { | |
126 | int i; | |
127 | ||
128 | for (i = 0; i < count; i++) { | |
129 | /* if (i % 1000 == 0) | |
130 | putchar('.'); */ | |
131 | if (idx[i] < start || idx[i] > end) { | |
132 | if (item_tag_get(tree, idx[i], totag)) { | |
73bc029b RS |
133 | printv(2, "%lu-%lu: %lu, tags %d-%d\n", start, |
134 | end, idx[i], item_tag_get(tree, idx[i], | |
135 | fromtag), | |
136 | item_tag_get(tree, idx[i], totag)); | |
1366c37e MW |
137 | } |
138 | assert(!item_tag_get(tree, idx[i], totag)); | |
139 | continue; | |
140 | } | |
141 | if (item_tag_get(tree, idx[i], fromtag) ^ | |
142 | item_tag_get(tree, idx[i], totag)) { | |
73bc029b RS |
143 | printv(2, "%lu-%lu: %lu, tags %d-%d\n", start, end, |
144 | idx[i], item_tag_get(tree, idx[i], fromtag), | |
145 | item_tag_get(tree, idx[i], totag)); | |
1366c37e MW |
146 | } |
147 | assert(!(item_tag_get(tree, idx[i], fromtag) ^ | |
148 | item_tag_get(tree, idx[i], totag))); | |
149 | } | |
150 | } | |
151 | ||
152 | #define ITEMS 50000 | |
153 | ||
154 | void copy_tag_check(void) | |
155 | { | |
156 | RADIX_TREE(tree, GFP_KERNEL); | |
157 | unsigned long idx[ITEMS]; | |
158 | unsigned long start, end, count = 0, tagged, cur, tmp; | |
159 | int i; | |
160 | ||
161 | // printf("generating radix tree indices...\n"); | |
162 | start = rand(); | |
163 | end = rand(); | |
164 | if (start > end && (rand() % 10)) { | |
165 | cur = start; | |
166 | start = end; | |
167 | end = cur; | |
168 | } | |
169 | /* Specifically create items around the start and the end of the range | |
170 | * with high probability to check for off by one errors */ | |
171 | cur = rand(); | |
172 | if (cur & 1) { | |
173 | item_insert(&tree, start); | |
174 | if (cur & 2) { | |
175 | if (start <= end) | |
176 | count++; | |
177 | item_tag_set(&tree, start, 0); | |
178 | } | |
179 | } | |
180 | if (cur & 4) { | |
181 | item_insert(&tree, start-1); | |
182 | if (cur & 8) | |
183 | item_tag_set(&tree, start-1, 0); | |
184 | } | |
185 | if (cur & 16) { | |
186 | item_insert(&tree, end); | |
187 | if (cur & 32) { | |
188 | if (start <= end) | |
189 | count++; | |
190 | item_tag_set(&tree, end, 0); | |
191 | } | |
192 | } | |
193 | if (cur & 64) { | |
194 | item_insert(&tree, end+1); | |
195 | if (cur & 128) | |
196 | item_tag_set(&tree, end+1, 0); | |
197 | } | |
198 | ||
199 | for (i = 0; i < ITEMS; i++) { | |
200 | do { | |
201 | idx[i] = rand(); | |
202 | } while (item_lookup(&tree, idx[i])); | |
203 | ||
204 | item_insert(&tree, idx[i]); | |
205 | if (rand() & 1) { | |
206 | item_tag_set(&tree, idx[i], 0); | |
207 | if (idx[i] >= start && idx[i] <= end) | |
208 | count++; | |
209 | } | |
210 | /* if (i % 1000 == 0) | |
211 | putchar('.'); */ | |
212 | } | |
213 | ||
214 | // printf("\ncopying tags...\n"); | |
268f42de | 215 | tagged = tag_tagged_items(&tree, NULL, start, end, ITEMS, 0, 1); |
1366c37e MW |
216 | |
217 | // printf("checking copied tags\n"); | |
218 | assert(tagged == count); | |
219 | check_copied_tags(&tree, start, end, idx, ITEMS, 0, 1); | |
220 | ||
221 | /* Copy tags in several rounds */ | |
222 | // printf("\ncopying tags...\n"); | |
268f42de MW |
223 | tmp = rand() % (count / 10 + 2); |
224 | tagged = tag_tagged_items(&tree, NULL, start, end, tmp, 0, 2); | |
225 | assert(tagged == count); | |
1366c37e MW |
226 | |
227 | // printf("%lu %lu %lu\n", tagged, tmp, count); | |
228 | // printf("checking copied tags\n"); | |
229 | check_copied_tags(&tree, start, end, idx, ITEMS, 0, 2); | |
1366c37e MW |
230 | verify_tag_consistency(&tree, 0); |
231 | verify_tag_consistency(&tree, 1); | |
232 | verify_tag_consistency(&tree, 2); | |
233 | // printf("\n"); | |
234 | item_kill_tree(&tree); | |
235 | } | |
236 | ||
eb73f7f3 | 237 | static void __locate_check(struct radix_tree_root *tree, unsigned long index, |
0a2efc6c | 238 | unsigned order) |
d42cb1a9 MW |
239 | { |
240 | struct item *item; | |
241 | unsigned long index2; | |
242 | ||
0a2efc6c | 243 | item_insert_order(tree, index, order); |
d42cb1a9 | 244 | item = item_lookup(tree, index); |
478922e2 | 245 | index2 = find_item(tree, item); |
d42cb1a9 | 246 | if (index != index2) { |
73bc029b | 247 | printv(2, "index %ld order %d inserted; found %ld\n", |
0a2efc6c | 248 | index, order, index2); |
d42cb1a9 MW |
249 | abort(); |
250 | } | |
251 | } | |
252 | ||
eb73f7f3 RZ |
253 | static void __order_0_locate_check(void) |
254 | { | |
255 | RADIX_TREE(tree, GFP_KERNEL); | |
256 | int i; | |
257 | ||
258 | for (i = 0; i < 50; i++) | |
259 | __locate_check(&tree, rand() % INT_MAX, 0); | |
260 | ||
261 | item_kill_tree(&tree); | |
262 | } | |
263 | ||
d42cb1a9 MW |
264 | static void locate_check(void) |
265 | { | |
266 | RADIX_TREE(tree, GFP_KERNEL); | |
0a2efc6c | 267 | unsigned order; |
d42cb1a9 MW |
268 | unsigned long offset, index; |
269 | ||
eb73f7f3 RZ |
270 | __order_0_locate_check(); |
271 | ||
0a2efc6c MW |
272 | for (order = 0; order < 20; order++) { |
273 | for (offset = 0; offset < (1 << (order + 3)); | |
274 | offset += (1UL << order)) { | |
275 | for (index = 0; index < (1UL << (order + 5)); | |
276 | index += (1UL << order)) { | |
277 | __locate_check(&tree, index + offset, order); | |
278 | } | |
478922e2 | 279 | if (find_item(&tree, &tree) != -1) |
0a2efc6c | 280 | abort(); |
d42cb1a9 | 281 | |
0a2efc6c MW |
282 | item_kill_tree(&tree); |
283 | } | |
d42cb1a9 MW |
284 | } |
285 | ||
478922e2 | 286 | if (find_item(&tree, &tree) != -1) |
d42cb1a9 | 287 | abort(); |
0a2efc6c | 288 | __locate_check(&tree, -1, 0); |
478922e2 | 289 | if (find_item(&tree, &tree) != -1) |
d42cb1a9 MW |
290 | abort(); |
291 | item_kill_tree(&tree); | |
292 | } | |
293 | ||
aa1d62d8 | 294 | static void single_thread_tests(bool long_run) |
1366c37e MW |
295 | { |
296 | int i; | |
297 | ||
73bc029b | 298 | printv(1, "starting single_thread_tests: %d allocated, preempt %d\n", |
847d3576 | 299 | nr_allocated, preempt_count); |
4f3755d1 | 300 | multiorder_checks(); |
af1c5cca | 301 | rcu_barrier(); |
73bc029b | 302 | printv(2, "after multiorder_check: %d allocated, preempt %d\n", |
847d3576 | 303 | nr_allocated, preempt_count); |
d42cb1a9 | 304 | locate_check(); |
af1c5cca | 305 | rcu_barrier(); |
73bc029b | 306 | printv(2, "after locate_check: %d allocated, preempt %d\n", |
847d3576 | 307 | nr_allocated, preempt_count); |
1366c37e | 308 | tag_check(); |
af1c5cca | 309 | rcu_barrier(); |
73bc029b | 310 | printv(2, "after tag_check: %d allocated, preempt %d\n", |
847d3576 | 311 | nr_allocated, preempt_count); |
1366c37e | 312 | gang_check(); |
af1c5cca | 313 | rcu_barrier(); |
73bc029b | 314 | printv(2, "after gang_check: %d allocated, preempt %d\n", |
847d3576 | 315 | nr_allocated, preempt_count); |
1366c37e | 316 | add_and_check(); |
af1c5cca | 317 | rcu_barrier(); |
73bc029b | 318 | printv(2, "after add_and_check: %d allocated, preempt %d\n", |
847d3576 | 319 | nr_allocated, preempt_count); |
1366c37e | 320 | dynamic_height_check(); |
af1c5cca | 321 | rcu_barrier(); |
73bc029b | 322 | printv(2, "after dynamic_height_check: %d allocated, preempt %d\n", |
847d3576 | 323 | nr_allocated, preempt_count); |
0a835c4f MW |
324 | idr_checks(); |
325 | ida_checks(); | |
326 | rcu_barrier(); | |
73bc029b | 327 | printv(2, "after idr_checks: %d allocated, preempt %d\n", |
0a835c4f | 328 | nr_allocated, preempt_count); |
aa1d62d8 | 329 | big_gang_check(long_run); |
af1c5cca | 330 | rcu_barrier(); |
73bc029b | 331 | printv(2, "after big_gang_check: %d allocated, preempt %d\n", |
847d3576 | 332 | nr_allocated, preempt_count); |
aa1d62d8 | 333 | for (i = 0; i < (long_run ? 2000 : 3); i++) { |
1366c37e | 334 | copy_tag_check(); |
73bc029b | 335 | printv(2, "%d ", i); |
1366c37e MW |
336 | fflush(stdout); |
337 | } | |
af1c5cca | 338 | rcu_barrier(); |
73bc029b | 339 | printv(2, "after copy_tag_check: %d allocated, preempt %d\n", |
847d3576 | 340 | nr_allocated, preempt_count); |
1366c37e MW |
341 | } |
342 | ||
aa1d62d8 | 343 | int main(int argc, char **argv) |
1366c37e | 344 | { |
aa1d62d8 RZ |
345 | bool long_run = false; |
346 | int opt; | |
061ef393 | 347 | unsigned int seed = time(NULL); |
aa1d62d8 | 348 | |
73bc029b | 349 | while ((opt = getopt(argc, argv, "ls:v")) != -1) { |
aa1d62d8 RZ |
350 | if (opt == 'l') |
351 | long_run = true; | |
061ef393 MW |
352 | else if (opt == 's') |
353 | seed = strtoul(optarg, NULL, 0); | |
73bc029b RS |
354 | else if (opt == 'v') |
355 | test_verbose++; | |
aa1d62d8 RZ |
356 | } |
357 | ||
061ef393 MW |
358 | printf("random seed %u\n", seed); |
359 | srand(seed); | |
360 | ||
73bc029b RS |
361 | printf("running tests\n"); |
362 | ||
1366c37e MW |
363 | rcu_register_thread(); |
364 | radix_tree_init(); | |
365 | ||
366 | regression1_test(); | |
367 | regression2_test(); | |
2d6f45b8 | 368 | regression3_test(); |
c0cdbf81 MW |
369 | iteration_test(0, 10 + 90 * long_run); |
370 | iteration_test(7, 10 + 90 * long_run); | |
aa1d62d8 | 371 | single_thread_tests(long_run); |
4ecd9542 | 372 | ida_thread_tests(); |
1366c37e | 373 | |
6df5ee78 MW |
374 | /* Free any remaining preallocated nodes */ |
375 | radix_tree_cpu_dead(0); | |
376 | ||
cfa40bcf KK |
377 | benchmark(); |
378 | ||
af1c5cca | 379 | rcu_barrier(); |
73bc029b | 380 | printv(2, "after rcu_barrier: %d allocated, preempt %d\n", |
847d3576 | 381 | nr_allocated, preempt_count); |
1366c37e MW |
382 | rcu_unregister_thread(); |
383 | ||
73bc029b RS |
384 | printf("tests completed\n"); |
385 | ||
1366c37e MW |
386 | exit(0); |
387 | } |