4 * Salman Qazi describes the following radix-tree bug:
6 * In the following case, we get can get a deadlock:
8 * 0. The radix tree contains two items, one has the index 0.
9 * 1. The reader (in this case find_get_pages) takes the rcu_read_lock.
10 * 2. The reader acquires slot(s) for item(s) including the index 0 item.
11 * 3. The non-zero index item is deleted, and as a consequence the other item
12 * is moved to the root of the tree. The place where it used to be is queued
13 * for deletion after the readers finish.
14 * 3b. The zero item is deleted, removing it from the direct slot, it remains in
15 * the rcu-delayed indirect node.
16 * 4. The reader looks at the index 0 slot, and finds that the page has 0 ref
18 * 5. The reader looks at it again, hoping that the item will either be freed
19 * or the ref count will increase. This never happens, as the slot it is
20 * looking at will never be updated. Also, this slot can never be reclaimed
21 * because the reader is holding rcu_read_lock and is in an infinite loop.
23 * The fix is to re-use the same "indirect" pointer case that requires a slot
24 * lookup retry into a general "retry the lookup" bit.
27 * This test should run to completion in a few seconds. The above bug would
28 * cause it to hang indefinitely.
33 #include <linux/kernel.h>
34 #include <linux/gfp.h>
35 #include <linux/slab.h>
36 #include <linux/radix-tree.h>
37 #include <linux/rcupdate.h>
43 #include "regression.h"
45 static RADIX_TREE(mt_tree
, GFP_KERNEL
);
46 static pthread_mutex_t mt_lock
;
55 static struct page
*page_alloc(void)
58 p
= malloc(sizeof(struct page
));
61 pthread_mutex_init(&p
->lock
, NULL
);
66 static void page_rcu_free(struct rcu_head
*rcu
)
68 struct page
*p
= container_of(rcu
, struct page
, rcu
);
70 pthread_mutex_destroy(&p
->lock
);
74 static void page_free(struct page
*p
)
76 call_rcu(&p
->rcu
, page_rcu_free
);
79 static unsigned find_get_pages(unsigned long start
,
80 unsigned int nr_pages
, struct page
**pages
)
84 unsigned int nr_found
;
88 nr_found
= radix_tree_gang_lookup_slot(&mt_tree
,
89 (void ***)pages
, NULL
, start
, nr_pages
);
91 for (i
= 0; i
< nr_found
; i
++) {
94 page
= radix_tree_deref_slot((void **)pages
[i
]);
98 if (radix_tree_exception(page
)) {
99 if (radix_tree_deref_retry(page
)) {
101 * Transient condition which can only trigger
102 * when entry at index 0 moves out of or back
103 * to root: none yet gotten, safe to restart.
105 assert((start
| i
) == 0);
109 * No exceptional entries are inserted in this test.
114 pthread_mutex_lock(&page
->lock
);
116 pthread_mutex_unlock(&page
->lock
);
119 /* don't actually update page refcount */
120 pthread_mutex_unlock(&page
->lock
);
122 /* Has the page moved? */
123 if (unlikely(page
!= *((void **)pages
[i
]))) {
134 static pthread_barrier_t worker_barrier
;
136 static void *regression1_fn(void *arg
)
138 rcu_register_thread();
140 if (pthread_barrier_wait(&worker_barrier
) ==
141 PTHREAD_BARRIER_SERIAL_THREAD
) {
144 for (j
= 0; j
< 1000000; j
++) {
148 pthread_mutex_lock(&mt_lock
);
149 radix_tree_insert(&mt_tree
, 0, p
);
150 pthread_mutex_unlock(&mt_lock
);
153 pthread_mutex_lock(&mt_lock
);
154 radix_tree_insert(&mt_tree
, 1, p
);
155 pthread_mutex_unlock(&mt_lock
);
157 pthread_mutex_lock(&mt_lock
);
158 p
= radix_tree_delete(&mt_tree
, 1);
159 pthread_mutex_lock(&p
->lock
);
161 pthread_mutex_unlock(&p
->lock
);
162 pthread_mutex_unlock(&mt_lock
);
165 pthread_mutex_lock(&mt_lock
);
166 p
= radix_tree_delete(&mt_tree
, 0);
167 pthread_mutex_lock(&p
->lock
);
169 pthread_mutex_unlock(&p
->lock
);
170 pthread_mutex_unlock(&mt_lock
);
176 for (j
= 0; j
< 100000000; j
++) {
177 struct page
*pages
[10];
179 find_get_pages(0, 10, pages
);
183 rcu_unregister_thread();
188 static pthread_t
*threads
;
189 void regression1_test(void)
196 printf("running regression test 1, should finish in under a minute\n");
198 pthread_barrier_init(&worker_barrier
, NULL
, nr_threads
);
200 threads
= malloc(nr_threads
* sizeof(pthread_t
*));
202 for (i
= 0; i
< nr_threads
; i
++) {
204 if (pthread_create(&threads
[i
], NULL
, regression1_fn
, (void *)arg
)) {
205 perror("pthread_create");
210 for (i
= 0; i
< nr_threads
; i
++) {
211 if (pthread_join(threads
[i
], NULL
)) {
212 perror("pthread_join");
219 printf("regression test 1, done\n");