2 * iteration_check.c: test races having to do with radix tree iteration
3 * Copyright (c) 2016 Intel Corporation
4 * Author: Ross Zwisler <ross.zwisler@linux.intel.com>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 #include <linux/radix-tree.h>
21 static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER;
22 static pthread_t threads[NUM_THREADS];
23 static unsigned int seeds[3];
24 RADIX_TREE(tree, GFP_KERNEL);
27 /* relentlessly fill the tree with tagged entries */
28 static void *add_entries_fn(void *arg)
32 rcu_register_thread();
34 while (!test_complete) {
35 for (pgoff = 0; pgoff < 100; pgoff++) {
36 pthread_mutex_lock(&tree_lock);
37 if (item_insert(&tree, pgoff) == 0)
38 item_tag_set(&tree, pgoff, TAG);
39 pthread_mutex_unlock(&tree_lock);
43 rcu_unregister_thread();
49 * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find
50 * things that have been removed and randomly resetting our iteration to the
51 * next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and
52 * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
53 * NULL 'slot' variable.
55 static void *tagged_iteration_fn(void *arg)
57 struct radix_tree_iter iter;
60 rcu_register_thread();
62 while (!test_complete) {
64 radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) {
68 /* busy wait to let removals happen */
69 for (i = 0; i < 1000000; i++)
72 entry = radix_tree_deref_slot(slot);
76 if (radix_tree_deref_retry(entry)) {
77 slot = radix_tree_iter_retry(&iter);
81 if (rand_r(&seeds[0]) % 50 == 0) {
82 slot = radix_tree_iter_next(&iter);
91 rcu_unregister_thread();
97 * Iterate over the entries, doing a radix_tree_iter_retry() as we find things
98 * that have been removed and randomly resetting our iteration to the next
99 * chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and
100 * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
101 * NULL 'slot' variable.
103 static void *untagged_iteration_fn(void *arg)
105 struct radix_tree_iter iter;
108 rcu_register_thread();
110 while (!test_complete) {
112 radix_tree_for_each_slot(slot, &tree, &iter, 0) {
116 /* busy wait to let removals happen */
117 for (i = 0; i < 1000000; i++)
120 entry = radix_tree_deref_slot(slot);
121 if (unlikely(!entry))
124 if (radix_tree_deref_retry(entry)) {
125 slot = radix_tree_iter_retry(&iter);
129 if (rand_r(&seeds[1]) % 50 == 0) {
130 slot = radix_tree_iter_next(&iter);
139 rcu_unregister_thread();
145 * Randomly remove entries to help induce radix_tree_iter_retry() calls in the
146 * two iteration functions.
148 static void *remove_entries_fn(void *arg)
150 rcu_register_thread();
152 while (!test_complete) {
155 pgoff = rand_r(&seeds[2]) % 100;
157 pthread_mutex_lock(&tree_lock);
158 item_delete(&tree, pgoff);
159 pthread_mutex_unlock(&tree_lock);
162 rcu_unregister_thread();
167 /* This is a unit test for a bug found by the syzkaller tester */
168 void iteration_test(void)
172 printf("Running iteration tests for 10 seconds\n");
174 test_complete = false;
176 for (i = 0; i < 3; i++)
179 if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) {
180 perror("pthread_create");
183 if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) {
184 perror("pthread_create");
187 if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) {
188 perror("pthread_create");
191 if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) {
192 perror("pthread_create");
197 test_complete = true;
199 for (i = 0; i < NUM_THREADS; i++) {
200 if (pthread_join(threads[i], NULL)) {
201 perror("pthread_join");
206 item_kill_tree(&tree);