regression1.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Regression1
  4. * Description:
  5. * Salman Qazi describes the following radix-tree bug:
  6. *
  7. * In the following case, we get can get a deadlock:
  8. *
  9. * 0. The radix tree contains two items, one has the index 0.
  10. * 1. The reader (in this case find_get_pages) takes the rcu_read_lock.
  11. * 2. The reader acquires slot(s) for item(s) including the index 0 item.
  12. * 3. The non-zero index item is deleted, and as a consequence the other item
  13. * is moved to the root of the tree. The place where it used to be is queued
  14. * for deletion after the readers finish.
  15. * 3b. The zero item is deleted, removing it from the direct slot, it remains in
  16. * the rcu-delayed indirect node.
  17. * 4. The reader looks at the index 0 slot, and finds that the page has 0 ref
  18. * count
  19. * 5. The reader looks at it again, hoping that the item will either be freed
  20. * or the ref count will increase. This never happens, as the slot it is
  21. * looking at will never be updated. Also, this slot can never be reclaimed
  22. * because the reader is holding rcu_read_lock and is in an infinite loop.
  23. *
  24. * The fix is to re-use the same "indirect" pointer case that requires a slot
  25. * lookup retry into a general "retry the lookup" bit.
  26. *
  27. * Running:
  28. * This test should run to completion in a few seconds. The above bug would
  29. * cause it to hang indefinitely.
  30. *
  31. * Upstream commit:
  32. * Not yet
  33. */
  34. #include <linux/kernel.h>
  35. #include <linux/gfp.h>
  36. #include <linux/slab.h>
  37. #include <linux/radix-tree.h>
  38. #include <linux/rcupdate.h>
  39. #include <stdlib.h>
  40. #include <pthread.h>
  41. #include <stdio.h>
  42. #include <assert.h>
  43. #include "regression.h"
  44. static RADIX_TREE(mt_tree, GFP_KERNEL);
  45. static pthread_mutex_t mt_lock = PTHREAD_MUTEX_INITIALIZER;
  46. struct page {
  47. pthread_mutex_t lock;
  48. struct rcu_head rcu;
  49. int count;
  50. unsigned long index;
  51. };
  52. static struct page *page_alloc(void)
  53. {
  54. struct page *p;
  55. p = malloc(sizeof(struct page));
  56. p->count = 1;
  57. p->index = 1;
  58. pthread_mutex_init(&p->lock, NULL);
  59. return p;
  60. }
  61. static void page_rcu_free(struct rcu_head *rcu)
  62. {
  63. struct page *p = container_of(rcu, struct page, rcu);
  64. assert(!p->count);
  65. pthread_mutex_destroy(&p->lock);
  66. free(p);
  67. }
  68. static void page_free(struct page *p)
  69. {
  70. call_rcu(&p->rcu, page_rcu_free);
  71. }
  72. static unsigned find_get_pages(unsigned long start,
  73. unsigned int nr_pages, struct page **pages)
  74. {
  75. unsigned int i;
  76. unsigned int ret;
  77. unsigned int nr_found;
  78. rcu_read_lock();
  79. restart:
  80. nr_found = radix_tree_gang_lookup_slot(&mt_tree,
  81. (void ***)pages, NULL, start, nr_pages);
  82. ret = 0;
  83. for (i = 0; i < nr_found; i++) {
  84. struct page *page;
  85. repeat:
  86. page = radix_tree_deref_slot((void **)pages[i]);
  87. if (unlikely(!page))
  88. continue;
  89. if (radix_tree_exception(page)) {
  90. if (radix_tree_deref_retry(page)) {
  91. /*
  92. * Transient condition which can only trigger
  93. * when entry at index 0 moves out of or back
  94. * to root: none yet gotten, safe to restart.
  95. */
  96. assert((start | i) == 0);
  97. goto restart;
  98. }
  99. /*
  100. * No exceptional entries are inserted in this test.
  101. */
  102. assert(0);
  103. }
  104. pthread_mutex_lock(&page->lock);
  105. if (!page->count) {
  106. pthread_mutex_unlock(&page->lock);
  107. goto repeat;
  108. }
  109. /* don't actually update page refcount */
  110. pthread_mutex_unlock(&page->lock);
  111. /* Has the page moved? */
  112. if (unlikely(page != *((void **)pages[i]))) {
  113. goto repeat;
  114. }
  115. pages[ret] = page;
  116. ret++;
  117. }
  118. rcu_read_unlock();
  119. return ret;
  120. }
  121. static pthread_barrier_t worker_barrier;
  122. static void *regression1_fn(void *arg)
  123. {
  124. rcu_register_thread();
  125. if (pthread_barrier_wait(&worker_barrier) ==
  126. PTHREAD_BARRIER_SERIAL_THREAD) {
  127. int j;
  128. for (j = 0; j < 1000000; j++) {
  129. struct page *p;
  130. p = page_alloc();
  131. pthread_mutex_lock(&mt_lock);
  132. radix_tree_insert(&mt_tree, 0, p);
  133. pthread_mutex_unlock(&mt_lock);
  134. p = page_alloc();
  135. pthread_mutex_lock(&mt_lock);
  136. radix_tree_insert(&mt_tree, 1, p);
  137. pthread_mutex_unlock(&mt_lock);
  138. pthread_mutex_lock(&mt_lock);
  139. p = radix_tree_delete(&mt_tree, 1);
  140. pthread_mutex_lock(&p->lock);
  141. p->count--;
  142. pthread_mutex_unlock(&p->lock);
  143. pthread_mutex_unlock(&mt_lock);
  144. page_free(p);
  145. pthread_mutex_lock(&mt_lock);
  146. p = radix_tree_delete(&mt_tree, 0);
  147. pthread_mutex_lock(&p->lock);
  148. p->count--;
  149. pthread_mutex_unlock(&p->lock);
  150. pthread_mutex_unlock(&mt_lock);
  151. page_free(p);
  152. }
  153. } else {
  154. int j;
  155. for (j = 0; j < 100000000; j++) {
  156. struct page *pages[10];
  157. find_get_pages(0, 10, pages);
  158. }
  159. }
  160. rcu_unregister_thread();
  161. return NULL;
  162. }
  163. static pthread_t *threads;
  164. void regression1_test(void)
  165. {
  166. int nr_threads;
  167. int i;
  168. long arg;
  169. /* Regression #1 */
  170. printv(1, "running regression test 1, should finish in under a minute\n");
  171. nr_threads = 2;
  172. pthread_barrier_init(&worker_barrier, NULL, nr_threads);
  173. threads = malloc(nr_threads * sizeof(pthread_t *));
  174. for (i = 0; i < nr_threads; i++) {
  175. arg = i;
  176. if (pthread_create(&threads[i], NULL, regression1_fn, (void *)arg)) {
  177. perror("pthread_create");
  178. exit(1);
  179. }
  180. }
  181. for (i = 0; i < nr_threads; i++) {
  182. if (pthread_join(threads[i], NULL)) {
  183. perror("pthread_join");
  184. exit(1);
  185. }
  186. }
  187. free(threads);
  188. printv(1, "regression test 1, done\n");
  189. }