1// SPDX-License-Identifier: GPL-2.0
2#include <stdlib.h>
3#include <assert.h>
4#include <stdio.h>
5#include <string.h>
6
7#include <linux/slab.h>
8#include <linux/radix-tree.h>
9
10#include "test.h"
11
12
13static void
14__simple_checks(struct radix_tree_root *tree, unsigned long index, int tag)
15{
16 unsigned long first = 0;
17 int ret;
18
19 item_check_absent(root: tree, index);
20 assert(item_tag_get(root: tree, index, tag) == 0);
21
22 item_insert(root: tree, index);
23 assert(item_tag_get(root: tree, index, tag) == 0);
24 item_tag_set(root: tree, index, tag);
25 ret = item_tag_get(root: tree, index, tag);
26 assert(ret != 0);
27 ret = tag_tagged_items(tree, start: first, end: ~0UL, batch: 10, iftag: tag, thentag: !tag);
28 assert(ret == 1);
29 ret = item_tag_get(root: tree, index, tag: !tag);
30 assert(ret != 0);
31 ret = item_delete(root: tree, index);
32 assert(ret != 0);
33 item_insert(root: tree, index);
34 ret = item_tag_get(root: tree, index, tag);
35 assert(ret == 0);
36 ret = item_delete(root: tree, index);
37 assert(ret != 0);
38 ret = item_delete(root: tree, index);
39 assert(ret == 0);
40}
41
42void simple_checks(void)
43{
44 unsigned long index;
45 RADIX_TREE(tree, GFP_KERNEL);
46
47 for (index = 0; index < 10000; index++) {
48 __simple_checks(tree: &tree, index, tag: 0);
49 __simple_checks(tree: &tree, index, tag: 1);
50 }
51 verify_tag_consistency(root: &tree, tag: 0);
52 verify_tag_consistency(root: &tree, tag: 1);
53 printv(2, "before item_kill_tree: %d allocated\n", nr_allocated);
54 item_kill_tree(root: &tree);
55 rcu_barrier();
56 printv(2, "after item_kill_tree: %d allocated\n", nr_allocated);
57}
58
59/*
60 * Check that tags propagate correctly when extending a tree.
61 */
62static void extend_checks(void)
63{
64 RADIX_TREE(tree, GFP_KERNEL);
65
66 item_insert(root: &tree, index: 43);
67 assert(item_tag_get(root: &tree, index: 43, tag: 0) == 0);
68 item_tag_set(root: &tree, index: 43, tag: 0);
69 assert(item_tag_get(root: &tree, index: 43, tag: 0) == 1);
70 item_insert(root: &tree, index: 1000000);
71 assert(item_tag_get(root: &tree, index: 43, tag: 0) == 1);
72
73 item_insert(root: &tree, index: 0);
74 item_tag_set(root: &tree, index: 0, tag: 0);
75 item_delete(root: &tree, index: 1000000);
76 assert(item_tag_get(root: &tree, index: 43, tag: 0) != 0);
77 item_delete(root: &tree, index: 43);
78 assert(item_tag_get(root: &tree, index: 43, tag: 0) == 0); /* crash */
79 assert(item_tag_get(root: &tree, index: 0, tag: 0) == 1);
80
81 verify_tag_consistency(root: &tree, tag: 0);
82
83 item_kill_tree(root: &tree);
84}
85
86/*
87 * Check that tags propagate correctly when contracting a tree.
88 */
89static void contract_checks(void)
90{
91 struct item *item;
92 int tmp;
93 RADIX_TREE(tree, GFP_KERNEL);
94
95 tmp = 1<<RADIX_TREE_MAP_SHIFT;
96 item_insert(root: &tree, index: tmp);
97 item_insert(root: &tree, index: tmp+1);
98 item_tag_set(root: &tree, index: tmp, tag: 0);
99 item_tag_set(root: &tree, index: tmp, tag: 1);
100 item_tag_set(root: &tree, index: tmp+1, tag: 0);
101 item_delete(root: &tree, index: tmp+1);
102 item_tag_clear(root: &tree, index: tmp, tag: 1);
103
104 assert(radix_tree_gang_lookup_tag(&tree, results: (void **)&item, first_index: 0, max_items: 1, tag: 0) == 1);
105 assert(radix_tree_gang_lookup_tag(&tree, results: (void **)&item, first_index: 0, max_items: 1, tag: 1) == 0);
106
107 assert(item_tag_get(root: &tree, index: tmp, tag: 0) == 1);
108 assert(item_tag_get(root: &tree, index: tmp, tag: 1) == 0);
109
110 verify_tag_consistency(root: &tree, tag: 0);
111 item_kill_tree(root: &tree);
112}
113
114/*
115 * Stupid tag thrasher
116 *
117 * Create a large linear array corresponding to the tree. Each element in
118 * the array is coherent with each node in the tree
119 */
120
121enum {
122 NODE_ABSENT = 0,
123 NODE_PRESENT = 1,
124 NODE_TAGGED = 2,
125};
126
127#define THRASH_SIZE (1000 * 1000)
128#define N 127
129#define BATCH 33
130
131static void gang_check(struct radix_tree_root *tree,
132 char *thrash_state, int tag)
133{
134 struct item *items[BATCH];
135 int nr_found;
136 unsigned long index = 0;
137 unsigned long last_index = 0;
138
139 while ((nr_found = radix_tree_gang_lookup_tag(tree, results: (void **)items,
140 first_index: index, BATCH, tag))) {
141 int i;
142
143 for (i = 0; i < nr_found; i++) {
144 struct item *item = items[i];
145
146 while (last_index < item->index) {
147 assert(thrash_state[last_index] != NODE_TAGGED);
148 last_index++;
149 }
150 assert(thrash_state[last_index] == NODE_TAGGED);
151 last_index++;
152 }
153 index = items[nr_found - 1]->index + 1;
154 }
155}
156
157static void do_thrash(struct radix_tree_root *tree, char *thrash_state, int tag)
158{
159 int insert_chunk;
160 int delete_chunk;
161 int tag_chunk;
162 int untag_chunk;
163 int total_tagged = 0;
164 int total_present = 0;
165
166 for (insert_chunk = 1; insert_chunk < THRASH_SIZE; insert_chunk *= N)
167 for (delete_chunk = 1; delete_chunk < THRASH_SIZE; delete_chunk *= N)
168 for (tag_chunk = 1; tag_chunk < THRASH_SIZE; tag_chunk *= N)
169 for (untag_chunk = 1; untag_chunk < THRASH_SIZE; untag_chunk *= N) {
170 int i;
171 unsigned long index;
172 int nr_inserted = 0;
173 int nr_deleted = 0;
174 int nr_tagged = 0;
175 int nr_untagged = 0;
176 int actual_total_tagged;
177 int actual_total_present;
178
179 for (i = 0; i < insert_chunk; i++) {
180 index = rand() % THRASH_SIZE;
181 if (thrash_state[index] != NODE_ABSENT)
182 continue;
183 item_check_absent(root: tree, index);
184 item_insert(root: tree, index);
185 assert(thrash_state[index] != NODE_PRESENT);
186 thrash_state[index] = NODE_PRESENT;
187 nr_inserted++;
188 total_present++;
189 }
190
191 for (i = 0; i < delete_chunk; i++) {
192 index = rand() % THRASH_SIZE;
193 if (thrash_state[index] == NODE_ABSENT)
194 continue;
195 item_check_present(root: tree, index);
196 if (item_tag_get(root: tree, index, tag)) {
197 assert(thrash_state[index] == NODE_TAGGED);
198 total_tagged--;
199 } else {
200 assert(thrash_state[index] == NODE_PRESENT);
201 }
202 item_delete(root: tree, index);
203 assert(thrash_state[index] != NODE_ABSENT);
204 thrash_state[index] = NODE_ABSENT;
205 nr_deleted++;
206 total_present--;
207 }
208
209 for (i = 0; i < tag_chunk; i++) {
210 index = rand() % THRASH_SIZE;
211 if (thrash_state[index] != NODE_PRESENT) {
212 if (item_lookup(root: tree, index))
213 assert(item_tag_get(root: tree, index, tag));
214 continue;
215 }
216 item_tag_set(root: tree, index, tag);
217 item_tag_set(root: tree, index, tag);
218 assert(thrash_state[index] != NODE_TAGGED);
219 thrash_state[index] = NODE_TAGGED;
220 nr_tagged++;
221 total_tagged++;
222 }
223
224 for (i = 0; i < untag_chunk; i++) {
225 index = rand() % THRASH_SIZE;
226 if (thrash_state[index] != NODE_TAGGED)
227 continue;
228 item_check_present(root: tree, index);
229 assert(item_tag_get(root: tree, index, tag));
230 item_tag_clear(root: tree, index, tag);
231 item_tag_clear(root: tree, index, tag);
232 assert(thrash_state[index] != NODE_PRESENT);
233 thrash_state[index] = NODE_PRESENT;
234 nr_untagged++;
235 total_tagged--;
236 }
237
238 actual_total_tagged = 0;
239 actual_total_present = 0;
240 for (index = 0; index < THRASH_SIZE; index++) {
241 switch (thrash_state[index]) {
242 case NODE_ABSENT:
243 item_check_absent(root: tree, index);
244 break;
245 case NODE_PRESENT:
246 item_check_present(root: tree, index);
247 assert(!item_tag_get(root: tree, index, tag));
248 actual_total_present++;
249 break;
250 case NODE_TAGGED:
251 item_check_present(root: tree, index);
252 assert(item_tag_get(root: tree, index, tag));
253 actual_total_present++;
254 actual_total_tagged++;
255 break;
256 }
257 }
258
259 gang_check(tree, thrash_state, tag);
260
261 printv(2, "%d(%d) %d(%d) %d(%d) %d(%d) / "
262 "%d(%d) present, %d(%d) tagged\n",
263 insert_chunk, nr_inserted,
264 delete_chunk, nr_deleted,
265 tag_chunk, nr_tagged,
266 untag_chunk, nr_untagged,
267 total_present, actual_total_present,
268 total_tagged, actual_total_tagged);
269 }
270}
271
272static void thrash_tags(void)
273{
274 RADIX_TREE(tree, GFP_KERNEL);
275 char *thrash_state;
276
277 thrash_state = malloc(THRASH_SIZE);
278 memset(thrash_state, 0, THRASH_SIZE);
279
280 do_thrash(tree: &tree, thrash_state, tag: 0);
281
282 verify_tag_consistency(root: &tree, tag: 0);
283 item_kill_tree(root: &tree);
284 free(thrash_state);
285}
286
287static void leak_check(void)
288{
289 RADIX_TREE(tree, GFP_KERNEL);
290
291 item_insert(root: &tree, index: 1000000);
292 item_delete(root: &tree, index: 1000000);
293 item_kill_tree(root: &tree);
294}
295
296static void __leak_check(void)
297{
298 RADIX_TREE(tree, GFP_KERNEL);
299
300 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
301 item_insert(root: &tree, index: 1000000);
302 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
303 item_delete(root: &tree, index: 1000000);
304 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
305 item_kill_tree(root: &tree);
306 printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
307}
308
309static void single_check(void)
310{
311 struct item *items[BATCH];
312 RADIX_TREE(tree, GFP_KERNEL);
313 int ret;
314 unsigned long first = 0;
315
316 item_insert(root: &tree, index: 0);
317 item_tag_set(root: &tree, index: 0, tag: 0);
318 ret = radix_tree_gang_lookup_tag(&tree, results: (void **)items, first_index: 0, BATCH, tag: 0);
319 assert(ret == 1);
320 ret = radix_tree_gang_lookup_tag(&tree, results: (void **)items, first_index: 1, BATCH, tag: 0);
321 assert(ret == 0);
322 verify_tag_consistency(root: &tree, tag: 0);
323 verify_tag_consistency(root: &tree, tag: 1);
324 ret = tag_tagged_items(&tree, start: first, end: 10, batch: 10, XA_MARK_0, XA_MARK_1);
325 assert(ret == 1);
326 ret = radix_tree_gang_lookup_tag(&tree, results: (void **)items, first_index: 0, BATCH, tag: 1);
327 assert(ret == 1);
328 item_tag_clear(root: &tree, index: 0, tag: 0);
329 ret = radix_tree_gang_lookup_tag(&tree, results: (void **)items, first_index: 0, BATCH, tag: 0);
330 assert(ret == 0);
331 item_kill_tree(root: &tree);
332}
333
334void tag_check(void)
335{
336 single_check();
337 extend_checks();
338 contract_checks();
339 rcu_barrier();
340 printv(2, "after extend_checks: %d allocated\n", nr_allocated);
341 __leak_check();
342 leak_check();
343 rcu_barrier();
344 printv(2, "after leak_check: %d allocated\n", nr_allocated);
345 simple_checks();
346 rcu_barrier();
347 printv(2, "after simple_checks: %d allocated\n", nr_allocated);
348 thrash_tags();
349 rcu_barrier();
350 printv(2, "after thrash_tags: %d allocated\n", nr_allocated);
351}
352

source code of linux/tools/testing/radix-tree/tag_check.c