regression2.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /*
  2. * Regression2
  3. * Description:
  4. * Toshiyuki Okajima describes the following radix-tree bug:
  5. *
  6. * In the following case, we can get a hangup on
  7. * radix_radix_tree_gang_lookup_tag_slot.
  8. *
  9. * 0. The radix tree contains RADIX_TREE_MAP_SIZE items. And the tag of
  10. * a certain item has PAGECACHE_TAG_DIRTY.
  11. * 1. radix_tree_range_tag_if_tagged(, start, end, , PAGECACHE_TAG_DIRTY,
  12. * PAGECACHE_TAG_TOWRITE) is called to add PAGECACHE_TAG_TOWRITE tag
  13. * for the tag which has PAGECACHE_TAG_DIRTY. However, there is no tag with
  14. * PAGECACHE_TAG_DIRTY within the range from start to end. As the result,
  15. * There is no tag with PAGECACHE_TAG_TOWRITE but the root tag has
  16. * PAGECACHE_TAG_TOWRITE.
  17. * 2. An item is added into the radix tree and then the level of it is
  18. * extended into 2 from 1. At that time, the new radix tree node succeeds
  19. * the tag status of the root tag. Therefore the tag of the new radix tree
  20. * node has PAGECACHE_TAG_TOWRITE but there is not slot with
  21. * PAGECACHE_TAG_TOWRITE tag in the child node of the new radix tree node.
  22. * 3. The tag of a certain item is cleared with PAGECACHE_TAG_DIRTY.
  23. * 4. All items within the index range from 0 to RADIX_TREE_MAP_SIZE - 1 are
  24. * released. (Only the item which index is RADIX_TREE_MAP_SIZE exist in the
  25. * radix tree.) As the result, the slot of the radix tree node is NULL but
  26. * the tag which corresponds to the slot has PAGECACHE_TAG_TOWRITE.
  27. * 5. radix_tree_gang_lookup_tag_slot(PAGECACHE_TAG_TOWRITE) calls
  28. * __lookup_tag. __lookup_tag returns with 0. And __lookup_tag doesn't
  29. * change the index that is the input and output parameter. Because the 1st
  30. * slot of the radix tree node is NULL, but the tag which corresponds to
  31. * the slot has PAGECACHE_TAG_TOWRITE.
  32. * Therefore radix_tree_gang_lookup_tag_slot tries to get some items by
  33. * calling __lookup_tag, but it cannot get any items forever.
  34. *
  35. * The fix is to change that radix_tree_tag_if_tagged doesn't tag the root tag
  36. * if it doesn't set any tags within the specified range.
  37. *
  38. * Running:
  39. * This test should run to completion immediately. The above bug would cause it
  40. * to hang indefinitely.
  41. *
  42. * Upstream commit:
  43. * Not yet
  44. */
  45. #include <linux/kernel.h>
  46. #include <linux/gfp.h>
  47. #include <linux/slab.h>
  48. #include <linux/radix-tree.h>
  49. #include <stdlib.h>
  50. #include <stdio.h>
  51. #include "regression.h"
  52. #ifdef __KERNEL__
  53. #define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6)
  54. #else
  55. #define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */
  56. #endif
  57. #define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT)
  58. #define PAGECACHE_TAG_DIRTY 0
  59. #define PAGECACHE_TAG_WRITEBACK 1
  60. #define PAGECACHE_TAG_TOWRITE 2
  61. static RADIX_TREE(mt_tree, GFP_KERNEL);
  62. unsigned long page_count = 0;
  63. struct page {
  64. unsigned long index;
  65. };
  66. static struct page *page_alloc(void)
  67. {
  68. struct page *p;
  69. p = malloc(sizeof(struct page));
  70. p->index = page_count++;
  71. return p;
  72. }
  73. void regression2_test(void)
  74. {
  75. int i;
  76. struct page *p;
  77. int max_slots = RADIX_TREE_MAP_SIZE;
  78. unsigned long int start, end;
  79. struct page *pages[1];
  80. printf("running regression test 2 (should take milliseconds)\n");
  81. /* 0. */
  82. for (i = 0; i <= max_slots - 1; i++) {
  83. p = page_alloc();
  84. radix_tree_insert(&mt_tree, i, p);
  85. }
  86. radix_tree_tag_set(&mt_tree, max_slots - 1, PAGECACHE_TAG_DIRTY);
  87. /* 1. */
  88. start = 0;
  89. end = max_slots - 2;
  90. radix_tree_range_tag_if_tagged(&mt_tree, &start, end, 1,
  91. PAGECACHE_TAG_DIRTY, PAGECACHE_TAG_TOWRITE);
  92. /* 2. */
  93. p = page_alloc();
  94. radix_tree_insert(&mt_tree, max_slots, p);
  95. /* 3. */
  96. radix_tree_tag_clear(&mt_tree, max_slots - 1, PAGECACHE_TAG_DIRTY);
  97. /* 4. */
  98. for (i = max_slots - 1; i >= 0; i--)
  99. radix_tree_delete(&mt_tree, i);
  100. /* 5. */
  101. // NOTE: start should not be 0 because radix_tree_gang_lookup_tag_slot
  102. // can return.
  103. start = 1;
  104. end = max_slots - 2;
  105. radix_tree_gang_lookup_tag_slot(&mt_tree, (void ***)pages, start, end,
  106. PAGECACHE_TAG_TOWRITE);
  107. /* We remove all the remained nodes */
  108. radix_tree_delete(&mt_tree, max_slots);
  109. printf("regression test 2, done\n");
  110. }