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