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