1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29d6dbe1bSGeert Uytterhoeven /*
39d6dbe1bSGeert Uytterhoeven * Resizable, Scalable, Concurrent Hash Table
49d6dbe1bSGeert Uytterhoeven *
51aa661f5SThomas Graf * Copyright (c) 2014-2015 Thomas Graf <[email protected]>
69d6dbe1bSGeert Uytterhoeven * Copyright (c) 2008-2014 Patrick McHardy <[email protected]>
79d6dbe1bSGeert Uytterhoeven */
89d6dbe1bSGeert Uytterhoeven
99d6dbe1bSGeert Uytterhoeven /**************************************************************************
109d6dbe1bSGeert Uytterhoeven * Self Test
119d6dbe1bSGeert Uytterhoeven **************************************************************************/
129d6dbe1bSGeert Uytterhoeven
139d6dbe1bSGeert Uytterhoeven #include <linux/init.h>
149d6dbe1bSGeert Uytterhoeven #include <linux/jhash.h>
159d6dbe1bSGeert Uytterhoeven #include <linux/kernel.h>
16f4a3e90bSPhil Sutter #include <linux/kthread.h>
179d6dbe1bSGeert Uytterhoeven #include <linux/module.h>
189d6dbe1bSGeert Uytterhoeven #include <linux/rcupdate.h>
191e2f2d31SKent Overstreet #include <linux/rcupdate_wait.h>
209d6dbe1bSGeert Uytterhoeven #include <linux/rhashtable.h>
219d6dbe1bSGeert Uytterhoeven #include <linux/slab.h>
22685a015eSThomas Graf #include <linux/sched.h>
23cdd4de37SFlorian Westphal #include <linux/random.h>
24f4a3e90bSPhil Sutter #include <linux/vmalloc.h>
25809c6705SArnd Bergmann #include <linux/wait.h>
269d6dbe1bSGeert Uytterhoeven
271aa661f5SThomas Graf #define MAX_ENTRIES 1000000
2867b7cbf4SThomas Graf #define TEST_INSERT_FAIL INT_MAX
291aa661f5SThomas Graf
30f651616eSFlorian Westphal static int parm_entries = 50000;
31f651616eSFlorian Westphal module_param(parm_entries, int, 0);
32f651616eSFlorian Westphal MODULE_PARM_DESC(parm_entries, "Number of entries to add (default: 50000)");
331aa661f5SThomas Graf
341aa661f5SThomas Graf static int runs = 4;
351aa661f5SThomas Graf module_param(runs, int, 0);
361aa661f5SThomas Graf MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
371aa661f5SThomas Graf
3895e435afSPhil Sutter static int max_size = 0;
391aa661f5SThomas Graf module_param(max_size, int, 0);
403b3bf80bSPhil Sutter MODULE_PARM_DESC(max_size, "Maximum table size (default: calculated)");
411aa661f5SThomas Graf
421aa661f5SThomas Graf static bool shrinking = false;
431aa661f5SThomas Graf module_param(shrinking, bool, 0);
441aa661f5SThomas Graf MODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)");
451aa661f5SThomas Graf
461aa661f5SThomas Graf static int size = 8;
471aa661f5SThomas Graf module_param(size, int, 0);
481aa661f5SThomas Graf MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
499d6dbe1bSGeert Uytterhoeven
50f4a3e90bSPhil Sutter static int tcount = 10;
51f4a3e90bSPhil Sutter module_param(tcount, int, 0);
52f4a3e90bSPhil Sutter MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
53f4a3e90bSPhil Sutter
54d662e037SPhil Sutter static bool enomem_retry = false;
55d662e037SPhil Sutter module_param(enomem_retry, bool, 0);
56d662e037SPhil Sutter MODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
57d662e037SPhil Sutter
58e859afe1SPhil Sutter struct test_obj_val {
59e859afe1SPhil Sutter int id;
60e859afe1SPhil Sutter int tid;
61e859afe1SPhil Sutter };
62e859afe1SPhil Sutter
639d6dbe1bSGeert Uytterhoeven struct test_obj {
64e859afe1SPhil Sutter struct test_obj_val value;
659d6dbe1bSGeert Uytterhoeven struct rhash_head node;
669d6dbe1bSGeert Uytterhoeven };
679d6dbe1bSGeert Uytterhoeven
68cdd4de37SFlorian Westphal struct test_obj_rhl {
69cdd4de37SFlorian Westphal struct test_obj_val value;
70cdd4de37SFlorian Westphal struct rhlist_head list_node;
71cdd4de37SFlorian Westphal };
72cdd4de37SFlorian Westphal
73f4a3e90bSPhil Sutter struct thread_data {
74f651616eSFlorian Westphal unsigned int entries;
75f4a3e90bSPhil Sutter int id;
76f4a3e90bSPhil Sutter struct task_struct *task;
77f4a3e90bSPhil Sutter struct test_obj *objs;
78f4a3e90bSPhil Sutter };
79f4a3e90bSPhil Sutter
my_hashfn(const void * data,u32 len,u32 seed)80499ac3b6SPaul Blakey static u32 my_hashfn(const void *data, u32 len, u32 seed)
81499ac3b6SPaul Blakey {
82499ac3b6SPaul Blakey const struct test_obj_rhl *obj = data;
83499ac3b6SPaul Blakey
849f9a7077SNeilBrown return (obj->value.id % 10);
85499ac3b6SPaul Blakey }
86499ac3b6SPaul Blakey
my_cmpfn(struct rhashtable_compare_arg * arg,const void * obj)87499ac3b6SPaul Blakey static int my_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
88499ac3b6SPaul Blakey {
89499ac3b6SPaul Blakey const struct test_obj_rhl *test_obj = obj;
90499ac3b6SPaul Blakey const struct test_obj_val *val = arg->key;
91499ac3b6SPaul Blakey
92499ac3b6SPaul Blakey return test_obj->value.id - val->id;
93499ac3b6SPaul Blakey }
94499ac3b6SPaul Blakey
951aa661f5SThomas Graf static struct rhashtable_params test_rht_params = {
96b182aa6eSHerbert Xu .head_offset = offsetof(struct test_obj, node),
97b182aa6eSHerbert Xu .key_offset = offsetof(struct test_obj, value),
98e859afe1SPhil Sutter .key_len = sizeof(struct test_obj_val),
99b182aa6eSHerbert Xu .hashfn = jhash,
100b182aa6eSHerbert Xu };
101b182aa6eSHerbert Xu
102499ac3b6SPaul Blakey static struct rhashtable_params test_rht_params_dup = {
103499ac3b6SPaul Blakey .head_offset = offsetof(struct test_obj_rhl, list_node),
104499ac3b6SPaul Blakey .key_offset = offsetof(struct test_obj_rhl, value),
105499ac3b6SPaul Blakey .key_len = sizeof(struct test_obj_val),
106499ac3b6SPaul Blakey .hashfn = jhash,
107499ac3b6SPaul Blakey .obj_hashfn = my_hashfn,
108499ac3b6SPaul Blakey .obj_cmpfn = my_cmpfn,
109499ac3b6SPaul Blakey .nelem_hint = 128,
110499ac3b6SPaul Blakey .automatic_shrinking = false,
111499ac3b6SPaul Blakey };
112499ac3b6SPaul Blakey
113809c6705SArnd Bergmann static atomic_t startup_count;
114809c6705SArnd Bergmann static DECLARE_WAIT_QUEUE_HEAD(startup_wait);
115f4a3e90bSPhil Sutter
insert_retry(struct rhashtable * ht,struct test_obj * obj,const struct rhashtable_params params)1167e936bd7SFlorian Westphal static int insert_retry(struct rhashtable *ht, struct test_obj *obj,
1179e9089e5SPhil Sutter const struct rhashtable_params params)
1189e9089e5SPhil Sutter {
119d662e037SPhil Sutter int err, retries = -1, enomem_retries = 0;
1209e9089e5SPhil Sutter
1219e9089e5SPhil Sutter do {
1229e9089e5SPhil Sutter retries++;
1239e9089e5SPhil Sutter cond_resched();
1247e936bd7SFlorian Westphal err = rhashtable_insert_fast(ht, &obj->node, params);
125d662e037SPhil Sutter if (err == -ENOMEM && enomem_retry) {
126d662e037SPhil Sutter enomem_retries++;
127d662e037SPhil Sutter err = -EBUSY;
128d662e037SPhil Sutter }
1299e9089e5SPhil Sutter } while (err == -EBUSY);
1309e9089e5SPhil Sutter
131d662e037SPhil Sutter if (enomem_retries)
132d662e037SPhil Sutter pr_info(" %u insertions retried after -ENOMEM\n",
133d662e037SPhil Sutter enomem_retries);
134d662e037SPhil Sutter
1359e9089e5SPhil Sutter return err ? : retries;
1369e9089e5SPhil Sutter }
1379e9089e5SPhil Sutter
test_rht_lookup(struct rhashtable * ht,struct test_obj * array,unsigned int entries)138f651616eSFlorian Westphal static int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array,
139f651616eSFlorian Westphal unsigned int entries)
1409d6dbe1bSGeert Uytterhoeven {
1419d6dbe1bSGeert Uytterhoeven unsigned int i;
1429d6dbe1bSGeert Uytterhoeven
143f651616eSFlorian Westphal for (i = 0; i < entries; i++) {
1449d6dbe1bSGeert Uytterhoeven struct test_obj *obj;
1459d6dbe1bSGeert Uytterhoeven bool expected = !(i % 2);
146e859afe1SPhil Sutter struct test_obj_val key = {
147e859afe1SPhil Sutter .id = i,
148e859afe1SPhil Sutter };
1499d6dbe1bSGeert Uytterhoeven
150e859afe1SPhil Sutter if (array[i / 2].value.id == TEST_INSERT_FAIL)
15167b7cbf4SThomas Graf expected = false;
15267b7cbf4SThomas Graf
153b182aa6eSHerbert Xu obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
1549d6dbe1bSGeert Uytterhoeven
1559d6dbe1bSGeert Uytterhoeven if (expected && !obj) {
156e859afe1SPhil Sutter pr_warn("Test failed: Could not find key %u\n", key.id);
1579d6dbe1bSGeert Uytterhoeven return -ENOENT;
1589d6dbe1bSGeert Uytterhoeven } else if (!expected && obj) {
1599d6dbe1bSGeert Uytterhoeven pr_warn("Test failed: Unexpected entry found for key %u\n",
160e859afe1SPhil Sutter key.id);
1619d6dbe1bSGeert Uytterhoeven return -EEXIST;
1629d6dbe1bSGeert Uytterhoeven } else if (expected && obj) {
163e859afe1SPhil Sutter if (obj->value.id != i) {
164c2c8a901SThomas Graf pr_warn("Test failed: Lookup value mismatch %u!=%u\n",
165e859afe1SPhil Sutter obj->value.id, i);
1669d6dbe1bSGeert Uytterhoeven return -EINVAL;
1679d6dbe1bSGeert Uytterhoeven }
1689d6dbe1bSGeert Uytterhoeven }
169685a015eSThomas Graf
170685a015eSThomas Graf cond_resched_rcu();
1719d6dbe1bSGeert Uytterhoeven }
1729d6dbe1bSGeert Uytterhoeven
1739d6dbe1bSGeert Uytterhoeven return 0;
1749d6dbe1bSGeert Uytterhoeven }
1759d6dbe1bSGeert Uytterhoeven
test_bucket_stats(struct rhashtable * ht,unsigned int entries)176f651616eSFlorian Westphal static void test_bucket_stats(struct rhashtable *ht, unsigned int entries)
1779d6dbe1bSGeert Uytterhoeven {
1786c4128f6SHerbert Xu unsigned int total = 0, chain_len = 0;
179246b23a7SThomas Graf struct rhashtable_iter hti;
1809d6dbe1bSGeert Uytterhoeven struct rhash_head *pos;
1819d6dbe1bSGeert Uytterhoeven
1826c4128f6SHerbert Xu rhashtable_walk_enter(ht, &hti);
18397a6ec4aSTom Herbert rhashtable_walk_start(&hti);
1849d6dbe1bSGeert Uytterhoeven
185246b23a7SThomas Graf while ((pos = rhashtable_walk_next(&hti))) {
186246b23a7SThomas Graf if (PTR_ERR(pos) == -EAGAIN) {
187246b23a7SThomas Graf pr_info("Info: encountered resize\n");
188246b23a7SThomas Graf chain_len++;
189246b23a7SThomas Graf continue;
190246b23a7SThomas Graf } else if (IS_ERR(pos)) {
191246b23a7SThomas Graf pr_warn("Test failed: rhashtable_walk_next() error: %ld\n",
192246b23a7SThomas Graf PTR_ERR(pos));
193246b23a7SThomas Graf break;
194246b23a7SThomas Graf }
195246b23a7SThomas Graf
1969d6dbe1bSGeert Uytterhoeven total++;
1979d6dbe1bSGeert Uytterhoeven }
1989d6dbe1bSGeert Uytterhoeven
199246b23a7SThomas Graf rhashtable_walk_stop(&hti);
200246b23a7SThomas Graf rhashtable_walk_exit(&hti);
2019d6dbe1bSGeert Uytterhoeven
202246b23a7SThomas Graf pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u\n",
203246b23a7SThomas Graf total, atomic_read(&ht->nelems), entries, chain_len);
2049d6dbe1bSGeert Uytterhoeven
2051aa661f5SThomas Graf if (total != atomic_read(&ht->nelems) || total != entries)
2069d6dbe1bSGeert Uytterhoeven pr_warn("Test failed: Total count mismatch ^^^");
2079d6dbe1bSGeert Uytterhoeven }
2089d6dbe1bSGeert Uytterhoeven
test_rhashtable(struct rhashtable * ht,struct test_obj * array,unsigned int entries)209f651616eSFlorian Westphal static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,
210f651616eSFlorian Westphal unsigned int entries)
2119d6dbe1bSGeert Uytterhoeven {
2129d6dbe1bSGeert Uytterhoeven struct test_obj *obj;
2139d6dbe1bSGeert Uytterhoeven int err;
2149e9089e5SPhil Sutter unsigned int i, insert_retries = 0;
2151aa661f5SThomas Graf s64 start, end;
2169d6dbe1bSGeert Uytterhoeven
2179d6dbe1bSGeert Uytterhoeven /*
2189d6dbe1bSGeert Uytterhoeven * Insertion Test:
2191aa661f5SThomas Graf * Insert entries into table with all keys even numbers
2209d6dbe1bSGeert Uytterhoeven */
2211aa661f5SThomas Graf pr_info(" Adding %d keys\n", entries);
2221aa661f5SThomas Graf start = ktime_get_ns();
2231aa661f5SThomas Graf for (i = 0; i < entries; i++) {
224fcc57020SThomas Graf struct test_obj *obj = &array[i];
2259d6dbe1bSGeert Uytterhoeven
226e859afe1SPhil Sutter obj->value.id = i * 2;
2277e936bd7SFlorian Westphal err = insert_retry(ht, obj, test_rht_params);
2289e9089e5SPhil Sutter if (err > 0)
2299e9089e5SPhil Sutter insert_retries += err;
2309e9089e5SPhil Sutter else if (err)
231fcc57020SThomas Graf return err;
2329d6dbe1bSGeert Uytterhoeven }
233685a015eSThomas Graf
2349e9089e5SPhil Sutter if (insert_retries)
2359e9089e5SPhil Sutter pr_info(" %u insertions retried due to memory pressure\n",
2369e9089e5SPhil Sutter insert_retries);
2379d6dbe1bSGeert Uytterhoeven
238f651616eSFlorian Westphal test_bucket_stats(ht, entries);
2399d6dbe1bSGeert Uytterhoeven rcu_read_lock();
240f651616eSFlorian Westphal test_rht_lookup(ht, array, entries);
2419d6dbe1bSGeert Uytterhoeven rcu_read_unlock();
2429d6dbe1bSGeert Uytterhoeven
243f651616eSFlorian Westphal test_bucket_stats(ht, entries);
2449d6dbe1bSGeert Uytterhoeven
2451aa661f5SThomas Graf pr_info(" Deleting %d keys\n", entries);
2461aa661f5SThomas Graf for (i = 0; i < entries; i++) {
24778369255SPhil Sutter struct test_obj_val key = {
24878369255SPhil Sutter .id = i * 2,
24978369255SPhil Sutter };
2509d6dbe1bSGeert Uytterhoeven
251e859afe1SPhil Sutter if (array[i].value.id != TEST_INSERT_FAIL) {
252b182aa6eSHerbert Xu obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
2539d6dbe1bSGeert Uytterhoeven BUG_ON(!obj);
2549d6dbe1bSGeert Uytterhoeven
255b182aa6eSHerbert Xu rhashtable_remove_fast(ht, &obj->node, test_rht_params);
2569d6dbe1bSGeert Uytterhoeven }
257685a015eSThomas Graf
258685a015eSThomas Graf cond_resched();
25967b7cbf4SThomas Graf }
2609d6dbe1bSGeert Uytterhoeven
2611aa661f5SThomas Graf end = ktime_get_ns();
2621aa661f5SThomas Graf pr_info(" Duration of test: %lld ns\n", end - start);
2631aa661f5SThomas Graf
2641aa661f5SThomas Graf return end - start;
2659d6dbe1bSGeert Uytterhoeven }
2669d6dbe1bSGeert Uytterhoeven
267b7f5e5c7SDaniel Borkmann static struct rhashtable ht;
268411d788aSFlorian Westphal static struct rhltable rhlt;
269cdd4de37SFlorian Westphal
test_rhltable(unsigned int entries)270cdd4de37SFlorian Westphal static int __init test_rhltable(unsigned int entries)
271cdd4de37SFlorian Westphal {
272cdd4de37SFlorian Westphal struct test_obj_rhl *rhl_test_objects;
273cdd4de37SFlorian Westphal unsigned long *obj_in_table;
274cdd4de37SFlorian Westphal unsigned int i, j, k;
275cdd4de37SFlorian Westphal int ret, err;
276cdd4de37SFlorian Westphal
277cdd4de37SFlorian Westphal if (entries == 0)
278cdd4de37SFlorian Westphal entries = 1;
279cdd4de37SFlorian Westphal
280fad953ceSKees Cook rhl_test_objects = vzalloc(array_size(entries,
281fad953ceSKees Cook sizeof(*rhl_test_objects)));
282cdd4de37SFlorian Westphal if (!rhl_test_objects)
283cdd4de37SFlorian Westphal return -ENOMEM;
284cdd4de37SFlorian Westphal
285cdd4de37SFlorian Westphal ret = -ENOMEM;
286fad953ceSKees Cook obj_in_table = vzalloc(array_size(sizeof(unsigned long),
287fad953ceSKees Cook BITS_TO_LONGS(entries)));
288cdd4de37SFlorian Westphal if (!obj_in_table)
289cdd4de37SFlorian Westphal goto out_free;
290cdd4de37SFlorian Westphal
291cdd4de37SFlorian Westphal err = rhltable_init(&rhlt, &test_rht_params);
292cdd4de37SFlorian Westphal if (WARN_ON(err))
293cdd4de37SFlorian Westphal goto out_free;
294cdd4de37SFlorian Westphal
295a251c17aSJason A. Donenfeld k = get_random_u32();
296cdd4de37SFlorian Westphal ret = 0;
297cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
298cdd4de37SFlorian Westphal rhl_test_objects[i].value.id = k;
299cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
300cdd4de37SFlorian Westphal test_rht_params);
301cdd4de37SFlorian Westphal if (WARN(err, "error %d on element %d\n", err, i))
302cdd4de37SFlorian Westphal break;
303cdd4de37SFlorian Westphal if (err == 0)
304cdd4de37SFlorian Westphal set_bit(i, obj_in_table);
305cdd4de37SFlorian Westphal }
306cdd4de37SFlorian Westphal
307cdd4de37SFlorian Westphal if (err)
308cdd4de37SFlorian Westphal ret = err;
309cdd4de37SFlorian Westphal
310cdd4de37SFlorian Westphal pr_info("test %d add/delete pairs into rhlist\n", entries);
311cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
312cdd4de37SFlorian Westphal struct rhlist_head *h, *pos;
313cdd4de37SFlorian Westphal struct test_obj_rhl *obj;
314cdd4de37SFlorian Westphal struct test_obj_val key = {
315cdd4de37SFlorian Westphal .id = k,
316cdd4de37SFlorian Westphal };
317cdd4de37SFlorian Westphal bool found;
318cdd4de37SFlorian Westphal
319cdd4de37SFlorian Westphal rcu_read_lock();
320cdd4de37SFlorian Westphal h = rhltable_lookup(&rhlt, &key, test_rht_params);
321cdd4de37SFlorian Westphal if (WARN(!h, "key not found during iteration %d of %d", i, entries)) {
322cdd4de37SFlorian Westphal rcu_read_unlock();
323cdd4de37SFlorian Westphal break;
324cdd4de37SFlorian Westphal }
325cdd4de37SFlorian Westphal
326cdd4de37SFlorian Westphal if (i) {
327cdd4de37SFlorian Westphal j = i - 1;
328cdd4de37SFlorian Westphal rhl_for_each_entry_rcu(obj, pos, h, list_node) {
329cdd4de37SFlorian Westphal if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone"))
330cdd4de37SFlorian Westphal break;
331cdd4de37SFlorian Westphal }
332cdd4de37SFlorian Westphal }
333cdd4de37SFlorian Westphal
334cdd4de37SFlorian Westphal cond_resched_rcu();
335cdd4de37SFlorian Westphal
336cdd4de37SFlorian Westphal found = false;
337cdd4de37SFlorian Westphal
338cdd4de37SFlorian Westphal rhl_for_each_entry_rcu(obj, pos, h, list_node) {
339cdd4de37SFlorian Westphal if (pos == &rhl_test_objects[i].list_node) {
340cdd4de37SFlorian Westphal found = true;
341cdd4de37SFlorian Westphal break;
342cdd4de37SFlorian Westphal }
343cdd4de37SFlorian Westphal }
344cdd4de37SFlorian Westphal
345cdd4de37SFlorian Westphal rcu_read_unlock();
346cdd4de37SFlorian Westphal
347cdd4de37SFlorian Westphal if (WARN(!found, "element %d not found", i))
348cdd4de37SFlorian Westphal break;
349cdd4de37SFlorian Westphal
350cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
351cdd4de37SFlorian Westphal WARN(err, "rhltable_remove: err %d for iteration %d\n", err, i);
352cdd4de37SFlorian Westphal if (err == 0)
353cdd4de37SFlorian Westphal clear_bit(i, obj_in_table);
354cdd4de37SFlorian Westphal }
355cdd4de37SFlorian Westphal
356cdd4de37SFlorian Westphal if (ret == 0 && err)
357cdd4de37SFlorian Westphal ret = err;
358cdd4de37SFlorian Westphal
359cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
360cdd4de37SFlorian Westphal WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i);
361cdd4de37SFlorian Westphal
362cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
363cdd4de37SFlorian Westphal test_rht_params);
364cdd4de37SFlorian Westphal if (WARN(err, "error %d on element %d\n", err, i))
365cdd4de37SFlorian Westphal break;
366cdd4de37SFlorian Westphal if (err == 0)
367cdd4de37SFlorian Westphal set_bit(i, obj_in_table);
368cdd4de37SFlorian Westphal }
369cdd4de37SFlorian Westphal
370cdd4de37SFlorian Westphal pr_info("test %d random rhlist add/delete operations\n", entries);
371cdd4de37SFlorian Westphal for (j = 0; j < entries; j++) {
3728032bf12SJason A. Donenfeld u32 i = get_random_u32_below(entries);
3738032bf12SJason A. Donenfeld u32 prand = get_random_u32_below(4);
374cdd4de37SFlorian Westphal
375cdd4de37SFlorian Westphal cond_resched();
376cdd4de37SFlorian Westphal
377cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
378cdd4de37SFlorian Westphal if (test_bit(i, obj_in_table)) {
379cdd4de37SFlorian Westphal clear_bit(i, obj_in_table);
380cdd4de37SFlorian Westphal if (WARN(err, "cannot remove element at slot %d", i))
381cdd4de37SFlorian Westphal continue;
382cdd4de37SFlorian Westphal } else {
38356b90fa0SColin Ian King if (WARN(err != -ENOENT, "removed non-existent element %d, error %d not %d",
384cdd4de37SFlorian Westphal i, err, -ENOENT))
385cdd4de37SFlorian Westphal continue;
386cdd4de37SFlorian Westphal }
387cdd4de37SFlorian Westphal
388cdd4de37SFlorian Westphal if (prand & 1) {
389cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
390cdd4de37SFlorian Westphal if (err == 0) {
391cdd4de37SFlorian Westphal if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i))
392cdd4de37SFlorian Westphal continue;
393cdd4de37SFlorian Westphal } else {
394cdd4de37SFlorian Westphal if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i))
395cdd4de37SFlorian Westphal continue;
396cdd4de37SFlorian Westphal }
397cdd4de37SFlorian Westphal }
398cdd4de37SFlorian Westphal
399c5f0a172SRolf Eike Beer if (prand & 2) {
4008032bf12SJason A. Donenfeld i = get_random_u32_below(entries);
401cdd4de37SFlorian Westphal if (test_bit(i, obj_in_table)) {
402cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
403cdd4de37SFlorian Westphal WARN(err, "cannot remove element at slot %d", i);
404cdd4de37SFlorian Westphal if (err == 0)
405cdd4de37SFlorian Westphal clear_bit(i, obj_in_table);
406cdd4de37SFlorian Westphal } else {
407cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
408cdd4de37SFlorian Westphal WARN(err, "failed to insert object %d", i);
409cdd4de37SFlorian Westphal if (err == 0)
410cdd4de37SFlorian Westphal set_bit(i, obj_in_table);
411cdd4de37SFlorian Westphal }
412cdd4de37SFlorian Westphal }
413c5f0a172SRolf Eike Beer }
414cdd4de37SFlorian Westphal
415cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
416cdd4de37SFlorian Westphal cond_resched();
417cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
418cdd4de37SFlorian Westphal if (test_bit(i, obj_in_table)) {
419cdd4de37SFlorian Westphal if (WARN(err, "cannot remove element at slot %d", i))
420cdd4de37SFlorian Westphal continue;
421cdd4de37SFlorian Westphal } else {
42256b90fa0SColin Ian King if (WARN(err != -ENOENT, "removed non-existent element, error %d not %d",
423cdd4de37SFlorian Westphal err, -ENOENT))
424cdd4de37SFlorian Westphal continue;
425cdd4de37SFlorian Westphal }
426cdd4de37SFlorian Westphal }
427cdd4de37SFlorian Westphal
428cdd4de37SFlorian Westphal rhltable_destroy(&rhlt);
429cdd4de37SFlorian Westphal out_free:
430cdd4de37SFlorian Westphal vfree(rhl_test_objects);
431cdd4de37SFlorian Westphal vfree(obj_in_table);
432cdd4de37SFlorian Westphal return ret;
433cdd4de37SFlorian Westphal }
434b7f5e5c7SDaniel Borkmann
test_rhashtable_max(struct test_obj * array,unsigned int entries)435a6359bd8SFlorian Westphal static int __init test_rhashtable_max(struct test_obj *array,
436a6359bd8SFlorian Westphal unsigned int entries)
437a6359bd8SFlorian Westphal {
438b084f6ccSJiapeng Chong unsigned int i;
439a6359bd8SFlorian Westphal int err;
440a6359bd8SFlorian Westphal
441a6359bd8SFlorian Westphal test_rht_params.max_size = roundup_pow_of_two(entries / 8);
442a6359bd8SFlorian Westphal err = rhashtable_init(&ht, &test_rht_params);
443a6359bd8SFlorian Westphal if (err)
444a6359bd8SFlorian Westphal return err;
445a6359bd8SFlorian Westphal
446a6359bd8SFlorian Westphal for (i = 0; i < ht.max_elems; i++) {
447a6359bd8SFlorian Westphal struct test_obj *obj = &array[i];
448a6359bd8SFlorian Westphal
449a6359bd8SFlorian Westphal obj->value.id = i * 2;
450a6359bd8SFlorian Westphal err = insert_retry(&ht, obj, test_rht_params);
451b084f6ccSJiapeng Chong if (err < 0)
452a6359bd8SFlorian Westphal return err;
453a6359bd8SFlorian Westphal }
454a6359bd8SFlorian Westphal
455a6359bd8SFlorian Westphal err = insert_retry(&ht, &array[ht.max_elems], test_rht_params);
456a6359bd8SFlorian Westphal if (err == -E2BIG) {
457a6359bd8SFlorian Westphal err = 0;
458a6359bd8SFlorian Westphal } else {
459a6359bd8SFlorian Westphal pr_info("insert element %u should have failed with %d, got %d\n",
460a6359bd8SFlorian Westphal ht.max_elems, -E2BIG, err);
461a6359bd8SFlorian Westphal if (err == 0)
462a6359bd8SFlorian Westphal err = -1;
463a6359bd8SFlorian Westphal }
464a6359bd8SFlorian Westphal
465a6359bd8SFlorian Westphal rhashtable_destroy(&ht);
466a6359bd8SFlorian Westphal
467a6359bd8SFlorian Westphal return err;
468a6359bd8SFlorian Westphal }
469a6359bd8SFlorian Westphal
print_ht(struct rhltable * rhlt)470499ac3b6SPaul Blakey static unsigned int __init print_ht(struct rhltable *rhlt)
471499ac3b6SPaul Blakey {
472499ac3b6SPaul Blakey struct rhashtable *ht;
473499ac3b6SPaul Blakey const struct bucket_table *tbl;
474499ac3b6SPaul Blakey char buff[512] = "";
4754adec7f8SArnd Bergmann int offset = 0;
476499ac3b6SPaul Blakey unsigned int i, cnt = 0;
477499ac3b6SPaul Blakey
478499ac3b6SPaul Blakey ht = &rhlt->ht;
479cbab9012SNeilBrown /* Take the mutex to avoid RCU warning */
480cbab9012SNeilBrown mutex_lock(&ht->mutex);
481499ac3b6SPaul Blakey tbl = rht_dereference(ht->tbl, ht);
482499ac3b6SPaul Blakey for (i = 0; i < tbl->size; i++) {
483499ac3b6SPaul Blakey struct rhash_head *pos, *next;
484499ac3b6SPaul Blakey struct test_obj_rhl *p;
485499ac3b6SPaul Blakey
486adc6a3abSNeilBrown pos = rht_ptr_exclusive(tbl->buckets + i);
487499ac3b6SPaul Blakey next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL;
488499ac3b6SPaul Blakey
489499ac3b6SPaul Blakey if (!rht_is_a_nulls(pos)) {
4904adec7f8SArnd Bergmann offset += sprintf(buff + offset, "\nbucket[%d] -> ", i);
491499ac3b6SPaul Blakey }
492499ac3b6SPaul Blakey
493499ac3b6SPaul Blakey while (!rht_is_a_nulls(pos)) {
494499ac3b6SPaul Blakey struct rhlist_head *list = container_of(pos, struct rhlist_head, rhead);
4954adec7f8SArnd Bergmann offset += sprintf(buff + offset, "[[");
496499ac3b6SPaul Blakey do {
497499ac3b6SPaul Blakey pos = &list->rhead;
498499ac3b6SPaul Blakey list = rht_dereference(list->next, ht);
499499ac3b6SPaul Blakey p = rht_obj(ht, pos);
500499ac3b6SPaul Blakey
5014adec7f8SArnd Bergmann offset += sprintf(buff + offset, " val %d (tid=%d)%s", p->value.id, p->value.tid,
502499ac3b6SPaul Blakey list? ", " : " ");
503499ac3b6SPaul Blakey cnt++;
504499ac3b6SPaul Blakey } while (list);
505499ac3b6SPaul Blakey
506499ac3b6SPaul Blakey pos = next,
507499ac3b6SPaul Blakey next = !rht_is_a_nulls(pos) ?
508499ac3b6SPaul Blakey rht_dereference(pos->next, ht) : NULL;
509499ac3b6SPaul Blakey
5104adec7f8SArnd Bergmann offset += sprintf(buff + offset, "]]%s", !rht_is_a_nulls(pos) ? " -> " : "");
511499ac3b6SPaul Blakey }
512499ac3b6SPaul Blakey }
513499ac3b6SPaul Blakey printk(KERN_ERR "\n---- ht: ----%s\n-------------\n", buff);
514cbab9012SNeilBrown mutex_unlock(&ht->mutex);
515499ac3b6SPaul Blakey
516499ac3b6SPaul Blakey return cnt;
517499ac3b6SPaul Blakey }
518499ac3b6SPaul Blakey
test_insert_dup(struct test_obj_rhl * rhl_test_objects,int cnt,bool slow)519499ac3b6SPaul Blakey static int __init test_insert_dup(struct test_obj_rhl *rhl_test_objects,
520499ac3b6SPaul Blakey int cnt, bool slow)
521499ac3b6SPaul Blakey {
522fc42a689SBart Van Assche struct rhltable *rhlt;
523499ac3b6SPaul Blakey unsigned int i, ret;
524499ac3b6SPaul Blakey const char *key;
525499ac3b6SPaul Blakey int err = 0;
526499ac3b6SPaul Blakey
527fc42a689SBart Van Assche rhlt = kmalloc(sizeof(*rhlt), GFP_KERNEL);
528fc42a689SBart Van Assche if (WARN_ON(!rhlt))
529fc42a689SBart Van Assche return -EINVAL;
530fc42a689SBart Van Assche
531fc42a689SBart Van Assche err = rhltable_init(rhlt, &test_rht_params_dup);
532fc42a689SBart Van Assche if (WARN_ON(err)) {
533fc42a689SBart Van Assche kfree(rhlt);
534499ac3b6SPaul Blakey return err;
535fc42a689SBart Van Assche }
536499ac3b6SPaul Blakey
537499ac3b6SPaul Blakey for (i = 0; i < cnt; i++) {
538499ac3b6SPaul Blakey rhl_test_objects[i].value.tid = i;
539fc42a689SBart Van Assche key = rht_obj(&rhlt->ht, &rhl_test_objects[i].list_node.rhead);
540499ac3b6SPaul Blakey key += test_rht_params_dup.key_offset;
541499ac3b6SPaul Blakey
542499ac3b6SPaul Blakey if (slow) {
543fc42a689SBart Van Assche err = PTR_ERR(rhashtable_insert_slow(&rhlt->ht, key,
544499ac3b6SPaul Blakey &rhl_test_objects[i].list_node.rhead));
545499ac3b6SPaul Blakey if (err == -EAGAIN)
546499ac3b6SPaul Blakey err = 0;
547499ac3b6SPaul Blakey } else
548fc42a689SBart Van Assche err = rhltable_insert(rhlt,
549499ac3b6SPaul Blakey &rhl_test_objects[i].list_node,
550499ac3b6SPaul Blakey test_rht_params_dup);
551499ac3b6SPaul Blakey if (WARN(err, "error %d on element %d/%d (%s)\n", err, i, cnt, slow? "slow" : "fast"))
552499ac3b6SPaul Blakey goto skip_print;
553499ac3b6SPaul Blakey }
554499ac3b6SPaul Blakey
555fc42a689SBart Van Assche ret = print_ht(rhlt);
556499ac3b6SPaul Blakey WARN(ret != cnt, "missing rhltable elements (%d != %d, %s)\n", ret, cnt, slow? "slow" : "fast");
557499ac3b6SPaul Blakey
558499ac3b6SPaul Blakey skip_print:
559fc42a689SBart Van Assche rhltable_destroy(rhlt);
560fc42a689SBart Van Assche kfree(rhlt);
561499ac3b6SPaul Blakey
562499ac3b6SPaul Blakey return 0;
563499ac3b6SPaul Blakey }
564499ac3b6SPaul Blakey
test_insert_duplicates_run(void)565499ac3b6SPaul Blakey static int __init test_insert_duplicates_run(void)
566499ac3b6SPaul Blakey {
567499ac3b6SPaul Blakey struct test_obj_rhl rhl_test_objects[3] = {};
568499ac3b6SPaul Blakey
569499ac3b6SPaul Blakey pr_info("test inserting duplicates\n");
570499ac3b6SPaul Blakey
571499ac3b6SPaul Blakey /* two different values that map to same bucket */
572499ac3b6SPaul Blakey rhl_test_objects[0].value.id = 1;
573499ac3b6SPaul Blakey rhl_test_objects[1].value.id = 21;
574499ac3b6SPaul Blakey
575499ac3b6SPaul Blakey /* and another duplicate with same as [0] value
576499ac3b6SPaul Blakey * which will be second on the bucket list */
577499ac3b6SPaul Blakey rhl_test_objects[2].value.id = rhl_test_objects[0].value.id;
578499ac3b6SPaul Blakey
579499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 2, false);
580499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 3, false);
581499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 2, true);
582499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 3, true);
583499ac3b6SPaul Blakey
584499ac3b6SPaul Blakey return 0;
585499ac3b6SPaul Blakey }
586499ac3b6SPaul Blakey
thread_lookup_test(struct thread_data * tdata)587f4a3e90bSPhil Sutter static int thread_lookup_test(struct thread_data *tdata)
588f4a3e90bSPhil Sutter {
589f651616eSFlorian Westphal unsigned int entries = tdata->entries;
590f4a3e90bSPhil Sutter int i, err = 0;
591f4a3e90bSPhil Sutter
592f4a3e90bSPhil Sutter for (i = 0; i < entries; i++) {
593f4a3e90bSPhil Sutter struct test_obj *obj;
594e859afe1SPhil Sutter struct test_obj_val key = {
595e859afe1SPhil Sutter .id = i,
596e859afe1SPhil Sutter .tid = tdata->id,
597e859afe1SPhil Sutter };
598f4a3e90bSPhil Sutter
599f4a3e90bSPhil Sutter obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
600e859afe1SPhil Sutter if (obj && (tdata->objs[i].value.id == TEST_INSERT_FAIL)) {
601e859afe1SPhil Sutter pr_err(" found unexpected object %d-%d\n", key.tid, key.id);
602f4a3e90bSPhil Sutter err++;
603e859afe1SPhil Sutter } else if (!obj && (tdata->objs[i].value.id != TEST_INSERT_FAIL)) {
604e859afe1SPhil Sutter pr_err(" object %d-%d not found!\n", key.tid, key.id);
605f4a3e90bSPhil Sutter err++;
606e859afe1SPhil Sutter } else if (obj && memcmp(&obj->value, &key, sizeof(key))) {
607e859afe1SPhil Sutter pr_err(" wrong object returned (got %d-%d, expected %d-%d)\n",
608e859afe1SPhil Sutter obj->value.tid, obj->value.id, key.tid, key.id);
609f4a3e90bSPhil Sutter err++;
610f4a3e90bSPhil Sutter }
611cd5b318dSPhil Sutter
612cd5b318dSPhil Sutter cond_resched();
613f4a3e90bSPhil Sutter }
614f4a3e90bSPhil Sutter return err;
615f4a3e90bSPhil Sutter }
616f4a3e90bSPhil Sutter
threadfunc(void * data)617f4a3e90bSPhil Sutter static int threadfunc(void *data)
618f4a3e90bSPhil Sutter {
6199e9089e5SPhil Sutter int i, step, err = 0, insert_retries = 0;
620f4a3e90bSPhil Sutter struct thread_data *tdata = data;
621f4a3e90bSPhil Sutter
622809c6705SArnd Bergmann if (atomic_dec_and_test(&startup_count))
623809c6705SArnd Bergmann wake_up(&startup_wait);
624809c6705SArnd Bergmann if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == -1)) {
625809c6705SArnd Bergmann pr_err(" thread[%d]: interrupted\n", tdata->id);
626809c6705SArnd Bergmann goto out;
627809c6705SArnd Bergmann }
628f4a3e90bSPhil Sutter
629f651616eSFlorian Westphal for (i = 0; i < tdata->entries; i++) {
630e859afe1SPhil Sutter tdata->objs[i].value.id = i;
631e859afe1SPhil Sutter tdata->objs[i].value.tid = tdata->id;
6327e936bd7SFlorian Westphal err = insert_retry(&ht, &tdata->objs[i], test_rht_params);
6339e9089e5SPhil Sutter if (err > 0) {
6349e9089e5SPhil Sutter insert_retries += err;
635f4a3e90bSPhil Sutter } else if (err) {
636f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_insert_fast failed\n",
637f4a3e90bSPhil Sutter tdata->id);
638f4a3e90bSPhil Sutter goto out;
639f4a3e90bSPhil Sutter }
640f4a3e90bSPhil Sutter }
6419e9089e5SPhil Sutter if (insert_retries)
6429e9089e5SPhil Sutter pr_info(" thread[%d]: %u insertions retried due to memory pressure\n",
6439e9089e5SPhil Sutter tdata->id, insert_retries);
644f4a3e90bSPhil Sutter
645f4a3e90bSPhil Sutter err = thread_lookup_test(tdata);
646f4a3e90bSPhil Sutter if (err) {
647f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_lookup_test failed\n",
648f4a3e90bSPhil Sutter tdata->id);
649f4a3e90bSPhil Sutter goto out;
650f4a3e90bSPhil Sutter }
651f4a3e90bSPhil Sutter
652f4a3e90bSPhil Sutter for (step = 10; step > 0; step--) {
653f651616eSFlorian Westphal for (i = 0; i < tdata->entries; i += step) {
654e859afe1SPhil Sutter if (tdata->objs[i].value.id == TEST_INSERT_FAIL)
655f4a3e90bSPhil Sutter continue;
656f4a3e90bSPhil Sutter err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
657f4a3e90bSPhil Sutter test_rht_params);
658f4a3e90bSPhil Sutter if (err) {
659f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_remove_fast failed\n",
660f4a3e90bSPhil Sutter tdata->id);
661f4a3e90bSPhil Sutter goto out;
662f4a3e90bSPhil Sutter }
663e859afe1SPhil Sutter tdata->objs[i].value.id = TEST_INSERT_FAIL;
664cd5b318dSPhil Sutter
665cd5b318dSPhil Sutter cond_resched();
666f4a3e90bSPhil Sutter }
667f4a3e90bSPhil Sutter err = thread_lookup_test(tdata);
668f4a3e90bSPhil Sutter if (err) {
669f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_lookup_test (2) failed\n",
670f4a3e90bSPhil Sutter tdata->id);
671f4a3e90bSPhil Sutter goto out;
672f4a3e90bSPhil Sutter }
673f4a3e90bSPhil Sutter }
674f4a3e90bSPhil Sutter out:
675f4a3e90bSPhil Sutter while (!kthread_should_stop()) {
676f4a3e90bSPhil Sutter set_current_state(TASK_INTERRUPTIBLE);
677f4a3e90bSPhil Sutter schedule();
678f4a3e90bSPhil Sutter }
679f4a3e90bSPhil Sutter return err;
680f4a3e90bSPhil Sutter }
681f4a3e90bSPhil Sutter
test_rht_init(void)6829d6dbe1bSGeert Uytterhoeven static int __init test_rht_init(void)
6839d6dbe1bSGeert Uytterhoeven {
684f651616eSFlorian Westphal unsigned int entries;
685f4a3e90bSPhil Sutter int i, err, started_threads = 0, failed_threads = 0;
6861aa661f5SThomas Graf u64 total_time = 0;
687f4a3e90bSPhil Sutter struct thread_data *tdata;
688f4a3e90bSPhil Sutter struct test_obj *objs;
6899d6dbe1bSGeert Uytterhoeven
690f651616eSFlorian Westphal if (parm_entries < 0)
691f651616eSFlorian Westphal parm_entries = 1;
692f651616eSFlorian Westphal
693f651616eSFlorian Westphal entries = min(parm_entries, MAX_ENTRIES);
6949d6dbe1bSGeert Uytterhoeven
6951aa661f5SThomas Graf test_rht_params.automatic_shrinking = shrinking;
69695e435afSPhil Sutter test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
6971aa661f5SThomas Graf test_rht_params.nelem_hint = size;
6981aa661f5SThomas Graf
699fad953ceSKees Cook objs = vzalloc(array_size(sizeof(struct test_obj),
700fad953ceSKees Cook test_rht_params.max_size + 1));
7017e936bd7SFlorian Westphal if (!objs)
7027e936bd7SFlorian Westphal return -ENOMEM;
7037e936bd7SFlorian Westphal
7041aa661f5SThomas Graf pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
7051aa661f5SThomas Graf size, max_size, shrinking);
7061aa661f5SThomas Graf
7071aa661f5SThomas Graf for (i = 0; i < runs; i++) {
7081aa661f5SThomas Graf s64 time;
7091aa661f5SThomas Graf
7101aa661f5SThomas Graf pr_info("Test %02d:\n", i);
7117e936bd7SFlorian Westphal memset(objs, 0, test_rht_params.max_size * sizeof(struct test_obj));
7127e936bd7SFlorian Westphal
713b182aa6eSHerbert Xu err = rhashtable_init(&ht, &test_rht_params);
7149d6dbe1bSGeert Uytterhoeven if (err < 0) {
7159d6dbe1bSGeert Uytterhoeven pr_warn("Test failed: Unable to initialize hashtable: %d\n",
7169d6dbe1bSGeert Uytterhoeven err);
7171aa661f5SThomas Graf continue;
7189d6dbe1bSGeert Uytterhoeven }
7199d6dbe1bSGeert Uytterhoeven
720f651616eSFlorian Westphal time = test_rhashtable(&ht, objs, entries);
7219d6dbe1bSGeert Uytterhoeven rhashtable_destroy(&ht);
7221aa661f5SThomas Graf if (time < 0) {
7237e936bd7SFlorian Westphal vfree(objs);
7241aa661f5SThomas Graf pr_warn("Test failed: return code %lld\n", time);
7251aa661f5SThomas Graf return -EINVAL;
7261aa661f5SThomas Graf }
7279d6dbe1bSGeert Uytterhoeven
7281aa661f5SThomas Graf total_time += time;
7291aa661f5SThomas Graf }
7301aa661f5SThomas Graf
731a6359bd8SFlorian Westphal pr_info("test if its possible to exceed max_size %d: %s\n",
732a6359bd8SFlorian Westphal test_rht_params.max_size, test_rhashtable_max(objs, entries) == 0 ?
733a6359bd8SFlorian Westphal "no, ok" : "YES, failed");
7347e936bd7SFlorian Westphal vfree(objs);
735a6359bd8SFlorian Westphal
7366decd63aSThomas Graf do_div(total_time, runs);
7376decd63aSThomas Graf pr_info("Average test time: %llu\n", total_time);
7381aa661f5SThomas Graf
739499ac3b6SPaul Blakey test_insert_duplicates_run();
740499ac3b6SPaul Blakey
741f4a3e90bSPhil Sutter if (!tcount)
742f4a3e90bSPhil Sutter return 0;
743f4a3e90bSPhil Sutter
744f4a3e90bSPhil Sutter pr_info("Testing concurrent rhashtable access from %d threads\n",
745f4a3e90bSPhil Sutter tcount);
746809c6705SArnd Bergmann atomic_set(&startup_count, tcount);
747fad953ceSKees Cook tdata = vzalloc(array_size(tcount, sizeof(struct thread_data)));
748f4a3e90bSPhil Sutter if (!tdata)
749f4a3e90bSPhil Sutter return -ENOMEM;
750fad953ceSKees Cook objs = vzalloc(array3_size(sizeof(struct test_obj), tcount, entries));
751f4a3e90bSPhil Sutter if (!objs) {
752f4a3e90bSPhil Sutter vfree(tdata);
753f4a3e90bSPhil Sutter return -ENOMEM;
754f4a3e90bSPhil Sutter }
755f4a3e90bSPhil Sutter
75695e435afSPhil Sutter test_rht_params.max_size = max_size ? :
75795e435afSPhil Sutter roundup_pow_of_two(tcount * entries);
758f4a3e90bSPhil Sutter err = rhashtable_init(&ht, &test_rht_params);
759f4a3e90bSPhil Sutter if (err < 0) {
760f4a3e90bSPhil Sutter pr_warn("Test failed: Unable to initialize hashtable: %d\n",
761f4a3e90bSPhil Sutter err);
762f4a3e90bSPhil Sutter vfree(tdata);
763f4a3e90bSPhil Sutter vfree(objs);
764f4a3e90bSPhil Sutter return -EINVAL;
765f4a3e90bSPhil Sutter }
766f4a3e90bSPhil Sutter for (i = 0; i < tcount; i++) {
767f4a3e90bSPhil Sutter tdata[i].id = i;
768f651616eSFlorian Westphal tdata[i].entries = entries;
769f4a3e90bSPhil Sutter tdata[i].objs = objs + i * entries;
770f4a3e90bSPhil Sutter tdata[i].task = kthread_run(threadfunc, &tdata[i],
771f4a3e90bSPhil Sutter "rhashtable_thrad[%d]", i);
772809c6705SArnd Bergmann if (IS_ERR(tdata[i].task)) {
773f4a3e90bSPhil Sutter pr_err(" kthread_run failed for thread %d\n", i);
774809c6705SArnd Bergmann atomic_dec(&startup_count);
775809c6705SArnd Bergmann } else {
776f4a3e90bSPhil Sutter started_threads++;
777f4a3e90bSPhil Sutter }
778809c6705SArnd Bergmann }
779809c6705SArnd Bergmann if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == 0))
780809c6705SArnd Bergmann pr_err(" wait_event interruptible failed\n");
781809c6705SArnd Bergmann /* count is 0 now, set it to -1 and wake up all threads together */
782809c6705SArnd Bergmann atomic_dec(&startup_count);
783809c6705SArnd Bergmann wake_up_all(&startup_wait);
784f4a3e90bSPhil Sutter for (i = 0; i < tcount; i++) {
785f4a3e90bSPhil Sutter if (IS_ERR(tdata[i].task))
786f4a3e90bSPhil Sutter continue;
787f4a3e90bSPhil Sutter if ((err = kthread_stop(tdata[i].task))) {
788f4a3e90bSPhil Sutter pr_warn("Test failed: thread %d returned: %d\n",
789f4a3e90bSPhil Sutter i, err);
790f4a3e90bSPhil Sutter failed_threads++;
791f4a3e90bSPhil Sutter }
792f4a3e90bSPhil Sutter }
793f4a3e90bSPhil Sutter rhashtable_destroy(&ht);
794f4a3e90bSPhil Sutter vfree(tdata);
795f4a3e90bSPhil Sutter vfree(objs);
796cdd4de37SFlorian Westphal
797cdd4de37SFlorian Westphal /*
798cdd4de37SFlorian Westphal * rhltable_remove is very expensive, default values can cause test
799cdd4de37SFlorian Westphal * to run for 2 minutes or more, use a smaller number instead.
800cdd4de37SFlorian Westphal */
801cdd4de37SFlorian Westphal err = test_rhltable(entries / 16);
802cdd4de37SFlorian Westphal pr_info("Started %d threads, %d failed, rhltable test returns %d\n",
803cdd4de37SFlorian Westphal started_threads, failed_threads, err);
8041aa661f5SThomas Graf return 0;
8059d6dbe1bSGeert Uytterhoeven }
8069d6dbe1bSGeert Uytterhoeven
test_rht_exit(void)8076dd0c165SDaniel Borkmann static void __exit test_rht_exit(void)
8086dd0c165SDaniel Borkmann {
8096dd0c165SDaniel Borkmann }
8106dd0c165SDaniel Borkmann
8119d6dbe1bSGeert Uytterhoeven module_init(test_rht_init);
8126dd0c165SDaniel Borkmann module_exit(test_rht_exit);
8139d6dbe1bSGeert Uytterhoeven
814*c6cab01dSJeff Johnson MODULE_DESCRIPTION("Resizable, Scalable, Concurrent Hash Table test module");
8159d6dbe1bSGeert Uytterhoeven MODULE_LICENSE("GPL v2");
816