xref: /linux-6.15/lib/interval_tree_test.c (revision ccaf3efc)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a88cc108SChris Wilson #include <linux/module.h>
3a54dae03SDavidlohr Bueso #include <linux/moduleparam.h>
4a88cc108SChris Wilson #include <linux/interval_tree.h>
5d46150d6SUros Bizjak #include <linux/prandom.h>
6a54dae03SDavidlohr Bueso #include <linux/slab.h>
7a88cc108SChris Wilson #include <asm/timex.h>
882114e45SWei Yang #include <linux/bitmap.h>
9*ccaf3efcSWei Yang #include <linux/maple_tree.h>
10a88cc108SChris Wilson 
11a54dae03SDavidlohr Bueso #define __param(type, name, init, msg)		\
12a54dae03SDavidlohr Bueso 	static type name = init;		\
13a54dae03SDavidlohr Bueso 	module_param(name, type, 0444);		\
14a54dae03SDavidlohr Bueso 	MODULE_PARM_DESC(name, msg);
15a54dae03SDavidlohr Bueso 
16a54dae03SDavidlohr Bueso __param(int, nnodes, 100, "Number of nodes in the interval tree");
170b548e33SDavidlohr Bueso __param(int, perf_loops, 1000, "Number of iterations modifying the tree");
18a54dae03SDavidlohr Bueso 
19a54dae03SDavidlohr Bueso __param(int, nsearches, 100, "Number of searches to the interval tree");
200b548e33SDavidlohr Bueso __param(int, search_loops, 1000, "Number of iterations searching the tree");
21c46ecce4SDavidlohr Bueso __param(bool, search_all, false, "Searches will iterate all nodes in the tree");
22a54dae03SDavidlohr Bueso 
23a8ec14d4SDavidlohr Bueso __param(uint, max_endpoint, ~0, "Largest value for the interval's endpoint");
2416b1936aSWei Yang __param(ullong, seed, 3141592653589793238ULL, "Random seed");
25a88cc108SChris Wilson 
26f808c13fSDavidlohr Bueso static struct rb_root_cached root = RB_ROOT_CACHED;
27a54dae03SDavidlohr Bueso static struct interval_tree_node *nodes = NULL;
28a54dae03SDavidlohr Bueso static u32 *queries = NULL;
29a88cc108SChris Wilson 
30a88cc108SChris Wilson static struct rnd_state rnd;
31a88cc108SChris Wilson 
32a88cc108SChris Wilson static inline unsigned long
search(struct rb_root_cached * root,unsigned long start,unsigned long last)33f808c13fSDavidlohr Bueso search(struct rb_root_cached *root, unsigned long start, unsigned long last)
34a88cc108SChris Wilson {
35a88cc108SChris Wilson 	struct interval_tree_node *node;
36a88cc108SChris Wilson 	unsigned long results = 0;
37a88cc108SChris Wilson 
38c46ecce4SDavidlohr Bueso 	for (node = interval_tree_iter_first(root, start, last); node;
39c46ecce4SDavidlohr Bueso 	     node = interval_tree_iter_next(node, start, last))
40a88cc108SChris Wilson 		results++;
41a88cc108SChris Wilson 	return results;
42a88cc108SChris Wilson }
43a88cc108SChris Wilson 
init(void)44a88cc108SChris Wilson static void init(void)
45a88cc108SChris Wilson {
46a88cc108SChris Wilson 	int i;
47a54dae03SDavidlohr Bueso 
48a54dae03SDavidlohr Bueso 	for (i = 0; i < nnodes; i++) {
49a8ec14d4SDavidlohr Bueso 		u32 b = (prandom_u32_state(&rnd) >> 4) % max_endpoint;
50a8ec14d4SDavidlohr Bueso 		u32 a = (prandom_u32_state(&rnd) >> 4) % b;
51a8ec14d4SDavidlohr Bueso 
52a88cc108SChris Wilson 		nodes[i].start = a;
53a88cc108SChris Wilson 		nodes[i].last = b;
54a88cc108SChris Wilson 	}
55a8ec14d4SDavidlohr Bueso 
56a8ec14d4SDavidlohr Bueso 	/*
57a8ec14d4SDavidlohr Bueso 	 * Limit the search scope to what the user defined.
58a8ec14d4SDavidlohr Bueso 	 * Otherwise we are merely measuring empty walks,
59a8ec14d4SDavidlohr Bueso 	 * which is pointless.
60a8ec14d4SDavidlohr Bueso 	 */
61a54dae03SDavidlohr Bueso 	for (i = 0; i < nsearches; i++)
62a8ec14d4SDavidlohr Bueso 		queries[i] = (prandom_u32_state(&rnd) >> 4) % max_endpoint;
63a88cc108SChris Wilson }
64a88cc108SChris Wilson 
basic_check(void)653e1d58cdSWei Yang static int basic_check(void)
66a88cc108SChris Wilson {
67a88cc108SChris Wilson 	int i, j;
68a88cc108SChris Wilson 	cycles_t time1, time2, time;
69a88cc108SChris Wilson 
70a88cc108SChris Wilson 	printk(KERN_ALERT "interval tree insert/remove");
71a88cc108SChris Wilson 
72a88cc108SChris Wilson 	init();
73a88cc108SChris Wilson 
74a88cc108SChris Wilson 	time1 = get_cycles();
75a88cc108SChris Wilson 
76a54dae03SDavidlohr Bueso 	for (i = 0; i < perf_loops; i++) {
77a54dae03SDavidlohr Bueso 		for (j = 0; j < nnodes; j++)
78a88cc108SChris Wilson 			interval_tree_insert(nodes + j, &root);
79a54dae03SDavidlohr Bueso 		for (j = 0; j < nnodes; j++)
80a88cc108SChris Wilson 			interval_tree_remove(nodes + j, &root);
81a88cc108SChris Wilson 	}
82a88cc108SChris Wilson 
83a88cc108SChris Wilson 	time2 = get_cycles();
84a88cc108SChris Wilson 	time = time2 - time1;
85a88cc108SChris Wilson 
86a54dae03SDavidlohr Bueso 	time = div_u64(time, perf_loops);
87a88cc108SChris Wilson 	printk(" -> %llu cycles\n", (unsigned long long)time);
88a88cc108SChris Wilson 
893e1d58cdSWei Yang 	return 0;
903e1d58cdSWei Yang }
913e1d58cdSWei Yang 
search_check(void)923e1d58cdSWei Yang static int search_check(void)
933e1d58cdSWei Yang {
943e1d58cdSWei Yang 	int i, j;
953e1d58cdSWei Yang 	unsigned long results;
963e1d58cdSWei Yang 	cycles_t time1, time2, time;
973e1d58cdSWei Yang 
98a88cc108SChris Wilson 	printk(KERN_ALERT "interval tree search");
99a88cc108SChris Wilson 
1003e1d58cdSWei Yang 	init();
1013e1d58cdSWei Yang 
102a54dae03SDavidlohr Bueso 	for (j = 0; j < nnodes; j++)
103a88cc108SChris Wilson 		interval_tree_insert(nodes + j, &root);
104a88cc108SChris Wilson 
105a88cc108SChris Wilson 	time1 = get_cycles();
106a88cc108SChris Wilson 
107a88cc108SChris Wilson 	results = 0;
108a54dae03SDavidlohr Bueso 	for (i = 0; i < search_loops; i++)
109c46ecce4SDavidlohr Bueso 		for (j = 0; j < nsearches; j++) {
110c46ecce4SDavidlohr Bueso 			unsigned long start = search_all ? 0 : queries[j];
111c46ecce4SDavidlohr Bueso 			unsigned long last = search_all ? max_endpoint : queries[j];
112c46ecce4SDavidlohr Bueso 
113c46ecce4SDavidlohr Bueso 			results += search(&root, start, last);
114c46ecce4SDavidlohr Bueso 		}
115a88cc108SChris Wilson 
116a88cc108SChris Wilson 	time2 = get_cycles();
117a88cc108SChris Wilson 	time = time2 - time1;
118a88cc108SChris Wilson 
119a54dae03SDavidlohr Bueso 	time = div_u64(time, search_loops);
120a54dae03SDavidlohr Bueso 	results = div_u64(results, search_loops);
121a88cc108SChris Wilson 	printk(" -> %llu cycles (%lu results)\n",
122a88cc108SChris Wilson 	       (unsigned long long)time, results);
123a88cc108SChris Wilson 
1243e1d58cdSWei Yang 	for (j = 0; j < nnodes; j++)
1253e1d58cdSWei Yang 		interval_tree_remove(nodes + j, &root);
1263e1d58cdSWei Yang 
1273e1d58cdSWei Yang 	return 0;
1283e1d58cdSWei Yang }
1293e1d58cdSWei Yang 
intersection_range_check(void)13082114e45SWei Yang static int intersection_range_check(void)
13182114e45SWei Yang {
13282114e45SWei Yang 	int i, j, k;
13382114e45SWei Yang 	unsigned long start, last;
13482114e45SWei Yang 	struct interval_tree_node *node;
13582114e45SWei Yang 	unsigned long *intxn1;
13682114e45SWei Yang 	unsigned long *intxn2;
13782114e45SWei Yang 
13882114e45SWei Yang 	printk(KERN_ALERT "interval tree iteration\n");
13982114e45SWei Yang 
14082114e45SWei Yang 	intxn1 = bitmap_alloc(nnodes, GFP_KERNEL);
14182114e45SWei Yang 	if (!intxn1) {
14282114e45SWei Yang 		WARN_ON_ONCE("Failed to allocate intxn1\n");
14382114e45SWei Yang 		return -ENOMEM;
14482114e45SWei Yang 	}
14582114e45SWei Yang 
14682114e45SWei Yang 	intxn2 = bitmap_alloc(nnodes, GFP_KERNEL);
14782114e45SWei Yang 	if (!intxn2) {
14882114e45SWei Yang 		WARN_ON_ONCE("Failed to allocate intxn2\n");
14982114e45SWei Yang 		bitmap_free(intxn1);
15082114e45SWei Yang 		return -ENOMEM;
15182114e45SWei Yang 	}
15282114e45SWei Yang 
15382114e45SWei Yang 	for (i = 0; i < search_loops; i++) {
15482114e45SWei Yang 		/* Initialize interval tree for each round */
15582114e45SWei Yang 		init();
15682114e45SWei Yang 		for (j = 0; j < nnodes; j++)
15782114e45SWei Yang 			interval_tree_insert(nodes + j, &root);
15882114e45SWei Yang 
15982114e45SWei Yang 		/* Let's try nsearches different ranges */
16082114e45SWei Yang 		for (k = 0; k < nsearches; k++) {
16182114e45SWei Yang 			/* Try whole range once */
16282114e45SWei Yang 			if (!k) {
16382114e45SWei Yang 				start = 0UL;
16482114e45SWei Yang 				last = ULONG_MAX;
16582114e45SWei Yang 			} else {
16682114e45SWei Yang 				last = (prandom_u32_state(&rnd) >> 4) % max_endpoint;
16782114e45SWei Yang 				start = (prandom_u32_state(&rnd) >> 4) % last;
16882114e45SWei Yang 			}
16982114e45SWei Yang 
17082114e45SWei Yang 			/* Walk nodes to mark intersection nodes */
17182114e45SWei Yang 			bitmap_zero(intxn1, nnodes);
17282114e45SWei Yang 			for (j = 0; j < nnodes; j++) {
17382114e45SWei Yang 				node = nodes + j;
17482114e45SWei Yang 
17582114e45SWei Yang 				if (start <= node->last && last >= node->start)
17682114e45SWei Yang 					bitmap_set(intxn1, j, 1);
17782114e45SWei Yang 			}
17882114e45SWei Yang 
17982114e45SWei Yang 			/* Iterate tree to clear intersection nodes */
18082114e45SWei Yang 			bitmap_zero(intxn2, nnodes);
18182114e45SWei Yang 			for (node = interval_tree_iter_first(&root, start, last); node;
18282114e45SWei Yang 			     node = interval_tree_iter_next(node, start, last))
18382114e45SWei Yang 				bitmap_set(intxn2, node - nodes, 1);
18482114e45SWei Yang 
18582114e45SWei Yang 			WARN_ON_ONCE(!bitmap_equal(intxn1, intxn2, nnodes));
18682114e45SWei Yang 		}
18782114e45SWei Yang 
18882114e45SWei Yang 		for (j = 0; j < nnodes; j++)
18982114e45SWei Yang 			interval_tree_remove(nodes + j, &root);
19082114e45SWei Yang 	}
19182114e45SWei Yang 
19282114e45SWei Yang 	bitmap_free(intxn1);
19382114e45SWei Yang 	bitmap_free(intxn2);
19482114e45SWei Yang 	return 0;
19582114e45SWei Yang }
19682114e45SWei Yang 
197*ccaf3efcSWei Yang #ifdef CONFIG_INTERVAL_TREE_SPAN_ITER
198*ccaf3efcSWei Yang /*
199*ccaf3efcSWei Yang  * Helper function to get span of current position from maple tree point of
200*ccaf3efcSWei Yang  * view.
201*ccaf3efcSWei Yang  */
mas_cur_span(struct ma_state * mas,struct interval_tree_span_iter * state)202*ccaf3efcSWei Yang static void mas_cur_span(struct ma_state *mas, struct interval_tree_span_iter *state)
203*ccaf3efcSWei Yang {
204*ccaf3efcSWei Yang 	unsigned long cur_start;
205*ccaf3efcSWei Yang 	unsigned long cur_last;
206*ccaf3efcSWei Yang 	int is_hole;
207*ccaf3efcSWei Yang 
208*ccaf3efcSWei Yang 	if (mas->status == ma_overflow)
209*ccaf3efcSWei Yang 		return;
210*ccaf3efcSWei Yang 
211*ccaf3efcSWei Yang 	/* walk to current position */
212*ccaf3efcSWei Yang 	state->is_hole = mas_walk(mas) ? 0 : 1;
213*ccaf3efcSWei Yang 
214*ccaf3efcSWei Yang 	cur_start = mas->index < state->first_index ?
215*ccaf3efcSWei Yang 			state->first_index : mas->index;
216*ccaf3efcSWei Yang 
217*ccaf3efcSWei Yang 	/* whether we have followers */
218*ccaf3efcSWei Yang 	do {
219*ccaf3efcSWei Yang 
220*ccaf3efcSWei Yang 		cur_last = mas->last > state->last_index ?
221*ccaf3efcSWei Yang 				state->last_index : mas->last;
222*ccaf3efcSWei Yang 
223*ccaf3efcSWei Yang 		is_hole = mas_next_range(mas, state->last_index) ? 0 : 1;
224*ccaf3efcSWei Yang 
225*ccaf3efcSWei Yang 	} while (mas->status != ma_overflow && is_hole == state->is_hole);
226*ccaf3efcSWei Yang 
227*ccaf3efcSWei Yang 	if (state->is_hole) {
228*ccaf3efcSWei Yang 		state->start_hole = cur_start;
229*ccaf3efcSWei Yang 		state->last_hole = cur_last;
230*ccaf3efcSWei Yang 	} else {
231*ccaf3efcSWei Yang 		state->start_used = cur_start;
232*ccaf3efcSWei Yang 		state->last_used = cur_last;
233*ccaf3efcSWei Yang 	}
234*ccaf3efcSWei Yang 
235*ccaf3efcSWei Yang 	/* advance position for next round */
236*ccaf3efcSWei Yang 	if (mas->status != ma_overflow)
237*ccaf3efcSWei Yang 		mas_set(mas, cur_last + 1);
238*ccaf3efcSWei Yang }
239*ccaf3efcSWei Yang 
span_iteration_check(void)240*ccaf3efcSWei Yang static int span_iteration_check(void)
241*ccaf3efcSWei Yang {
242*ccaf3efcSWei Yang 	int i, j, k;
243*ccaf3efcSWei Yang 	unsigned long start, last;
244*ccaf3efcSWei Yang 	struct interval_tree_span_iter span, mas_span;
245*ccaf3efcSWei Yang 
246*ccaf3efcSWei Yang 	DEFINE_MTREE(tree);
247*ccaf3efcSWei Yang 
248*ccaf3efcSWei Yang 	MA_STATE(mas, &tree, 0, 0);
249*ccaf3efcSWei Yang 
250*ccaf3efcSWei Yang 	printk(KERN_ALERT "interval tree span iteration\n");
251*ccaf3efcSWei Yang 
252*ccaf3efcSWei Yang 	for (i = 0; i < search_loops; i++) {
253*ccaf3efcSWei Yang 		/* Initialize interval tree for each round */
254*ccaf3efcSWei Yang 		init();
255*ccaf3efcSWei Yang 		for (j = 0; j < nnodes; j++)
256*ccaf3efcSWei Yang 			interval_tree_insert(nodes + j, &root);
257*ccaf3efcSWei Yang 
258*ccaf3efcSWei Yang 		/* Put all the range into maple tree */
259*ccaf3efcSWei Yang 		mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE);
260*ccaf3efcSWei Yang 		mt_set_in_rcu(&tree);
261*ccaf3efcSWei Yang 
262*ccaf3efcSWei Yang 		for (j = 0; j < nnodes; j++)
263*ccaf3efcSWei Yang 			WARN_ON_ONCE(mtree_store_range(&tree, nodes[j].start,
264*ccaf3efcSWei Yang 					nodes[j].last, nodes + j, GFP_KERNEL));
265*ccaf3efcSWei Yang 
266*ccaf3efcSWei Yang 		/* Let's try nsearches different ranges */
267*ccaf3efcSWei Yang 		for (k = 0; k < nsearches; k++) {
268*ccaf3efcSWei Yang 			/* Try whole range once */
269*ccaf3efcSWei Yang 			if (!k) {
270*ccaf3efcSWei Yang 				start = 0UL;
271*ccaf3efcSWei Yang 				last = ULONG_MAX;
272*ccaf3efcSWei Yang 			} else {
273*ccaf3efcSWei Yang 				last = (prandom_u32_state(&rnd) >> 4) % max_endpoint;
274*ccaf3efcSWei Yang 				start = (prandom_u32_state(&rnd) >> 4) % last;
275*ccaf3efcSWei Yang 			}
276*ccaf3efcSWei Yang 
277*ccaf3efcSWei Yang 			mas_span.first_index = start;
278*ccaf3efcSWei Yang 			mas_span.last_index = last;
279*ccaf3efcSWei Yang 			mas_span.is_hole = -1;
280*ccaf3efcSWei Yang 			mas_set(&mas, start);
281*ccaf3efcSWei Yang 
282*ccaf3efcSWei Yang 			interval_tree_for_each_span(&span, &root, start, last) {
283*ccaf3efcSWei Yang 				mas_cur_span(&mas, &mas_span);
284*ccaf3efcSWei Yang 
285*ccaf3efcSWei Yang 				WARN_ON_ONCE(span.is_hole != mas_span.is_hole);
286*ccaf3efcSWei Yang 
287*ccaf3efcSWei Yang 				if (span.is_hole) {
288*ccaf3efcSWei Yang 					WARN_ON_ONCE(span.start_hole != mas_span.start_hole);
289*ccaf3efcSWei Yang 					WARN_ON_ONCE(span.last_hole != mas_span.last_hole);
290*ccaf3efcSWei Yang 				} else {
291*ccaf3efcSWei Yang 					WARN_ON_ONCE(span.start_used != mas_span.start_used);
292*ccaf3efcSWei Yang 					WARN_ON_ONCE(span.last_used != mas_span.last_used);
293*ccaf3efcSWei Yang 				}
294*ccaf3efcSWei Yang 			}
295*ccaf3efcSWei Yang 
296*ccaf3efcSWei Yang 		}
297*ccaf3efcSWei Yang 
298*ccaf3efcSWei Yang 		WARN_ON_ONCE(mas.status != ma_overflow);
299*ccaf3efcSWei Yang 
300*ccaf3efcSWei Yang 		/* Cleanup maple tree for each round */
301*ccaf3efcSWei Yang 		mtree_destroy(&tree);
302*ccaf3efcSWei Yang 		/* Cleanup interval tree for each round */
303*ccaf3efcSWei Yang 		for (j = 0; j < nnodes; j++)
304*ccaf3efcSWei Yang 			interval_tree_remove(nodes + j, &root);
305*ccaf3efcSWei Yang 	}
306*ccaf3efcSWei Yang 	return 0;
307*ccaf3efcSWei Yang }
308*ccaf3efcSWei Yang #else
span_iteration_check(void)309*ccaf3efcSWei Yang static inline int span_iteration_check(void) {return 0; }
310*ccaf3efcSWei Yang #endif
311*ccaf3efcSWei Yang 
interval_tree_test_init(void)3123e1d58cdSWei Yang static int interval_tree_test_init(void)
3133e1d58cdSWei Yang {
3143e1d58cdSWei Yang 	nodes = kmalloc_array(nnodes, sizeof(struct interval_tree_node),
3153e1d58cdSWei Yang 			      GFP_KERNEL);
3163e1d58cdSWei Yang 	if (!nodes)
3173e1d58cdSWei Yang 		return -ENOMEM;
3183e1d58cdSWei Yang 
3193e1d58cdSWei Yang 	queries = kmalloc_array(nsearches, sizeof(int), GFP_KERNEL);
3203e1d58cdSWei Yang 	if (!queries) {
3213e1d58cdSWei Yang 		kfree(nodes);
3223e1d58cdSWei Yang 		return -ENOMEM;
3233e1d58cdSWei Yang 	}
3243e1d58cdSWei Yang 
32516b1936aSWei Yang 	prandom_seed_state(&rnd, seed);
3263e1d58cdSWei Yang 
3273e1d58cdSWei Yang 	basic_check();
3283e1d58cdSWei Yang 	search_check();
32982114e45SWei Yang 	intersection_range_check();
330*ccaf3efcSWei Yang 	span_iteration_check();
3313e1d58cdSWei Yang 
332a54dae03SDavidlohr Bueso 	kfree(queries);
333a54dae03SDavidlohr Bueso 	kfree(nodes);
334a54dae03SDavidlohr Bueso 
335a88cc108SChris Wilson 	return -EAGAIN; /* Fail will directly unload the module */
336a88cc108SChris Wilson }
337a88cc108SChris Wilson 
interval_tree_test_exit(void)338a88cc108SChris Wilson static void interval_tree_test_exit(void)
339a88cc108SChris Wilson {
340a88cc108SChris Wilson 	printk(KERN_ALERT "test exit\n");
341a88cc108SChris Wilson }
342a88cc108SChris Wilson 
343a88cc108SChris Wilson module_init(interval_tree_test_init)
344a88cc108SChris Wilson module_exit(interval_tree_test_exit)
345a88cc108SChris Wilson 
346a88cc108SChris Wilson MODULE_LICENSE("GPL");
347a88cc108SChris Wilson MODULE_AUTHOR("Michel Lespinasse");
348a88cc108SChris Wilson MODULE_DESCRIPTION("Interval Tree test");
349