File Index
Files and directories
Sign in
FILE

tag_check.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/radix-tree/tag_check.c C
  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(tree, index);
 20	assert(item_tag_get(tree, index, tag) == 0);
 21
 22	item_insert(tree, index);
 23	assert(item_tag_get(tree, index, tag) == 0);
 24	item_tag_set(tree, index, tag);
 25	ret = item_tag_get(tree, index, tag);
 26	assert(ret != 0);
 27	ret = tag_tagged_items(tree, first, ~0UL, 10, tag, !tag);
 28	assert(ret == 1);
 29	ret = item_tag_get(tree, index, !tag);
 30	assert(ret != 0);
 31	ret = item_delete(tree, index);
 32	assert(ret != 0);
 33	item_insert(tree, index);
 34	ret = item_tag_get(tree, index, tag);
 35	assert(ret == 0);
 36	ret = item_delete(tree, index);
 37	assert(ret != 0);
 38	ret = item_delete(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, index, 0);
 49		__simple_checks(&tree, index, 1);
 50	}
 51	verify_tag_consistency(&tree, 0);
 52	verify_tag_consistency(&tree, 1);
 53	printv(2, "before item_kill_tree: %d allocated\n", nr_allocated);
 54	item_kill_tree(&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(&tree, 43);
 67	assert(item_tag_get(&tree, 43, 0) == 0);
 68	item_tag_set(&tree, 43, 0);
 69	assert(item_tag_get(&tree, 43, 0) == 1);
 70	item_insert(&tree, 1000000);
 71	assert(item_tag_get(&tree, 43, 0) == 1);
 72
 73	item_insert(&tree, 0);
 74	item_tag_set(&tree, 0, 0);
 75	item_delete(&tree, 1000000);
 76	assert(item_tag_get(&tree, 43, 0) != 0);
 77	item_delete(&tree, 43);
 78	assert(item_tag_get(&tree, 43, 0) == 0);	/* crash */
 79	assert(item_tag_get(&tree, 0, 0) == 1);
 80
 81	verify_tag_consistency(&tree, 0);
 82
 83	item_kill_tree(&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(&tree, tmp);
 97	item_insert(&tree, tmp+1);
 98	item_tag_set(&tree, tmp, 0);
 99	item_tag_set(&tree, tmp, 1);
100	item_tag_set(&tree, tmp+1, 0);
101	item_delete(&tree, tmp+1);
102	item_tag_clear(&tree, tmp, 1);
103
104	assert(radix_tree_gang_lookup_tag(&tree, (void **)&item, 0, 1, 0) == 1);
105	assert(radix_tree_gang_lookup_tag(&tree, (void **)&item, 0, 1, 1) == 0);
106
107	assert(item_tag_get(&tree, tmp, 0) == 1);
108	assert(item_tag_get(&tree, tmp, 1) == 0);
109
110	verify_tag_consistency(&tree, 0);
111	item_kill_tree(&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, (void **)items,
140					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(tree, index);
184			item_insert(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(tree, index);
196			if (item_tag_get(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(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(tree, index))
213					assert(item_tag_get(tree, index, tag));
214				continue;
215			}
216			item_tag_set(tree, index, tag);
217			item_tag_set(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(tree, index);
229			assert(item_tag_get(tree, index, tag));
230			item_tag_clear(tree, index, tag);
231			item_tag_clear(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(tree, index);
244				break;
245			case NODE_PRESENT:
246				item_check_present(tree, index);
247				assert(!item_tag_get(tree, index, tag));
248				actual_total_present++;
249				break;
250			case NODE_TAGGED:
251				item_check_present(tree, index);
252				assert(item_tag_get(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, thrash_state, 0);
281
282	verify_tag_consistency(&tree, 0);
283	item_kill_tree(&tree);
284	free(thrash_state);
285}
286
287static void leak_check(void)
288{
289	RADIX_TREE(tree, GFP_KERNEL);
290
291	item_insert(&tree, 1000000);
292	item_delete(&tree, 1000000);
293	item_kill_tree(&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(&tree, 1000000);
302	printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
303	item_delete(&tree, 1000000);
304	printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
305	item_kill_tree(&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(&tree, 0);
317	item_tag_set(&tree, 0, 0);
318	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 0);
319	assert(ret == 1);
320	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 1, BATCH, 0);
321	assert(ret == 0);
322	verify_tag_consistency(&tree, 0);
323	verify_tag_consistency(&tree, 1);
324	ret = tag_tagged_items(&tree, first, 10, 10, XA_MARK_0, XA_MARK_1);
325	assert(ret == 1);
326	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 1);
327	assert(ret == 1);
328	item_tag_clear(&tree, 0, 0);
329	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 0);
330	assert(ret == 0);
331	item_kill_tree(&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}
~/ Read-only file preview.