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