test_rhashtable.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * Resizable, Scalable, Concurrent Hash Table
  3. *
  4. * Copyright (c) 2014 Thomas Graf <tgraf@suug.ch>
  5. * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
  6. *
  7. * Based on the following paper:
  8. * https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf
  9. *
  10. * Code partially derived from nft_hash
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License version 2 as
  14. * published by the Free Software Foundation.
  15. */
  16. /**************************************************************************
  17. * Self Test
  18. **************************************************************************/
  19. #include <linux/init.h>
  20. #include <linux/jhash.h>
  21. #include <linux/kernel.h>
  22. #include <linux/module.h>
  23. #include <linux/rcupdate.h>
  24. #include <linux/rhashtable.h>
  25. #include <linux/slab.h>
  26. #define TEST_HT_SIZE 8
  27. #define TEST_ENTRIES 2048
  28. #define TEST_PTR ((void *) 0xdeadbeef)
  29. #define TEST_NEXPANDS 4
  30. struct test_obj {
  31. void *ptr;
  32. int value;
  33. struct rhash_head node;
  34. };
  35. static int __init test_rht_lookup(struct rhashtable *ht)
  36. {
  37. unsigned int i;
  38. for (i = 0; i < TEST_ENTRIES * 2; i++) {
  39. struct test_obj *obj;
  40. bool expected = !(i % 2);
  41. u32 key = i;
  42. obj = rhashtable_lookup(ht, &key);
  43. if (expected && !obj) {
  44. pr_warn("Test failed: Could not find key %u\n", key);
  45. return -ENOENT;
  46. } else if (!expected && obj) {
  47. pr_warn("Test failed: Unexpected entry found for key %u\n",
  48. key);
  49. return -EEXIST;
  50. } else if (expected && obj) {
  51. if (obj->ptr != TEST_PTR || obj->value != i) {
  52. pr_warn("Test failed: Lookup value mismatch %p!=%p, %u!=%u\n",
  53. obj->ptr, TEST_PTR, obj->value, i);
  54. return -EINVAL;
  55. }
  56. }
  57. }
  58. return 0;
  59. }
  60. static void test_bucket_stats(struct rhashtable *ht, bool quiet)
  61. {
  62. unsigned int cnt, rcu_cnt, i, total = 0;
  63. struct rhash_head *pos;
  64. struct test_obj *obj;
  65. struct bucket_table *tbl;
  66. tbl = rht_dereference_rcu(ht->tbl, ht);
  67. for (i = 0; i < tbl->size; i++) {
  68. rcu_cnt = cnt = 0;
  69. if (!quiet)
  70. pr_info(" [%#4x/%zu]", i, tbl->size);
  71. rht_for_each_entry_rcu(obj, pos, tbl, i, node) {
  72. cnt++;
  73. total++;
  74. if (!quiet)
  75. pr_cont(" [%p],", obj);
  76. }
  77. rht_for_each_entry_rcu(obj, pos, tbl, i, node)
  78. rcu_cnt++;
  79. if (rcu_cnt != cnt)
  80. pr_warn("Test failed: Chain count mismach %d != %d",
  81. cnt, rcu_cnt);
  82. if (!quiet)
  83. pr_cont("\n [%#x] first element: %p, chain length: %u\n",
  84. i, tbl->buckets[i], cnt);
  85. }
  86. pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d\n",
  87. total, atomic_read(&ht->nelems), TEST_ENTRIES);
  88. if (total != atomic_read(&ht->nelems) || total != TEST_ENTRIES)
  89. pr_warn("Test failed: Total count mismatch ^^^");
  90. }
  91. static int __init test_rhashtable(struct rhashtable *ht)
  92. {
  93. struct bucket_table *tbl;
  94. struct test_obj *obj;
  95. struct rhash_head *pos, *next;
  96. int err;
  97. unsigned int i;
  98. /*
  99. * Insertion Test:
  100. * Insert TEST_ENTRIES into table with all keys even numbers
  101. */
  102. pr_info(" Adding %d keys\n", TEST_ENTRIES);
  103. for (i = 0; i < TEST_ENTRIES; i++) {
  104. struct test_obj *obj;
  105. obj = kzalloc(sizeof(*obj), GFP_KERNEL);
  106. if (!obj) {
  107. err = -ENOMEM;
  108. goto error;
  109. }
  110. obj->ptr = TEST_PTR;
  111. obj->value = i * 2;
  112. rhashtable_insert(ht, &obj->node);
  113. }
  114. rcu_read_lock();
  115. test_bucket_stats(ht, true);
  116. test_rht_lookup(ht);
  117. rcu_read_unlock();
  118. for (i = 0; i < TEST_NEXPANDS; i++) {
  119. pr_info(" Table expansion iteration %u...\n", i);
  120. mutex_lock(&ht->mutex);
  121. rhashtable_expand(ht);
  122. mutex_unlock(&ht->mutex);
  123. rcu_read_lock();
  124. pr_info(" Verifying lookups...\n");
  125. test_rht_lookup(ht);
  126. rcu_read_unlock();
  127. }
  128. for (i = 0; i < TEST_NEXPANDS; i++) {
  129. pr_info(" Table shrinkage iteration %u...\n", i);
  130. mutex_lock(&ht->mutex);
  131. rhashtable_shrink(ht);
  132. mutex_unlock(&ht->mutex);
  133. rcu_read_lock();
  134. pr_info(" Verifying lookups...\n");
  135. test_rht_lookup(ht);
  136. rcu_read_unlock();
  137. }
  138. rcu_read_lock();
  139. test_bucket_stats(ht, true);
  140. rcu_read_unlock();
  141. pr_info(" Deleting %d keys\n", TEST_ENTRIES);
  142. for (i = 0; i < TEST_ENTRIES; i++) {
  143. u32 key = i * 2;
  144. obj = rhashtable_lookup(ht, &key);
  145. BUG_ON(!obj);
  146. rhashtable_remove(ht, &obj->node);
  147. kfree(obj);
  148. }
  149. return 0;
  150. error:
  151. tbl = rht_dereference_rcu(ht->tbl, ht);
  152. for (i = 0; i < tbl->size; i++)
  153. rht_for_each_entry_safe(obj, pos, next, tbl, i, node)
  154. kfree(obj);
  155. return err;
  156. }
  157. static struct rhashtable ht;
  158. static int __init test_rht_init(void)
  159. {
  160. struct rhashtable_params params = {
  161. .nelem_hint = TEST_HT_SIZE,
  162. .head_offset = offsetof(struct test_obj, node),
  163. .key_offset = offsetof(struct test_obj, value),
  164. .key_len = sizeof(int),
  165. .hashfn = jhash,
  166. .nulls_base = (3U << RHT_BASE_SHIFT),
  167. };
  168. int err;
  169. pr_info("Running resizable hashtable tests...\n");
  170. err = rhashtable_init(&ht, &params);
  171. if (err < 0) {
  172. pr_warn("Test failed: Unable to initialize hashtable: %d\n",
  173. err);
  174. return err;
  175. }
  176. err = test_rhashtable(&ht);
  177. rhashtable_destroy(&ht);
  178. return err;
  179. }
  180. static void __exit test_rht_exit(void)
  181. {
  182. }
  183. module_init(test_rht_init);
  184. module_exit(test_rht_exit);
  185. MODULE_LICENSE("GPL v2");