test_rhashtable.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Resizable, Scalable, Concurrent Hash Table
  4. *
  5. * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
  6. * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
  7. */
  8. /**************************************************************************
  9. * Self Test
  10. **************************************************************************/
  11. #include <linux/init.h>
  12. #include <linux/jhash.h>
  13. #include <linux/kernel.h>
  14. #include <linux/kthread.h>
  15. #include <linux/module.h>
  16. #include <linux/rcupdate.h>
  17. #include <linux/rcupdate_wait.h>
  18. #include <linux/rhashtable.h>
  19. #include <linux/slab.h>
  20. #include <linux/sched.h>
  21. #include <linux/random.h>
  22. #include <linux/vmalloc.h>
  23. #include <linux/wait.h>
  24. #define MAX_ENTRIES 1000000
  25. #define TEST_INSERT_FAIL INT_MAX
  26. static int parm_entries = 50000;
  27. module_param(parm_entries, int, 0);
  28. MODULE_PARM_DESC(parm_entries, "Number of entries to add (default: 50000)");
  29. static int runs = 4;
  30. module_param(runs, int, 0);
  31. MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
  32. static int max_size = 0;
  33. module_param(max_size, int, 0);
  34. MODULE_PARM_DESC(max_size, "Maximum table size (default: calculated)");
  35. static bool shrinking = false;
  36. module_param(shrinking, bool, 0);
  37. MODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)");
  38. static int size = 8;
  39. module_param(size, int, 0);
  40. MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
  41. static int tcount = 10;
  42. module_param(tcount, int, 0);
  43. MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
  44. static bool enomem_retry = false;
  45. module_param(enomem_retry, bool, 0);
  46. MODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
  47. struct test_obj_val {
  48. int id;
  49. int tid;
  50. };
  51. struct test_obj {
  52. struct test_obj_val value;
  53. struct rhash_head node;
  54. };
  55. struct test_obj_rhl {
  56. struct test_obj_val value;
  57. struct rhlist_head list_node;
  58. };
  59. struct thread_data {
  60. unsigned int entries;
  61. int id;
  62. struct task_struct *task;
  63. struct test_obj *objs;
  64. };
  65. static u32 my_hashfn(const void *data, u32 len, u32 seed)
  66. {
  67. const struct test_obj_rhl *obj = data;
  68. return (obj->value.id % 10);
  69. }
  70. static int my_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
  71. {
  72. const struct test_obj_rhl *test_obj = obj;
  73. const struct test_obj_val *val = arg->key;
  74. return test_obj->value.id - val->id;
  75. }
  76. static struct rhashtable_params test_rht_params = {
  77. .head_offset = offsetof(struct test_obj, node),
  78. .key_offset = offsetof(struct test_obj, value),
  79. .key_len = sizeof(struct test_obj_val),
  80. .hashfn = jhash,
  81. };
  82. static struct rhashtable_params test_rht_params_dup = {
  83. .head_offset = offsetof(struct test_obj_rhl, list_node),
  84. .key_offset = offsetof(struct test_obj_rhl, value),
  85. .key_len = sizeof(struct test_obj_val),
  86. .hashfn = jhash,
  87. .obj_hashfn = my_hashfn,
  88. .obj_cmpfn = my_cmpfn,
  89. .nelem_hint = 128,
  90. .automatic_shrinking = false,
  91. };
  92. static atomic_t startup_count;
  93. static DECLARE_WAIT_QUEUE_HEAD(startup_wait);
  94. static int insert_retry(struct rhashtable *ht, struct test_obj *obj,
  95. const struct rhashtable_params params)
  96. {
  97. int err, retries = -1, enomem_retries = 0;
  98. do {
  99. retries++;
  100. cond_resched();
  101. err = rhashtable_insert_fast(ht, &obj->node, params);
  102. if (err == -ENOMEM && enomem_retry) {
  103. enomem_retries++;
  104. err = -EBUSY;
  105. }
  106. } while (err == -EBUSY);
  107. if (enomem_retries)
  108. pr_info(" %u insertions retried after -ENOMEM\n",
  109. enomem_retries);
  110. return err ? : retries;
  111. }
  112. static int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array,
  113. unsigned int entries)
  114. {
  115. unsigned int i;
  116. for (i = 0; i < entries; i++) {
  117. struct test_obj *obj;
  118. bool expected = !(i % 2);
  119. struct test_obj_val key = {
  120. .id = i,
  121. };
  122. if (array[i / 2].value.id == TEST_INSERT_FAIL)
  123. expected = false;
  124. obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
  125. if (expected && !obj) {
  126. pr_warn("Test failed: Could not find key %u\n", key.id);
  127. return -ENOENT;
  128. } else if (!expected && obj) {
  129. pr_warn("Test failed: Unexpected entry found for key %u\n",
  130. key.id);
  131. return -EEXIST;
  132. } else if (expected && obj) {
  133. if (obj->value.id != i) {
  134. pr_warn("Test failed: Lookup value mismatch %u!=%u\n",
  135. obj->value.id, i);
  136. return -EINVAL;
  137. }
  138. }
  139. cond_resched_rcu();
  140. }
  141. return 0;
  142. }
  143. static void test_bucket_stats(struct rhashtable *ht, unsigned int entries)
  144. {
  145. unsigned int total = 0, chain_len = 0;
  146. struct rhashtable_iter hti;
  147. struct rhash_head *pos;
  148. rhashtable_walk_enter(ht, &hti);
  149. rhashtable_walk_start(&hti);
  150. while ((pos = rhashtable_walk_next(&hti))) {
  151. if (PTR_ERR(pos) == -EAGAIN) {
  152. pr_info("Info: encountered resize\n");
  153. chain_len++;
  154. continue;
  155. } else if (IS_ERR(pos)) {
  156. pr_warn("Test failed: rhashtable_walk_next() error: %ld\n",
  157. PTR_ERR(pos));
  158. break;
  159. }
  160. total++;
  161. }
  162. rhashtable_walk_stop(&hti);
  163. rhashtable_walk_exit(&hti);
  164. pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u\n",
  165. total, atomic_read(&ht->nelems), entries, chain_len);
  166. if (total != atomic_read(&ht->nelems) || total != entries)
  167. pr_warn("Test failed: Total count mismatch ^^^");
  168. }
  169. static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,
  170. unsigned int entries)
  171. {
  172. struct test_obj *obj;
  173. int err;
  174. unsigned int i, insert_retries = 0;
  175. s64 start, end;
  176. /*
  177. * Insertion Test:
  178. * Insert entries into table with all keys even numbers
  179. */
  180. pr_info(" Adding %d keys\n", entries);
  181. start = ktime_get_ns();
  182. for (i = 0; i < entries; i++) {
  183. struct test_obj *obj = &array[i];
  184. obj->value.id = i * 2;
  185. err = insert_retry(ht, obj, test_rht_params);
  186. if (err > 0)
  187. insert_retries += err;
  188. else if (err)
  189. return err;
  190. }
  191. if (insert_retries)
  192. pr_info(" %u insertions retried due to memory pressure\n",
  193. insert_retries);
  194. test_bucket_stats(ht, entries);
  195. rcu_read_lock();
  196. test_rht_lookup(ht, array, entries);
  197. rcu_read_unlock();
  198. test_bucket_stats(ht, entries);
  199. pr_info(" Deleting %d keys\n", entries);
  200. for (i = 0; i < entries; i++) {
  201. struct test_obj_val key = {
  202. .id = i * 2,
  203. };
  204. if (array[i].value.id != TEST_INSERT_FAIL) {
  205. obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
  206. BUG_ON(!obj);
  207. rhashtable_remove_fast(ht, &obj->node, test_rht_params);
  208. }
  209. cond_resched();
  210. }
  211. end = ktime_get_ns();
  212. pr_info(" Duration of test: %lld ns\n", end - start);
  213. return end - start;
  214. }
  215. static struct rhashtable ht;
  216. static struct rhltable rhlt;
  217. static int __init test_rhltable(unsigned int entries)
  218. {
  219. struct test_obj_rhl *rhl_test_objects;
  220. unsigned long *obj_in_table;
  221. unsigned int i, j, k;
  222. int ret, err;
  223. if (entries == 0)
  224. entries = 1;
  225. rhl_test_objects = vzalloc(array_size(entries,
  226. sizeof(*rhl_test_objects)));
  227. if (!rhl_test_objects)
  228. return -ENOMEM;
  229. ret = -ENOMEM;
  230. obj_in_table = vzalloc(array_size(sizeof(unsigned long),
  231. BITS_TO_LONGS(entries)));
  232. if (!obj_in_table)
  233. goto out_free;
  234. err = rhltable_init(&rhlt, &test_rht_params);
  235. if (WARN_ON(err))
  236. goto out_free;
  237. k = get_random_u32();
  238. ret = 0;
  239. for (i = 0; i < entries; i++) {
  240. rhl_test_objects[i].value.id = k;
  241. err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
  242. test_rht_params);
  243. if (WARN(err, "error %d on element %d\n", err, i))
  244. break;
  245. if (err == 0)
  246. set_bit(i, obj_in_table);
  247. }
  248. if (err)
  249. ret = err;
  250. pr_info("test %d add/delete pairs into rhlist\n", entries);
  251. for (i = 0; i < entries; i++) {
  252. struct rhlist_head *h, *pos;
  253. struct test_obj_rhl *obj;
  254. struct test_obj_val key = {
  255. .id = k,
  256. };
  257. bool found;
  258. rcu_read_lock();
  259. h = rhltable_lookup(&rhlt, &key, test_rht_params);
  260. if (WARN(!h, "key not found during iteration %d of %d", i, entries)) {
  261. rcu_read_unlock();
  262. break;
  263. }
  264. if (i) {
  265. j = i - 1;
  266. rhl_for_each_entry_rcu(obj, pos, h, list_node) {
  267. if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone"))
  268. break;
  269. }
  270. }
  271. cond_resched_rcu();
  272. found = false;
  273. rhl_for_each_entry_rcu(obj, pos, h, list_node) {
  274. if (pos == &rhl_test_objects[i].list_node) {
  275. found = true;
  276. break;
  277. }
  278. }
  279. rcu_read_unlock();
  280. if (WARN(!found, "element %d not found", i))
  281. break;
  282. err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  283. WARN(err, "rhltable_remove: err %d for iteration %d\n", err, i);
  284. if (err == 0)
  285. clear_bit(i, obj_in_table);
  286. }
  287. if (ret == 0 && err)
  288. ret = err;
  289. for (i = 0; i < entries; i++) {
  290. WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i);
  291. err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
  292. test_rht_params);
  293. if (WARN(err, "error %d on element %d\n", err, i))
  294. break;
  295. if (err == 0)
  296. set_bit(i, obj_in_table);
  297. }
  298. pr_info("test %d random rhlist add/delete operations\n", entries);
  299. for (j = 0; j < entries; j++) {
  300. u32 i = get_random_u32_below(entries);
  301. u32 prand = get_random_u32_below(4);
  302. cond_resched();
  303. err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  304. if (test_bit(i, obj_in_table)) {
  305. clear_bit(i, obj_in_table);
  306. if (WARN(err, "cannot remove element at slot %d", i))
  307. continue;
  308. } else {
  309. if (WARN(err != -ENOENT, "removed non-existent element %d, error %d not %d",
  310. i, err, -ENOENT))
  311. continue;
  312. }
  313. if (prand & 1) {
  314. err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  315. if (err == 0) {
  316. if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i))
  317. continue;
  318. } else {
  319. if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i))
  320. continue;
  321. }
  322. }
  323. if (prand & 2) {
  324. i = get_random_u32_below(entries);
  325. if (test_bit(i, obj_in_table)) {
  326. err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  327. WARN(err, "cannot remove element at slot %d", i);
  328. if (err == 0)
  329. clear_bit(i, obj_in_table);
  330. } else {
  331. err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  332. WARN(err, "failed to insert object %d", i);
  333. if (err == 0)
  334. set_bit(i, obj_in_table);
  335. }
  336. }
  337. }
  338. for (i = 0; i < entries; i++) {
  339. cond_resched();
  340. err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
  341. if (test_bit(i, obj_in_table)) {
  342. if (WARN(err, "cannot remove element at slot %d", i))
  343. continue;
  344. } else {
  345. if (WARN(err != -ENOENT, "removed non-existent element, error %d not %d",
  346. err, -ENOENT))
  347. continue;
  348. }
  349. }
  350. rhltable_destroy(&rhlt);
  351. out_free:
  352. vfree(rhl_test_objects);
  353. vfree(obj_in_table);
  354. return ret;
  355. }
  356. static int __init test_rhashtable_max(struct test_obj *array,
  357. unsigned int entries)
  358. {
  359. unsigned int i;
  360. int err;
  361. test_rht_params.max_size = roundup_pow_of_two(entries / 8);
  362. err = rhashtable_init(&ht, &test_rht_params);
  363. if (err)
  364. return err;
  365. for (i = 0; i < ht.max_elems; i++) {
  366. struct test_obj *obj = &array[i];
  367. obj->value.id = i * 2;
  368. err = insert_retry(&ht, obj, test_rht_params);
  369. if (err < 0)
  370. return err;
  371. }
  372. err = insert_retry(&ht, &array[ht.max_elems], test_rht_params);
  373. if (err == -E2BIG) {
  374. err = 0;
  375. } else {
  376. pr_info("insert element %u should have failed with %d, got %d\n",
  377. ht.max_elems, -E2BIG, err);
  378. if (err == 0)
  379. err = -1;
  380. }
  381. rhashtable_destroy(&ht);
  382. return err;
  383. }
  384. static unsigned int __init print_ht(struct rhltable *rhlt)
  385. {
  386. struct rhashtable *ht;
  387. const struct bucket_table *tbl;
  388. char buff[512] = "";
  389. int offset = 0;
  390. unsigned int i, cnt = 0;
  391. ht = &rhlt->ht;
  392. /* Take the mutex to avoid RCU warning */
  393. mutex_lock(&ht->mutex);
  394. tbl = rht_dereference(ht->tbl, ht);
  395. for (i = 0; i < tbl->size; i++) {
  396. struct rhash_head *pos, *next;
  397. struct test_obj_rhl *p;
  398. pos = rht_ptr_exclusive(tbl->buckets + i);
  399. next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL;
  400. if (!rht_is_a_nulls(pos)) {
  401. offset += sprintf(buff + offset, "\nbucket[%d] -> ", i);
  402. }
  403. while (!rht_is_a_nulls(pos)) {
  404. struct rhlist_head *list = container_of(pos, struct rhlist_head, rhead);
  405. offset += sprintf(buff + offset, "[[");
  406. do {
  407. pos = &list->rhead;
  408. list = rht_dereference(list->next, ht);
  409. p = rht_obj(ht, pos);
  410. offset += sprintf(buff + offset, " val %d (tid=%d)%s", p->value.id, p->value.tid,
  411. list? ", " : " ");
  412. cnt++;
  413. } while (list);
  414. pos = next,
  415. next = !rht_is_a_nulls(pos) ?
  416. rht_dereference(pos->next, ht) : NULL;
  417. offset += sprintf(buff + offset, "]]%s", !rht_is_a_nulls(pos) ? " -> " : "");
  418. }
  419. }
  420. printk(KERN_ERR "\n---- ht: ----%s\n-------------\n", buff);
  421. mutex_unlock(&ht->mutex);
  422. return cnt;
  423. }
  424. static int __init test_insert_dup(struct test_obj_rhl *rhl_test_objects,
  425. int cnt, bool slow)
  426. {
  427. struct rhltable *rhlt;
  428. unsigned int i, ret;
  429. const char *key;
  430. int err = 0;
  431. rhlt = kmalloc_obj(*rhlt);
  432. if (WARN_ON(!rhlt))
  433. return -EINVAL;
  434. err = rhltable_init(rhlt, &test_rht_params_dup);
  435. if (WARN_ON(err)) {
  436. kfree(rhlt);
  437. return err;
  438. }
  439. for (i = 0; i < cnt; i++) {
  440. rhl_test_objects[i].value.tid = i;
  441. key = rht_obj(&rhlt->ht, &rhl_test_objects[i].list_node.rhead);
  442. key += test_rht_params_dup.key_offset;
  443. if (slow) {
  444. err = PTR_ERR(rhashtable_insert_slow(&rhlt->ht, key,
  445. &rhl_test_objects[i].list_node.rhead));
  446. if (err == -EAGAIN)
  447. err = 0;
  448. } else
  449. err = rhltable_insert(rhlt,
  450. &rhl_test_objects[i].list_node,
  451. test_rht_params_dup);
  452. if (WARN(err, "error %d on element %d/%d (%s)\n", err, i, cnt, slow? "slow" : "fast"))
  453. goto skip_print;
  454. }
  455. ret = print_ht(rhlt);
  456. WARN(ret != cnt, "missing rhltable elements (%d != %d, %s)\n", ret, cnt, slow? "slow" : "fast");
  457. skip_print:
  458. rhltable_destroy(rhlt);
  459. kfree(rhlt);
  460. return 0;
  461. }
  462. static int __init test_insert_duplicates_run(void)
  463. {
  464. struct test_obj_rhl rhl_test_objects[3] = {};
  465. pr_info("test inserting duplicates\n");
  466. /* two different values that map to same bucket */
  467. rhl_test_objects[0].value.id = 1;
  468. rhl_test_objects[1].value.id = 21;
  469. /* and another duplicate with same as [0] value
  470. * which will be second on the bucket list */
  471. rhl_test_objects[2].value.id = rhl_test_objects[0].value.id;
  472. test_insert_dup(rhl_test_objects, 2, false);
  473. test_insert_dup(rhl_test_objects, 3, false);
  474. test_insert_dup(rhl_test_objects, 2, true);
  475. test_insert_dup(rhl_test_objects, 3, true);
  476. return 0;
  477. }
  478. static int thread_lookup_test(struct thread_data *tdata)
  479. {
  480. unsigned int entries = tdata->entries;
  481. int i, err = 0;
  482. for (i = 0; i < entries; i++) {
  483. struct test_obj *obj;
  484. struct test_obj_val key = {
  485. .id = i,
  486. .tid = tdata->id,
  487. };
  488. obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
  489. if (obj && (tdata->objs[i].value.id == TEST_INSERT_FAIL)) {
  490. pr_err(" found unexpected object %d-%d\n", key.tid, key.id);
  491. err++;
  492. } else if (!obj && (tdata->objs[i].value.id != TEST_INSERT_FAIL)) {
  493. pr_err(" object %d-%d not found!\n", key.tid, key.id);
  494. err++;
  495. } else if (obj && memcmp(&obj->value, &key, sizeof(key))) {
  496. pr_err(" wrong object returned (got %d-%d, expected %d-%d)\n",
  497. obj->value.tid, obj->value.id, key.tid, key.id);
  498. err++;
  499. }
  500. cond_resched();
  501. }
  502. return err;
  503. }
  504. static int threadfunc(void *data)
  505. {
  506. int i, step, err = 0, insert_retries = 0;
  507. struct thread_data *tdata = data;
  508. if (atomic_dec_and_test(&startup_count))
  509. wake_up(&startup_wait);
  510. if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == -1)) {
  511. pr_err(" thread[%d]: interrupted\n", tdata->id);
  512. goto out;
  513. }
  514. for (i = 0; i < tdata->entries; i++) {
  515. tdata->objs[i].value.id = i;
  516. tdata->objs[i].value.tid = tdata->id;
  517. err = insert_retry(&ht, &tdata->objs[i], test_rht_params);
  518. if (err > 0) {
  519. insert_retries += err;
  520. } else if (err) {
  521. pr_err(" thread[%d]: rhashtable_insert_fast failed\n",
  522. tdata->id);
  523. goto out;
  524. }
  525. }
  526. if (insert_retries)
  527. pr_info(" thread[%d]: %u insertions retried due to memory pressure\n",
  528. tdata->id, insert_retries);
  529. err = thread_lookup_test(tdata);
  530. if (err) {
  531. pr_err(" thread[%d]: rhashtable_lookup_test failed\n",
  532. tdata->id);
  533. goto out;
  534. }
  535. for (step = 10; step > 0; step--) {
  536. for (i = 0; i < tdata->entries; i += step) {
  537. if (tdata->objs[i].value.id == TEST_INSERT_FAIL)
  538. continue;
  539. err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
  540. test_rht_params);
  541. if (err) {
  542. pr_err(" thread[%d]: rhashtable_remove_fast failed\n",
  543. tdata->id);
  544. goto out;
  545. }
  546. tdata->objs[i].value.id = TEST_INSERT_FAIL;
  547. cond_resched();
  548. }
  549. err = thread_lookup_test(tdata);
  550. if (err) {
  551. pr_err(" thread[%d]: rhashtable_lookup_test (2) failed\n",
  552. tdata->id);
  553. goto out;
  554. }
  555. }
  556. out:
  557. while (!kthread_should_stop()) {
  558. set_current_state(TASK_INTERRUPTIBLE);
  559. schedule();
  560. }
  561. return err;
  562. }
  563. static int __init test_rht_init(void)
  564. {
  565. unsigned int entries;
  566. int i, err, started_threads = 0, failed_threads = 0;
  567. u64 total_time = 0;
  568. struct thread_data *tdata;
  569. struct test_obj *objs;
  570. if (parm_entries < 0)
  571. parm_entries = 1;
  572. entries = min(parm_entries, MAX_ENTRIES);
  573. test_rht_params.automatic_shrinking = shrinking;
  574. test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
  575. test_rht_params.nelem_hint = size;
  576. objs = vzalloc(array_size(sizeof(struct test_obj),
  577. test_rht_params.max_size + 1));
  578. if (!objs)
  579. return -ENOMEM;
  580. pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
  581. size, max_size, shrinking);
  582. for (i = 0; i < runs; i++) {
  583. s64 time;
  584. pr_info("Test %02d:\n", i);
  585. memset(objs, 0, test_rht_params.max_size * sizeof(struct test_obj));
  586. err = rhashtable_init(&ht, &test_rht_params);
  587. if (err < 0) {
  588. pr_warn("Test failed: Unable to initialize hashtable: %d\n",
  589. err);
  590. continue;
  591. }
  592. time = test_rhashtable(&ht, objs, entries);
  593. rhashtable_destroy(&ht);
  594. if (time < 0) {
  595. vfree(objs);
  596. pr_warn("Test failed: return code %lld\n", time);
  597. return -EINVAL;
  598. }
  599. total_time += time;
  600. }
  601. pr_info("test if its possible to exceed max_size %d: %s\n",
  602. test_rht_params.max_size, test_rhashtable_max(objs, entries) == 0 ?
  603. "no, ok" : "YES, failed");
  604. vfree(objs);
  605. do_div(total_time, runs);
  606. pr_info("Average test time: %llu\n", total_time);
  607. test_insert_duplicates_run();
  608. if (!tcount)
  609. return 0;
  610. pr_info("Testing concurrent rhashtable access from %d threads\n",
  611. tcount);
  612. atomic_set(&startup_count, tcount);
  613. tdata = vzalloc(array_size(tcount, sizeof(struct thread_data)));
  614. if (!tdata)
  615. return -ENOMEM;
  616. objs = vzalloc(array3_size(sizeof(struct test_obj), tcount, entries));
  617. if (!objs) {
  618. vfree(tdata);
  619. return -ENOMEM;
  620. }
  621. test_rht_params.max_size = max_size ? :
  622. roundup_pow_of_two(tcount * entries);
  623. err = rhashtable_init(&ht, &test_rht_params);
  624. if (err < 0) {
  625. pr_warn("Test failed: Unable to initialize hashtable: %d\n",
  626. err);
  627. vfree(tdata);
  628. vfree(objs);
  629. return -EINVAL;
  630. }
  631. for (i = 0; i < tcount; i++) {
  632. tdata[i].id = i;
  633. tdata[i].entries = entries;
  634. tdata[i].objs = objs + i * entries;
  635. tdata[i].task = kthread_run(threadfunc, &tdata[i],
  636. "rhashtable_thrad[%d]", i);
  637. if (IS_ERR(tdata[i].task)) {
  638. pr_err(" kthread_run failed for thread %d\n", i);
  639. atomic_dec(&startup_count);
  640. } else {
  641. started_threads++;
  642. }
  643. }
  644. if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == 0))
  645. pr_err(" wait_event interruptible failed\n");
  646. /* count is 0 now, set it to -1 and wake up all threads together */
  647. atomic_dec(&startup_count);
  648. wake_up_all(&startup_wait);
  649. for (i = 0; i < tcount; i++) {
  650. if (IS_ERR(tdata[i].task))
  651. continue;
  652. if ((err = kthread_stop(tdata[i].task))) {
  653. pr_warn("Test failed: thread %d returned: %d\n",
  654. i, err);
  655. failed_threads++;
  656. }
  657. }
  658. rhashtable_destroy(&ht);
  659. vfree(tdata);
  660. vfree(objs);
  661. /*
  662. * rhltable_remove is very expensive, default values can cause test
  663. * to run for 2 minutes or more, use a smaller number instead.
  664. */
  665. err = test_rhltable(entries / 16);
  666. pr_info("Started %d threads, %d failed, rhltable test returns %d\n",
  667. started_threads, failed_threads, err);
  668. return 0;
  669. }
  670. static void __exit test_rht_exit(void)
  671. {
  672. }
  673. module_init(test_rht_init);
  674. module_exit(test_rht_exit);
  675. MODULE_DESCRIPTION("Resizable, Scalable, Concurrent Hash Table test module");
  676. MODULE_LICENSE("GPL v2");