regression1.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. 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(int index)
  52. {
  53. struct page *p;
  54. p = malloc(sizeof(struct page));
  55. p->count = 1;
  56. p->index = index;
  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. XA_STATE(xas, &mt_tree, start);
  75. struct page *page;
  76. unsigned int ret = 0;
  77. rcu_read_lock();
  78. xas_for_each(&xas, page, ULONG_MAX) {
  79. if (xas_retry(&xas, page))
  80. continue;
  81. pthread_mutex_lock(&page->lock);
  82. if (!page->count)
  83. goto unlock;
  84. /* don't actually update page refcount */
  85. pthread_mutex_unlock(&page->lock);
  86. /* Has the page moved? */
  87. if (unlikely(page != xas_reload(&xas)))
  88. goto put_page;
  89. pages[ret] = page;
  90. ret++;
  91. continue;
  92. unlock:
  93. pthread_mutex_unlock(&page->lock);
  94. put_page:
  95. xas_reset(&xas);
  96. }
  97. rcu_read_unlock();
  98. return ret;
  99. }
  100. static pthread_barrier_t worker_barrier;
  101. static void *regression1_fn(void *arg)
  102. {
  103. rcu_register_thread();
  104. if (pthread_barrier_wait(&worker_barrier) ==
  105. PTHREAD_BARRIER_SERIAL_THREAD) {
  106. int j;
  107. for (j = 0; j < 1000000; j++) {
  108. struct page *p;
  109. p = page_alloc(0);
  110. xa_lock(&mt_tree);
  111. radix_tree_insert(&mt_tree, 0, p);
  112. xa_unlock(&mt_tree);
  113. p = page_alloc(1);
  114. xa_lock(&mt_tree);
  115. radix_tree_insert(&mt_tree, 1, p);
  116. xa_unlock(&mt_tree);
  117. xa_lock(&mt_tree);
  118. p = radix_tree_delete(&mt_tree, 1);
  119. pthread_mutex_lock(&p->lock);
  120. p->count--;
  121. pthread_mutex_unlock(&p->lock);
  122. xa_unlock(&mt_tree);
  123. page_free(p);
  124. xa_lock(&mt_tree);
  125. p = radix_tree_delete(&mt_tree, 0);
  126. pthread_mutex_lock(&p->lock);
  127. p->count--;
  128. pthread_mutex_unlock(&p->lock);
  129. xa_unlock(&mt_tree);
  130. page_free(p);
  131. }
  132. } else {
  133. int j;
  134. for (j = 0; j < 100000000; j++) {
  135. struct page *pages[10];
  136. find_get_pages(0, 10, pages);
  137. }
  138. }
  139. rcu_unregister_thread();
  140. return NULL;
  141. }
  142. static pthread_t *threads;
  143. void regression1_test(void)
  144. {
  145. int nr_threads;
  146. int i;
  147. long arg;
  148. /* Regression #1 */
  149. printv(1, "running regression test 1, should finish in under a minute\n");
  150. nr_threads = 2;
  151. pthread_barrier_init(&worker_barrier, NULL, nr_threads);
  152. threads = malloc(nr_threads * sizeof(*threads));
  153. for (i = 0; i < nr_threads; i++) {
  154. arg = i;
  155. if (pthread_create(&threads[i], NULL, regression1_fn, (void *)arg)) {
  156. perror("pthread_create");
  157. exit(1);
  158. }
  159. }
  160. for (i = 0; i < nr_threads; i++) {
  161. if (pthread_join(threads[i], NULL)) {
  162. perror("pthread_join");
  163. exit(1);
  164. }
  165. }
  166. free(threads);
  167. printv(1, "regression test 1, done\n");
  168. }