regression1.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * Regression1
  3. * Description:
  4. * Salman Qazi describes the following radix-tree bug:
  5. *
  6. * In the following case, we get can get a deadlock:
  7. *
  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
  17. * count
  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.
  22. *
  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.
  25. *
  26. * Running:
  27. * This test should run to completion in a few seconds. The above bug would
  28. * cause it to hang indefinitely.
  29. *
  30. * Upstream commit:
  31. * Not yet
  32. */
  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>
  38. #include <stdlib.h>
  39. #include <pthread.h>
  40. #include <stdio.h>
  41. #include <assert.h>
  42. #include "regression.h"
  43. static RADIX_TREE(mt_tree, GFP_KERNEL);
  44. static pthread_mutex_t mt_lock = PTHREAD_MUTEX_INITIALIZER;
  45. struct page {
  46. pthread_mutex_t lock;
  47. struct rcu_head rcu;
  48. int count;
  49. unsigned long index;
  50. };
  51. static struct page *page_alloc(void)
  52. {
  53. struct page *p;
  54. p = malloc(sizeof(struct page));
  55. p->count = 1;
  56. p->index = 1;
  57. pthread_mutex_init(&p->lock, NULL);
  58. return p;
  59. }
  60. static void page_rcu_free(struct rcu_head *rcu)
  61. {
  62. struct page *p = container_of(rcu, struct page, rcu);
  63. assert(!p->count);
  64. pthread_mutex_destroy(&p->lock);
  65. free(p);
  66. }
  67. static void page_free(struct page *p)
  68. {
  69. call_rcu(&p->rcu, page_rcu_free);
  70. }
  71. static unsigned find_get_pages(unsigned long start,
  72. unsigned int nr_pages, struct page **pages)
  73. {
  74. unsigned int i;
  75. unsigned int ret;
  76. unsigned int nr_found;
  77. rcu_read_lock();
  78. restart:
  79. nr_found = radix_tree_gang_lookup_slot(&mt_tree,
  80. (void ***)pages, NULL, start, nr_pages);
  81. ret = 0;
  82. for (i = 0; i < nr_found; i++) {
  83. struct page *page;
  84. repeat:
  85. page = radix_tree_deref_slot((void **)pages[i]);
  86. if (unlikely(!page))
  87. continue;
  88. if (radix_tree_exception(page)) {
  89. if (radix_tree_deref_retry(page)) {
  90. /*
  91. * Transient condition which can only trigger
  92. * when entry at index 0 moves out of or back
  93. * to root: none yet gotten, safe to restart.
  94. */
  95. assert((start | i) == 0);
  96. goto restart;
  97. }
  98. /*
  99. * No exceptional entries are inserted in this test.
  100. */
  101. assert(0);
  102. }
  103. pthread_mutex_lock(&page->lock);
  104. if (!page->count) {
  105. pthread_mutex_unlock(&page->lock);
  106. goto repeat;
  107. }
  108. /* don't actually update page refcount */
  109. pthread_mutex_unlock(&page->lock);
  110. /* Has the page moved? */
  111. if (unlikely(page != *((void **)pages[i]))) {
  112. goto repeat;
  113. }
  114. pages[ret] = page;
  115. ret++;
  116. }
  117. rcu_read_unlock();
  118. return ret;
  119. }
  120. static pthread_barrier_t worker_barrier;
  121. static void *regression1_fn(void *arg)
  122. {
  123. rcu_register_thread();
  124. if (pthread_barrier_wait(&worker_barrier) ==
  125. PTHREAD_BARRIER_SERIAL_THREAD) {
  126. int j;
  127. for (j = 0; j < 1000000; j++) {
  128. struct page *p;
  129. p = page_alloc();
  130. pthread_mutex_lock(&mt_lock);
  131. radix_tree_insert(&mt_tree, 0, p);
  132. pthread_mutex_unlock(&mt_lock);
  133. p = page_alloc();
  134. pthread_mutex_lock(&mt_lock);
  135. radix_tree_insert(&mt_tree, 1, p);
  136. pthread_mutex_unlock(&mt_lock);
  137. pthread_mutex_lock(&mt_lock);
  138. p = radix_tree_delete(&mt_tree, 1);
  139. pthread_mutex_lock(&p->lock);
  140. p->count--;
  141. pthread_mutex_unlock(&p->lock);
  142. pthread_mutex_unlock(&mt_lock);
  143. page_free(p);
  144. pthread_mutex_lock(&mt_lock);
  145. p = radix_tree_delete(&mt_tree, 0);
  146. pthread_mutex_lock(&p->lock);
  147. p->count--;
  148. pthread_mutex_unlock(&p->lock);
  149. pthread_mutex_unlock(&mt_lock);
  150. page_free(p);
  151. }
  152. } else {
  153. int j;
  154. for (j = 0; j < 100000000; j++) {
  155. struct page *pages[10];
  156. find_get_pages(0, 10, pages);
  157. }
  158. }
  159. rcu_unregister_thread();
  160. return NULL;
  161. }
  162. static pthread_t *threads;
  163. void regression1_test(void)
  164. {
  165. int nr_threads;
  166. int i;
  167. long arg;
  168. /* Regression #1 */
  169. printf("running regression test 1, should finish in under a minute\n");
  170. nr_threads = 2;
  171. pthread_barrier_init(&worker_barrier, NULL, nr_threads);
  172. threads = malloc(nr_threads * sizeof(pthread_t *));
  173. for (i = 0; i < nr_threads; i++) {
  174. arg = i;
  175. if (pthread_create(&threads[i], NULL, regression1_fn, (void *)arg)) {
  176. perror("pthread_create");
  177. exit(1);
  178. }
  179. }
  180. for (i = 0; i < nr_threads; i++) {
  181. if (pthread_join(threads[i], NULL)) {
  182. perror("pthread_join");
  183. exit(1);
  184. }
  185. }
  186. free(threads);
  187. printf("regression test 1, done\n");
  188. }