diff options
Diffstat (limited to 'tools/testing/radix-tree/iteration_check.c')
-rw-r--r-- | tools/testing/radix-tree/iteration_check.c | 123 |
1 files changed, 82 insertions, 41 deletions
diff --git a/tools/testing/radix-tree/iteration_check.c b/tools/testing/radix-tree/iteration_check.c index 9adb8e7415a6..7572b7ed930e 100644 --- a/tools/testing/radix-tree/iteration_check.c +++ b/tools/testing/radix-tree/iteration_check.c | |||
@@ -16,35 +16,50 @@ | |||
16 | #include <pthread.h> | 16 | #include <pthread.h> |
17 | #include "test.h" | 17 | #include "test.h" |
18 | 18 | ||
19 | #define NUM_THREADS 4 | 19 | #define NUM_THREADS 5 |
20 | #define TAG 0 | 20 | #define MAX_IDX 100 |
21 | #define TAG 0 | ||
22 | #define NEW_TAG 1 | ||
23 | |||
21 | static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER; | 24 | static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER; |
22 | static pthread_t threads[NUM_THREADS]; | 25 | static pthread_t threads[NUM_THREADS]; |
23 | RADIX_TREE(tree, GFP_KERNEL); | 26 | static unsigned int seeds[3]; |
24 | bool test_complete; | 27 | static RADIX_TREE(tree, GFP_KERNEL); |
28 | static bool test_complete; | ||
29 | static int max_order; | ||
25 | 30 | ||
26 | /* relentlessly fill the tree with tagged entries */ | 31 | /* relentlessly fill the tree with tagged entries */ |
27 | static void *add_entries_fn(void *arg) | 32 | static void *add_entries_fn(void *arg) |
28 | { | 33 | { |
29 | int pgoff; | 34 | rcu_register_thread(); |
30 | 35 | ||
31 | while (!test_complete) { | 36 | while (!test_complete) { |
32 | for (pgoff = 0; pgoff < 100; pgoff++) { | 37 | unsigned long pgoff; |
38 | int order; | ||
39 | |||
40 | for (pgoff = 0; pgoff < MAX_IDX; pgoff++) { | ||
33 | pthread_mutex_lock(&tree_lock); | 41 | pthread_mutex_lock(&tree_lock); |
34 | if (item_insert(&tree, pgoff) == 0) | 42 | for (order = max_order; order >= 0; order--) { |
35 | item_tag_set(&tree, pgoff, TAG); | 43 | if (item_insert_order(&tree, pgoff, order) |
44 | == 0) { | ||
45 | item_tag_set(&tree, pgoff, TAG); | ||
46 | break; | ||
47 | } | ||
48 | } | ||
36 | pthread_mutex_unlock(&tree_lock); | 49 | pthread_mutex_unlock(&tree_lock); |
37 | } | 50 | } |
38 | } | 51 | } |
39 | 52 | ||
53 | rcu_unregister_thread(); | ||
54 | |||
40 | return NULL; | 55 | return NULL; |
41 | } | 56 | } |
42 | 57 | ||
43 | /* | 58 | /* |
44 | * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find | 59 | * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find |
45 | * things that have been removed and randomly resetting our iteration to the | 60 | * things that have been removed and randomly resetting our iteration to the |
46 | * next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and | 61 | * next chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and |
47 | * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a | 62 | * radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a |
48 | * NULL 'slot' variable. | 63 | * NULL 'slot' variable. |
49 | */ | 64 | */ |
50 | static void *tagged_iteration_fn(void *arg) | 65 | static void *tagged_iteration_fn(void *arg) |
@@ -52,17 +67,12 @@ static void *tagged_iteration_fn(void *arg) | |||
52 | struct radix_tree_iter iter; | 67 | struct radix_tree_iter iter; |
53 | void **slot; | 68 | void **slot; |
54 | 69 | ||
70 | rcu_register_thread(); | ||
71 | |||
55 | while (!test_complete) { | 72 | while (!test_complete) { |
56 | rcu_read_lock(); | 73 | rcu_read_lock(); |
57 | radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) { | 74 | radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) { |
58 | void *entry; | 75 | void *entry = radix_tree_deref_slot(slot); |
59 | int i; | ||
60 | |||
61 | /* busy wait to let removals happen */ | ||
62 | for (i = 0; i < 1000000; i++) | ||
63 | ; | ||
64 | |||
65 | entry = radix_tree_deref_slot(slot); | ||
66 | if (unlikely(!entry)) | 76 | if (unlikely(!entry)) |
67 | continue; | 77 | continue; |
68 | 78 | ||
@@ -71,20 +81,26 @@ static void *tagged_iteration_fn(void *arg) | |||
71 | continue; | 81 | continue; |
72 | } | 82 | } |
73 | 83 | ||
74 | if (rand() % 50 == 0) | 84 | if (rand_r(&seeds[0]) % 50 == 0) { |
75 | slot = radix_tree_iter_next(&iter); | 85 | slot = radix_tree_iter_resume(slot, &iter); |
86 | rcu_read_unlock(); | ||
87 | rcu_barrier(); | ||
88 | rcu_read_lock(); | ||
89 | } | ||
76 | } | 90 | } |
77 | rcu_read_unlock(); | 91 | rcu_read_unlock(); |
78 | } | 92 | } |
79 | 93 | ||
94 | rcu_unregister_thread(); | ||
95 | |||
80 | return NULL; | 96 | return NULL; |
81 | } | 97 | } |
82 | 98 | ||
83 | /* | 99 | /* |
84 | * Iterate over the entries, doing a radix_tree_iter_retry() as we find things | 100 | * Iterate over the entries, doing a radix_tree_iter_retry() as we find things |
85 | * that have been removed and randomly resetting our iteration to the next | 101 | * that have been removed and randomly resetting our iteration to the next |
86 | * chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and | 102 | * chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and |
87 | * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a | 103 | * radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a |
88 | * NULL 'slot' variable. | 104 | * NULL 'slot' variable. |
89 | */ | 105 | */ |
90 | static void *untagged_iteration_fn(void *arg) | 106 | static void *untagged_iteration_fn(void *arg) |
@@ -92,17 +108,12 @@ static void *untagged_iteration_fn(void *arg) | |||
92 | struct radix_tree_iter iter; | 108 | struct radix_tree_iter iter; |
93 | void **slot; | 109 | void **slot; |
94 | 110 | ||
111 | rcu_register_thread(); | ||
112 | |||
95 | while (!test_complete) { | 113 | while (!test_complete) { |
96 | rcu_read_lock(); | 114 | rcu_read_lock(); |
97 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { | 115 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { |
98 | void *entry; | 116 | void *entry = radix_tree_deref_slot(slot); |
99 | int i; | ||
100 | |||
101 | /* busy wait to let removals happen */ | ||
102 | for (i = 0; i < 1000000; i++) | ||
103 | ; | ||
104 | |||
105 | entry = radix_tree_deref_slot(slot); | ||
106 | if (unlikely(!entry)) | 117 | if (unlikely(!entry)) |
107 | continue; | 118 | continue; |
108 | 119 | ||
@@ -111,12 +122,18 @@ static void *untagged_iteration_fn(void *arg) | |||
111 | continue; | 122 | continue; |
112 | } | 123 | } |
113 | 124 | ||
114 | if (rand() % 50 == 0) | 125 | if (rand_r(&seeds[1]) % 50 == 0) { |
115 | slot = radix_tree_iter_next(&iter); | 126 | slot = radix_tree_iter_resume(slot, &iter); |
127 | rcu_read_unlock(); | ||
128 | rcu_barrier(); | ||
129 | rcu_read_lock(); | ||
130 | } | ||
116 | } | 131 | } |
117 | rcu_read_unlock(); | 132 | rcu_read_unlock(); |
118 | } | 133 | } |
119 | 134 | ||
135 | rcu_unregister_thread(); | ||
136 | |||
120 | return NULL; | 137 | return NULL; |
121 | } | 138 | } |
122 | 139 | ||
@@ -126,47 +143,71 @@ static void *untagged_iteration_fn(void *arg) | |||
126 | */ | 143 | */ |
127 | static void *remove_entries_fn(void *arg) | 144 | static void *remove_entries_fn(void *arg) |
128 | { | 145 | { |
146 | rcu_register_thread(); | ||
147 | |||
129 | while (!test_complete) { | 148 | while (!test_complete) { |
130 | int pgoff; | 149 | int pgoff; |
131 | 150 | ||
132 | pgoff = rand() % 100; | 151 | pgoff = rand_r(&seeds[2]) % MAX_IDX; |
133 | 152 | ||
134 | pthread_mutex_lock(&tree_lock); | 153 | pthread_mutex_lock(&tree_lock); |
135 | item_delete(&tree, pgoff); | 154 | item_delete(&tree, pgoff); |
136 | pthread_mutex_unlock(&tree_lock); | 155 | pthread_mutex_unlock(&tree_lock); |
137 | } | 156 | } |
138 | 157 | ||
158 | rcu_unregister_thread(); | ||
159 | |||
160 | return NULL; | ||
161 | } | ||
162 | |||
163 | static void *tag_entries_fn(void *arg) | ||
164 | { | ||
165 | rcu_register_thread(); | ||
166 | |||
167 | while (!test_complete) { | ||
168 | tag_tagged_items(&tree, &tree_lock, 0, MAX_IDX, 10, TAG, | ||
169 | NEW_TAG); | ||
170 | } | ||
171 | rcu_unregister_thread(); | ||
139 | return NULL; | 172 | return NULL; |
140 | } | 173 | } |
141 | 174 | ||
142 | /* This is a unit test for a bug found by the syzkaller tester */ | 175 | /* This is a unit test for a bug found by the syzkaller tester */ |
143 | void iteration_test(void) | 176 | void iteration_test(unsigned order, unsigned test_duration) |
144 | { | 177 | { |
145 | int i; | 178 | int i; |
146 | 179 | ||
147 | printf("Running iteration tests for 10 seconds\n"); | 180 | printf("Running %siteration tests for %d seconds\n", |
181 | order > 0 ? "multiorder " : "", test_duration); | ||
148 | 182 | ||
149 | srand(time(0)); | 183 | max_order = order; |
150 | test_complete = false; | 184 | test_complete = false; |
151 | 185 | ||
186 | for (i = 0; i < 3; i++) | ||
187 | seeds[i] = rand(); | ||
188 | |||
152 | if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) { | 189 | if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) { |
153 | perror("pthread_create"); | 190 | perror("create tagged iteration thread"); |
154 | exit(1); | 191 | exit(1); |
155 | } | 192 | } |
156 | if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) { | 193 | if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) { |
157 | perror("pthread_create"); | 194 | perror("create untagged iteration thread"); |
158 | exit(1); | 195 | exit(1); |
159 | } | 196 | } |
160 | if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) { | 197 | if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) { |
161 | perror("pthread_create"); | 198 | perror("create add entry thread"); |
162 | exit(1); | 199 | exit(1); |
163 | } | 200 | } |
164 | if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) { | 201 | if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) { |
165 | perror("pthread_create"); | 202 | perror("create remove entry thread"); |
203 | exit(1); | ||
204 | } | ||
205 | if (pthread_create(&threads[4], NULL, tag_entries_fn, NULL)) { | ||
206 | perror("create tag entry thread"); | ||
166 | exit(1); | 207 | exit(1); |
167 | } | 208 | } |
168 | 209 | ||
169 | sleep(10); | 210 | sleep(test_duration); |
170 | test_complete = true; | 211 | test_complete = true; |
171 | 212 | ||
172 | for (i = 0; i < NUM_THREADS; i++) { | 213 | for (i = 0; i < NUM_THREADS; i++) { |