iteration_check.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  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. #define NUM_THREADS 5
  19. #define MAX_IDX 100
  20. #define TAG 0
  21. #define NEW_TAG 1
  22. static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER;
  23. static pthread_t threads[NUM_THREADS];
  24. static unsigned int seeds[3];
  25. static RADIX_TREE(tree, GFP_KERNEL);
  26. static bool test_complete;
  27. static int max_order;
  28. /* relentlessly fill the tree with tagged entries */
  29. static void *add_entries_fn(void *arg)
  30. {
  31. rcu_register_thread();
  32. while (!test_complete) {
  33. unsigned long pgoff;
  34. int order;
  35. for (pgoff = 0; pgoff < MAX_IDX; pgoff++) {
  36. pthread_mutex_lock(&tree_lock);
  37. for (order = max_order; order >= 0; order--) {
  38. if (item_insert_order(&tree, pgoff, order)
  39. == 0) {
  40. item_tag_set(&tree, pgoff, TAG);
  41. break;
  42. }
  43. }
  44. pthread_mutex_unlock(&tree_lock);
  45. }
  46. }
  47. rcu_unregister_thread();
  48. return NULL;
  49. }
  50. /*
  51. * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find
  52. * things that have been removed and randomly resetting our iteration to the
  53. * next chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and
  54. * radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a
  55. * NULL 'slot' variable.
  56. */
  57. static void *tagged_iteration_fn(void *arg)
  58. {
  59. struct radix_tree_iter iter;
  60. void **slot;
  61. rcu_register_thread();
  62. while (!test_complete) {
  63. rcu_read_lock();
  64. radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) {
  65. void *entry = radix_tree_deref_slot(slot);
  66. if (unlikely(!entry))
  67. continue;
  68. if (radix_tree_deref_retry(entry)) {
  69. slot = radix_tree_iter_retry(&iter);
  70. continue;
  71. }
  72. if (rand_r(&seeds[0]) % 50 == 0) {
  73. slot = radix_tree_iter_resume(slot, &iter);
  74. rcu_read_unlock();
  75. rcu_barrier();
  76. rcu_read_lock();
  77. }
  78. }
  79. rcu_read_unlock();
  80. }
  81. rcu_unregister_thread();
  82. return NULL;
  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_resume(). Both radix_tree_iter_retry() and
  88. * radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a
  89. * NULL 'slot' variable.
  90. */
  91. static void *untagged_iteration_fn(void *arg)
  92. {
  93. struct radix_tree_iter iter;
  94. void **slot;
  95. rcu_register_thread();
  96. while (!test_complete) {
  97. rcu_read_lock();
  98. radix_tree_for_each_slot(slot, &tree, &iter, 0) {
  99. void *entry = radix_tree_deref_slot(slot);
  100. if (unlikely(!entry))
  101. continue;
  102. if (radix_tree_deref_retry(entry)) {
  103. slot = radix_tree_iter_retry(&iter);
  104. continue;
  105. }
  106. if (rand_r(&seeds[1]) % 50 == 0) {
  107. slot = radix_tree_iter_resume(slot, &iter);
  108. rcu_read_unlock();
  109. rcu_barrier();
  110. rcu_read_lock();
  111. }
  112. }
  113. rcu_read_unlock();
  114. }
  115. rcu_unregister_thread();
  116. return NULL;
  117. }
  118. /*
  119. * Randomly remove entries to help induce radix_tree_iter_retry() calls in the
  120. * two iteration functions.
  121. */
  122. static void *remove_entries_fn(void *arg)
  123. {
  124. rcu_register_thread();
  125. while (!test_complete) {
  126. int pgoff;
  127. pgoff = rand_r(&seeds[2]) % MAX_IDX;
  128. pthread_mutex_lock(&tree_lock);
  129. item_delete(&tree, pgoff);
  130. pthread_mutex_unlock(&tree_lock);
  131. }
  132. rcu_unregister_thread();
  133. return NULL;
  134. }
  135. static void *tag_entries_fn(void *arg)
  136. {
  137. rcu_register_thread();
  138. while (!test_complete) {
  139. tag_tagged_items(&tree, &tree_lock, 0, MAX_IDX, 10, TAG,
  140. NEW_TAG);
  141. }
  142. rcu_unregister_thread();
  143. return NULL;
  144. }
  145. /* This is a unit test for a bug found by the syzkaller tester */
  146. void iteration_test(unsigned order, unsigned test_duration)
  147. {
  148. int i;
  149. printv(1, "Running %siteration tests for %d seconds\n",
  150. order > 0 ? "multiorder " : "", test_duration);
  151. max_order = order;
  152. test_complete = false;
  153. for (i = 0; i < 3; i++)
  154. seeds[i] = rand();
  155. if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) {
  156. perror("create tagged iteration thread");
  157. exit(1);
  158. }
  159. if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) {
  160. perror("create untagged iteration thread");
  161. exit(1);
  162. }
  163. if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) {
  164. perror("create add entry thread");
  165. exit(1);
  166. }
  167. if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) {
  168. perror("create remove entry thread");
  169. exit(1);
  170. }
  171. if (pthread_create(&threads[4], NULL, tag_entries_fn, NULL)) {
  172. perror("create tag entry thread");
  173. exit(1);
  174. }
  175. sleep(test_duration);
  176. test_complete = true;
  177. for (i = 0; i < NUM_THREADS; i++) {
  178. if (pthread_join(threads[i], NULL)) {
  179. perror("pthread_join");
  180. exit(1);
  181. }
  182. }
  183. item_kill_tree(&tree);
  184. }