xref: /linux/lib/test_rhashtable.c (revision 84a73014d86fd660822a20c032625e3afe99ca58)
19d6dbe1bSGeert Uytterhoeven /*
29d6dbe1bSGeert Uytterhoeven  * Resizable, Scalable, Concurrent Hash Table
39d6dbe1bSGeert Uytterhoeven  *
41aa661f5SThomas Graf  * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
59d6dbe1bSGeert Uytterhoeven  * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
69d6dbe1bSGeert Uytterhoeven  *
79d6dbe1bSGeert Uytterhoeven  * This program is free software; you can redistribute it and/or modify
89d6dbe1bSGeert Uytterhoeven  * it under the terms of the GNU General Public License version 2 as
99d6dbe1bSGeert Uytterhoeven  * published by the Free Software Foundation.
109d6dbe1bSGeert Uytterhoeven  */
119d6dbe1bSGeert Uytterhoeven 
129d6dbe1bSGeert Uytterhoeven /**************************************************************************
139d6dbe1bSGeert Uytterhoeven  * Self Test
149d6dbe1bSGeert Uytterhoeven  **************************************************************************/
159d6dbe1bSGeert Uytterhoeven 
169d6dbe1bSGeert Uytterhoeven #include <linux/init.h>
179d6dbe1bSGeert Uytterhoeven #include <linux/jhash.h>
189d6dbe1bSGeert Uytterhoeven #include <linux/kernel.h>
19f4a3e90bSPhil Sutter #include <linux/kthread.h>
209d6dbe1bSGeert Uytterhoeven #include <linux/module.h>
219d6dbe1bSGeert Uytterhoeven #include <linux/rcupdate.h>
229d6dbe1bSGeert Uytterhoeven #include <linux/rhashtable.h>
23f4a3e90bSPhil Sutter #include <linux/semaphore.h>
249d6dbe1bSGeert Uytterhoeven #include <linux/slab.h>
25685a015eSThomas Graf #include <linux/sched.h>
26f4a3e90bSPhil Sutter #include <linux/vmalloc.h>
279d6dbe1bSGeert Uytterhoeven 
281aa661f5SThomas Graf #define MAX_ENTRIES	1000000
2967b7cbf4SThomas Graf #define TEST_INSERT_FAIL INT_MAX
301aa661f5SThomas Graf 
311aa661f5SThomas Graf static int entries = 50000;
321aa661f5SThomas Graf module_param(entries, int, 0);
331aa661f5SThomas Graf MODULE_PARM_DESC(entries, "Number of entries to add (default: 50000)");
341aa661f5SThomas Graf 
351aa661f5SThomas Graf static int runs = 4;
361aa661f5SThomas Graf module_param(runs, int, 0);
371aa661f5SThomas Graf MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
381aa661f5SThomas Graf 
391aa661f5SThomas Graf static int max_size = 65536;
401aa661f5SThomas Graf module_param(max_size, int, 0);
411aa661f5SThomas Graf MODULE_PARM_DESC(runs, "Maximum table size (default: 65536)");
421aa661f5SThomas Graf 
431aa661f5SThomas Graf static bool shrinking = false;
441aa661f5SThomas Graf module_param(shrinking, bool, 0);
451aa661f5SThomas Graf MODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)");
461aa661f5SThomas Graf 
471aa661f5SThomas Graf static int size = 8;
481aa661f5SThomas Graf module_param(size, int, 0);
491aa661f5SThomas Graf MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
509d6dbe1bSGeert Uytterhoeven 
51f4a3e90bSPhil Sutter static int tcount = 10;
52f4a3e90bSPhil Sutter module_param(tcount, int, 0);
53f4a3e90bSPhil Sutter MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
54f4a3e90bSPhil Sutter 
559d6dbe1bSGeert Uytterhoeven struct test_obj {
569d6dbe1bSGeert Uytterhoeven 	int			value;
579d6dbe1bSGeert Uytterhoeven 	struct rhash_head	node;
589d6dbe1bSGeert Uytterhoeven };
599d6dbe1bSGeert Uytterhoeven 
60f4a3e90bSPhil Sutter struct thread_data {
61f4a3e90bSPhil Sutter 	int id;
62f4a3e90bSPhil Sutter 	struct task_struct *task;
63f4a3e90bSPhil Sutter 	struct test_obj *objs;
64f4a3e90bSPhil Sutter };
65f4a3e90bSPhil Sutter 
66fcc57020SThomas Graf static struct test_obj array[MAX_ENTRIES];
67fcc57020SThomas Graf 
681aa661f5SThomas Graf static struct rhashtable_params test_rht_params = {
69b182aa6eSHerbert Xu 	.head_offset = offsetof(struct test_obj, node),
70b182aa6eSHerbert Xu 	.key_offset = offsetof(struct test_obj, value),
71b182aa6eSHerbert Xu 	.key_len = sizeof(int),
72b182aa6eSHerbert Xu 	.hashfn = jhash,
73b182aa6eSHerbert Xu 	.nulls_base = (3U << RHT_BASE_SHIFT),
74b182aa6eSHerbert Xu };
75b182aa6eSHerbert Xu 
76f4a3e90bSPhil Sutter static struct semaphore prestart_sem;
77f4a3e90bSPhil Sutter static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0);
78f4a3e90bSPhil Sutter 
799d6dbe1bSGeert Uytterhoeven static int __init test_rht_lookup(struct rhashtable *ht)
809d6dbe1bSGeert Uytterhoeven {
819d6dbe1bSGeert Uytterhoeven 	unsigned int i;
829d6dbe1bSGeert Uytterhoeven 
831aa661f5SThomas Graf 	for (i = 0; i < entries * 2; i++) {
849d6dbe1bSGeert Uytterhoeven 		struct test_obj *obj;
859d6dbe1bSGeert Uytterhoeven 		bool expected = !(i % 2);
869d6dbe1bSGeert Uytterhoeven 		u32 key = i;
879d6dbe1bSGeert Uytterhoeven 
8867b7cbf4SThomas Graf 		if (array[i / 2].value == TEST_INSERT_FAIL)
8967b7cbf4SThomas Graf 			expected = false;
9067b7cbf4SThomas Graf 
91b182aa6eSHerbert Xu 		obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
929d6dbe1bSGeert Uytterhoeven 
939d6dbe1bSGeert Uytterhoeven 		if (expected && !obj) {
949d6dbe1bSGeert Uytterhoeven 			pr_warn("Test failed: Could not find key %u\n", key);
959d6dbe1bSGeert Uytterhoeven 			return -ENOENT;
969d6dbe1bSGeert Uytterhoeven 		} else if (!expected && obj) {
979d6dbe1bSGeert Uytterhoeven 			pr_warn("Test failed: Unexpected entry found for key %u\n",
989d6dbe1bSGeert Uytterhoeven 				key);
999d6dbe1bSGeert Uytterhoeven 			return -EEXIST;
1009d6dbe1bSGeert Uytterhoeven 		} else if (expected && obj) {
101c2c8a901SThomas Graf 			if (obj->value != i) {
102c2c8a901SThomas Graf 				pr_warn("Test failed: Lookup value mismatch %u!=%u\n",
103c2c8a901SThomas Graf 					obj->value, i);
1049d6dbe1bSGeert Uytterhoeven 				return -EINVAL;
1059d6dbe1bSGeert Uytterhoeven 			}
1069d6dbe1bSGeert Uytterhoeven 		}
107685a015eSThomas Graf 
108685a015eSThomas Graf 		cond_resched_rcu();
1099d6dbe1bSGeert Uytterhoeven 	}
1109d6dbe1bSGeert Uytterhoeven 
1119d6dbe1bSGeert Uytterhoeven 	return 0;
1129d6dbe1bSGeert Uytterhoeven }
1139d6dbe1bSGeert Uytterhoeven 
114246b23a7SThomas Graf static void test_bucket_stats(struct rhashtable *ht)
1159d6dbe1bSGeert Uytterhoeven {
116246b23a7SThomas Graf 	unsigned int err, total = 0, chain_len = 0;
117246b23a7SThomas Graf 	struct rhashtable_iter hti;
1189d6dbe1bSGeert Uytterhoeven 	struct rhash_head *pos;
1199d6dbe1bSGeert Uytterhoeven 
120246b23a7SThomas Graf 	err = rhashtable_walk_init(ht, &hti);
121246b23a7SThomas Graf 	if (err) {
122246b23a7SThomas Graf 		pr_warn("Test failed: allocation error");
123246b23a7SThomas Graf 		return;
124246b23a7SThomas Graf 	}
1259d6dbe1bSGeert Uytterhoeven 
126246b23a7SThomas Graf 	err = rhashtable_walk_start(&hti);
127246b23a7SThomas Graf 	if (err && err != -EAGAIN) {
128246b23a7SThomas Graf 		pr_warn("Test failed: iterator failed: %d\n", err);
129246b23a7SThomas Graf 		return;
130246b23a7SThomas Graf 	}
1319d6dbe1bSGeert Uytterhoeven 
132246b23a7SThomas Graf 	while ((pos = rhashtable_walk_next(&hti))) {
133246b23a7SThomas Graf 		if (PTR_ERR(pos) == -EAGAIN) {
134246b23a7SThomas Graf 			pr_info("Info: encountered resize\n");
135246b23a7SThomas Graf 			chain_len++;
136246b23a7SThomas Graf 			continue;
137246b23a7SThomas Graf 		} else if (IS_ERR(pos)) {
138246b23a7SThomas Graf 			pr_warn("Test failed: rhashtable_walk_next() error: %ld\n",
139246b23a7SThomas Graf 				PTR_ERR(pos));
140246b23a7SThomas Graf 			break;
141246b23a7SThomas Graf 		}
142246b23a7SThomas Graf 
1439d6dbe1bSGeert Uytterhoeven 		total++;
1449d6dbe1bSGeert Uytterhoeven 	}
1459d6dbe1bSGeert Uytterhoeven 
146246b23a7SThomas Graf 	rhashtable_walk_stop(&hti);
147246b23a7SThomas Graf 	rhashtable_walk_exit(&hti);
1489d6dbe1bSGeert Uytterhoeven 
149246b23a7SThomas Graf 	pr_info("  Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u\n",
150246b23a7SThomas Graf 		total, atomic_read(&ht->nelems), entries, chain_len);
1519d6dbe1bSGeert Uytterhoeven 
1521aa661f5SThomas Graf 	if (total != atomic_read(&ht->nelems) || total != entries)
1539d6dbe1bSGeert Uytterhoeven 		pr_warn("Test failed: Total count mismatch ^^^");
1549d6dbe1bSGeert Uytterhoeven }
1559d6dbe1bSGeert Uytterhoeven 
1561aa661f5SThomas Graf static s64 __init test_rhashtable(struct rhashtable *ht)
1579d6dbe1bSGeert Uytterhoeven {
1589d6dbe1bSGeert Uytterhoeven 	struct test_obj *obj;
1599d6dbe1bSGeert Uytterhoeven 	int err;
16067b7cbf4SThomas Graf 	unsigned int i, insert_fails = 0;
1611aa661f5SThomas Graf 	s64 start, end;
1629d6dbe1bSGeert Uytterhoeven 
1639d6dbe1bSGeert Uytterhoeven 	/*
1649d6dbe1bSGeert Uytterhoeven 	 * Insertion Test:
1651aa661f5SThomas Graf 	 * Insert entries into table with all keys even numbers
1669d6dbe1bSGeert Uytterhoeven 	 */
1671aa661f5SThomas Graf 	pr_info("  Adding %d keys\n", entries);
1681aa661f5SThomas Graf 	start = ktime_get_ns();
1691aa661f5SThomas Graf 	for (i = 0; i < entries; i++) {
170fcc57020SThomas Graf 		struct test_obj *obj = &array[i];
1719d6dbe1bSGeert Uytterhoeven 
1729d6dbe1bSGeert Uytterhoeven 		obj->value = i * 2;
1739d6dbe1bSGeert Uytterhoeven 
174b182aa6eSHerbert Xu 		err = rhashtable_insert_fast(ht, &obj->node, test_rht_params);
17567b7cbf4SThomas Graf 		if (err == -ENOMEM || err == -EBUSY) {
17667b7cbf4SThomas Graf 			/* Mark failed inserts but continue */
17767b7cbf4SThomas Graf 			obj->value = TEST_INSERT_FAIL;
17867b7cbf4SThomas Graf 			insert_fails++;
17967b7cbf4SThomas Graf 		} else if (err) {
180fcc57020SThomas Graf 			return err;
1819d6dbe1bSGeert Uytterhoeven 		}
182685a015eSThomas Graf 
183685a015eSThomas Graf 		cond_resched();
18467b7cbf4SThomas Graf 	}
18567b7cbf4SThomas Graf 
18667b7cbf4SThomas Graf 	if (insert_fails)
18767b7cbf4SThomas Graf 		pr_info("  %u insertions failed due to memory pressure\n",
18867b7cbf4SThomas Graf 			insert_fails);
1899d6dbe1bSGeert Uytterhoeven 
190246b23a7SThomas Graf 	test_bucket_stats(ht);
1919d6dbe1bSGeert Uytterhoeven 	rcu_read_lock();
1929d6dbe1bSGeert Uytterhoeven 	test_rht_lookup(ht);
1939d6dbe1bSGeert Uytterhoeven 	rcu_read_unlock();
1949d6dbe1bSGeert Uytterhoeven 
195246b23a7SThomas Graf 	test_bucket_stats(ht);
1969d6dbe1bSGeert Uytterhoeven 
1971aa661f5SThomas Graf 	pr_info("  Deleting %d keys\n", entries);
1981aa661f5SThomas Graf 	for (i = 0; i < entries; i++) {
1999d6dbe1bSGeert Uytterhoeven 		u32 key = i * 2;
2009d6dbe1bSGeert Uytterhoeven 
20167b7cbf4SThomas Graf 		if (array[i].value != TEST_INSERT_FAIL) {
202b182aa6eSHerbert Xu 			obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
2039d6dbe1bSGeert Uytterhoeven 			BUG_ON(!obj);
2049d6dbe1bSGeert Uytterhoeven 
205b182aa6eSHerbert Xu 			rhashtable_remove_fast(ht, &obj->node, test_rht_params);
2069d6dbe1bSGeert Uytterhoeven 		}
207685a015eSThomas Graf 
208685a015eSThomas Graf 		cond_resched();
20967b7cbf4SThomas Graf 	}
2109d6dbe1bSGeert Uytterhoeven 
2111aa661f5SThomas Graf 	end = ktime_get_ns();
2121aa661f5SThomas Graf 	pr_info("  Duration of test: %lld ns\n", end - start);
2131aa661f5SThomas Graf 
2141aa661f5SThomas Graf 	return end - start;
2159d6dbe1bSGeert Uytterhoeven }
2169d6dbe1bSGeert Uytterhoeven 
217b7f5e5c7SDaniel Borkmann static struct rhashtable ht;
218b7f5e5c7SDaniel Borkmann 
219f4a3e90bSPhil Sutter static int thread_lookup_test(struct thread_data *tdata)
220f4a3e90bSPhil Sutter {
221f4a3e90bSPhil Sutter 	int i, err = 0;
222f4a3e90bSPhil Sutter 
223f4a3e90bSPhil Sutter 	for (i = 0; i < entries; i++) {
224f4a3e90bSPhil Sutter 		struct test_obj *obj;
225f4a3e90bSPhil Sutter 		int key = (tdata->id << 16) | i;
226f4a3e90bSPhil Sutter 
227f4a3e90bSPhil Sutter 		obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
228f4a3e90bSPhil Sutter 		if (obj && (tdata->objs[i].value == TEST_INSERT_FAIL)) {
229f4a3e90bSPhil Sutter 			pr_err("  found unexpected object %d\n", key);
230f4a3e90bSPhil Sutter 			err++;
231f4a3e90bSPhil Sutter 		} else if (!obj && (tdata->objs[i].value != TEST_INSERT_FAIL)) {
232f4a3e90bSPhil Sutter 			pr_err("  object %d not found!\n", key);
233f4a3e90bSPhil Sutter 			err++;
234f4a3e90bSPhil Sutter 		} else if (obj && (obj->value != key)) {
235f4a3e90bSPhil Sutter 			pr_err("  wrong object returned (got %d, expected %d)\n",
236f4a3e90bSPhil Sutter 			       obj->value, key);
237f4a3e90bSPhil Sutter 			err++;
238f4a3e90bSPhil Sutter 		}
239f4a3e90bSPhil Sutter 	}
240f4a3e90bSPhil Sutter 	return err;
241f4a3e90bSPhil Sutter }
242f4a3e90bSPhil Sutter 
243f4a3e90bSPhil Sutter static int threadfunc(void *data)
244f4a3e90bSPhil Sutter {
245f4a3e90bSPhil Sutter 	int i, step, err = 0, insert_fails = 0;
246f4a3e90bSPhil Sutter 	struct thread_data *tdata = data;
247f4a3e90bSPhil Sutter 
248f4a3e90bSPhil Sutter 	up(&prestart_sem);
249f4a3e90bSPhil Sutter 	if (down_interruptible(&startup_sem))
250f4a3e90bSPhil Sutter 		pr_err("  thread[%d]: down_interruptible failed\n", tdata->id);
251f4a3e90bSPhil Sutter 
252f4a3e90bSPhil Sutter 	for (i = 0; i < entries; i++) {
253f4a3e90bSPhil Sutter 		tdata->objs[i].value = (tdata->id << 16) | i;
254f4a3e90bSPhil Sutter 		err = rhashtable_insert_fast(&ht, &tdata->objs[i].node,
255f4a3e90bSPhil Sutter 		                             test_rht_params);
256f4a3e90bSPhil Sutter 		if (err == -ENOMEM || err == -EBUSY) {
257f4a3e90bSPhil Sutter 			tdata->objs[i].value = TEST_INSERT_FAIL;
258f4a3e90bSPhil Sutter 			insert_fails++;
259f4a3e90bSPhil Sutter 		} else if (err) {
260f4a3e90bSPhil Sutter 			pr_err("  thread[%d]: rhashtable_insert_fast failed\n",
261f4a3e90bSPhil Sutter 			       tdata->id);
262f4a3e90bSPhil Sutter 			goto out;
263f4a3e90bSPhil Sutter 		}
264f4a3e90bSPhil Sutter 	}
265f4a3e90bSPhil Sutter 	if (insert_fails)
266f4a3e90bSPhil Sutter 		pr_info("  thread[%d]: %d insert failures\n",
267f4a3e90bSPhil Sutter 		        tdata->id, insert_fails);
268f4a3e90bSPhil Sutter 
269f4a3e90bSPhil Sutter 	err = thread_lookup_test(tdata);
270f4a3e90bSPhil Sutter 	if (err) {
271f4a3e90bSPhil Sutter 		pr_err("  thread[%d]: rhashtable_lookup_test failed\n",
272f4a3e90bSPhil Sutter 		       tdata->id);
273f4a3e90bSPhil Sutter 		goto out;
274f4a3e90bSPhil Sutter 	}
275f4a3e90bSPhil Sutter 
276f4a3e90bSPhil Sutter 	for (step = 10; step > 0; step--) {
277f4a3e90bSPhil Sutter 		for (i = 0; i < entries; i += step) {
278f4a3e90bSPhil Sutter 			if (tdata->objs[i].value == TEST_INSERT_FAIL)
279f4a3e90bSPhil Sutter 				continue;
280f4a3e90bSPhil Sutter 			err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
281f4a3e90bSPhil Sutter 			                             test_rht_params);
282f4a3e90bSPhil Sutter 			if (err) {
283f4a3e90bSPhil Sutter 				pr_err("  thread[%d]: rhashtable_remove_fast failed\n",
284f4a3e90bSPhil Sutter 				       tdata->id);
285f4a3e90bSPhil Sutter 				goto out;
286f4a3e90bSPhil Sutter 			}
287f4a3e90bSPhil Sutter 			tdata->objs[i].value = TEST_INSERT_FAIL;
288f4a3e90bSPhil Sutter 		}
289f4a3e90bSPhil Sutter 		err = thread_lookup_test(tdata);
290f4a3e90bSPhil Sutter 		if (err) {
291f4a3e90bSPhil Sutter 			pr_err("  thread[%d]: rhashtable_lookup_test (2) failed\n",
292f4a3e90bSPhil Sutter 			       tdata->id);
293f4a3e90bSPhil Sutter 			goto out;
294f4a3e90bSPhil Sutter 		}
295f4a3e90bSPhil Sutter 	}
296f4a3e90bSPhil Sutter out:
297f4a3e90bSPhil Sutter 	while (!kthread_should_stop()) {
298f4a3e90bSPhil Sutter 		set_current_state(TASK_INTERRUPTIBLE);
299f4a3e90bSPhil Sutter 		schedule();
300f4a3e90bSPhil Sutter 	}
301f4a3e90bSPhil Sutter 	return err;
302f4a3e90bSPhil Sutter }
303f4a3e90bSPhil Sutter 
3049d6dbe1bSGeert Uytterhoeven static int __init test_rht_init(void)
3059d6dbe1bSGeert Uytterhoeven {
306f4a3e90bSPhil Sutter 	int i, err, started_threads = 0, failed_threads = 0;
3071aa661f5SThomas Graf 	u64 total_time = 0;
308f4a3e90bSPhil Sutter 	struct thread_data *tdata;
309f4a3e90bSPhil Sutter 	struct test_obj *objs;
3109d6dbe1bSGeert Uytterhoeven 
3111aa661f5SThomas Graf 	entries = min(entries, MAX_ENTRIES);
3129d6dbe1bSGeert Uytterhoeven 
3131aa661f5SThomas Graf 	test_rht_params.automatic_shrinking = shrinking;
3141aa661f5SThomas Graf 	test_rht_params.max_size = max_size;
3151aa661f5SThomas Graf 	test_rht_params.nelem_hint = size;
3161aa661f5SThomas Graf 
3171aa661f5SThomas Graf 	pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
3181aa661f5SThomas Graf 		size, max_size, shrinking);
3191aa661f5SThomas Graf 
3201aa661f5SThomas Graf 	for (i = 0; i < runs; i++) {
3211aa661f5SThomas Graf 		s64 time;
3221aa661f5SThomas Graf 
3231aa661f5SThomas Graf 		pr_info("Test %02d:\n", i);
324fcc57020SThomas Graf 		memset(&array, 0, sizeof(array));
325b182aa6eSHerbert Xu 		err = rhashtable_init(&ht, &test_rht_params);
3269d6dbe1bSGeert Uytterhoeven 		if (err < 0) {
3279d6dbe1bSGeert Uytterhoeven 			pr_warn("Test failed: Unable to initialize hashtable: %d\n",
3289d6dbe1bSGeert Uytterhoeven 				err);
3291aa661f5SThomas Graf 			continue;
3309d6dbe1bSGeert Uytterhoeven 		}
3319d6dbe1bSGeert Uytterhoeven 
3321aa661f5SThomas Graf 		time = test_rhashtable(&ht);
3339d6dbe1bSGeert Uytterhoeven 		rhashtable_destroy(&ht);
3341aa661f5SThomas Graf 		if (time < 0) {
3351aa661f5SThomas Graf 			pr_warn("Test failed: return code %lld\n", time);
3361aa661f5SThomas Graf 			return -EINVAL;
3371aa661f5SThomas Graf 		}
3389d6dbe1bSGeert Uytterhoeven 
3391aa661f5SThomas Graf 		total_time += time;
3401aa661f5SThomas Graf 	}
3411aa661f5SThomas Graf 
3426decd63aSThomas Graf 	do_div(total_time, runs);
3436decd63aSThomas Graf 	pr_info("Average test time: %llu\n", total_time);
3441aa661f5SThomas Graf 
345f4a3e90bSPhil Sutter 	if (!tcount)
346f4a3e90bSPhil Sutter 		return 0;
347f4a3e90bSPhil Sutter 
348f4a3e90bSPhil Sutter 	pr_info("Testing concurrent rhashtable access from %d threads\n",
349f4a3e90bSPhil Sutter 	        tcount);
350f4a3e90bSPhil Sutter 	sema_init(&prestart_sem, 1 - tcount);
351f4a3e90bSPhil Sutter 	tdata = vzalloc(tcount * sizeof(struct thread_data));
352f4a3e90bSPhil Sutter 	if (!tdata)
353f4a3e90bSPhil Sutter 		return -ENOMEM;
354f4a3e90bSPhil Sutter 	objs  = vzalloc(tcount * entries * sizeof(struct test_obj));
355f4a3e90bSPhil Sutter 	if (!objs) {
356f4a3e90bSPhil Sutter 		vfree(tdata);
357f4a3e90bSPhil Sutter 		return -ENOMEM;
358f4a3e90bSPhil Sutter 	}
359f4a3e90bSPhil Sutter 
360f4a3e90bSPhil Sutter 	err = rhashtable_init(&ht, &test_rht_params);
361f4a3e90bSPhil Sutter 	if (err < 0) {
362f4a3e90bSPhil Sutter 		pr_warn("Test failed: Unable to initialize hashtable: %d\n",
363f4a3e90bSPhil Sutter 			err);
364f4a3e90bSPhil Sutter 		vfree(tdata);
365f4a3e90bSPhil Sutter 		vfree(objs);
366f4a3e90bSPhil Sutter 		return -EINVAL;
367f4a3e90bSPhil Sutter 	}
368f4a3e90bSPhil Sutter 	for (i = 0; i < tcount; i++) {
369f4a3e90bSPhil Sutter 		tdata[i].id = i;
370f4a3e90bSPhil Sutter 		tdata[i].objs = objs + i * entries;
371f4a3e90bSPhil Sutter 		tdata[i].task = kthread_run(threadfunc, &tdata[i],
372f4a3e90bSPhil Sutter 		                            "rhashtable_thrad[%d]", i);
373f4a3e90bSPhil Sutter 		if (IS_ERR(tdata[i].task))
374f4a3e90bSPhil Sutter 			pr_err(" kthread_run failed for thread %d\n", i);
375f4a3e90bSPhil Sutter 		else
376f4a3e90bSPhil Sutter 			started_threads++;
377f4a3e90bSPhil Sutter 	}
378f4a3e90bSPhil Sutter 	if (down_interruptible(&prestart_sem))
379f4a3e90bSPhil Sutter 		pr_err("  down interruptible failed\n");
380f4a3e90bSPhil Sutter 	for (i = 0; i < tcount; i++)
381f4a3e90bSPhil Sutter 		up(&startup_sem);
382f4a3e90bSPhil Sutter 	for (i = 0; i < tcount; i++) {
383f4a3e90bSPhil Sutter 		if (IS_ERR(tdata[i].task))
384f4a3e90bSPhil Sutter 			continue;
385f4a3e90bSPhil Sutter 		if ((err = kthread_stop(tdata[i].task))) {
386f4a3e90bSPhil Sutter 			pr_warn("Test failed: thread %d returned: %d\n",
387f4a3e90bSPhil Sutter 			        i, err);
388f4a3e90bSPhil Sutter 			failed_threads++;
389f4a3e90bSPhil Sutter 		}
390f4a3e90bSPhil Sutter 	}
391f4a3e90bSPhil Sutter 	pr_info("Started %d threads, %d failed\n",
392f4a3e90bSPhil Sutter 	        started_threads, failed_threads);
393f4a3e90bSPhil Sutter 	rhashtable_destroy(&ht);
394f4a3e90bSPhil Sutter 	vfree(tdata);
395f4a3e90bSPhil Sutter 	vfree(objs);
3961aa661f5SThomas Graf 	return 0;
3979d6dbe1bSGeert Uytterhoeven }
3989d6dbe1bSGeert Uytterhoeven 
3996dd0c165SDaniel Borkmann static void __exit test_rht_exit(void)
4006dd0c165SDaniel Borkmann {
4016dd0c165SDaniel Borkmann }
4026dd0c165SDaniel Borkmann 
4039d6dbe1bSGeert Uytterhoeven module_init(test_rht_init);
4046dd0c165SDaniel Borkmann module_exit(test_rht_exit);
4059d6dbe1bSGeert Uytterhoeven 
4069d6dbe1bSGeert Uytterhoeven MODULE_LICENSE("GPL v2");
407