Commit f4a3e90ba5739cfd761b6befadae9728bd3641ed

Authored by Phil Sutter
Committed by David S. Miller
1 parent c1f066d4ee

rhashtable-test: extend to test concurrency

After having tested insertion, lookup, table walk and removal, spawn a
number of threads running operations on the same rhashtable. Each of
them will:

1) insert it's own set of objects,
2) lookup every successfully inserted object and finally
3) remove objects in several rounds until all of them have been removed,
   making sure the remaining ones are still found after each round.

This should put a good amount of load onto the system and due to
synchronising thread startup via two semaphores also extensive
concurrent table access.

The default number of ten threads returned within half a second on my
local VM with two cores. Running 200 threads took about four seconds. If
slow systems suffer too much from this though, the default could be
lowered or even set to zero so this extended test does not run at all by
default.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 155 additions and 1 deletions Side-by-side Diff

lib/test_rhashtable.c
... ... @@ -16,11 +16,14 @@
16 16 #include <linux/init.h>
17 17 #include <linux/jhash.h>
18 18 #include <linux/kernel.h>
  19 +#include <linux/kthread.h>
19 20 #include <linux/module.h>
20 21 #include <linux/rcupdate.h>
21 22 #include <linux/rhashtable.h>
  23 +#include <linux/semaphore.h>
22 24 #include <linux/slab.h>
23 25 #include <linux/sched.h>
  26 +#include <linux/vmalloc.h>
24 27  
25 28 #define MAX_ENTRIES 1000000
26 29 #define TEST_INSERT_FAIL INT_MAX
27 30  
... ... @@ -45,11 +48,21 @@
45 48 module_param(size, int, 0);
46 49 MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
47 50  
  51 +static int tcount = 10;
  52 +module_param(tcount, int, 0);
  53 +MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
  54 +
48 55 struct test_obj {
49 56 int value;
50 57 struct rhash_head node;
51 58 };
52 59  
  60 +struct thread_data {
  61 + int id;
  62 + struct task_struct *task;
  63 + struct test_obj *objs;
  64 +};
  65 +
53 66 static struct test_obj array[MAX_ENTRIES];
54 67  
55 68 static struct rhashtable_params test_rht_params = {
... ... @@ -60,6 +73,9 @@
60 73 .nulls_base = (3U << RHT_BASE_SHIFT),
61 74 };
62 75  
  76 +static struct semaphore prestart_sem;
  77 +static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0);
  78 +
63 79 static int __init test_rht_lookup(struct rhashtable *ht)
64 80 {
65 81 unsigned int i;
66 82  
67 83  
... ... @@ -200,10 +216,97 @@
200 216  
201 217 static struct rhashtable ht;
202 218  
  219 +static int thread_lookup_test(struct thread_data *tdata)
  220 +{
  221 + int i, err = 0;
  222 +
  223 + for (i = 0; i < entries; i++) {
  224 + struct test_obj *obj;
  225 + int key = (tdata->id << 16) | i;
  226 +
  227 + obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
  228 + if (obj && (tdata->objs[i].value == TEST_INSERT_FAIL)) {
  229 + pr_err(" found unexpected object %d\n", key);
  230 + err++;
  231 + } else if (!obj && (tdata->objs[i].value != TEST_INSERT_FAIL)) {
  232 + pr_err(" object %d not found!\n", key);
  233 + err++;
  234 + } else if (obj && (obj->value != key)) {
  235 + pr_err(" wrong object returned (got %d, expected %d)\n",
  236 + obj->value, key);
  237 + err++;
  238 + }
  239 + }
  240 + return err;
  241 +}
  242 +
  243 +static int threadfunc(void *data)
  244 +{
  245 + int i, step, err = 0, insert_fails = 0;
  246 + struct thread_data *tdata = data;
  247 +
  248 + up(&prestart_sem);
  249 + if (down_interruptible(&startup_sem))
  250 + pr_err(" thread[%d]: down_interruptible failed\n", tdata->id);
  251 +
  252 + for (i = 0; i < entries; i++) {
  253 + tdata->objs[i].value = (tdata->id << 16) | i;
  254 + err = rhashtable_insert_fast(&ht, &tdata->objs[i].node,
  255 + test_rht_params);
  256 + if (err == -ENOMEM || err == -EBUSY) {
  257 + tdata->objs[i].value = TEST_INSERT_FAIL;
  258 + insert_fails++;
  259 + } else if (err) {
  260 + pr_err(" thread[%d]: rhashtable_insert_fast failed\n",
  261 + tdata->id);
  262 + goto out;
  263 + }
  264 + }
  265 + if (insert_fails)
  266 + pr_info(" thread[%d]: %d insert failures\n",
  267 + tdata->id, insert_fails);
  268 +
  269 + err = thread_lookup_test(tdata);
  270 + if (err) {
  271 + pr_err(" thread[%d]: rhashtable_lookup_test failed\n",
  272 + tdata->id);
  273 + goto out;
  274 + }
  275 +
  276 + for (step = 10; step > 0; step--) {
  277 + for (i = 0; i < entries; i += step) {
  278 + if (tdata->objs[i].value == TEST_INSERT_FAIL)
  279 + continue;
  280 + err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
  281 + test_rht_params);
  282 + if (err) {
  283 + pr_err(" thread[%d]: rhashtable_remove_fast failed\n",
  284 + tdata->id);
  285 + goto out;
  286 + }
  287 + tdata->objs[i].value = TEST_INSERT_FAIL;
  288 + }
  289 + err = thread_lookup_test(tdata);
  290 + if (err) {
  291 + pr_err(" thread[%d]: rhashtable_lookup_test (2) failed\n",
  292 + tdata->id);
  293 + goto out;
  294 + }
  295 + }
  296 +out:
  297 + while (!kthread_should_stop()) {
  298 + set_current_state(TASK_INTERRUPTIBLE);
  299 + schedule();
  300 + }
  301 + return err;
  302 +}
  303 +
