18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Resizable, Scalable, Concurrent Hash Table
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
68c2ecf20Sopenharmony_ci * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/**************************************************************************
108c2ecf20Sopenharmony_ci * Self Test
118c2ecf20Sopenharmony_ci **************************************************************************/
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/jhash.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/kthread.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
198c2ecf20Sopenharmony_ci#include <linux/rhashtable.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/sched.h>
228c2ecf20Sopenharmony_ci#include <linux/random.h>
238c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
248c2ecf20Sopenharmony_ci#include <linux/wait.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define MAX_ENTRIES	1000000
278c2ecf20Sopenharmony_ci#define TEST_INSERT_FAIL INT_MAX
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int parm_entries = 50000;
308c2ecf20Sopenharmony_cimodule_param(parm_entries, int, 0);
318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(parm_entries, "Number of entries to add (default: 50000)");
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int runs = 4;
348c2ecf20Sopenharmony_cimodule_param(runs, int, 0);
358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int max_size = 0;
388c2ecf20Sopenharmony_cimodule_param(max_size, int, 0);
398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_size, "Maximum table size (default: calculated)");
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic bool shrinking = false;
428c2ecf20Sopenharmony_cimodule_param(shrinking, bool, 0);
438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)");
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int size = 8;
468c2ecf20Sopenharmony_cimodule_param(size, int, 0);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int tcount = 10;
508c2ecf20Sopenharmony_cimodule_param(tcount, int, 0);
518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic bool enomem_retry = false;
548c2ecf20Sopenharmony_cimodule_param(enomem_retry, bool, 0);
558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct test_obj_val {
588c2ecf20Sopenharmony_ci	int	id;
598c2ecf20Sopenharmony_ci	int	tid;
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistruct test_obj {
638c2ecf20Sopenharmony_ci	struct test_obj_val	value;
648c2ecf20Sopenharmony_ci	struct rhash_head	node;
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct test_obj_rhl {
688c2ecf20Sopenharmony_ci	struct test_obj_val	value;
698c2ecf20Sopenharmony_ci	struct rhlist_head	list_node;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct thread_data {
738c2ecf20Sopenharmony_ci	unsigned int entries;
748c2ecf20Sopenharmony_ci	int id;
758c2ecf20Sopenharmony_ci	struct task_struct *task;
768c2ecf20Sopenharmony_ci	struct test_obj *objs;
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic u32 my_hashfn(const void *data, u32 len, u32 seed)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	const struct test_obj_rhl *obj = data;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return (obj->value.id % 10);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int my_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	const struct test_obj_rhl *test_obj = obj;
898c2ecf20Sopenharmony_ci	const struct test_obj_val *val = arg->key;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return test_obj->value.id - val->id;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct rhashtable_params test_rht_params = {
958c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct test_obj, node),
968c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct test_obj, value),
978c2ecf20Sopenharmony_ci	.key_len = sizeof(struct test_obj_val),
988c2ecf20Sopenharmony_ci	.hashfn = jhash,
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic struct rhashtable_params test_rht_params_dup = {
1028c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct test_obj_rhl, list_node),
1038c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct test_obj_rhl, value),
1048c2ecf20Sopenharmony_ci	.key_len = sizeof(struct test_obj_val),
1058c2ecf20Sopenharmony_ci	.hashfn = jhash,
1068c2ecf20Sopenharmony_ci	.obj_hashfn = my_hashfn,
1078c2ecf20Sopenharmony_ci	.obj_cmpfn = my_cmpfn,
1088c2ecf20Sopenharmony_ci	.nelem_hint = 128,
1098c2ecf20Sopenharmony_ci	.automatic_shrinking = false,
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic atomic_t startup_count;
1138c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(startup_wait);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int insert_retry(struct rhashtable *ht, struct test_obj *obj,
1168c2ecf20Sopenharmony_ci                        const struct rhashtable_params params)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int err, retries = -1, enomem_retries = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	do {
1218c2ecf20Sopenharmony_ci		retries++;
1228c2ecf20Sopenharmony_ci		cond_resched();
1238c2ecf20Sopenharmony_ci		err = rhashtable_insert_fast(ht, &obj->node, params);
1248c2ecf20Sopenharmony_ci		if (err == -ENOMEM && enomem_retry) {
1258c2ecf20Sopenharmony_ci			enomem_retries++;
1268c2ecf20Sopenharmony_ci			err = -EBUSY;
1278c2ecf20Sopenharmony_ci		}
1288c2ecf20Sopenharmony_ci	} while (err == -EBUSY);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (enomem_retries)
1318c2ecf20Sopenharmony_ci		pr_info(" %u insertions retried after -ENOMEM\n",
1328c2ecf20Sopenharmony_ci			enomem_retries);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return err ? : retries;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array,
1388c2ecf20Sopenharmony_ci				  unsigned int entries)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	unsigned int i;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
1438c2ecf20Sopenharmony_ci		struct test_obj *obj;
1448c2ecf20Sopenharmony_ci		bool expected = !(i % 2);
1458c2ecf20Sopenharmony_ci		struct test_obj_val key = {
1468c2ecf20Sopenharmony_ci			.id = i,
1478c2ecf20Sopenharmony_ci		};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		if (array[i / 2].value.id == TEST_INSERT_FAIL)
1508c2ecf20Sopenharmony_ci			expected = false;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		if (expected && !obj) {
1558c2ecf20Sopenharmony_ci			pr_warn("Test failed: Could not find key %u\n", key.id);
1568c2ecf20Sopenharmony_ci			return -ENOENT;
1578c2ecf20Sopenharmony_ci		} else if (!expected && obj) {
1588c2ecf20Sopenharmony_ci			pr_warn("Test failed: Unexpected entry found for key %u\n",
1598c2ecf20Sopenharmony_ci				key.id);
1608c2ecf20Sopenharmony_ci			return -EEXIST;
1618c2ecf20Sopenharmony_ci		} else if (expected && obj) {
1628c2ecf20Sopenharmony_ci			if (obj->value.id != i) {
1638c2ecf20Sopenharmony_ci				pr_warn("Test failed: Lookup value mismatch %u!=%u\n",
1648c2ecf20Sopenharmony_ci					obj->value.id, i);
1658c2ecf20Sopenharmony_ci				return -EINVAL;
1668c2ecf20Sopenharmony_ci			}
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		cond_resched_rcu();
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void test_bucket_stats(struct rhashtable *ht, unsigned int entries)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	unsigned int total = 0, chain_len = 0;
1788c2ecf20Sopenharmony_ci	struct rhashtable_iter hti;
1798c2ecf20Sopenharmony_ci	struct rhash_head *pos;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	rhashtable_walk_enter(ht, &hti);
1828c2ecf20Sopenharmony_ci	rhashtable_walk_start(&hti);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	while ((pos = rhashtable_walk_next(&hti))) {
1858c2ecf20Sopenharmony_ci		if (PTR_ERR(pos) == -EAGAIN) {
1868c2ecf20Sopenharmony_ci			pr_info("Info: encountered resize\n");
1878c2ecf20Sopenharmony_ci			chain_len++;
1888c2ecf20Sopenharmony_ci			continue;
1898c2ecf20Sopenharmony_ci		} else if (IS_ERR(pos)) {
1908c2ecf20Sopenharmony_ci			pr_warn("Test failed: rhashtable_walk_next() error: %ld\n",
1918c2ecf20Sopenharmony_ci				PTR_ERR(pos));
1928c2ecf20Sopenharmony_ci			break;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		total++;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	rhashtable_walk_stop(&hti);
1998c2ecf20Sopenharmony_ci	rhashtable_walk_exit(&hti);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	pr_info("  Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u\n",
2028c2ecf20Sopenharmony_ci		total, atomic_read(&ht->nelems), entries, chain_len);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (total != atomic_read(&ht->nelems) || total != entries)
2058c2ecf20Sopenharmony_ci		pr_warn("Test failed: Total count mismatch ^^^");
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,
2098c2ecf20Sopenharmony_ci				  unsigned int entries)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct test_obj *obj;
2128c2ecf20Sopenharmony_ci	int err;
2138c2ecf20Sopenharmony_ci	unsigned int i, insert_retries = 0;
2148c2ecf20Sopenharmony_ci	s64 start, end;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/*
2178c2ecf20Sopenharmony_ci	 * Insertion Test:
2188c2ecf20Sopenharmony_ci	 * Insert entries into table with all keys even numbers
2198c2ecf20Sopenharmony_ci	 */
2208c2ecf20Sopenharmony_ci	pr_info("  Adding %d keys\n", entries);
2218c2ecf20Sopenharmony_ci	start = ktime_get_ns();
2228c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
2238c2ecf20Sopenharmony_ci		struct test_obj *obj = &array[i];
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		obj->value.id = i * 2;
2268c2ecf20Sopenharmony_ci		err = insert_retry(ht, obj, test_rht_params);
2278c2ecf20Sopenharmony_ci		if (err > 0)
2288c2ecf20Sopenharmony_ci			insert_retries += err;
2298c2ecf20Sopenharmony_ci		else if (err)
2308c2ecf20Sopenharmony_ci			return err;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (insert_retries)
2348c2ecf20Sopenharmony_ci		pr_info("  %u insertions retried due to memory pressure\n",
2358c2ecf20Sopenharmony_ci			insert_retries);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	test_bucket_stats(ht, entries);
2388c2ecf20Sopenharmony_ci	rcu_read_lock();
2398c2ecf20Sopenharmony_ci	test_rht_lookup(ht, array, entries);
2408c2ecf20Sopenharmony_ci	rcu_read_unlock();
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	test_bucket_stats(ht, entries);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	pr_info("  Deleting %d keys\n", entries);
2458c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
2468c2ecf20Sopenharmony_ci		struct test_obj_val key = {
2478c2ecf20Sopenharmony_ci			.id = i * 2,
2488c2ecf20Sopenharmony_ci		};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		if (array[i].value.id != TEST_INSERT_FAIL) {
2518c2ecf20Sopenharmony_ci			obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
2528c2ecf20Sopenharmony_ci			BUG_ON(!obj);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci			rhashtable_remove_fast(ht, &obj->node, test_rht_params);
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		cond_resched();
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	end = ktime_get_ns();
2618c2ecf20Sopenharmony_ci	pr_info("  Duration of test: %lld ns\n", end - start);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return end - start;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic struct rhashtable ht;
2678c2ecf20Sopenharmony_cistatic struct rhltable rhlt;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int __init test_rhltable(unsigned int entries)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct test_obj_rhl *rhl_test_objects;
2728c2ecf20Sopenharmony_ci	unsigned long *obj_in_table;
2738c2ecf20Sopenharmony_ci	unsigned int i, j, k;
2748c2ecf20Sopenharmony_ci	int ret, err;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (entries == 0)
2778c2ecf20Sopenharmony_ci		entries = 1;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	rhl_test_objects = vzalloc(array_size(entries,
2808c2ecf20Sopenharmony_ci					      sizeof(*rhl_test_objects)));
2818c2ecf20Sopenharmony_ci	if (!rhl_test_objects)
2828c2ecf20Sopenharmony_ci		return -ENOMEM;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	ret = -ENOMEM;
2858c2ecf20Sopenharmony_ci	obj_in_table = vzalloc(array_size(sizeof(unsigned long),
2868c2ecf20Sopenharmony_ci					  BITS_TO_LONGS(entries)));
2878c2ecf20Sopenharmony_ci	if (!obj_in_table)
2888c2ecf20Sopenharmony_ci		goto out_free;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	err = rhltable_init(&rhlt, &test_rht_params);
2918c2ecf20Sopenharmony_ci	if (WARN_ON(err))
2928c2ecf20Sopenharmony_ci		goto out_free;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	k = prandom_u32();
2958c2ecf20Sopenharmony_ci	ret = 0;
2968c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
2978c2ecf20Sopenharmony_ci		rhl_test_objects[i].value.id = k;
2988c2ecf20Sopenharmony_ci		err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
2998c2ecf20Sopenharmony_ci				      test_rht_params);
3008c2ecf20Sopenharmony_ci		if (WARN(err, "error %d on element %d\n", err, i))
3018c2ecf20Sopenharmony_ci			break;
3028c2ecf20Sopenharmony_ci		if (err == 0)
3038c2ecf20Sopenharmony_ci			set_bit(i, obj_in_table);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (err)
3078c2ecf20Sopenharmony_ci		ret = err;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	pr_info("test %d add/delete pairs into rhlist\n", entries);
3108c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
3118c2ecf20Sopenharmony_ci		struct rhlist_head *h, *pos;
3128c2ecf20Sopenharmony_ci		struct test_obj_rhl *obj;
3138c2ecf20Sopenharmony_ci		struct test_obj_val key = {
3148c2ecf20Sopenharmony_ci			.id = k,
3158c2ecf20Sopenharmony_ci		};
3168c2ecf20Sopenharmony_ci		bool found;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		rcu_read_lock();
3198c2ecf20Sopenharmony_ci		h = rhltable_lookup(&rhlt, &key, test_rht_params);
3208c2ecf20Sopenharmony_ci		if (WARN(!h, "key not found during iteration %d of %d", i, entries)) {
3218c2ecf20Sopenharmony_ci			rcu_read_unlock();
3228c2ecf20Sopenharmony_ci			break;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		if (i) {
3268c2ecf20Sopenharmony_ci			j = i - 1;
3278c2ecf20Sopenharmony_ci			rhl_for_each_entry_rcu(obj, pos, h, list_node) {
3288c2ecf20Sopenharmony_ci				if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone"))
3298c2ecf20Sopenharmony_ci					break;
3308c2ecf20Sopenharmony_ci			}
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		cond_resched_rcu();
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		found = false;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		rhl_for_each_entry_rcu(obj, pos, h, list_node) {
3388c2ecf20Sopenharmony_ci			if (pos == &rhl_test_objects[i].list_node) {
3398c2ecf20Sopenharmony_ci				found = true;
3408c2ecf20Sopenharmony_ci				break;
3418c2ecf20Sopenharmony_ci			}
3428c2ecf20Sopenharmony_ci		}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		rcu_read_unlock();
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		if (WARN(!found, "element %d not found", i))
3478c2ecf20Sopenharmony_ci			break;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
3508c2ecf20Sopenharmony_ci		WARN(err, "rhltable_remove: err %d for iteration %d\n", err, i);
3518c2ecf20Sopenharmony_ci		if (err == 0)
3528c2ecf20Sopenharmony_ci			clear_bit(i, obj_in_table);
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (ret == 0 && err)
3568c2ecf20Sopenharmony_ci		ret = err;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
3598c2ecf20Sopenharmony_ci		WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
3628c2ecf20Sopenharmony_ci				      test_rht_params);
3638c2ecf20Sopenharmony_ci		if (WARN(err, "error %d on element %d\n", err, i))
3648c2ecf20Sopenharmony_ci			break;
3658c2ecf20Sopenharmony_ci		if (err == 0)
3668c2ecf20Sopenharmony_ci			set_bit(i, obj_in_table);
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	pr_info("test %d random rhlist add/delete operations\n", entries);
3708c2ecf20Sopenharmony_ci	for (j = 0; j < entries; j++) {
3718c2ecf20Sopenharmony_ci		u32 i = prandom_u32_max(entries);
3728c2ecf20Sopenharmony_ci		u32 prand = prandom_u32();
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		cond_resched();
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		if (prand == 0)
3778c2ecf20Sopenharmony_ci			prand = prandom_u32();
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		if (prand & 1) {
3808c2ecf20Sopenharmony_ci			prand >>= 1;
3818c2ecf20Sopenharmony_ci			continue;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
3858c2ecf20Sopenharmony_ci		if (test_bit(i, obj_in_table)) {
3868c2ecf20Sopenharmony_ci			clear_bit(i, obj_in_table);
3878c2ecf20Sopenharmony_ci			if (WARN(err, "cannot remove element at slot %d", i))
3888c2ecf20Sopenharmony_ci				continue;
3898c2ecf20Sopenharmony_ci		} else {
3908c2ecf20Sopenharmony_ci			if (WARN(err != -ENOENT, "removed non-existent element %d, error %d not %d",
3918c2ecf20Sopenharmony_ci			     i, err, -ENOENT))
3928c2ecf20Sopenharmony_ci				continue;
3938c2ecf20Sopenharmony_ci		}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		if (prand & 1) {
3968c2ecf20Sopenharmony_ci			prand >>= 1;
3978c2ecf20Sopenharmony_ci			continue;
3988c2ecf20Sopenharmony_ci		}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
4018c2ecf20Sopenharmony_ci		if (err == 0) {
4028c2ecf20Sopenharmony_ci			if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i))
4038c2ecf20Sopenharmony_ci				continue;
4048c2ecf20Sopenharmony_ci		} else {
4058c2ecf20Sopenharmony_ci			if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i))
4068c2ecf20Sopenharmony_ci				continue;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		if (prand & 1) {
4108c2ecf20Sopenharmony_ci			prand >>= 1;
4118c2ecf20Sopenharmony_ci			continue;
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		i = prandom_u32_max(entries);
4158c2ecf20Sopenharmony_ci		if (test_bit(i, obj_in_table)) {
4168c2ecf20Sopenharmony_ci			err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
4178c2ecf20Sopenharmony_ci			WARN(err, "cannot remove element at slot %d", i);
4188c2ecf20Sopenharmony_ci			if (err == 0)
4198c2ecf20Sopenharmony_ci				clear_bit(i, obj_in_table);
4208c2ecf20Sopenharmony_ci		} else {
4218c2ecf20Sopenharmony_ci			err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
4228c2ecf20Sopenharmony_ci			WARN(err, "failed to insert object %d", i);
4238c2ecf20Sopenharmony_ci			if (err == 0)
4248c2ecf20Sopenharmony_ci				set_bit(i, obj_in_table);
4258c2ecf20Sopenharmony_ci		}
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
4298c2ecf20Sopenharmony_ci		cond_resched();
4308c2ecf20Sopenharmony_ci		err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
4318c2ecf20Sopenharmony_ci		if (test_bit(i, obj_in_table)) {
4328c2ecf20Sopenharmony_ci			if (WARN(err, "cannot remove element at slot %d", i))
4338c2ecf20Sopenharmony_ci				continue;
4348c2ecf20Sopenharmony_ci		} else {
4358c2ecf20Sopenharmony_ci			if (WARN(err != -ENOENT, "removed non-existent element, error %d not %d",
4368c2ecf20Sopenharmony_ci				 err, -ENOENT))
4378c2ecf20Sopenharmony_ci				continue;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	rhltable_destroy(&rhlt);
4428c2ecf20Sopenharmony_ciout_free:
4438c2ecf20Sopenharmony_ci	vfree(rhl_test_objects);
4448c2ecf20Sopenharmony_ci	vfree(obj_in_table);
4458c2ecf20Sopenharmony_ci	return ret;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic int __init test_rhashtable_max(struct test_obj *array,
4498c2ecf20Sopenharmony_ci				      unsigned int entries)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	unsigned int i, insert_retries = 0;
4528c2ecf20Sopenharmony_ci	int err;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	test_rht_params.max_size = roundup_pow_of_two(entries / 8);
4558c2ecf20Sopenharmony_ci	err = rhashtable_init(&ht, &test_rht_params);
4568c2ecf20Sopenharmony_ci	if (err)
4578c2ecf20Sopenharmony_ci		return err;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	for (i = 0; i < ht.max_elems; i++) {
4608c2ecf20Sopenharmony_ci		struct test_obj *obj = &array[i];
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		obj->value.id = i * 2;
4638c2ecf20Sopenharmony_ci		err = insert_retry(&ht, obj, test_rht_params);
4648c2ecf20Sopenharmony_ci		if (err > 0)
4658c2ecf20Sopenharmony_ci			insert_retries += err;
4668c2ecf20Sopenharmony_ci		else if (err)
4678c2ecf20Sopenharmony_ci			return err;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	err = insert_retry(&ht, &array[ht.max_elems], test_rht_params);
4718c2ecf20Sopenharmony_ci	if (err == -E2BIG) {
4728c2ecf20Sopenharmony_ci		err = 0;
4738c2ecf20Sopenharmony_ci	} else {
4748c2ecf20Sopenharmony_ci		pr_info("insert element %u should have failed with %d, got %d\n",
4758c2ecf20Sopenharmony_ci				ht.max_elems, -E2BIG, err);
4768c2ecf20Sopenharmony_ci		if (err == 0)
4778c2ecf20Sopenharmony_ci			err = -1;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	rhashtable_destroy(&ht);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	return err;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic unsigned int __init print_ht(struct rhltable *rhlt)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct rhashtable *ht;
4888c2ecf20Sopenharmony_ci	const struct bucket_table *tbl;
4898c2ecf20Sopenharmony_ci	char buff[512] = "";
4908c2ecf20Sopenharmony_ci	unsigned int i, cnt = 0;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	ht = &rhlt->ht;
4938c2ecf20Sopenharmony_ci	/* Take the mutex to avoid RCU warning */
4948c2ecf20Sopenharmony_ci	mutex_lock(&ht->mutex);
4958c2ecf20Sopenharmony_ci	tbl = rht_dereference(ht->tbl, ht);
4968c2ecf20Sopenharmony_ci	for (i = 0; i < tbl->size; i++) {
4978c2ecf20Sopenharmony_ci		struct rhash_head *pos, *next;
4988c2ecf20Sopenharmony_ci		struct test_obj_rhl *p;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci		pos = rht_ptr_exclusive(tbl->buckets + i);
5018c2ecf20Sopenharmony_ci		next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		if (!rht_is_a_nulls(pos)) {
5048c2ecf20Sopenharmony_ci			sprintf(buff, "%s\nbucket[%d] -> ", buff, i);
5058c2ecf20Sopenharmony_ci		}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		while (!rht_is_a_nulls(pos)) {
5088c2ecf20Sopenharmony_ci			struct rhlist_head *list = container_of(pos, struct rhlist_head, rhead);
5098c2ecf20Sopenharmony_ci			sprintf(buff, "%s[[", buff);
5108c2ecf20Sopenharmony_ci			do {
5118c2ecf20Sopenharmony_ci				pos = &list->rhead;
5128c2ecf20Sopenharmony_ci				list = rht_dereference(list->next, ht);
5138c2ecf20Sopenharmony_ci				p = rht_obj(ht, pos);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci				sprintf(buff, "%s val %d (tid=%d)%s", buff, p->value.id, p->value.tid,
5168c2ecf20Sopenharmony_ci					list? ", " : " ");
5178c2ecf20Sopenharmony_ci				cnt++;
5188c2ecf20Sopenharmony_ci			} while (list);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci			pos = next,
5218c2ecf20Sopenharmony_ci			next = !rht_is_a_nulls(pos) ?
5228c2ecf20Sopenharmony_ci				rht_dereference(pos->next, ht) : NULL;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci			sprintf(buff, "%s]]%s", buff, !rht_is_a_nulls(pos) ? " -> " : "");
5258c2ecf20Sopenharmony_ci		}
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci	printk(KERN_ERR "\n---- ht: ----%s\n-------------\n", buff);
5288c2ecf20Sopenharmony_ci	mutex_unlock(&ht->mutex);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return cnt;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic int __init test_insert_dup(struct test_obj_rhl *rhl_test_objects,
5348c2ecf20Sopenharmony_ci				  int cnt, bool slow)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct rhltable *rhlt;
5378c2ecf20Sopenharmony_ci	unsigned int i, ret;
5388c2ecf20Sopenharmony_ci	const char *key;
5398c2ecf20Sopenharmony_ci	int err = 0;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	rhlt = kmalloc(sizeof(*rhlt), GFP_KERNEL);
5428c2ecf20Sopenharmony_ci	if (WARN_ON(!rhlt))
5438c2ecf20Sopenharmony_ci		return -EINVAL;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	err = rhltable_init(rhlt, &test_rht_params_dup);
5468c2ecf20Sopenharmony_ci	if (WARN_ON(err)) {
5478c2ecf20Sopenharmony_ci		kfree(rhlt);
5488c2ecf20Sopenharmony_ci		return err;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	for (i = 0; i < cnt; i++) {
5528c2ecf20Sopenharmony_ci		rhl_test_objects[i].value.tid = i;
5538c2ecf20Sopenharmony_ci		key = rht_obj(&rhlt->ht, &rhl_test_objects[i].list_node.rhead);
5548c2ecf20Sopenharmony_ci		key += test_rht_params_dup.key_offset;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		if (slow) {
5578c2ecf20Sopenharmony_ci			err = PTR_ERR(rhashtable_insert_slow(&rhlt->ht, key,
5588c2ecf20Sopenharmony_ci							     &rhl_test_objects[i].list_node.rhead));
5598c2ecf20Sopenharmony_ci			if (err == -EAGAIN)
5608c2ecf20Sopenharmony_ci				err = 0;
5618c2ecf20Sopenharmony_ci		} else
5628c2ecf20Sopenharmony_ci			err = rhltable_insert(rhlt,
5638c2ecf20Sopenharmony_ci					      &rhl_test_objects[i].list_node,
5648c2ecf20Sopenharmony_ci					      test_rht_params_dup);
5658c2ecf20Sopenharmony_ci		if (WARN(err, "error %d on element %d/%d (%s)\n", err, i, cnt, slow? "slow" : "fast"))
5668c2ecf20Sopenharmony_ci			goto skip_print;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	ret = print_ht(rhlt);
5708c2ecf20Sopenharmony_ci	WARN(ret != cnt, "missing rhltable elements (%d != %d, %s)\n", ret, cnt, slow? "slow" : "fast");
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ciskip_print:
5738c2ecf20Sopenharmony_ci	rhltable_destroy(rhlt);
5748c2ecf20Sopenharmony_ci	kfree(rhlt);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int __init test_insert_duplicates_run(void)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct test_obj_rhl rhl_test_objects[3] = {};
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	pr_info("test inserting duplicates\n");
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/* two different values that map to same bucket */
5868c2ecf20Sopenharmony_ci	rhl_test_objects[0].value.id = 1;
5878c2ecf20Sopenharmony_ci	rhl_test_objects[1].value.id = 21;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* and another duplicate with same as [0] value
5908c2ecf20Sopenharmony_ci	 * which will be second on the bucket list */
5918c2ecf20Sopenharmony_ci	rhl_test_objects[2].value.id = rhl_test_objects[0].value.id;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	test_insert_dup(rhl_test_objects, 2, false);
5948c2ecf20Sopenharmony_ci	test_insert_dup(rhl_test_objects, 3, false);
5958c2ecf20Sopenharmony_ci	test_insert_dup(rhl_test_objects, 2, true);
5968c2ecf20Sopenharmony_ci	test_insert_dup(rhl_test_objects, 3, true);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	return 0;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic int thread_lookup_test(struct thread_data *tdata)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	unsigned int entries = tdata->entries;
6048c2ecf20Sopenharmony_ci	int i, err = 0;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
6078c2ecf20Sopenharmony_ci		struct test_obj *obj;
6088c2ecf20Sopenharmony_ci		struct test_obj_val key = {
6098c2ecf20Sopenharmony_ci			.id = i,
6108c2ecf20Sopenharmony_ci			.tid = tdata->id,
6118c2ecf20Sopenharmony_ci		};
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
6148c2ecf20Sopenharmony_ci		if (obj && (tdata->objs[i].value.id == TEST_INSERT_FAIL)) {
6158c2ecf20Sopenharmony_ci			pr_err("  found unexpected object %d-%d\n", key.tid, key.id);
6168c2ecf20Sopenharmony_ci			err++;
6178c2ecf20Sopenharmony_ci		} else if (!obj && (tdata->objs[i].value.id != TEST_INSERT_FAIL)) {
6188c2ecf20Sopenharmony_ci			pr_err("  object %d-%d not found!\n", key.tid, key.id);
6198c2ecf20Sopenharmony_ci			err++;
6208c2ecf20Sopenharmony_ci		} else if (obj && memcmp(&obj->value, &key, sizeof(key))) {
6218c2ecf20Sopenharmony_ci			pr_err("  wrong object returned (got %d-%d, expected %d-%d)\n",
6228c2ecf20Sopenharmony_ci			       obj->value.tid, obj->value.id, key.tid, key.id);
6238c2ecf20Sopenharmony_ci			err++;
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		cond_resched();
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci	return err;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic int threadfunc(void *data)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	int i, step, err = 0, insert_retries = 0;
6348c2ecf20Sopenharmony_ci	struct thread_data *tdata = data;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&startup_count))
6378c2ecf20Sopenharmony_ci		wake_up(&startup_wait);
6388c2ecf20Sopenharmony_ci	if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == -1)) {
6398c2ecf20Sopenharmony_ci		pr_err("  thread[%d]: interrupted\n", tdata->id);
6408c2ecf20Sopenharmony_ci		goto out;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	for (i = 0; i < tdata->entries; i++) {
6448c2ecf20Sopenharmony_ci		tdata->objs[i].value.id = i;
6458c2ecf20Sopenharmony_ci		tdata->objs[i].value.tid = tdata->id;
6468c2ecf20Sopenharmony_ci		err = insert_retry(&ht, &tdata->objs[i], test_rht_params);
6478c2ecf20Sopenharmony_ci		if (err > 0) {
6488c2ecf20Sopenharmony_ci			insert_retries += err;
6498c2ecf20Sopenharmony_ci		} else if (err) {
6508c2ecf20Sopenharmony_ci			pr_err("  thread[%d]: rhashtable_insert_fast failed\n",
6518c2ecf20Sopenharmony_ci			       tdata->id);
6528c2ecf20Sopenharmony_ci			goto out;
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci	if (insert_retries)
6568c2ecf20Sopenharmony_ci		pr_info("  thread[%d]: %u insertions retried due to memory pressure\n",
6578c2ecf20Sopenharmony_ci			tdata->id, insert_retries);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	err = thread_lookup_test(tdata);
6608c2ecf20Sopenharmony_ci	if (err) {
6618c2ecf20Sopenharmony_ci		pr_err("  thread[%d]: rhashtable_lookup_test failed\n",
6628c2ecf20Sopenharmony_ci		       tdata->id);
6638c2ecf20Sopenharmony_ci		goto out;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	for (step = 10; step > 0; step--) {
6678c2ecf20Sopenharmony_ci		for (i = 0; i < tdata->entries; i += step) {
6688c2ecf20Sopenharmony_ci			if (tdata->objs[i].value.id == TEST_INSERT_FAIL)
6698c2ecf20Sopenharmony_ci				continue;
6708c2ecf20Sopenharmony_ci			err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
6718c2ecf20Sopenharmony_ci			                             test_rht_params);
6728c2ecf20Sopenharmony_ci			if (err) {
6738c2ecf20Sopenharmony_ci				pr_err("  thread[%d]: rhashtable_remove_fast failed\n",
6748c2ecf20Sopenharmony_ci				       tdata->id);
6758c2ecf20Sopenharmony_ci				goto out;
6768c2ecf20Sopenharmony_ci			}
6778c2ecf20Sopenharmony_ci			tdata->objs[i].value.id = TEST_INSERT_FAIL;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci			cond_resched();
6808c2ecf20Sopenharmony_ci		}
6818c2ecf20Sopenharmony_ci		err = thread_lookup_test(tdata);
6828c2ecf20Sopenharmony_ci		if (err) {
6838c2ecf20Sopenharmony_ci			pr_err("  thread[%d]: rhashtable_lookup_test (2) failed\n",
6848c2ecf20Sopenharmony_ci			       tdata->id);
6858c2ecf20Sopenharmony_ci			goto out;
6868c2ecf20Sopenharmony_ci		}
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ciout:
6898c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
6908c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
6918c2ecf20Sopenharmony_ci		schedule();
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci	return err;
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_cistatic int __init test_rht_init(void)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	unsigned int entries;
6998c2ecf20Sopenharmony_ci	int i, err, started_threads = 0, failed_threads = 0;
7008c2ecf20Sopenharmony_ci	u64 total_time = 0;
7018c2ecf20Sopenharmony_ci	struct thread_data *tdata;
7028c2ecf20Sopenharmony_ci	struct test_obj *objs;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (parm_entries < 0)
7058c2ecf20Sopenharmony_ci		parm_entries = 1;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	entries = min(parm_entries, MAX_ENTRIES);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	test_rht_params.automatic_shrinking = shrinking;
7108c2ecf20Sopenharmony_ci	test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
7118c2ecf20Sopenharmony_ci	test_rht_params.nelem_hint = size;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	objs = vzalloc(array_size(sizeof(struct test_obj),
7148c2ecf20Sopenharmony_ci				  test_rht_params.max_size + 1));
7158c2ecf20Sopenharmony_ci	if (!objs)
7168c2ecf20Sopenharmony_ci		return -ENOMEM;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
7198c2ecf20Sopenharmony_ci		size, max_size, shrinking);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	for (i = 0; i < runs; i++) {
7228c2ecf20Sopenharmony_ci		s64 time;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		pr_info("Test %02d:\n", i);
7258c2ecf20Sopenharmony_ci		memset(objs, 0, test_rht_params.max_size * sizeof(struct test_obj));
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		err = rhashtable_init(&ht, &test_rht_params);
7288c2ecf20Sopenharmony_ci		if (err < 0) {
7298c2ecf20Sopenharmony_ci			pr_warn("Test failed: Unable to initialize hashtable: %d\n",
7308c2ecf20Sopenharmony_ci				err);
7318c2ecf20Sopenharmony_ci			continue;
7328c2ecf20Sopenharmony_ci		}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci		time = test_rhashtable(&ht, objs, entries);
7358c2ecf20Sopenharmony_ci		rhashtable_destroy(&ht);
7368c2ecf20Sopenharmony_ci		if (time < 0) {
7378c2ecf20Sopenharmony_ci			vfree(objs);
7388c2ecf20Sopenharmony_ci			pr_warn("Test failed: return code %lld\n", time);
7398c2ecf20Sopenharmony_ci			return -EINVAL;
7408c2ecf20Sopenharmony_ci		}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci		total_time += time;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	pr_info("test if its possible to exceed max_size %d: %s\n",
7468c2ecf20Sopenharmony_ci			test_rht_params.max_size, test_rhashtable_max(objs, entries) == 0 ?
7478c2ecf20Sopenharmony_ci			"no, ok" : "YES, failed");
7488c2ecf20Sopenharmony_ci	vfree(objs);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	do_div(total_time, runs);
7518c2ecf20Sopenharmony_ci	pr_info("Average test time: %llu\n", total_time);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	test_insert_duplicates_run();
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (!tcount)
7568c2ecf20Sopenharmony_ci		return 0;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	pr_info("Testing concurrent rhashtable access from %d threads\n",
7598c2ecf20Sopenharmony_ci	        tcount);
7608c2ecf20Sopenharmony_ci	atomic_set(&startup_count, tcount);
7618c2ecf20Sopenharmony_ci	tdata = vzalloc(array_size(tcount, sizeof(struct thread_data)));
7628c2ecf20Sopenharmony_ci	if (!tdata)
7638c2ecf20Sopenharmony_ci		return -ENOMEM;
7648c2ecf20Sopenharmony_ci	objs  = vzalloc(array3_size(sizeof(struct test_obj), tcount, entries));
7658c2ecf20Sopenharmony_ci	if (!objs) {
7668c2ecf20Sopenharmony_ci		vfree(tdata);
7678c2ecf20Sopenharmony_ci		return -ENOMEM;
7688c2ecf20Sopenharmony_ci	}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	test_rht_params.max_size = max_size ? :
7718c2ecf20Sopenharmony_ci	                           roundup_pow_of_two(tcount * entries);
7728c2ecf20Sopenharmony_ci	err = rhashtable_init(&ht, &test_rht_params);
7738c2ecf20Sopenharmony_ci	if (err < 0) {
7748c2ecf20Sopenharmony_ci		pr_warn("Test failed: Unable to initialize hashtable: %d\n",
7758c2ecf20Sopenharmony_ci			err);
7768c2ecf20Sopenharmony_ci		vfree(tdata);
7778c2ecf20Sopenharmony_ci		vfree(objs);
7788c2ecf20Sopenharmony_ci		return -EINVAL;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci	for (i = 0; i < tcount; i++) {
7818c2ecf20Sopenharmony_ci		tdata[i].id = i;
7828c2ecf20Sopenharmony_ci		tdata[i].entries = entries;
7838c2ecf20Sopenharmony_ci		tdata[i].objs = objs + i * entries;
7848c2ecf20Sopenharmony_ci		tdata[i].task = kthread_run(threadfunc, &tdata[i],
7858c2ecf20Sopenharmony_ci		                            "rhashtable_thrad[%d]", i);
7868c2ecf20Sopenharmony_ci		if (IS_ERR(tdata[i].task)) {
7878c2ecf20Sopenharmony_ci			pr_err(" kthread_run failed for thread %d\n", i);
7888c2ecf20Sopenharmony_ci			atomic_dec(&startup_count);
7898c2ecf20Sopenharmony_ci		} else {
7908c2ecf20Sopenharmony_ci			started_threads++;
7918c2ecf20Sopenharmony_ci		}
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci	if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == 0))
7948c2ecf20Sopenharmony_ci		pr_err("  wait_event interruptible failed\n");
7958c2ecf20Sopenharmony_ci	/* count is 0 now, set it to -1 and wake up all threads together */
7968c2ecf20Sopenharmony_ci	atomic_dec(&startup_count);
7978c2ecf20Sopenharmony_ci	wake_up_all(&startup_wait);
7988c2ecf20Sopenharmony_ci	for (i = 0; i < tcount; i++) {
7998c2ecf20Sopenharmony_ci		if (IS_ERR(tdata[i].task))
8008c2ecf20Sopenharmony_ci			continue;
8018c2ecf20Sopenharmony_ci		if ((err = kthread_stop(tdata[i].task))) {
8028c2ecf20Sopenharmony_ci			pr_warn("Test failed: thread %d returned: %d\n",
8038c2ecf20Sopenharmony_ci			        i, err);
8048c2ecf20Sopenharmony_ci			failed_threads++;
8058c2ecf20Sopenharmony_ci		}
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci	rhashtable_destroy(&ht);
8088c2ecf20Sopenharmony_ci	vfree(tdata);
8098c2ecf20Sopenharmony_ci	vfree(objs);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	/*
8128c2ecf20Sopenharmony_ci	 * rhltable_remove is very expensive, default values can cause test
8138c2ecf20Sopenharmony_ci	 * to run for 2 minutes or more,  use a smaller number instead.
8148c2ecf20Sopenharmony_ci	 */
8158c2ecf20Sopenharmony_ci	err = test_rhltable(entries / 16);
8168c2ecf20Sopenharmony_ci	pr_info("Started %d threads, %d failed, rhltable test returns %d\n",
8178c2ecf20Sopenharmony_ci	        started_threads, failed_threads, err);
8188c2ecf20Sopenharmony_ci	return 0;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic void __exit test_rht_exit(void)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cimodule_init(test_rht_init);
8268c2ecf20Sopenharmony_cimodule_exit(test_rht_exit);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
829