test_rhashtable.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 const struct rhashtable_params test_rht_params = {
  36. .nelem_hint = TEST_HT_SIZE,
  37. .head_offset = offsetof(struct test_obj, node),
  38. .key_offset = offsetof(struct test_obj, value),
  39. .key_len = sizeof(int),
  40. .hashfn = jhash,
  41. .max_size = 2, /* we expand/shrink manually here */
  42. .nulls_base = (3U << RHT_BASE_SHIFT),
  43. };
  44. static int __init test_rht_lookup(struct rhashtable *ht)
  45. {
  46. unsigned int i;
  47. for (i = 0; i < TEST_ENTRIES * 2; i++) {
  48. struct test_obj *obj;
  49. bool expected = !(i % 2);
  50. u32 key = i;
  51. obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
  52. if (expected && !obj) {
  53. pr_warn("Test failed: Could not find key %u\n", key);
  54. return -ENOENT;
  55. } else if (!expected && obj) {
  56. pr_warn("Test failed: Unexpected entry found for key %u\n",
  57. key);
  58. return -EEXIST;
  59. } else if (expected && obj) {
  60. if (obj->ptr != TEST_PTR || obj->value != i) {
  61. pr_warn("Test failed: Lookup value mismatch %p!=%p, %u!=%u\n",
  62. obj->ptr, TEST_PTR, obj->value, i);
  63. return -EINVAL;
  64. }
  65. }
  66. }
  67. return 0;
  68. }
  69. static void test_bucket_stats(struct rhashtable *ht, bool quiet)
  70. {
  71. unsigned int cnt, rcu_cnt, i, total = 0;
  72. struct rhash_head *pos;
  73. struct test_obj *obj;
  74. struct bucket_table *tbl;
  75. tbl = rht_dereference_rcu(ht->tbl, ht);
  76. for (i = 0; i < tbl->size; i++) {
  77. rcu_cnt = cnt = 0;
  78. if (!quiet)
  79. pr_info(" [%#4x/%u]", i, tbl->size);
  80. rht_for_each_entry_rcu(obj, pos, tbl, i, node) {
  81. cnt++;
  82. total++;
  83. if (!quiet)
  84. pr_cont(" [%p],", obj);
  85. }
  86. rht_for_each_entry_rcu(obj, pos, tbl, i, node)
  87. rcu_cnt++;
  88. if (rcu_cnt != cnt)
  89. pr_warn("Test failed: Chain count mismach %d != %d",
  90. cnt, rcu_cnt);
  91. if (!quiet)
  92. pr_cont("\n [%#x] first element: %p, chain length: %u\n",
  93. i, tbl->buckets[i], cnt);
  94. }
  95. pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d\n",
  96. total, atomic_read(&ht->nelems), TEST_ENTRIES);
  97. if (total != atomic_read(&ht->nelems) || total != TEST_ENTRIES)
  98. pr_warn("Test failed: Total count mismatch ^^^");
  99. }
  100. static int __init test_rhashtable(struct rhashtable *ht)
  101. {
  102. struct bucket_table *tbl;
  103. struct test_obj *obj;
  104. struct rhash_head *pos, *next;
  105. int err;
  106. unsigned int i;
  107. /*
  108. * Insertion Test:
  109. * Insert TEST_ENTRIES into table with all keys even numbers
  110. */
  111. pr_info(" Adding %d keys\n", TEST_ENTRIES);
  112. for (i = 0; i < TEST_ENTRIES; i++) {
  113. struct test_obj *obj;
  114. obj = kzalloc(sizeof(*obj), GFP_KERNEL);
  115. if (!obj) {
  116. err = -ENOMEM;
  117. goto error;
  118. }
  119. obj->ptr = TEST_PTR;
  120. obj->value = i * 2;
  121. err = rhashtable_insert_fast(ht, &obj->node, test_rht_params);
  122. if (err) {
  123. kfree(obj);
  124. goto error;
  125. }
  126. }
  127. rcu_read_lock();
  128. test_bucket_stats(ht, true);
  129. test_rht_lookup(ht);
  130. rcu_read_unlock();
  131. rcu_read_lock();
  132. test_bucket_stats(ht, true);
  133. rcu_read_unlock();
  134. pr_info(" Deleting %d keys\n", TEST_ENTRIES);
  135. for (i = 0; i < TEST_ENTRIES; i++) {
  136. u32 key = i * 2;
  137. obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
  138. BUG_ON(!obj);
  139. rhashtable_remove_fast(ht, &obj->node, test_rht_params);
  140. kfree(obj);
  141. }
  142. return 0;
  143. error:
  144. tbl = rht_dereference_rcu(ht->tbl, ht);
  145. for (i = 0; i < tbl->size; i++)
  146. rht_for_each_entry_safe(obj, pos, next, tbl, i, node)
  147. kfree(obj);
  148. return err;
  149. }
  150. static struct rhashtable ht;
  151. static int __init test_rht_init(void)
  152. {
  153. int err;
  154. pr_info("Running resizable hashtable tests...\n");
  155. err = rhashtable_init(&ht, &test_rht_params);
  156. if (err < 0) {
  157. pr_warn("Test failed: Unable to initialize hashtable: %d\n",
  158. err);
  159. return err;
  160. }
  161. err = test_rhashtable(&ht);
  162. rhashtable_destroy(&ht);
  163. return err;
  164. }
  165. static void __exit test_rht_exit(void)
  166. {
  167. }
  168. module_init(test_rht_init);
  169. module_exit(test_rht_exit);
  170. MODULE_LICENSE("GPL v2");