18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <stdio.h>
38c2ecf20Sopenharmony_ci#include <stdlib.h>
48c2ecf20Sopenharmony_ci#include <unistd.h>
58c2ecf20Sopenharmony_ci#include <time.h>
68c2ecf20Sopenharmony_ci#include <assert.h>
78c2ecf20Sopenharmony_ci#include <limits.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/radix-tree.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "test.h"
138c2ecf20Sopenharmony_ci#include "regression.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_civoid __gang_check(unsigned long middle, long down, long up, int chunk, int hop)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	long idx;
188c2ecf20Sopenharmony_ci	RADIX_TREE(tree, GFP_KERNEL);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	middle = 1 << 30;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	for (idx = -down; idx < up; idx++)
238c2ecf20Sopenharmony_ci		item_insert(&tree, middle + idx);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	item_check_absent(&tree, middle - down - 1);
268c2ecf20Sopenharmony_ci	for (idx = -down; idx < up; idx++)
278c2ecf20Sopenharmony_ci		item_check_present(&tree, middle + idx);
288c2ecf20Sopenharmony_ci	item_check_absent(&tree, middle + up);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (chunk > 0) {
318c2ecf20Sopenharmony_ci		item_gang_check_present(&tree, middle - down, up + down,
328c2ecf20Sopenharmony_ci				chunk, hop);
338c2ecf20Sopenharmony_ci		item_full_scan(&tree, middle - down, down + up, chunk);
348c2ecf20Sopenharmony_ci	}
358c2ecf20Sopenharmony_ci	item_kill_tree(&tree);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_civoid gang_check(void)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	__gang_check(1UL << 30, 128, 128, 35, 2);
418c2ecf20Sopenharmony_ci	__gang_check(1UL << 31, 128, 128, 32, 32);
428c2ecf20Sopenharmony_ci	__gang_check(1UL << 31, 128, 128, 32, 100);
438c2ecf20Sopenharmony_ci	__gang_check(1UL << 31, 128, 128, 17, 7);
448c2ecf20Sopenharmony_ci	__gang_check(0xffff0000UL, 0, 65536, 17, 7);
458c2ecf20Sopenharmony_ci	__gang_check(0xfffffffeUL, 1, 1, 17, 7);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid __big_gang_check(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	unsigned long start;
518c2ecf20Sopenharmony_ci	int wrapped = 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	start = 0;
548c2ecf20Sopenharmony_ci	do {
558c2ecf20Sopenharmony_ci		unsigned long old_start;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci//		printf("0x%08lx\n", start);
588c2ecf20Sopenharmony_ci		__gang_check(start, rand() % 113 + 1, rand() % 71,
598c2ecf20Sopenharmony_ci				rand() % 157, rand() % 91 + 1);
608c2ecf20Sopenharmony_ci		old_start = start;
618c2ecf20Sopenharmony_ci		start += rand() % 1000000;
628c2ecf20Sopenharmony_ci		start %= 1ULL << 33;
638c2ecf20Sopenharmony_ci		if (start < old_start)
648c2ecf20Sopenharmony_ci			wrapped = 1;
658c2ecf20Sopenharmony_ci	} while (!wrapped);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_civoid big_gang_check(bool long_run)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int i;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	for (i = 0; i < (long_run ? 1000 : 3); i++) {
738c2ecf20Sopenharmony_ci		__big_gang_check();
748c2ecf20Sopenharmony_ci		printv(2, "%d ", i);
758c2ecf20Sopenharmony_ci		fflush(stdout);
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_civoid add_and_check(void)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	RADIX_TREE(tree, GFP_KERNEL);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	item_insert(&tree, 44);
848c2ecf20Sopenharmony_ci	item_check_present(&tree, 44);
858c2ecf20Sopenharmony_ci	item_check_absent(&tree, 43);
868c2ecf20Sopenharmony_ci	item_kill_tree(&tree);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_civoid dynamic_height_check(void)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int i;
928c2ecf20Sopenharmony_ci	RADIX_TREE(tree, GFP_KERNEL);
938c2ecf20Sopenharmony_ci	tree_verify_min_height(&tree, 0);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	item_insert(&tree, 42);
968c2ecf20Sopenharmony_ci	tree_verify_min_height(&tree, 42);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	item_insert(&tree, 1000000);
998c2ecf20Sopenharmony_ci	tree_verify_min_height(&tree, 1000000);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	assert(item_delete(&tree, 1000000));
1028c2ecf20Sopenharmony_ci	tree_verify_min_height(&tree, 42);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	assert(item_delete(&tree, 42));
1058c2ecf20Sopenharmony_ci	tree_verify_min_height(&tree, 0);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	for (i = 0; i < 1000; i++) {
1088c2ecf20Sopenharmony_ci		item_insert(&tree, i);
1098c2ecf20Sopenharmony_ci		tree_verify_min_height(&tree, i);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	i--;
1138c2ecf20Sopenharmony_ci	for (;;) {
1148c2ecf20Sopenharmony_ci		assert(item_delete(&tree, i));
1158c2ecf20Sopenharmony_ci		if (i == 0) {
1168c2ecf20Sopenharmony_ci			tree_verify_min_height(&tree, 0);
1178c2ecf20Sopenharmony_ci			break;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci		i--;
1208c2ecf20Sopenharmony_ci		tree_verify_min_height(&tree, i);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	item_kill_tree(&tree);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_civoid check_copied_tags(struct radix_tree_root *tree, unsigned long start, unsigned long end, unsigned long *idx, int count, int fromtag, int totag)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	int i;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
1318c2ecf20Sopenharmony_ci/*		if (i % 1000 == 0)
1328c2ecf20Sopenharmony_ci			putchar('.'); */
1338c2ecf20Sopenharmony_ci		if (idx[i] < start || idx[i] > end) {
1348c2ecf20Sopenharmony_ci			if (item_tag_get(tree, idx[i], totag)) {
1358c2ecf20Sopenharmony_ci				printv(2, "%lu-%lu: %lu, tags %d-%d\n", start,
1368c2ecf20Sopenharmony_ci				       end, idx[i], item_tag_get(tree, idx[i],
1378c2ecf20Sopenharmony_ci								 fromtag),
1388c2ecf20Sopenharmony_ci				       item_tag_get(tree, idx[i], totag));
1398c2ecf20Sopenharmony_ci			}
1408c2ecf20Sopenharmony_ci			assert(!item_tag_get(tree, idx[i], totag));
1418c2ecf20Sopenharmony_ci			continue;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		if (item_tag_get(tree, idx[i], fromtag) ^
1448c2ecf20Sopenharmony_ci			item_tag_get(tree, idx[i], totag)) {
1458c2ecf20Sopenharmony_ci			printv(2, "%lu-%lu: %lu, tags %d-%d\n", start, end,
1468c2ecf20Sopenharmony_ci			       idx[i], item_tag_get(tree, idx[i], fromtag),
1478c2ecf20Sopenharmony_ci			       item_tag_get(tree, idx[i], totag));
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci		assert(!(item_tag_get(tree, idx[i], fromtag) ^
1508c2ecf20Sopenharmony_ci			 item_tag_get(tree, idx[i], totag)));
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#define ITEMS 50000
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_civoid copy_tag_check(void)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	RADIX_TREE(tree, GFP_KERNEL);
1598c2ecf20Sopenharmony_ci	unsigned long idx[ITEMS];
1608c2ecf20Sopenharmony_ci	unsigned long start, end, count = 0, tagged, cur, tmp;
1618c2ecf20Sopenharmony_ci	int i;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci//	printf("generating radix tree indices...\n");
1648c2ecf20Sopenharmony_ci	start = rand();
1658c2ecf20Sopenharmony_ci	end = rand();
1668c2ecf20Sopenharmony_ci	if (start > end && (rand() % 10)) {
1678c2ecf20Sopenharmony_ci		cur = start;
1688c2ecf20Sopenharmony_ci		start = end;
1698c2ecf20Sopenharmony_ci		end = cur;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	/* Specifically create items around the start and the end of the range
1728c2ecf20Sopenharmony_ci	 * with high probability to check for off by one errors */
1738c2ecf20Sopenharmony_ci	cur = rand();
1748c2ecf20Sopenharmony_ci	if (cur & 1) {
1758c2ecf20Sopenharmony_ci		item_insert(&tree, start);
1768c2ecf20Sopenharmony_ci		if (cur & 2) {
1778c2ecf20Sopenharmony_ci			if (start <= end)
1788c2ecf20Sopenharmony_ci				count++;
1798c2ecf20Sopenharmony_ci			item_tag_set(&tree, start, 0);
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	if (cur & 4) {
1838c2ecf20Sopenharmony_ci		item_insert(&tree, start-1);
1848c2ecf20Sopenharmony_ci		if (cur & 8)
1858c2ecf20Sopenharmony_ci			item_tag_set(&tree, start-1, 0);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	if (cur & 16) {
1888c2ecf20Sopenharmony_ci		item_insert(&tree, end);
1898c2ecf20Sopenharmony_ci		if (cur & 32) {
1908c2ecf20Sopenharmony_ci			if (start <= end)
1918c2ecf20Sopenharmony_ci				count++;
1928c2ecf20Sopenharmony_ci			item_tag_set(&tree, end, 0);
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	if (cur & 64) {
1968c2ecf20Sopenharmony_ci		item_insert(&tree, end+1);
1978c2ecf20Sopenharmony_ci		if (cur & 128)
1988c2ecf20Sopenharmony_ci			item_tag_set(&tree, end+1, 0);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	for (i = 0; i < ITEMS; i++) {
2028c2ecf20Sopenharmony_ci		do {
2038c2ecf20Sopenharmony_ci			idx[i] = rand();
2048c2ecf20Sopenharmony_ci		} while (item_lookup(&tree, idx[i]));
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		item_insert(&tree, idx[i]);
2078c2ecf20Sopenharmony_ci		if (rand() & 1) {
2088c2ecf20Sopenharmony_ci			item_tag_set(&tree, idx[i], 0);
2098c2ecf20Sopenharmony_ci			if (idx[i] >= start && idx[i] <= end)
2108c2ecf20Sopenharmony_ci				count++;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci/*		if (i % 1000 == 0)
2138c2ecf20Sopenharmony_ci			putchar('.'); */
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci//	printf("\ncopying tags...\n");
2178c2ecf20Sopenharmony_ci	tagged = tag_tagged_items(&tree, start, end, ITEMS, XA_MARK_0, XA_MARK_1);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci//	printf("checking copied tags\n");
2208c2ecf20Sopenharmony_ci	assert(tagged == count);
2218c2ecf20Sopenharmony_ci	check_copied_tags(&tree, start, end, idx, ITEMS, 0, 1);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* Copy tags in several rounds */
2248c2ecf20Sopenharmony_ci//	printf("\ncopying tags...\n");
2258c2ecf20Sopenharmony_ci	tmp = rand() % (count / 10 + 2);
2268c2ecf20Sopenharmony_ci	tagged = tag_tagged_items(&tree, start, end, tmp, XA_MARK_0, XA_MARK_2);
2278c2ecf20Sopenharmony_ci	assert(tagged == count);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci//	printf("%lu %lu %lu\n", tagged, tmp, count);
2308c2ecf20Sopenharmony_ci//	printf("checking copied tags\n");
2318c2ecf20Sopenharmony_ci	check_copied_tags(&tree, start, end, idx, ITEMS, 0, 2);
2328c2ecf20Sopenharmony_ci	verify_tag_consistency(&tree, 0);
2338c2ecf20Sopenharmony_ci	verify_tag_consistency(&tree, 1);
2348c2ecf20Sopenharmony_ci	verify_tag_consistency(&tree, 2);
2358c2ecf20Sopenharmony_ci//	printf("\n");
2368c2ecf20Sopenharmony_ci	item_kill_tree(&tree);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void single_thread_tests(bool long_run)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	int i;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	printv(1, "starting single_thread_tests: %d allocated, preempt %d\n",
2448c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2458c2ecf20Sopenharmony_ci	multiorder_checks();
2468c2ecf20Sopenharmony_ci	rcu_barrier();
2478c2ecf20Sopenharmony_ci	printv(2, "after multiorder_check: %d allocated, preempt %d\n",
2488c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2498c2ecf20Sopenharmony_ci	tag_check();
2508c2ecf20Sopenharmony_ci	rcu_barrier();
2518c2ecf20Sopenharmony_ci	printv(2, "after tag_check: %d allocated, preempt %d\n",
2528c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2538c2ecf20Sopenharmony_ci	gang_check();
2548c2ecf20Sopenharmony_ci	rcu_barrier();
2558c2ecf20Sopenharmony_ci	printv(2, "after gang_check: %d allocated, preempt %d\n",
2568c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2578c2ecf20Sopenharmony_ci	add_and_check();
2588c2ecf20Sopenharmony_ci	rcu_barrier();
2598c2ecf20Sopenharmony_ci	printv(2, "after add_and_check: %d allocated, preempt %d\n",
2608c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2618c2ecf20Sopenharmony_ci	dynamic_height_check();
2628c2ecf20Sopenharmony_ci	rcu_barrier();
2638c2ecf20Sopenharmony_ci	printv(2, "after dynamic_height_check: %d allocated, preempt %d\n",
2648c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2658c2ecf20Sopenharmony_ci	idr_checks();
2668c2ecf20Sopenharmony_ci	ida_tests();
2678c2ecf20Sopenharmony_ci	rcu_barrier();
2688c2ecf20Sopenharmony_ci	printv(2, "after idr_checks: %d allocated, preempt %d\n",
2698c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2708c2ecf20Sopenharmony_ci	big_gang_check(long_run);
2718c2ecf20Sopenharmony_ci	rcu_barrier();
2728c2ecf20Sopenharmony_ci	printv(2, "after big_gang_check: %d allocated, preempt %d\n",
2738c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2748c2ecf20Sopenharmony_ci	for (i = 0; i < (long_run ? 2000 : 3); i++) {
2758c2ecf20Sopenharmony_ci		copy_tag_check();
2768c2ecf20Sopenharmony_ci		printv(2, "%d ", i);
2778c2ecf20Sopenharmony_ci		fflush(stdout);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	rcu_barrier();
2808c2ecf20Sopenharmony_ci	printv(2, "after copy_tag_check: %d allocated, preempt %d\n",
2818c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ciint main(int argc, char **argv)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	bool long_run = false;
2878c2ecf20Sopenharmony_ci	int opt;
2888c2ecf20Sopenharmony_ci	unsigned int seed = time(NULL);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	while ((opt = getopt(argc, argv, "ls:v")) != -1) {
2918c2ecf20Sopenharmony_ci		if (opt == 'l')
2928c2ecf20Sopenharmony_ci			long_run = true;
2938c2ecf20Sopenharmony_ci		else if (opt == 's')
2948c2ecf20Sopenharmony_ci			seed = strtoul(optarg, NULL, 0);
2958c2ecf20Sopenharmony_ci		else if (opt == 'v')
2968c2ecf20Sopenharmony_ci			test_verbose++;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	printf("random seed %u\n", seed);
3008c2ecf20Sopenharmony_ci	srand(seed);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	printf("running tests\n");
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	rcu_register_thread();
3058c2ecf20Sopenharmony_ci	radix_tree_init();
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	xarray_tests();
3088c2ecf20Sopenharmony_ci	regression1_test();
3098c2ecf20Sopenharmony_ci	regression2_test();
3108c2ecf20Sopenharmony_ci	regression3_test();
3118c2ecf20Sopenharmony_ci	regression4_test();
3128c2ecf20Sopenharmony_ci	iteration_test(0, 10 + 90 * long_run);
3138c2ecf20Sopenharmony_ci	iteration_test(7, 10 + 90 * long_run);
3148c2ecf20Sopenharmony_ci	iteration_test2(10 + 90 * long_run);
3158c2ecf20Sopenharmony_ci	single_thread_tests(long_run);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* Free any remaining preallocated nodes */
3188c2ecf20Sopenharmony_ci	radix_tree_cpu_dead(0);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	benchmark();
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	rcu_barrier();
3238c2ecf20Sopenharmony_ci	printv(2, "after rcu_barrier: %d allocated, preempt %d\n",
3248c2ecf20Sopenharmony_ci		nr_allocated, preempt_count);
3258c2ecf20Sopenharmony_ci	rcu_unregister_thread();
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	printf("tests completed\n");
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	exit(0);
3308c2ecf20Sopenharmony_ci}
331