blob: 11d570c3fc83c7b8523f91522d987a2c9ffe3f95 [file] [log] [blame]
Ross Zwislereec48522016-10-11 13:51:21 -07001/*
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>
5 *
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.
9 *
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
13 * more details.
14 */
15#include <linux/radix-tree.h>
16#include <pthread.h>
17#include "test.h"
18
19#define NUM_THREADS 4
20#define TAG 0
21static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER;
22static pthread_t threads[NUM_THREADS];
Matthew Wilcox061ef392016-12-14 15:08:08 -080023static unsigned int seeds[3];
Ross Zwislereec48522016-10-11 13:51:21 -070024RADIX_TREE(tree, GFP_KERNEL);
25bool test_complete;
26
27/* relentlessly fill the tree with tagged entries */
28static void *add_entries_fn(void *arg)
29{
30 int pgoff;
31
32 while (!test_complete) {
33 for (pgoff = 0; pgoff < 100; pgoff++) {
34 pthread_mutex_lock(&tree_lock);
35 if (item_insert(&tree, pgoff) == 0)
36 item_tag_set(&tree, pgoff, TAG);
37 pthread_mutex_unlock(&tree_lock);
38 }
39 }
40
41 return NULL;
42}
43
44/*
45 * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find
46 * things that have been removed and randomly resetting our iteration to the
47 * next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and
48 * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
49 * NULL 'slot' variable.
50 */
51static void *tagged_iteration_fn(void *arg)
52{
53 struct radix_tree_iter iter;
54 void **slot;
55
56 while (!test_complete) {
57 rcu_read_lock();
58 radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) {
59 void *entry;
60 int i;
61
62 /* busy wait to let removals happen */
63 for (i = 0; i < 1000000; i++)
64 ;
65
66 entry = radix_tree_deref_slot(slot);
67 if (unlikely(!entry))
68 continue;
69
70 if (radix_tree_deref_retry(entry)) {
71 slot = radix_tree_iter_retry(&iter);
72 continue;
73 }
74
Matthew Wilcox061ef392016-12-14 15:08:08 -080075 if (rand_r(&seeds[0]) % 50 == 0)
Ross Zwislereec48522016-10-11 13:51:21 -070076 slot = radix_tree_iter_next(&iter);
77 }
78 rcu_read_unlock();
79 }
80
81 return NULL;
82}
83
84/*
85 * Iterate over the entries, doing a radix_tree_iter_retry() as we find things
86 * that have been removed and randomly resetting our iteration to the next
87 * chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and
88 * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
89 * NULL 'slot' variable.
90 */
91static void *untagged_iteration_fn(void *arg)
92{
93 struct radix_tree_iter iter;
94 void **slot;
95
96 while (!test_complete) {
97 rcu_read_lock();
98 radix_tree_for_each_slot(slot, &tree, &iter, 0) {
99 void *entry;
100 int i;
101
102 /* busy wait to let removals happen */
103 for (i = 0; i < 1000000; i++)
104 ;
105
106 entry = radix_tree_deref_slot(slot);
107 if (unlikely(!entry))
108 continue;
109
110 if (radix_tree_deref_retry(entry)) {
111 slot = radix_tree_iter_retry(&iter);
112 continue;
113 }
114
Matthew Wilcox061ef392016-12-14 15:08:08 -0800115 if (rand_r(&seeds[1]) % 50 == 0)
Ross Zwislereec48522016-10-11 13:51:21 -0700116 slot = radix_tree_iter_next(&iter);
117 }
118 rcu_read_unlock();
119 }
120
121 return NULL;
122}
123
124/*
125 * Randomly remove entries to help induce radix_tree_iter_retry() calls in the
126 * two iteration functions.
127 */
128static void *remove_entries_fn(void *arg)
129{
130 while (!test_complete) {
131 int pgoff;
132
Matthew Wilcox061ef392016-12-14 15:08:08 -0800133 pgoff = rand_r(&seeds[2]) % 100;
Ross Zwislereec48522016-10-11 13:51:21 -0700134
135 pthread_mutex_lock(&tree_lock);
136 item_delete(&tree, pgoff);
137 pthread_mutex_unlock(&tree_lock);
138 }
139
140 return NULL;
141}
142
143/* This is a unit test for a bug found by the syzkaller tester */
144void iteration_test(void)
145{
146 int i;
147
148 printf("Running iteration tests for 10 seconds\n");
149
Ross Zwislereec48522016-10-11 13:51:21 -0700150 test_complete = false;
151
Matthew Wilcox061ef392016-12-14 15:08:08 -0800152 for (i = 0; i < 3; i++)
153 seeds[i] = rand();
154
Ross Zwislereec48522016-10-11 13:51:21 -0700155 if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) {
156 perror("pthread_create");
157 exit(1);
158 }
159 if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) {
160 perror("pthread_create");
161 exit(1);
162 }
163 if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) {
164 perror("pthread_create");
165 exit(1);
166 }
167 if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) {
168 perror("pthread_create");
169 exit(1);
170 }
171
172 sleep(10);
173 test_complete = true;
174
175 for (i = 0; i < NUM_THREADS; i++) {
176 if (pthread_join(threads[i], NULL)) {
177 perror("pthread_join");
178 exit(1);
179 }
180 }
181
182 item_kill_tree(&tree);
183}