|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  | #include <time.h> | 
|  | #include <assert.h> | 
|  | #include <limits.h> | 
|  |  | 
|  | #include <linux/slab.h> | 
|  | #include <linux/radix-tree.h> | 
|  |  | 
|  | #include "test.h" | 
|  | #include "regression.h" | 
|  |  | 
|  | void __gang_check(unsigned long middle, long down, long up, int chunk, int hop) | 
|  | { | 
|  | long idx; | 
|  | RADIX_TREE(tree, GFP_KERNEL); | 
|  |  | 
|  | middle = 1 << 30; | 
|  |  | 
|  | for (idx = -down; idx < up; idx++) | 
|  | item_insert(&tree, middle + idx); | 
|  |  | 
|  | item_check_absent(&tree, middle - down - 1); | 
|  | for (idx = -down; idx < up; idx++) | 
|  | item_check_present(&tree, middle + idx); | 
|  | item_check_absent(&tree, middle + up); | 
|  |  | 
|  | item_gang_check_present(&tree, middle - down, | 
|  | up + down, chunk, hop); | 
|  | item_full_scan(&tree, middle - down, down + up, chunk); | 
|  | item_kill_tree(&tree); | 
|  | } | 
|  |  | 
|  | void gang_check(void) | 
|  | { | 
|  | __gang_check(1 << 30, 128, 128, 35, 2); | 
|  | __gang_check(1 << 31, 128, 128, 32, 32); | 
|  | __gang_check(1 << 31, 128, 128, 32, 100); | 
|  | __gang_check(1 << 31, 128, 128, 17, 7); | 
|  | __gang_check(0xffff0000, 0, 65536, 17, 7); | 
|  | __gang_check(0xfffffffe, 1, 1, 17, 7); | 
|  | } | 
|  |  | 
|  | void __big_gang_check(void) | 
|  | { | 
|  | unsigned long start; | 
|  | int wrapped = 0; | 
|  |  | 
|  | start = 0; | 
|  | do { | 
|  | unsigned long old_start; | 
|  |  | 
|  | //		printf("0x%08lx\n", start); | 
|  | __gang_check(start, rand() % 113 + 1, rand() % 71, | 
|  | rand() % 157, rand() % 91 + 1); | 
|  | old_start = start; | 
|  | start += rand() % 1000000; | 
|  | start %= 1ULL << 33; | 
|  | if (start < old_start) | 
|  | wrapped = 1; | 
|  | } while (!wrapped); | 
|  | } | 
|  |  | 
|  | void big_gang_check(bool long_run) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < (long_run ? 1000 : 3); i++) { | 
|  | __big_gang_check(); | 
|  | printv(2, "%d ", i); | 
|  | fflush(stdout); | 
|  | } | 
|  | } | 
|  |  | 
|  | void add_and_check(void) | 
|  | { | 
|  | RADIX_TREE(tree, GFP_KERNEL); | 
|  |  | 
|  | item_insert(&tree, 44); | 
|  | item_check_present(&tree, 44); | 
|  | item_check_absent(&tree, 43); | 
|  | item_kill_tree(&tree); | 
|  | } | 
|  |  | 
|  | void dynamic_height_check(void) | 
|  | { | 
|  | int i; | 
|  | RADIX_TREE(tree, GFP_KERNEL); | 
|  | tree_verify_min_height(&tree, 0); | 
|  |  | 
|  | item_insert(&tree, 42); | 
|  | tree_verify_min_height(&tree, 42); | 
|  |  | 
|  | item_insert(&tree, 1000000); | 
|  | tree_verify_min_height(&tree, 1000000); | 
|  |  | 
|  | assert(item_delete(&tree, 1000000)); | 
|  | tree_verify_min_height(&tree, 42); | 
|  |  | 
|  | assert(item_delete(&tree, 42)); | 
|  | tree_verify_min_height(&tree, 0); | 
|  |  | 
|  | for (i = 0; i < 1000; i++) { | 
|  | item_insert(&tree, i); | 
|  | tree_verify_min_height(&tree, i); | 
|  | } | 
|  |  | 
|  | i--; | 
|  | for (;;) { | 
|  | assert(item_delete(&tree, i)); | 
|  | if (i == 0) { | 
|  | tree_verify_min_height(&tree, 0); | 
|  | break; | 
|  | } | 
|  | i--; | 
|  | tree_verify_min_height(&tree, i); | 
|  | } | 
|  |  | 
|  | item_kill_tree(&tree); | 
|  | } | 
|  |  | 
|  | void check_copied_tags(struct radix_tree_root *tree, unsigned long start, unsigned long end, unsigned long *idx, int count, int fromtag, int totag) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | /*		if (i % 1000 == 0) | 
|  | putchar('.'); */ | 
|  | if (idx[i] < start || idx[i] > end) { | 
|  | if (item_tag_get(tree, idx[i], totag)) { | 
|  | printv(2, "%lu-%lu: %lu, tags %d-%d\n", start, | 
|  | end, idx[i], item_tag_get(tree, idx[i], | 
|  | fromtag), | 
|  | item_tag_get(tree, idx[i], totag)); | 
|  | } | 
|  | assert(!item_tag_get(tree, idx[i], totag)); | 
|  | continue; | 
|  | } | 
|  | if (item_tag_get(tree, idx[i], fromtag) ^ | 
|  | item_tag_get(tree, idx[i], totag)) { | 
|  | printv(2, "%lu-%lu: %lu, tags %d-%d\n", start, end, | 
|  | idx[i], item_tag_get(tree, idx[i], fromtag), | 
|  | item_tag_get(tree, idx[i], totag)); | 
|  | } | 
|  | assert(!(item_tag_get(tree, idx[i], fromtag) ^ | 
|  | item_tag_get(tree, idx[i], totag))); | 
|  | } | 
|  | } | 
|  |  | 
|  | #define ITEMS 50000 | 
|  |  | 
|  | void copy_tag_check(void) | 
|  | { | 
|  | RADIX_TREE(tree, GFP_KERNEL); | 
|  | unsigned long idx[ITEMS]; | 
|  | unsigned long start, end, count = 0, tagged, cur, tmp; | 
|  | int i; | 
|  |  | 
|  | //	printf("generating radix tree indices...\n"); | 
|  | start = rand(); | 
|  | end = rand(); | 
|  | if (start > end && (rand() % 10)) { | 
|  | cur = start; | 
|  | start = end; | 
|  | end = cur; | 
|  | } | 
|  | /* Specifically create items around the start and the end of the range | 
|  | * with high probability to check for off by one errors */ | 
|  | cur = rand(); | 
|  | if (cur & 1) { | 
|  | item_insert(&tree, start); | 
|  | if (cur & 2) { | 
|  | if (start <= end) | 
|  | count++; | 
|  | item_tag_set(&tree, start, 0); | 
|  | } | 
|  | } | 
|  | if (cur & 4) { | 
|  | item_insert(&tree, start-1); | 
|  | if (cur & 8) | 
|  | item_tag_set(&tree, start-1, 0); | 
|  | } | 
|  | if (cur & 16) { | 
|  | item_insert(&tree, end); | 
|  | if (cur & 32) { | 
|  | if (start <= end) | 
|  | count++; | 
|  | item_tag_set(&tree, end, 0); | 
|  | } | 
|  | } | 
|  | if (cur & 64) { | 
|  | item_insert(&tree, end+1); | 
|  | if (cur & 128) | 
|  | item_tag_set(&tree, end+1, 0); | 
|  | } | 
|  |  | 
|  | for (i = 0; i < ITEMS; i++) { | 
|  | do { | 
|  | idx[i] = rand(); | 
|  | } while (item_lookup(&tree, idx[i])); | 
|  |  | 
|  | item_insert(&tree, idx[i]); | 
|  | if (rand() & 1) { | 
|  | item_tag_set(&tree, idx[i], 0); | 
|  | if (idx[i] >= start && idx[i] <= end) | 
|  | count++; | 
|  | } | 
|  | /*		if (i % 1000 == 0) | 
|  | putchar('.'); */ | 
|  | } | 
|  |  | 
|  | //	printf("\ncopying tags...\n"); | 
|  | tagged = tag_tagged_items(&tree, NULL, start, end, ITEMS, 0, 1); | 
|  |  | 
|  | //	printf("checking copied tags\n"); | 
|  | assert(tagged == count); | 
|  | check_copied_tags(&tree, start, end, idx, ITEMS, 0, 1); | 
|  |  | 
|  | /* Copy tags in several rounds */ | 
|  | //	printf("\ncopying tags...\n"); | 
|  | tmp = rand() % (count / 10 + 2); | 
|  | tagged = tag_tagged_items(&tree, NULL, start, end, tmp, 0, 2); | 
|  | assert(tagged == count); | 
|  |  | 
|  | //	printf("%lu %lu %lu\n", tagged, tmp, count); | 
|  | //	printf("checking copied tags\n"); | 
|  | check_copied_tags(&tree, start, end, idx, ITEMS, 0, 2); | 
|  | verify_tag_consistency(&tree, 0); | 
|  | verify_tag_consistency(&tree, 1); | 
|  | verify_tag_consistency(&tree, 2); | 
|  | //	printf("\n"); | 
|  | item_kill_tree(&tree); | 
|  | } | 
|  |  | 
|  | static void __locate_check(struct radix_tree_root *tree, unsigned long index, | 
|  | unsigned order) | 
|  | { | 
|  | struct item *item; | 
|  | unsigned long index2; | 
|  |  | 
|  | item_insert_order(tree, index, order); | 
|  | item = item_lookup(tree, index); | 
|  | index2 = find_item(tree, item); | 
|  | if (index != index2) { | 
|  | printv(2, "index %ld order %d inserted; found %ld\n", | 
|  | index, order, index2); | 
|  | abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void __order_0_locate_check(void) | 
|  | { | 
|  | RADIX_TREE(tree, GFP_KERNEL); | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < 50; i++) | 
|  | __locate_check(&tree, rand() % INT_MAX, 0); | 
|  |  | 
|  | item_kill_tree(&tree); | 
|  | } | 
|  |  | 
|  | static void locate_check(void) | 
|  | { | 
|  | RADIX_TREE(tree, GFP_KERNEL); | 
|  | unsigned order; | 
|  | unsigned long offset, index; | 
|  |  | 
|  | __order_0_locate_check(); | 
|  |  | 
|  | for (order = 0; order < 20; order++) { | 
|  | for (offset = 0; offset < (1 << (order + 3)); | 
|  | offset += (1UL << order)) { | 
|  | for (index = 0; index < (1UL << (order + 5)); | 
|  | index += (1UL << order)) { | 
|  | __locate_check(&tree, index + offset, order); | 
|  | } | 
|  | if (find_item(&tree, &tree) != -1) | 
|  | abort(); | 
|  |  | 
|  | item_kill_tree(&tree); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (find_item(&tree, &tree) != -1) | 
|  | abort(); | 
|  | __locate_check(&tree, -1, 0); | 
|  | if (find_item(&tree, &tree) != -1) | 
|  | abort(); | 
|  | item_kill_tree(&tree); | 
|  | } | 
|  |  | 
|  | static void single_thread_tests(bool long_run) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | printv(1, "starting single_thread_tests: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | multiorder_checks(); | 
|  | rcu_barrier(); | 
|  | printv(2, "after multiorder_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | locate_check(); | 
|  | rcu_barrier(); | 
|  | printv(2, "after locate_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | tag_check(); | 
|  | rcu_barrier(); | 
|  | printv(2, "after tag_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | gang_check(); | 
|  | rcu_barrier(); | 
|  | printv(2, "after gang_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | add_and_check(); | 
|  | rcu_barrier(); | 
|  | printv(2, "after add_and_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | dynamic_height_check(); | 
|  | rcu_barrier(); | 
|  | printv(2, "after dynamic_height_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | idr_checks(); | 
|  | ida_checks(); | 
|  | rcu_barrier(); | 
|  | printv(2, "after idr_checks: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | big_gang_check(long_run); | 
|  | rcu_barrier(); | 
|  | printv(2, "after big_gang_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | for (i = 0; i < (long_run ? 2000 : 3); i++) { | 
|  | copy_tag_check(); | 
|  | printv(2, "%d ", i); | 
|  | fflush(stdout); | 
|  | } | 
|  | rcu_barrier(); | 
|  | printv(2, "after copy_tag_check: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | } | 
|  |  | 
|  | int main(int argc, char **argv) | 
|  | { | 
|  | bool long_run = false; | 
|  | int opt; | 
|  | unsigned int seed = time(NULL); | 
|  |  | 
|  | while ((opt = getopt(argc, argv, "ls:v")) != -1) { | 
|  | if (opt == 'l') | 
|  | long_run = true; | 
|  | else if (opt == 's') | 
|  | seed = strtoul(optarg, NULL, 0); | 
|  | else if (opt == 'v') | 
|  | test_verbose++; | 
|  | } | 
|  |  | 
|  | printf("random seed %u\n", seed); | 
|  | srand(seed); | 
|  |  | 
|  | printf("running tests\n"); | 
|  |  | 
|  | rcu_register_thread(); | 
|  | radix_tree_init(); | 
|  |  | 
|  | regression1_test(); | 
|  | regression2_test(); | 
|  | regression3_test(); | 
|  | iteration_test(0, 10 + 90 * long_run); | 
|  | iteration_test(7, 10 + 90 * long_run); | 
|  | single_thread_tests(long_run); | 
|  | ida_thread_tests(); | 
|  |  | 
|  | /* Free any remaining preallocated nodes */ | 
|  | radix_tree_cpu_dead(0); | 
|  |  | 
|  | benchmark(); | 
|  |  | 
|  | rcu_barrier(); | 
|  | printv(2, "after rcu_barrier: %d allocated, preempt %d\n", | 
|  | nr_allocated, preempt_count); | 
|  | rcu_unregister_thread(); | 
|  |  | 
|  | printf("tests completed\n"); | 
|  |  | 
|  | exit(0); | 
|  | } |