regression2.c 3.9 KB

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