203 304 static int __init test_rht_init(void)
204 305 {
205   - int i, err;
  306 + int i, err, started_threads = 0, failed_threads = 0;
206 307 u64 total_time = 0;
  308 + struct thread_data *tdata;
  309 + struct test_obj *objs;
207 310  
208 311 entries = min(entries, MAX_ENTRIES);
209 312  
... ... @@ -239,6 +342,57 @@
239 342 do_div(total_time, runs);
240 343 pr_info("Average test time: %llu\n", total_time);
241 344  
  345 + if (!tcount)
  346 + return 0;
  347 +
  348 + pr_info("Testing concurrent rhashtable access from %d threads\n",
  349 + tcount);
  350 + sema_init(&prestart_sem, 1 - tcount);
  351 + tdata = vzalloc(tcount * sizeof(struct thread_data));
  352 + if (!tdata)
  353 + return -ENOMEM;
  354 + objs = vzalloc(tcount * entries * sizeof(struct test_obj));
  355 + if (!objs) {
  356 + vfree(tdata);
  357 + return -ENOMEM;
  358 + }
  359 +
  360 + err = rhashtable_init(&ht, &test_rht_params);
  361 + if (err < 0) {
  362 + pr_warn("Test failed: Unable to initialize hashtable: %d\n",
  363 + err);
  364 + vfree(tdata);
  365 + vfree(objs);
  366 + return -EINVAL;
  367 + }
  368 + for (i = 0; i < tcount; i++) {
  369 + tdata[i].id = i;
  370 + tdata[i].objs = objs + i * entries;
  371 + tdata[i].task = kthread_run(threadfunc, &tdata[i],
  372 + "rhashtable_thrad[%d]", i);
  373 + if (IS_ERR(tdata[i].task))
  374 + pr_err(" kthread_run failed for thread %d\n", i);
  375 + else
  376 + started_threads++;
  377 + }
  378 + if (down_interruptible(&prestart_sem))
  379 + pr_err(" down interruptible failed\n");
  380 + for (i = 0; i < tcount; i++)
  381 + up(&startup_sem);
  382 + for (i = 0; i < tcount; i++) {
  383 + if (IS_ERR(tdata[i].task))
  384 + continue;
  385 + if ((err = kthread_stop(tdata[i].task))) {
  386 + pr_warn("Test failed: thread %d returned: %d\n",
  387 + i, err);
  388 + failed_threads++;
  389 + }
  390 + }
  391 + pr_info("Started %d threads, %d failed\n",
  392 + started_threads, failed_threads);
  393 + rhashtable_destroy(&ht);
  394 + vfree(tdata);
  395 + vfree(objs);
242 396 return 0;
243 397 }
244 398