iteration_check.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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 4
  19. #define TAG 0
  20. static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER;
  21. static pthread_t threads[NUM_THREADS];
  22. RADIX_TREE(tree, GFP_KERNEL);
  23. bool test_complete;
  24. /* relentlessly fill the tree with tagged entries */
  25. static void *add_entries_fn(void *arg)
  26. {
  27. int pgoff;
  28. while (!test_complete) {
  29. for (pgoff = 0; pgoff < 100; pgoff++) {
  30. pthread_mutex_lock(&tree_lock);
  31. if (item_insert(&tree, pgoff) == 0)
  32. item_tag_set(&tree, pgoff, TAG);
  33. pthread_mutex_unlock(&tree_lock);
  34. }
  35. }
  36. return NULL;
  37. }
  38. /*
  39. * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find
  40. * things that have been removed and randomly resetting our iteration to the
  41. * next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and
  42. * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
  43. * NULL 'slot' variable.
  44. */
  45. static void *tagged_iteration_fn(void *arg)
  46. {
  47. struct radix_tree_iter iter;
  48. void **slot;
  49. while (!test_complete) {
  50. rcu_read_lock();
  51. radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) {
  52. void *entry;
  53. int i;
  54. /* busy wait to let removals happen */
  55. for (i = 0; i < 1000000; i++)
  56. ;
  57. entry = radix_tree_deref_slot(slot);
  58. if (unlikely(!entry))
  59. continue;
  60. if (radix_tree_deref_retry(entry)) {
  61. slot = radix_tree_iter_retry(&iter);
  62. continue;
  63. }
  64. if (rand() % 50 == 0)
  65. slot = radix_tree_iter_next(&iter);
  66. }
  67. rcu_read_unlock();
  68. }
  69. return NULL;
  70. }
  71. /*
  72. * Iterate over the entries, doing a radix_tree_iter_retry() as we find things
  73. * that have been removed and randomly resetting our iteration to the next
  74. * chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and
  75. * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
  76. * NULL 'slot' variable.
  77. */
  78. static void *untagged_iteration_fn(void *arg)
  79. {
  80. struct radix_tree_iter iter;
  81. void **slot;
  82. while (!test_complete) {
  83. rcu_read_lock();
  84. radix_tree_for_each_slot(slot, &tree, &iter, 0) {
  85. void *entry;
  86. int i;
  87. /* busy wait to let removals happen */
  88. for (i = 0; i < 1000000; i++)
  89. ;
  90. entry = radix_tree_deref_slot(slot);
  91. if (unlikely(!entry))
  92. continue;
  93. if (radix_tree_deref_retry(entry)) {
  94. slot = radix_tree_iter_retry(&iter);
  95. continue;
  96. }
  97. if (rand() % 50 == 0)
  98. slot = radix_tree_iter_next(&iter);
  99. }
  100. rcu_read_unlock();
  101. }
  102. return NULL;
  103. }
  104. /*
  105. * Randomly remove entries to help induce radix_tree_iter_retry() calls in the
  106. * two iteration functions.
  107. */
  108. static void *remove_entries_fn(void *arg)
  109. {
  110. while (!test_complete) {
  111. int pgoff;
  112. pgoff = rand() % 100;
  113. pthread_mutex_lock(&tree_lock);
  114. item_delete(&tree, pgoff);
  115. pthread_mutex_unlock(&tree_lock);
  116. }
  117. return NULL;
  118. }
  119. /* This is a unit test for a bug found by the syzkaller tester */
  120. void iteration_test(void)
  121. {
  122. int i;
  123. printf("Running iteration tests for 10 seconds\n");
  124. srand(time(0));
  125. test_complete = false;
  126. if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) {
  127. perror("pthread_create");
  128. exit(1);
  129. }
  130. if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) {
  131. perror("pthread_create");
  132. exit(1);
  133. }
  134. if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) {
  135. perror("pthread_create");
  136. exit(1);
  137. }
  138. if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) {
  139. perror("pthread_create");
  140. exit(1);
  141. }
  142. sleep(10);
  143. test_complete = true;
  144. for (i = 0; i < NUM_THREADS; i++) {
  145. if (pthread_join(threads[i], NULL)) {
  146. perror("pthread_join");
  147. exit(1);
  148. }
  149. }
  150. item_kill_tree(&tree);
  151. }