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