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 <tgraf@suug.ch>
69d6dbe1bSGeert Uytterhoeven * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
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>
199d6dbe1bSGeert Uytterhoeven #include <linux/rhashtable.h>
209d6dbe1bSGeert Uytterhoeven #include <linux/slab.h>
21685a015eSThomas Graf #include <linux/sched.h>
22cdd4de37SFlorian Westphal #include <linux/random.h>
23f4a3e90bSPhil Sutter #include <linux/vmalloc.h>
24809c6705SArnd Bergmann #include <linux/wait.h>
259d6dbe1bSGeert Uytterhoeven
261aa661f5SThomas Graf #define MAX_ENTRIES 1000000
2767b7cbf4SThomas Graf #define TEST_INSERT_FAIL INT_MAX
281aa661f5SThomas Graf
29f651616eSFlorian Westphal static int parm_entries = 50000;
30f651616eSFlorian Westphal module_param(parm_entries, int, 0);
31f651616eSFlorian Westphal MODULE_PARM_DESC(parm_entries, "Number of entries to add (default: 50000)");
321aa661f5SThomas Graf
331aa661f5SThomas Graf static int runs = 4;
341aa661f5SThomas Graf module_param(runs, int, 0);
351aa661f5SThomas Graf MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
361aa661f5SThomas Graf
3795e435afSPhil Sutter static int max_size = 0;
381aa661f5SThomas Graf module_param(max_size, int, 0);
393b3bf80bSPhil Sutter MODULE_PARM_DESC(max_size, "Maximum table size (default: calculated)");
401aa661f5SThomas Graf
411aa661f5SThomas Graf static bool shrinking = false;
421aa661f5SThomas Graf module_param(shrinking, bool, 0);
431aa661f5SThomas Graf MODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)");
441aa661f5SThomas Graf
451aa661f5SThomas Graf static int size = 8;
461aa661f5SThomas Graf module_param(size, int, 0);
471aa661f5SThomas Graf MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
489d6dbe1bSGeert Uytterhoeven
49f4a3e90bSPhil Sutter static int tcount = 10;
50f4a3e90bSPhil Sutter module_param(tcount, int, 0);
51f4a3e90bSPhil Sutter MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
52f4a3e90bSPhil Sutter
53d662e037SPhil Sutter static bool enomem_retry = false;
54d662e037SPhil Sutter module_param(enomem_retry, bool, 0);
55d662e037SPhil Sutter MODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
56d662e037SPhil Sutter
57e859afe1SPhil Sutter struct test_obj_val {
58e859afe1SPhil Sutter int id;
59e859afe1SPhil Sutter int tid;
60e859afe1SPhil Sutter };
61e859afe1SPhil Sutter
629d6dbe1bSGeert Uytterhoeven struct test_obj {
63e859afe1SPhil Sutter struct test_obj_val value;
649d6dbe1bSGeert Uytterhoeven struct rhash_head node;
659d6dbe1bSGeert Uytterhoeven };
669d6dbe1bSGeert Uytterhoeven
67cdd4de37SFlorian Westphal struct test_obj_rhl {
68cdd4de37SFlorian Westphal struct test_obj_val value;
69cdd4de37SFlorian Westphal struct rhlist_head list_node;
70cdd4de37SFlorian Westphal };
71cdd4de37SFlorian Westphal
72f4a3e90bSPhil Sutter struct thread_data {
73f651616eSFlorian Westphal unsigned int entries;
74f4a3e90bSPhil Sutter int id;
75f4a3e90bSPhil Sutter struct task_struct *task;
76f4a3e90bSPhil Sutter struct test_obj *objs;
77f4a3e90bSPhil Sutter };
78f4a3e90bSPhil Sutter
my_hashfn(const void * data,u32 len,u32 seed)79499ac3b6SPaul Blakey static u32 my_hashfn(const void *data, u32 len, u32 seed)
80499ac3b6SPaul Blakey {
81499ac3b6SPaul Blakey const struct test_obj_rhl *obj = data;
82499ac3b6SPaul Blakey
839f9a7077SNeilBrown return (obj->value.id % 10);
84499ac3b6SPaul Blakey }
85499ac3b6SPaul Blakey
my_cmpfn(struct rhashtable_compare_arg * arg,const void * obj)86499ac3b6SPaul Blakey static int my_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
87499ac3b6SPaul Blakey {
88499ac3b6SPaul Blakey const struct test_obj_rhl *test_obj = obj;
89499ac3b6SPaul Blakey const struct test_obj_val *val = arg->key;
90499ac3b6SPaul Blakey
91499ac3b6SPaul Blakey return test_obj->value.id - val->id;
92499ac3b6SPaul Blakey }
93499ac3b6SPaul Blakey
941aa661f5SThomas Graf static struct rhashtable_params test_rht_params = {
95b182aa6eSHerbert Xu .head_offset = offsetof(struct test_obj, node),
96b182aa6eSHerbert Xu .key_offset = offsetof(struct test_obj, value),
97e859afe1SPhil Sutter .key_len = sizeof(struct test_obj_val),
98b182aa6eSHerbert Xu .hashfn = jhash,
99b182aa6eSHerbert Xu };
100b182aa6eSHerbert Xu
101499ac3b6SPaul Blakey static struct rhashtable_params test_rht_params_dup = {
102499ac3b6SPaul Blakey .head_offset = offsetof(struct test_obj_rhl, list_node),
103499ac3b6SPaul Blakey .key_offset = offsetof(struct test_obj_rhl, value),
104499ac3b6SPaul Blakey .key_len = sizeof(struct test_obj_val),
105499ac3b6SPaul Blakey .hashfn = jhash,
106499ac3b6SPaul Blakey .obj_hashfn = my_hashfn,
107499ac3b6SPaul Blakey .obj_cmpfn = my_cmpfn,
108499ac3b6SPaul Blakey .nelem_hint = 128,
109499ac3b6SPaul Blakey .automatic_shrinking = false,
110499ac3b6SPaul Blakey };
111499ac3b6SPaul Blakey
112809c6705SArnd Bergmann static atomic_t startup_count;
113809c6705SArnd Bergmann static DECLARE_WAIT_QUEUE_HEAD(startup_wait);
114f4a3e90bSPhil Sutter
insert_retry(struct rhashtable * ht,struct test_obj * obj,const struct rhashtable_params params)1157e936bd7SFlorian Westphal static int insert_retry(struct rhashtable *ht, struct test_obj *obj,
1169e9089e5SPhil Sutter const struct rhashtable_params params)
1179e9089e5SPhil Sutter {
118d662e037SPhil Sutter int err, retries = -1, enomem_retries = 0;
1199e9089e5SPhil Sutter
1209e9089e5SPhil Sutter do {
1219e9089e5SPhil Sutter retries++;
1229e9089e5SPhil Sutter cond_resched();
1237e936bd7SFlorian Westphal err = rhashtable_insert_fast(ht, &obj->node, params);
124d662e037SPhil Sutter if (err == -ENOMEM && enomem_retry) {
125d662e037SPhil Sutter enomem_retries++;
126d662e037SPhil Sutter err = -EBUSY;
127d662e037SPhil Sutter }
1289e9089e5SPhil Sutter } while (err == -EBUSY);
1299e9089e5SPhil Sutter
130d662e037SPhil Sutter if (enomem_retries)
131d662e037SPhil Sutter pr_info(" %u insertions retried after -ENOMEM\n",
132d662e037SPhil Sutter enomem_retries);
133d662e037SPhil Sutter
1349e9089e5SPhil Sutter return err ? : retries;
1359e9089e5SPhil Sutter }
1369e9089e5SPhil Sutter
test_rht_lookup(struct rhashtable * ht,struct test_obj * array,unsigned int entries)137f651616eSFlorian Westphal static int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array,
138f651616eSFlorian Westphal unsigned int entries)
1399d6dbe1bSGeert Uytterhoeven {
1409d6dbe1bSGeert Uytterhoeven unsigned int i;
1419d6dbe1bSGeert Uytterhoeven
142f651616eSFlorian Westphal for (i = 0; i < entries; i++) {
1439d6dbe1bSGeert Uytterhoeven struct test_obj *obj;
1449d6dbe1bSGeert Uytterhoeven bool expected = !(i % 2);
145e859afe1SPhil Sutter struct test_obj_val key = {
146e859afe1SPhil Sutter .id = i,
147e859afe1SPhil Sutter };
1489d6dbe1bSGeert Uytterhoeven
149e859afe1SPhil Sutter if (array[i / 2].value.id == TEST_INSERT_FAIL)
15067b7cbf4SThomas Graf expected = false;
15167b7cbf4SThomas Graf
152b182aa6eSHerbert Xu obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
1539d6dbe1bSGeert Uytterhoeven
1549d6dbe1bSGeert Uytterhoeven if (expected && !obj) {
155e859afe1SPhil Sutter pr_warn("Test failed: Could not find key %u\n", key.id);
1569d6dbe1bSGeert Uytterhoeven return -ENOENT;
1579d6dbe1bSGeert Uytterhoeven } else if (!expected && obj) {
1589d6dbe1bSGeert Uytterhoeven pr_warn("Test failed: Unexpected entry found for key %u\n",
159e859afe1SPhil Sutter key.id);
1609d6dbe1bSGeert Uytterhoeven return -EEXIST;
1619d6dbe1bSGeert Uytterhoeven } else if (expected && obj) {
162e859afe1SPhil Sutter if (obj->value.id != i) {
163c2c8a901SThomas Graf pr_warn("Test failed: Lookup value mismatch %u!=%u\n",
164e859afe1SPhil Sutter obj->value.id, i);
1659d6dbe1bSGeert Uytterhoeven return -EINVAL;
1669d6dbe1bSGeert Uytterhoeven }
1679d6dbe1bSGeert Uytterhoeven }
168685a015eSThomas Graf
169685a015eSThomas Graf cond_resched_rcu();
1709d6dbe1bSGeert Uytterhoeven }
1719d6dbe1bSGeert Uytterhoeven
1729d6dbe1bSGeert Uytterhoeven return 0;
1739d6dbe1bSGeert Uytterhoeven }
1749d6dbe1bSGeert Uytterhoeven
test_bucket_stats(struct rhashtable * ht,unsigned int entries)175f651616eSFlorian Westphal static void test_bucket_stats(struct rhashtable *ht, unsigned int entries)
1769d6dbe1bSGeert Uytterhoeven {
1776c4128f6SHerbert Xu unsigned int total = 0, chain_len = 0;
178246b23a7SThomas Graf struct rhashtable_iter hti;
1799d6dbe1bSGeert Uytterhoeven struct rhash_head *pos;
1809d6dbe1bSGeert Uytterhoeven
1816c4128f6SHerbert Xu rhashtable_walk_enter(ht, &hti);
18297a6ec4aSTom Herbert rhashtable_walk_start(&hti);
1839d6dbe1bSGeert Uytterhoeven
184246b23a7SThomas Graf while ((pos = rhashtable_walk_next(&hti))) {
185246b23a7SThomas Graf if (PTR_ERR(pos) == -EAGAIN) {
186246b23a7SThomas Graf pr_info("Info: encountered resize\n");
187246b23a7SThomas Graf chain_len++;
188246b23a7SThomas Graf continue;
189246b23a7SThomas Graf } else if (IS_ERR(pos)) {
190246b23a7SThomas Graf pr_warn("Test failed: rhashtable_walk_next() error: %ld\n",
191246b23a7SThomas Graf PTR_ERR(pos));
192246b23a7SThomas Graf break;
193246b23a7SThomas Graf }
194246b23a7SThomas Graf
1959d6dbe1bSGeert Uytterhoeven total++;
1969d6dbe1bSGeert Uytterhoeven }
1979d6dbe1bSGeert Uytterhoeven
198246b23a7SThomas Graf rhashtable_walk_stop(&hti);
199246b23a7SThomas Graf rhashtable_walk_exit(&hti);
2009d6dbe1bSGeert Uytterhoeven
201246b23a7SThomas Graf pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u\n",
202246b23a7SThomas Graf total, atomic_read(&ht->nelems), entries, chain_len);
2039d6dbe1bSGeert Uytterhoeven
2041aa661f5SThomas Graf if (total != atomic_read(&ht->nelems) || total != entries)
2059d6dbe1bSGeert Uytterhoeven pr_warn("Test failed: Total count mismatch ^^^");
2069d6dbe1bSGeert Uytterhoeven }
2079d6dbe1bSGeert Uytterhoeven
test_rhashtable(struct rhashtable * ht,struct test_obj * array,unsigned int entries)208f651616eSFlorian Westphal static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,
209f651616eSFlorian Westphal unsigned int entries)
2109d6dbe1bSGeert Uytterhoeven {
2119d6dbe1bSGeert Uytterhoeven struct test_obj *obj;
2129d6dbe1bSGeert Uytterhoeven int err;
2139e9089e5SPhil Sutter unsigned int i, insert_retries = 0;
2141aa661f5SThomas Graf s64 start, end;
2159d6dbe1bSGeert Uytterhoeven
2169d6dbe1bSGeert Uytterhoeven /*
2179d6dbe1bSGeert Uytterhoeven * Insertion Test:
2181aa661f5SThomas Graf * Insert entries into table with all keys even numbers
2199d6dbe1bSGeert Uytterhoeven */
2201aa661f5SThomas Graf pr_info(" Adding %d keys\n", entries);
2211aa661f5SThomas Graf start = ktime_get_ns();
2221aa661f5SThomas Graf for (i = 0; i < entries; i++) {
223fcc57020SThomas Graf struct test_obj *obj = &array[i];
2249d6dbe1bSGeert Uytterhoeven
225e859afe1SPhil Sutter obj->value.id = i * 2;
2267e936bd7SFlorian Westphal err = insert_retry(ht, obj, test_rht_params);
2279e9089e5SPhil Sutter if (err > 0)
2289e9089e5SPhil Sutter insert_retries += err;
2299e9089e5SPhil Sutter else if (err)
230fcc57020SThomas Graf return err;
2319d6dbe1bSGeert Uytterhoeven }
232685a015eSThomas Graf
2339e9089e5SPhil Sutter if (insert_retries)
2349e9089e5SPhil Sutter pr_info(" %u insertions retried due to memory pressure\n",
2359e9089e5SPhil Sutter insert_retries);
2369d6dbe1bSGeert Uytterhoeven
237f651616eSFlorian Westphal test_bucket_stats(ht, entries);
2389d6dbe1bSGeert Uytterhoeven rcu_read_lock();
239f651616eSFlorian Westphal test_rht_lookup(ht, array, entries);
2409d6dbe1bSGeert Uytterhoeven rcu_read_unlock();
2419d6dbe1bSGeert Uytterhoeven
242f651616eSFlorian Westphal test_bucket_stats(ht, entries);
2439d6dbe1bSGeert Uytterhoeven
2441aa661f5SThomas Graf pr_info(" Deleting %d keys\n", entries);
2451aa661f5SThomas Graf for (i = 0; i < entries; i++) {
24678369255SPhil Sutter struct test_obj_val key = {
24778369255SPhil Sutter .id = i * 2,
24878369255SPhil Sutter };
2499d6dbe1bSGeert Uytterhoeven
250e859afe1SPhil Sutter if (array[i].value.id != TEST_INSERT_FAIL) {
251b182aa6eSHerbert Xu obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
2529d6dbe1bSGeert Uytterhoeven BUG_ON(!obj);
2539d6dbe1bSGeert Uytterhoeven
254b182aa6eSHerbert Xu rhashtable_remove_fast(ht, &obj->node, test_rht_params);
2559d6dbe1bSGeert Uytterhoeven }
256685a015eSThomas Graf
257685a015eSThomas Graf cond_resched();
25867b7cbf4SThomas Graf }
2599d6dbe1bSGeert Uytterhoeven
2601aa661f5SThomas Graf end = ktime_get_ns();
2611aa661f5SThomas Graf pr_info(" Duration of test: %lld ns\n", end - start);
2621aa661f5SThomas Graf
2631aa661f5SThomas Graf return end - start;
2649d6dbe1bSGeert Uytterhoeven }
2659d6dbe1bSGeert Uytterhoeven
266b7f5e5c7SDaniel Borkmann static struct rhashtable ht;
267411d788aSFlorian Westphal static struct rhltable rhlt;
268cdd4de37SFlorian Westphal
test_rhltable(unsigned int entries)269cdd4de37SFlorian Westphal static int __init test_rhltable(unsigned int entries)
270cdd4de37SFlorian Westphal {
271cdd4de37SFlorian Westphal struct test_obj_rhl *rhl_test_objects;
272cdd4de37SFlorian Westphal unsigned long *obj_in_table;
273cdd4de37SFlorian Westphal unsigned int i, j, k;
274cdd4de37SFlorian Westphal int ret, err;
275cdd4de37SFlorian Westphal
276cdd4de37SFlorian Westphal if (entries == 0)
277cdd4de37SFlorian Westphal entries = 1;
278cdd4de37SFlorian Westphal
279fad953ceSKees Cook rhl_test_objects = vzalloc(array_size(entries,
280fad953ceSKees Cook sizeof(*rhl_test_objects)));
281cdd4de37SFlorian Westphal if (!rhl_test_objects)
282cdd4de37SFlorian Westphal return -ENOMEM;
283cdd4de37SFlorian Westphal
284cdd4de37SFlorian Westphal ret = -ENOMEM;
285fad953ceSKees Cook obj_in_table = vzalloc(array_size(sizeof(unsigned long),
286fad953ceSKees Cook BITS_TO_LONGS(entries)));
287cdd4de37SFlorian Westphal if (!obj_in_table)
288cdd4de37SFlorian Westphal goto out_free;
289cdd4de37SFlorian Westphal
290cdd4de37SFlorian Westphal err = rhltable_init(&rhlt, &test_rht_params);
291cdd4de37SFlorian Westphal if (WARN_ON(err))
292cdd4de37SFlorian Westphal goto out_free;
293cdd4de37SFlorian Westphal
294a251c17aSJason A. Donenfeld k = get_random_u32();
295cdd4de37SFlorian Westphal ret = 0;
296cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
297cdd4de37SFlorian Westphal rhl_test_objects[i].value.id = k;
298cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
299cdd4de37SFlorian Westphal test_rht_params);
300cdd4de37SFlorian Westphal if (WARN(err, "error %d on element %d\n", err, i))
301cdd4de37SFlorian Westphal break;
302cdd4de37SFlorian Westphal if (err == 0)
303cdd4de37SFlorian Westphal set_bit(i, obj_in_table);
304cdd4de37SFlorian Westphal }
305cdd4de37SFlorian Westphal
306cdd4de37SFlorian Westphal if (err)
307cdd4de37SFlorian Westphal ret = err;
308cdd4de37SFlorian Westphal
309cdd4de37SFlorian Westphal pr_info("test %d add/delete pairs into rhlist\n", entries);
310cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
311cdd4de37SFlorian Westphal struct rhlist_head *h, *pos;
312cdd4de37SFlorian Westphal struct test_obj_rhl *obj;
313cdd4de37SFlorian Westphal struct test_obj_val key = {
314cdd4de37SFlorian Westphal .id = k,
315cdd4de37SFlorian Westphal };
316cdd4de37SFlorian Westphal bool found;
317cdd4de37SFlorian Westphal
318cdd4de37SFlorian Westphal rcu_read_lock();
319cdd4de37SFlorian Westphal h = rhltable_lookup(&rhlt, &key, test_rht_params);
320cdd4de37SFlorian Westphal if (WARN(!h, "key not found during iteration %d of %d", i, entries)) {
321cdd4de37SFlorian Westphal rcu_read_unlock();
322cdd4de37SFlorian Westphal break;
323cdd4de37SFlorian Westphal }
324cdd4de37SFlorian Westphal
325cdd4de37SFlorian Westphal if (i) {
326cdd4de37SFlorian Westphal j = i - 1;
327cdd4de37SFlorian Westphal rhl_for_each_entry_rcu(obj, pos, h, list_node) {
328cdd4de37SFlorian Westphal if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone"))
329cdd4de37SFlorian Westphal break;
330cdd4de37SFlorian Westphal }
331cdd4de37SFlorian Westphal }
332cdd4de37SFlorian Westphal
333cdd4de37SFlorian Westphal cond_resched_rcu();
334cdd4de37SFlorian Westphal
335cdd4de37SFlorian Westphal found = false;
336cdd4de37SFlorian Westphal
337cdd4de37SFlorian Westphal rhl_for_each_entry_rcu(obj, pos, h, list_node) {
338cdd4de37SFlorian Westphal if (pos == &rhl_test_objects[i].list_node) {
339cdd4de37SFlorian Westphal found = true;
340cdd4de37SFlorian Westphal break;
341cdd4de37SFlorian Westphal }
342cdd4de37SFlorian Westphal }
343cdd4de37SFlorian Westphal
344cdd4de37SFlorian Westphal rcu_read_unlock();
345cdd4de37SFlorian Westphal
346cdd4de37SFlorian Westphal if (WARN(!found, "element %d not found", i))
347cdd4de37SFlorian Westphal break;
348cdd4de37SFlorian Westphal
349cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
350cdd4de37SFlorian Westphal WARN(err, "rhltable_remove: err %d for iteration %d\n", err, i);
351cdd4de37SFlorian Westphal if (err == 0)
352cdd4de37SFlorian Westphal clear_bit(i, obj_in_table);
353cdd4de37SFlorian Westphal }
354cdd4de37SFlorian Westphal
355cdd4de37SFlorian Westphal if (ret == 0 && err)
356cdd4de37SFlorian Westphal ret = err;
357cdd4de37SFlorian Westphal
358cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
359cdd4de37SFlorian Westphal WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i);
360cdd4de37SFlorian Westphal
361cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
362cdd4de37SFlorian Westphal test_rht_params);
363cdd4de37SFlorian Westphal if (WARN(err, "error %d on element %d\n", err, i))
364cdd4de37SFlorian Westphal break;
365cdd4de37SFlorian Westphal if (err == 0)
366cdd4de37SFlorian Westphal set_bit(i, obj_in_table);
367cdd4de37SFlorian Westphal }
368cdd4de37SFlorian Westphal
369cdd4de37SFlorian Westphal pr_info("test %d random rhlist add/delete operations\n", entries);
370cdd4de37SFlorian Westphal for (j = 0; j < entries; j++) {
3718032bf12SJason A. Donenfeld u32 i = get_random_u32_below(entries);
3728032bf12SJason A. Donenfeld u32 prand = get_random_u32_below(4);
373cdd4de37SFlorian Westphal
374cdd4de37SFlorian Westphal cond_resched();
375cdd4de37SFlorian Westphal
376cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
377cdd4de37SFlorian Westphal if (test_bit(i, obj_in_table)) {
378cdd4de37SFlorian Westphal clear_bit(i, obj_in_table);
379cdd4de37SFlorian Westphal if (WARN(err, "cannot remove element at slot %d", i))
380cdd4de37SFlorian Westphal continue;
381cdd4de37SFlorian Westphal } else {
38256b90fa0SColin Ian King if (WARN(err != -ENOENT, "removed non-existent element %d, error %d not %d",
383cdd4de37SFlorian Westphal i, err, -ENOENT))
384cdd4de37SFlorian Westphal continue;
385cdd4de37SFlorian Westphal }
386cdd4de37SFlorian Westphal
387cdd4de37SFlorian Westphal if (prand & 1) {
388cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
389cdd4de37SFlorian Westphal if (err == 0) {
390cdd4de37SFlorian Westphal if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i))
391cdd4de37SFlorian Westphal continue;
392cdd4de37SFlorian Westphal } else {
393cdd4de37SFlorian Westphal if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i))
394cdd4de37SFlorian Westphal continue;
395cdd4de37SFlorian Westphal }
396cdd4de37SFlorian Westphal }
397cdd4de37SFlorian Westphal
398c5f0a172SRolf Eike Beer if (prand & 2) {
3998032bf12SJason A. Donenfeld i = get_random_u32_below(entries);
400cdd4de37SFlorian Westphal if (test_bit(i, obj_in_table)) {
401cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
402cdd4de37SFlorian Westphal WARN(err, "cannot remove element at slot %d", i);
403cdd4de37SFlorian Westphal if (err == 0)
404cdd4de37SFlorian Westphal clear_bit(i, obj_in_table);
405cdd4de37SFlorian Westphal } else {
406cdd4de37SFlorian Westphal err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
407cdd4de37SFlorian Westphal WARN(err, "failed to insert object %d", i);
408cdd4de37SFlorian Westphal if (err == 0)
409cdd4de37SFlorian Westphal set_bit(i, obj_in_table);
410cdd4de37SFlorian Westphal }
411cdd4de37SFlorian Westphal }
412c5f0a172SRolf Eike Beer }
413cdd4de37SFlorian Westphal
414cdd4de37SFlorian Westphal for (i = 0; i < entries; i++) {
415cdd4de37SFlorian Westphal cond_resched();
416cdd4de37SFlorian Westphal err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
417cdd4de37SFlorian Westphal if (test_bit(i, obj_in_table)) {
418cdd4de37SFlorian Westphal if (WARN(err, "cannot remove element at slot %d", i))
419cdd4de37SFlorian Westphal continue;
420cdd4de37SFlorian Westphal } else {
42156b90fa0SColin Ian King if (WARN(err != -ENOENT, "removed non-existent element, error %d not %d",
422cdd4de37SFlorian Westphal err, -ENOENT))
423cdd4de37SFlorian Westphal continue;
424cdd4de37SFlorian Westphal }
425cdd4de37SFlorian Westphal }
426cdd4de37SFlorian Westphal
427cdd4de37SFlorian Westphal rhltable_destroy(&rhlt);
428cdd4de37SFlorian Westphal out_free:
429cdd4de37SFlorian Westphal vfree(rhl_test_objects);
430cdd4de37SFlorian Westphal vfree(obj_in_table);
431cdd4de37SFlorian Westphal return ret;
432cdd4de37SFlorian Westphal }
433b7f5e5c7SDaniel Borkmann
test_rhashtable_max(struct test_obj * array,unsigned int entries)434a6359bd8SFlorian Westphal static int __init test_rhashtable_max(struct test_obj *array,
435a6359bd8SFlorian Westphal unsigned int entries)
436a6359bd8SFlorian Westphal {
437*b084f6ccSJiapeng Chong unsigned int i;
438a6359bd8SFlorian Westphal int err;
439a6359bd8SFlorian Westphal
440a6359bd8SFlorian Westphal test_rht_params.max_size = roundup_pow_of_two(entries / 8);
441a6359bd8SFlorian Westphal err = rhashtable_init(&ht, &test_rht_params);
442a6359bd8SFlorian Westphal if (err)
443a6359bd8SFlorian Westphal return err;
444a6359bd8SFlorian Westphal
445a6359bd8SFlorian Westphal for (i = 0; i < ht.max_elems; i++) {
446a6359bd8SFlorian Westphal struct test_obj *obj = &array[i];
447a6359bd8SFlorian Westphal
448a6359bd8SFlorian Westphal obj->value.id = i * 2;
449a6359bd8SFlorian Westphal err = insert_retry(&ht, obj, test_rht_params);
450*b084f6ccSJiapeng Chong if (err < 0)
451a6359bd8SFlorian Westphal return err;
452a6359bd8SFlorian Westphal }
453a6359bd8SFlorian Westphal
454a6359bd8SFlorian Westphal err = insert_retry(&ht, &array[ht.max_elems], test_rht_params);
455a6359bd8SFlorian Westphal if (err == -E2BIG) {
456a6359bd8SFlorian Westphal err = 0;
457a6359bd8SFlorian Westphal } else {
458a6359bd8SFlorian Westphal pr_info("insert element %u should have failed with %d, got %d\n",
459a6359bd8SFlorian Westphal ht.max_elems, -E2BIG, err);
460a6359bd8SFlorian Westphal if (err == 0)
461a6359bd8SFlorian Westphal err = -1;
462a6359bd8SFlorian Westphal }
463a6359bd8SFlorian Westphal
464a6359bd8SFlorian Westphal rhashtable_destroy(&ht);
465a6359bd8SFlorian Westphal
466a6359bd8SFlorian Westphal return err;
467a6359bd8SFlorian Westphal }
468a6359bd8SFlorian Westphal
print_ht(struct rhltable * rhlt)469499ac3b6SPaul Blakey static unsigned int __init print_ht(struct rhltable *rhlt)
470499ac3b6SPaul Blakey {
471499ac3b6SPaul Blakey struct rhashtable *ht;
472499ac3b6SPaul Blakey const struct bucket_table *tbl;
473499ac3b6SPaul Blakey char buff[512] = "";
4744adec7f8SArnd Bergmann int offset = 0;
475499ac3b6SPaul Blakey unsigned int i, cnt = 0;
476499ac3b6SPaul Blakey
477499ac3b6SPaul Blakey ht = &rhlt->ht;
478cbab9012SNeilBrown /* Take the mutex to avoid RCU warning */
479cbab9012SNeilBrown mutex_lock(&ht->mutex);
480499ac3b6SPaul Blakey tbl = rht_dereference(ht->tbl, ht);
481499ac3b6SPaul Blakey for (i = 0; i < tbl->size; i++) {
482499ac3b6SPaul Blakey struct rhash_head *pos, *next;
483499ac3b6SPaul Blakey struct test_obj_rhl *p;
484499ac3b6SPaul Blakey
485adc6a3abSNeilBrown pos = rht_ptr_exclusive(tbl->buckets + i);
486499ac3b6SPaul Blakey next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL;
487499ac3b6SPaul Blakey
488499ac3b6SPaul Blakey if (!rht_is_a_nulls(pos)) {
4894adec7f8SArnd Bergmann offset += sprintf(buff + offset, "\nbucket[%d] -> ", i);
490499ac3b6SPaul Blakey }
491499ac3b6SPaul Blakey
492499ac3b6SPaul Blakey while (!rht_is_a_nulls(pos)) {
493499ac3b6SPaul Blakey struct rhlist_head *list = container_of(pos, struct rhlist_head, rhead);
4944adec7f8SArnd Bergmann offset += sprintf(buff + offset, "[[");
495499ac3b6SPaul Blakey do {
496499ac3b6SPaul Blakey pos = &list->rhead;
497499ac3b6SPaul Blakey list = rht_dereference(list->next, ht);
498499ac3b6SPaul Blakey p = rht_obj(ht, pos);
499499ac3b6SPaul Blakey
5004adec7f8SArnd Bergmann offset += sprintf(buff + offset, " val %d (tid=%d)%s", p->value.id, p->value.tid,
501499ac3b6SPaul Blakey list? ", " : " ");
502499ac3b6SPaul Blakey cnt++;
503499ac3b6SPaul Blakey } while (list);
504499ac3b6SPaul Blakey
505499ac3b6SPaul Blakey pos = next,
506499ac3b6SPaul Blakey next = !rht_is_a_nulls(pos) ?
507499ac3b6SPaul Blakey rht_dereference(pos->next, ht) : NULL;
508499ac3b6SPaul Blakey
5094adec7f8SArnd Bergmann offset += sprintf(buff + offset, "]]%s", !rht_is_a_nulls(pos) ? " -> " : "");
510499ac3b6SPaul Blakey }
511499ac3b6SPaul Blakey }
512499ac3b6SPaul Blakey printk(KERN_ERR "\n---- ht: ----%s\n-------------\n", buff);
513cbab9012SNeilBrown mutex_unlock(&ht->mutex);
514499ac3b6SPaul Blakey
515499ac3b6SPaul Blakey return cnt;
516499ac3b6SPaul Blakey }
517499ac3b6SPaul Blakey
test_insert_dup(struct test_obj_rhl * rhl_test_objects,int cnt,bool slow)518499ac3b6SPaul Blakey static int __init test_insert_dup(struct test_obj_rhl *rhl_test_objects,
519499ac3b6SPaul Blakey int cnt, bool slow)
520499ac3b6SPaul Blakey {
521fc42a689SBart Van Assche struct rhltable *rhlt;
522499ac3b6SPaul Blakey unsigned int i, ret;
523499ac3b6SPaul Blakey const char *key;
524499ac3b6SPaul Blakey int err = 0;
525499ac3b6SPaul Blakey
526fc42a689SBart Van Assche rhlt = kmalloc(sizeof(*rhlt), GFP_KERNEL);
527fc42a689SBart Van Assche if (WARN_ON(!rhlt))
528fc42a689SBart Van Assche return -EINVAL;
529fc42a689SBart Van Assche
530fc42a689SBart Van Assche err = rhltable_init(rhlt, &test_rht_params_dup);
531fc42a689SBart Van Assche if (WARN_ON(err)) {
532fc42a689SBart Van Assche kfree(rhlt);
533499ac3b6SPaul Blakey return err;
534fc42a689SBart Van Assche }
535499ac3b6SPaul Blakey
536499ac3b6SPaul Blakey for (i = 0; i < cnt; i++) {
537499ac3b6SPaul Blakey rhl_test_objects[i].value.tid = i;
538fc42a689SBart Van Assche key = rht_obj(&rhlt->ht, &rhl_test_objects[i].list_node.rhead);
539499ac3b6SPaul Blakey key += test_rht_params_dup.key_offset;
540499ac3b6SPaul Blakey
541499ac3b6SPaul Blakey if (slow) {
542fc42a689SBart Van Assche err = PTR_ERR(rhashtable_insert_slow(&rhlt->ht, key,
543499ac3b6SPaul Blakey &rhl_test_objects[i].list_node.rhead));
544499ac3b6SPaul Blakey if (err == -EAGAIN)
545499ac3b6SPaul Blakey err = 0;
546499ac3b6SPaul Blakey } else
547fc42a689SBart Van Assche err = rhltable_insert(rhlt,
548499ac3b6SPaul Blakey &rhl_test_objects[i].list_node,
549499ac3b6SPaul Blakey test_rht_params_dup);
550499ac3b6SPaul Blakey if (WARN(err, "error %d on element %d/%d (%s)\n", err, i, cnt, slow? "slow" : "fast"))
551499ac3b6SPaul Blakey goto skip_print;
552499ac3b6SPaul Blakey }
553499ac3b6SPaul Blakey
554fc42a689SBart Van Assche ret = print_ht(rhlt);
555499ac3b6SPaul Blakey WARN(ret != cnt, "missing rhltable elements (%d != %d, %s)\n", ret, cnt, slow? "slow" : "fast");
556499ac3b6SPaul Blakey
557499ac3b6SPaul Blakey skip_print:
558fc42a689SBart Van Assche rhltable_destroy(rhlt);
559fc42a689SBart Van Assche kfree(rhlt);
560499ac3b6SPaul Blakey
561499ac3b6SPaul Blakey return 0;
562499ac3b6SPaul Blakey }
563499ac3b6SPaul Blakey
test_insert_duplicates_run(void)564499ac3b6SPaul Blakey static int __init test_insert_duplicates_run(void)
565499ac3b6SPaul Blakey {
566499ac3b6SPaul Blakey struct test_obj_rhl rhl_test_objects[3] = {};
567499ac3b6SPaul Blakey
568499ac3b6SPaul Blakey pr_info("test inserting duplicates\n");
569499ac3b6SPaul Blakey
570499ac3b6SPaul Blakey /* two different values that map to same bucket */
571499ac3b6SPaul Blakey rhl_test_objects[0].value.id = 1;
572499ac3b6SPaul Blakey rhl_test_objects[1].value.id = 21;
573499ac3b6SPaul Blakey
574499ac3b6SPaul Blakey /* and another duplicate with same as [0] value
575499ac3b6SPaul Blakey * which will be second on the bucket list */
576499ac3b6SPaul Blakey rhl_test_objects[2].value.id = rhl_test_objects[0].value.id;
577499ac3b6SPaul Blakey
578499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 2, false);
579499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 3, false);
580499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 2, true);
581499ac3b6SPaul Blakey test_insert_dup(rhl_test_objects, 3, true);
582499ac3b6SPaul Blakey
583499ac3b6SPaul Blakey return 0;
584499ac3b6SPaul Blakey }
585499ac3b6SPaul Blakey
thread_lookup_test(struct thread_data * tdata)586f4a3e90bSPhil Sutter static int thread_lookup_test(struct thread_data *tdata)
587f4a3e90bSPhil Sutter {
588f651616eSFlorian Westphal unsigned int entries = tdata->entries;
589f4a3e90bSPhil Sutter int i, err = 0;
590f4a3e90bSPhil Sutter
591f4a3e90bSPhil Sutter for (i = 0; i < entries; i++) {
592f4a3e90bSPhil Sutter struct test_obj *obj;
593e859afe1SPhil Sutter struct test_obj_val key = {
594e859afe1SPhil Sutter .id = i,
595e859afe1SPhil Sutter .tid = tdata->id,
596e859afe1SPhil Sutter };
597f4a3e90bSPhil Sutter
598f4a3e90bSPhil Sutter obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
599e859afe1SPhil Sutter if (obj && (tdata->objs[i].value.id == TEST_INSERT_FAIL)) {
600e859afe1SPhil Sutter pr_err(" found unexpected object %d-%d\n", key.tid, key.id);
601f4a3e90bSPhil Sutter err++;
602e859afe1SPhil Sutter } else if (!obj && (tdata->objs[i].value.id != TEST_INSERT_FAIL)) {
603e859afe1SPhil Sutter pr_err(" object %d-%d not found!\n", key.tid, key.id);
604f4a3e90bSPhil Sutter err++;
605e859afe1SPhil Sutter } else if (obj && memcmp(&obj->value, &key, sizeof(key))) {
606e859afe1SPhil Sutter pr_err(" wrong object returned (got %d-%d, expected %d-%d)\n",
607e859afe1SPhil Sutter obj->value.tid, obj->value.id, key.tid, key.id);
608f4a3e90bSPhil Sutter err++;
609f4a3e90bSPhil Sutter }
610cd5b318dSPhil Sutter
611cd5b318dSPhil Sutter cond_resched();
612f4a3e90bSPhil Sutter }
613f4a3e90bSPhil Sutter return err;
614f4a3e90bSPhil Sutter }
615f4a3e90bSPhil Sutter
threadfunc(void * data)616f4a3e90bSPhil Sutter static int threadfunc(void *data)
617f4a3e90bSPhil Sutter {
6189e9089e5SPhil Sutter int i, step, err = 0, insert_retries = 0;
619f4a3e90bSPhil Sutter struct thread_data *tdata = data;
620f4a3e90bSPhil Sutter
621809c6705SArnd Bergmann if (atomic_dec_and_test(&startup_count))
622809c6705SArnd Bergmann wake_up(&startup_wait);
623809c6705SArnd Bergmann if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == -1)) {
624809c6705SArnd Bergmann pr_err(" thread[%d]: interrupted\n", tdata->id);
625809c6705SArnd Bergmann goto out;
626809c6705SArnd Bergmann }
627f4a3e90bSPhil Sutter
628f651616eSFlorian Westphal for (i = 0; i < tdata->entries; i++) {
629e859afe1SPhil Sutter tdata->objs[i].value.id = i;
630e859afe1SPhil Sutter tdata->objs[i].value.tid = tdata->id;
6317e936bd7SFlorian Westphal err = insert_retry(&ht, &tdata->objs[i], test_rht_params);
6329e9089e5SPhil Sutter if (err > 0) {
6339e9089e5SPhil Sutter insert_retries += err;
634f4a3e90bSPhil Sutter } else if (err) {
635f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_insert_fast failed\n",
636f4a3e90bSPhil Sutter tdata->id);
637f4a3e90bSPhil Sutter goto out;
638f4a3e90bSPhil Sutter }
639f4a3e90bSPhil Sutter }
6409e9089e5SPhil Sutter if (insert_retries)
6419e9089e5SPhil Sutter pr_info(" thread[%d]: %u insertions retried due to memory pressure\n",
6429e9089e5SPhil Sutter tdata->id, insert_retries);
643f4a3e90bSPhil Sutter
644f4a3e90bSPhil Sutter err = thread_lookup_test(tdata);
645f4a3e90bSPhil Sutter if (err) {
646f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_lookup_test failed\n",
647f4a3e90bSPhil Sutter tdata->id);
648f4a3e90bSPhil Sutter goto out;
649f4a3e90bSPhil Sutter }
650f4a3e90bSPhil Sutter
651f4a3e90bSPhil Sutter for (step = 10; step > 0; step--) {
652f651616eSFlorian Westphal for (i = 0; i < tdata->entries; i += step) {
653e859afe1SPhil Sutter if (tdata->objs[i].value.id == TEST_INSERT_FAIL)
654f4a3e90bSPhil Sutter continue;
655f4a3e90bSPhil Sutter err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
656f4a3e90bSPhil Sutter test_rht_params);
657f4a3e90bSPhil Sutter if (err) {
658f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_remove_fast failed\n",
659f4a3e90bSPhil Sutter tdata->id);
660f4a3e90bSPhil Sutter goto out;
661f4a3e90bSPhil Sutter }
662e859afe1SPhil Sutter tdata->objs[i].value.id = TEST_INSERT_FAIL;
663cd5b318dSPhil Sutter
664cd5b318dSPhil Sutter cond_resched();
665f4a3e90bSPhil Sutter }
666f4a3e90bSPhil Sutter err = thread_lookup_test(tdata);
667f4a3e90bSPhil Sutter if (err) {
668f4a3e90bSPhil Sutter pr_err(" thread[%d]: rhashtable_lookup_test (2) failed\n",
669f4a3e90bSPhil Sutter tdata->id);
670f4a3e90bSPhil Sutter goto out;
671f4a3e90bSPhil Sutter }
672f4a3e90bSPhil Sutter }
673f4a3e90bSPhil Sutter out:
674f4a3e90bSPhil Sutter while (!kthread_should_stop()) {
675f4a3e90bSPhil Sutter set_current_state(TASK_INTERRUPTIBLE);
676f4a3e90bSPhil Sutter schedule();
677f4a3e90bSPhil Sutter }
678f4a3e90bSPhil Sutter return err;
679f4a3e90bSPhil Sutter }
680f4a3e90bSPhil Sutter
test_rht_init(void)6819d6dbe1bSGeert Uytterhoeven static int __init test_rht_init(void)
6829d6dbe1bSGeert Uytterhoeven {
683f651616eSFlorian Westphal unsigned int entries;
684f4a3e90bSPhil Sutter int i, err, started_threads = 0, failed_threads = 0;
6851aa661f5SThomas Graf u64 total_time = 0;
686f4a3e90bSPhil Sutter struct thread_data *tdata;
687f4a3e90bSPhil Sutter struct test_obj *objs;
6889d6dbe1bSGeert Uytterhoeven
689f651616eSFlorian Westphal if (parm_entries < 0)
690f651616eSFlorian Westphal parm_entries = 1;
691f651616eSFlorian Westphal
692f651616eSFlorian Westphal entries = min(parm_entries, MAX_ENTRIES);
6939d6dbe1bSGeert Uytterhoeven
6941aa661f5SThomas Graf test_rht_params.automatic_shrinking = shrinking;
69595e435afSPhil Sutter test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
6961aa661f5SThomas Graf test_rht_params.nelem_hint = size;
6971aa661f5SThomas Graf
698fad953ceSKees Cook objs = vzalloc(array_size(sizeof(struct test_obj),
699fad953ceSKees Cook test_rht_params.max_size + 1));
7007e936bd7SFlorian Westphal if (!objs)
7017e936bd7SFlorian Westphal return -ENOMEM;
7027e936bd7SFlorian Westphal
7031aa661f5SThomas Graf pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
7041aa661f5SThomas Graf size, max_size, shrinking);
7051aa661f5SThomas Graf
7061aa661f5SThomas Graf for (i = 0; i < runs; i++) {
7071aa661f5SThomas Graf s64 time;
7081aa661f5SThomas Graf
7091aa661f5SThomas Graf pr_info("Test %02d:\n", i);
7107e936bd7SFlorian Westphal memset(objs, 0, test_rht_params.max_size * sizeof(struct test_obj));
7117e936bd7SFlorian Westphal
712b182aa6eSHerbert Xu err = rhashtable_init(&ht, &test_rht_params);
7139d6dbe1bSGeert Uytterhoeven if (err < 0) {
7149d6dbe1bSGeert Uytterhoeven pr_warn("Test failed: Unable to initialize hashtable: %d\n",
7159d6dbe1bSGeert Uytterhoeven err);
7161aa661f5SThomas Graf continue;
7179d6dbe1bSGeert Uytterhoeven }
7189d6dbe1bSGeert Uytterhoeven
719f651616eSFlorian Westphal time = test_rhashtable(&ht, objs, entries);
7209d6dbe1bSGeert Uytterhoeven rhashtable_destroy(&ht);
7211aa661f5SThomas Graf if (time < 0) {
7227e936bd7SFlorian Westphal vfree(objs);
7231aa661f5SThomas Graf pr_warn("Test failed: return code %lld\n", time);
7241aa661f5SThomas Graf return -EINVAL;
7251aa661f5SThomas Graf }
7269d6dbe1bSGeert Uytterhoeven
7271aa661f5SThomas Graf total_time += time;
7281aa661f5SThomas Graf }
7291aa661f5SThomas Graf
730a6359bd8SFlorian Westphal pr_info("test if its possible to exceed max_size %d: %s\n",
731a6359bd8SFlorian Westphal test_rht_params.max_size, test_rhashtable_max(objs, entries) == 0 ?
732a6359bd8SFlorian Westphal "no, ok" : "YES, failed");
7337e936bd7SFlorian Westphal vfree(objs);
734a6359bd8SFlorian Westphal
7356decd63aSThomas Graf do_div(total_time, runs);
7366decd63aSThomas Graf pr_info("Average test time: %llu\n", total_time);
7371aa661f5SThomas Graf
738499ac3b6SPaul Blakey test_insert_duplicates_run();
739499ac3b6SPaul Blakey
740f4a3e90bSPhil Sutter if (!tcount)
741f4a3e90bSPhil Sutter return 0;
742f4a3e90bSPhil Sutter
743f4a3e90bSPhil Sutter pr_info("Testing concurrent rhashtable access from %d threads\n",
744f4a3e90bSPhil Sutter tcount);
745809c6705SArnd Bergmann atomic_set(&startup_count, tcount);
746fad953ceSKees Cook tdata = vzalloc(array_size(tcount, sizeof(struct thread_data)));
747f4a3e90bSPhil Sutter if (!tdata)
748f4a3e90bSPhil Sutter return -ENOMEM;
749fad953ceSKees Cook objs = vzalloc(array3_size(sizeof(struct test_obj), tcount, entries));
750f4a3e90bSPhil Sutter if (!objs) {
751f4a3e90bSPhil Sutter vfree(tdata);
752f4a3e90bSPhil Sutter return -ENOMEM;
753f4a3e90bSPhil Sutter }
754f4a3e90bSPhil Sutter
75595e435afSPhil Sutter test_rht_params.max_size = max_size ? :
75695e435afSPhil Sutter roundup_pow_of_two(tcount * entries);
757f4a3e90bSPhil Sutter err = rhashtable_init(&ht, &test_rht_params);
758f4a3e90bSPhil Sutter if (err < 0) {
759f4a3e90bSPhil Sutter pr_warn("Test failed: Unable to initialize hashtable: %d\n",
760f4a3e90bSPhil Sutter err);
761f4a3e90bSPhil Sutter vfree(tdata);
762f4a3e90bSPhil Sutter vfree(objs);
763f4a3e90bSPhil Sutter return -EINVAL;
764f4a3e90bSPhil Sutter }
765f4a3e90bSPhil Sutter for (i = 0; i < tcount; i++) {
766f4a3e90bSPhil Sutter tdata[i].id = i;
767f651616eSFlorian Westphal tdata[i].entries = entries;
768f4a3e90bSPhil Sutter tdata[i].objs = objs + i * entries;
769f4a3e90bSPhil Sutter tdata[i].task = kthread_run(threadfunc, &tdata[i],
770f4a3e90bSPhil Sutter "rhashtable_thrad[%d]", i);
771809c6705SArnd Bergmann if (IS_ERR(tdata[i].task)) {
772f4a3e90bSPhil Sutter pr_err(" kthread_run failed for thread %d\n", i);
773809c6705SArnd Bergmann atomic_dec(&startup_count);
774809c6705SArnd Bergmann } else {
775f4a3e90bSPhil Sutter started_threads++;
776f4a3e90bSPhil Sutter }
777809c6705SArnd Bergmann }
778809c6705SArnd Bergmann if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == 0))
779809c6705SArnd Bergmann pr_err(" wait_event interruptible failed\n");
780809c6705SArnd Bergmann /* count is 0 now, set it to -1 and wake up all threads together */
781809c6705SArnd Bergmann atomic_dec(&startup_count);
782809c6705SArnd Bergmann wake_up_all(&startup_wait);
783f4a3e90bSPhil Sutter for (i = 0; i < tcount; i++) {
784f4a3e90bSPhil Sutter if (IS_ERR(tdata[i].task))
785f4a3e90bSPhil Sutter continue;
786f4a3e90bSPhil Sutter if ((err = kthread_stop(tdata[i].task))) {
787f4a3e90bSPhil Sutter pr_warn("Test failed: thread %d returned: %d\n",
788f4a3e90bSPhil Sutter i, err);
789f4a3e90bSPhil Sutter failed_threads++;
790f4a3e90bSPhil Sutter }
791f4a3e90bSPhil Sutter }
792f4a3e90bSPhil Sutter rhashtable_destroy(&ht);
793f4a3e90bSPhil Sutter vfree(tdata);
794f4a3e90bSPhil Sutter vfree(objs);
795cdd4de37SFlorian Westphal
796cdd4de37SFlorian Westphal /*
797cdd4de37SFlorian Westphal * rhltable_remove is very expensive, default values can cause test
798cdd4de37SFlorian Westphal * to run for 2 minutes or more, use a smaller number instead.
799cdd4de37SFlorian Westphal */
800cdd4de37SFlorian Westphal err = test_rhltable(entries / 16);
801cdd4de37SFlorian Westphal pr_info("Started %d threads, %d failed, rhltable test returns %d\n",
802cdd4de37SFlorian Westphal started_threads, failed_threads, err);
8031aa661f5SThomas Graf return 0;
8049d6dbe1bSGeert Uytterhoeven }
8059d6dbe1bSGeert Uytterhoeven
test_rht_exit(void)8066dd0c165SDaniel Borkmann static void __exit test_rht_exit(void)
8076dd0c165SDaniel Borkmann {
8086dd0c165SDaniel Borkmann }
8096dd0c165SDaniel Borkmann
8109d6dbe1bSGeert Uytterhoeven module_init(test_rht_init);
8116dd0c165SDaniel Borkmann module_exit(test_rht_exit);
8129d6dbe1bSGeert Uytterhoeven
8139d6dbe1bSGeert Uytterhoeven MODULE_LICENSE("GPL v2");
814