xref: /linux-6.15/lib/test_rhashtable.c (revision c6cab01d)
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