linux/lib/test_rhashtable.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Resizable, Scalable, Concurrent Hash Table
   4 *
   5 * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch>
   6 * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
   7 */
   8
   9/**************************************************************************
  10 * Self Test
  11 **************************************************************************/
  12
  13#include <linux/init.h>
  14#include <linux/jhash.h>
  15#include <linux/kernel.h>
  16#include <linux/kthread.h>
  17#include <linux/module.h>
  18#include <linux/rcupdate.h>
  19#include <linux/rhashtable.h>
  20#include <linux/slab.h>
  21#include <linux/sched.h>
  22#include <linux/random.h>
  23#include <linux/vmalloc.h>
  24#include <linux/wait.h>
  25
  26#define MAX_ENTRIES     1000000
  27#define TEST_INSERT_FAIL INT_MAX
  28
  29static int parm_entries = 50000;
  30module_param(parm_entries, int, 0);
  31MODULE_PARM_DESC(parm_entries, "Number of entries to add (default: 50000)");
  32
  33static int runs = 4;
  34module_param(runs, int, 0);
  35MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
  36
  37static int max_size = 0;
  38module_param(max_size, int, 0);
  39MODULE_PARM_DESC(max_size, "Maximum table size (default: calculated)");
  40
  41static bool shrinking = false;
  42module_param(shrinking, bool, 0);
  43MODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)");
  44
  45static int size = 8;
  46module_param(size, int, 0);
  47MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)");
  48
  49static int tcount = 10;
  50module_param(tcount, int, 0);
  51MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
  52
  53static bool enomem_retry = false;
  54module_param(enomem_retry, bool, 0);
  55MODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
  56
  57struct test_obj_val {
  58        int     id;
  59        int     tid;
  60};
  61
  62struct test_obj {
  63        struct test_obj_val     value;
  64        struct rhash_head       node;
  65};
  66
  67struct test_obj_rhl {
  68        struct test_obj_val     value;
  69        struct rhlist_head      list_node;
  70};
  71
  72struct thread_data {
  73        unsigned int entries;
  74        int id;
  75        struct task_struct *task;
  76        struct test_obj *objs;
  77};
  78
  79static u32 my_hashfn(const void *data, u32 len, u32 seed)
  80{
  81        const struct test_obj_rhl *obj = data;
  82
  83        return (obj->value.id % 10);
  84}
  85
  86static int my_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
  87{
  88        const struct test_obj_rhl *test_obj = obj;
  89        const struct test_obj_val *val = arg->key;
  90
  91        return test_obj->value.id - val->id;
  92}
  93
  94static struct rhashtable_params test_rht_params = {
  95        .head_offset = offsetof(struct test_obj, node),
  96        .key_offset = offsetof(struct test_obj, value),
  97        .key_len = sizeof(struct test_obj_val),
  98        .hashfn = jhash,
  99};
 100
 101static struct rhashtable_params test_rht_params_dup = {
 102        .head_offset = offsetof(struct test_obj_rhl, list_node),
 103        .key_offset = offsetof(struct test_obj_rhl, value),
 104        .key_len = sizeof(struct test_obj_val),
 105        .hashfn = jhash,
 106        .obj_hashfn = my_hashfn,
 107        .obj_cmpfn = my_cmpfn,
 108        .nelem_hint = 128,
 109        .automatic_shrinking = false,
 110};
 111
 112static atomic_t startup_count;
 113static DECLARE_WAIT_QUEUE_HEAD(startup_wait);
 114
 115static int insert_retry(struct rhashtable *ht, struct test_obj *obj,
 116                        const struct rhashtable_params params)
 117{
 118        int err, retries = -1, enomem_retries = 0;
 119
 120        do {
 121                retries++;
 122                cond_resched();
 123                err = rhashtable_insert_fast(ht, &obj->node, params);
 124                if (err == -ENOMEM && enomem_retry) {
 125                        enomem_retries++;
 126                        err = -EBUSY;
 127                }
 128        } while (err == -EBUSY);
 129
 130        if (enomem_retries)
 131                pr_info(" %u insertions retried after -ENOMEM\n",
 132                        enomem_retries);
 133
 134        return err ? : retries;
 135}
 136
 137static int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array,
 138                                  unsigned int entries)
 139{
 140        unsigned int i;
 141
 142        for (i = 0; i < entries; i++) {
 143                struct test_obj *obj;
 144                bool expected = !(i % 2);
 145                struct test_obj_val key = {
 146                        .id = i,
 147                };
 148
 149                if (array[i / 2].value.id == TEST_INSERT_FAIL)
 150                        expected = false;
 151
 152                obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
 153
 154                if (expected && !obj) {
 155                        pr_warn("Test failed: Could not find key %u\n", key.id);
 156                        return -ENOENT;
 157                } else if (!expected && obj) {
 158                        pr_warn("Test failed: Unexpected entry found for key %u\n",
 159                                key.id);
 160                        return -EEXIST;
 161                } else if (expected && obj) {
 162                        if (obj->value.id != i) {
 163                                pr_warn("Test failed: Lookup value mismatch %u!=%u\n",
 164                                        obj->value.id, i);
 165                                return -EINVAL;
 166                        }
 167                }
 168
 169                cond_resched_rcu();
 170        }
 171
 172        return 0;
 173}
 174
 175static void test_bucket_stats(struct rhashtable *ht, unsigned int entries)
 176{
 177        unsigned int total = 0, chain_len = 0;
 178        struct rhashtable_iter hti;
 179        struct rhash_head *pos;
 180
 181        rhashtable_walk_enter(ht, &hti);
 182        rhashtable_walk_start(&hti);
 183
 184        while ((pos = rhashtable_walk_next(&hti))) {
 185                if (PTR_ERR(pos) == -EAGAIN) {
 186                        pr_info("Info: encountered resize\n");
 187                        chain_len++;
 188                        continue;
 189                } else if (IS_ERR(pos)) {
 190                        pr_warn("Test failed: rhashtable_walk_next() error: %ld\n",
 191                                PTR_ERR(pos));
 192                        break;
 193                }
 194
 195                total++;
 196        }
 197
 198        rhashtable_walk_stop(&hti);
 199        rhashtable_walk_exit(&hti);
 200
 201        pr_info("  Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u\n",
 202                total, atomic_read(&ht->nelems), entries, chain_len);
 203
 204        if (total != atomic_read(&ht->nelems) || total != entries)
 205                pr_warn("Test failed: Total count mismatch ^^^");
 206}
 207
 208static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,
 209                                  unsigned int entries)
 210{
 211        struct test_obj *obj;
 212        int err;
 213        unsigned int i, insert_retries = 0;
 214        s64 start, end;
 215
 216        /*
 217         * Insertion Test:
 218         * Insert entries into table with all keys even numbers
 219         */
 220        pr_info("  Adding %d keys\n", entries);
 221        start = ktime_get_ns();
 222        for (i = 0; i < entries; i++) {
 223                struct test_obj *obj = &array[i];
 224
 225                obj->value.id = i * 2;
 226                err = insert_retry(ht, obj, test_rht_params);
 227                if (err > 0)
 228                        insert_retries += err;
 229                else if (err)
 230                        return err;
 231        }
 232
 233        if (insert_retries)
 234                pr_info("  %u insertions retried due to memory pressure\n",
 235                        insert_retries);
 236
 237        test_bucket_stats(ht, entries);
 238        rcu_read_lock();
 239        test_rht_lookup(ht, array, entries);
 240        rcu_read_unlock();
 241
 242        test_bucket_stats(ht, entries);
 243
 244        pr_info("  Deleting %d keys\n", entries);
 245        for (i = 0; i < entries; i++) {
 246                struct test_obj_val key = {
 247                        .id = i * 2,
 248                };
 249
 250                if (array[i].value.id != TEST_INSERT_FAIL) {
 251                        obj = rhashtable_lookup_fast(ht, &key, test_rht_params);
 252                        BUG_ON(!obj);
 253
 254                        rhashtable_remove_fast(ht, &obj->node, test_rht_params);
 255                }
 256
 257                cond_resched();
 258        }
 259
 260        end = ktime_get_ns();
 261        pr_info("  Duration of test: %lld ns\n", end - start);
 262
 263        return end - start;
 264}
 265
 266static struct rhashtable ht;
 267static struct rhltable rhlt;
 268
 269static int __init test_rhltable(unsigned int entries)
 270{
 271        struct test_obj_rhl *rhl_test_objects;
 272        unsigned long *obj_in_table;
 273        unsigned int i, j, k;
 274        int ret, err;
 275
 276        if (entries == 0)
 277                entries = 1;
 278
 279        rhl_test_objects = vzalloc(array_size(entries,
 280                                              sizeof(*rhl_test_objects)));
 281        if (!rhl_test_objects)
 282                return -ENOMEM;
 283
 284        ret = -ENOMEM;
 285        obj_in_table = vzalloc(array_size(sizeof(unsigned long),
 286                                          BITS_TO_LONGS(entries)));
 287        if (!obj_in_table)
 288                goto out_free;
 289
 290        err = rhltable_init(&rhlt, &test_rht_params);
 291        if (WARN_ON(err))
 292                goto out_free;
 293
 294        k = prandom_u32();
 295        ret = 0;
 296        for (i = 0; i < entries; i++) {
 297                rhl_test_objects[i].value.id = k;
 298                err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
 299                                      test_rht_params);
 300                if (WARN(err, "error %d on element %d\n", err, i))
 301                        break;
 302                if (err == 0)
 303                        set_bit(i, obj_in_table);
 304        }
 305
 306        if (err)
 307                ret = err;
 308
 309        pr_info("test %d add/delete pairs into rhlist\n", entries);
 310        for (i = 0; i < entries; i++) {
 311                struct rhlist_head *h, *pos;
 312                struct test_obj_rhl *obj;
 313                struct test_obj_val key = {
 314                        .id = k,
 315                };
 316                bool found;
 317
 318                rcu_read_lock();
 319                h = rhltable_lookup(&rhlt, &key, test_rht_params);
 320                if (WARN(!h, "key not found during iteration %d of %d", i, entries)) {
 321                        rcu_read_unlock();
 322                        break;
 323                }
 324
 325                if (i) {
 326                        j = i - 1;
 327                        rhl_for_each_entry_rcu(obj, pos, h, list_node) {
 328                                if (WARN(pos == &rhl_test_objects[j].list_node, "old element found, should be gone"))
 329                                        break;
 330                        }
 331                }
 332
 333                cond_resched_rcu();
 334
 335                found = false;
 336
 337                rhl_for_each_entry_rcu(obj, pos, h, list_node) {
 338                        if (pos == &rhl_test_objects[i].list_node) {
 339                                found = true;
 340                                break;
 341                        }
 342                }
 343
 344                rcu_read_unlock();
 345
 346                if (WARN(!found, "element %d not found", i))
 347                        break;
 348
 349                err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
 350                WARN(err, "rhltable_remove: err %d for iteration %d\n", err, i);
 351                if (err == 0)
 352                        clear_bit(i, obj_in_table);
 353        }
 354
 355        if (ret == 0 && err)
 356                ret = err;
 357
 358        for (i = 0; i < entries; i++) {
 359                WARN(test_bit(i, obj_in_table), "elem %d allegedly still present", i);
 360
 361                err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node,
 362                                      test_rht_params);
 363                if (WARN(err, "error %d on element %d\n", err, i))
 364                        break;
 365                if (err == 0)
 366                        set_bit(i, obj_in_table);
 367        }
 368
 369        pr_info("test %d random rhlist add/delete operations\n", entries);
 370        for (j = 0; j < entries; j++) {
 371                u32 i = prandom_u32_max(entries);
 372                u32 prand = prandom_u32();
 373
 374                cond_resched();
 375
 376                if (prand == 0)
 377                        prand = prandom_u32();
 378
 379                if (prand & 1) {
 380                        prand >>= 1;
 381                        continue;
 382                }
 383
 384                err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
 385                if (test_bit(i, obj_in_table)) {
 386                        clear_bit(i, obj_in_table);
 387                        if (WARN(err, "cannot remove element at slot %d", i))
 388                                continue;
 389                } else {
 390                        if (WARN(err != -ENOENT, "removed non-existent element %d, error %d not %d",
 391                             i, err, -ENOENT))
 392                                continue;
 393                }
 394
 395                if (prand & 1) {
 396                        prand >>= 1;
 397                        continue;
 398                }
 399
 400                err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
 401                if (err == 0) {
 402                        if (WARN(test_and_set_bit(i, obj_in_table), "succeeded to insert same object %d", i))
 403                                continue;
 404                } else {
 405                        if (WARN(!test_bit(i, obj_in_table), "failed to insert object %d", i))
 406                                continue;
 407                }
 408
 409                if (prand & 1) {
 410                        prand >>= 1;
 411                        continue;
 412                }
 413
 414                i = prandom_u32_max(entries);
 415                if (test_bit(i, obj_in_table)) {
 416                        err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
 417                        WARN(err, "cannot remove element at slot %d", i);
 418                        if (err == 0)
 419                                clear_bit(i, obj_in_table);
 420                } else {
 421                        err = rhltable_insert(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
 422                        WARN(err, "failed to insert object %d", i);
 423                        if (err == 0)
 424                                set_bit(i, obj_in_table);
 425                }
 426        }
 427
 428        for (i = 0; i < entries; i++) {
 429                cond_resched();
 430                err = rhltable_remove(&rhlt, &rhl_test_objects[i].list_node, test_rht_params);
 431                if (test_bit(i, obj_in_table)) {
 432                        if (WARN(err, "cannot remove element at slot %d", i))
 433                                continue;
 434                } else {
 435                        if (WARN(err != -ENOENT, "removed non-existent element, error %d not %d",
 436                                 err, -ENOENT))
 437                        continue;
 438                }
 439        }
 440
 441        rhltable_destroy(&rhlt);
 442out_free:
 443        vfree(rhl_test_objects);
 444        vfree(obj_in_table);
 445        return ret;
 446}
 447
 448static int __init test_rhashtable_max(struct test_obj *array,
 449                                      unsigned int entries)
 450{
 451        unsigned int i, insert_retries = 0;
 452        int err;
 453
 454        test_rht_params.max_size = roundup_pow_of_two(entries / 8);
 455        err = rhashtable_init(&ht, &test_rht_params);
 456        if (err)
 457                return err;
 458
 459        for (i = 0; i < ht.max_elems; i++) {
 460                struct test_obj *obj = &array[i];
 461
 462                obj->value.id = i * 2;
 463                err = insert_retry(&ht, obj, test_rht_params);
 464                if (err > 0)
 465                        insert_retries += err;
 466                else if (err)
 467                        return err;
 468        }
 469
 470        err = insert_retry(&ht, &array[ht.max_elems], test_rht_params);
 471        if (err == -E2BIG) {
 472                err = 0;
 473        } else {
 474                pr_info("insert element %u should have failed with %d, got %d\n",
 475                                ht.max_elems, -E2BIG, err);
 476                if (err == 0)
 477                        err = -1;
 478        }
 479
 480        rhashtable_destroy(&ht);
 481
 482        return err;
 483}
 484
 485static unsigned int __init print_ht(struct rhltable *rhlt)
 486{
 487        struct rhashtable *ht;
 488        const struct bucket_table *tbl;
 489        char buff[512] = "";
 490        unsigned int i, cnt = 0;
 491
 492        ht = &rhlt->ht;
 493        /* Take the mutex to avoid RCU warning */
 494        mutex_lock(&ht->mutex);
 495        tbl = rht_dereference(ht->tbl, ht);
 496        for (i = 0; i < tbl->size; i++) {
 497                struct rhash_head *pos, *next;
 498                struct test_obj_rhl *p;
 499
 500                pos = rht_ptr_exclusive(tbl->buckets + i);
 501                next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL;
 502
 503                if (!rht_is_a_nulls(pos)) {
 504                        sprintf(buff, "%s\nbucket[%d] -> ", buff, i);
 505                }
 506
 507                while (!rht_is_a_nulls(pos)) {
 508                        struct rhlist_head *list = container_of(pos, struct rhlist_head, rhead);
 509                        sprintf(buff, "%s[[", buff);
 510                        do {
 511                                pos = &list->rhead;
 512                                list = rht_dereference(list->next, ht);
 513                                p = rht_obj(ht, pos);
 514
 515                                sprintf(buff, "%s val %d (tid=%d)%s", buff, p->value.id, p->value.tid,
 516                                        list? ", " : " ");
 517                                cnt++;
 518                        } while (list);
 519
 520                        pos = next,
 521                        next = !rht_is_a_nulls(pos) ?
 522                                rht_dereference(pos->next, ht) : NULL;
 523
 524                        sprintf(buff, "%s]]%s", buff, !rht_is_a_nulls(pos) ? " -> " : "");
 525                }
 526        }
 527        printk(KERN_ERR "\n---- ht: ----%s\n-------------\n", buff);
 528        mutex_unlock(&ht->mutex);
 529
 530        return cnt;
 531}
 532
 533static int __init test_insert_dup(struct test_obj_rhl *rhl_test_objects,
 534                                  int cnt, bool slow)
 535{
 536        struct rhltable *rhlt;
 537        unsigned int i, ret;
 538        const char *key;
 539        int err = 0;
 540
 541        rhlt = kmalloc(sizeof(*rhlt), GFP_KERNEL);
 542        if (WARN_ON(!rhlt))
 543                return -EINVAL;
 544
 545        err = rhltable_init(rhlt, &test_rht_params_dup);
 546        if (WARN_ON(err)) {
 547                kfree(rhlt);
 548                return err;
 549        }
 550
 551        for (i = 0; i < cnt; i++) {
 552                rhl_test_objects[i].value.tid = i;
 553                key = rht_obj(&rhlt->ht, &rhl_test_objects[i].list_node.rhead);
 554                key += test_rht_params_dup.key_offset;
 555
 556                if (slow) {
 557                        err = PTR_ERR(rhashtable_insert_slow(&rhlt->ht, key,
 558                                                             &rhl_test_objects[i].list_node.rhead));
 559                        if (err == -EAGAIN)
 560                                err = 0;
 561                } else
 562                        err = rhltable_insert(rhlt,
 563                                              &rhl_test_objects[i].list_node,
 564                                              test_rht_params_dup);
 565                if (WARN(err, "error %d on element %d/%d (%s)\n", err, i, cnt, slow? "slow" : "fast"))
 566                        goto skip_print;
 567        }
 568
 569        ret = print_ht(rhlt);
 570        WARN(ret != cnt, "missing rhltable elements (%d != %d, %s)\n", ret, cnt, slow? "slow" : "fast");
 571
 572skip_print:
 573        rhltable_destroy(rhlt);
 574        kfree(rhlt);
 575
 576        return 0;
 577}
 578
 579static int __init test_insert_duplicates_run(void)
 580{
 581        struct test_obj_rhl rhl_test_objects[3] = {};
 582
 583        pr_info("test inserting duplicates\n");
 584
 585        /* two different values that map to same bucket */
 586        rhl_test_objects[0].value.id = 1;
 587        rhl_test_objects[1].value.id = 21;
 588
 589        /* and another duplicate with same as [0] value
 590         * which will be second on the bucket list */
 591        rhl_test_objects[2].value.id = rhl_test_objects[0].value.id;
 592
 593        test_insert_dup(rhl_test_objects, 2, false);
 594        test_insert_dup(rhl_test_objects, 3, false);
 595        test_insert_dup(rhl_test_objects, 2, true);
 596        test_insert_dup(rhl_test_objects, 3, true);
 597
 598        return 0;
 599}
 600
 601static int thread_lookup_test(struct thread_data *tdata)
 602{
 603        unsigned int entries = tdata->entries;
 604        int i, err = 0;
 605
 606        for (i = 0; i < entries; i++) {
 607                struct test_obj *obj;
 608                struct test_obj_val key = {
 609                        .id = i,
 610                        .tid = tdata->id,
 611                };
 612
 613                obj = rhashtable_lookup_fast(&ht, &key, test_rht_params);
 614                if (obj && (tdata->objs[i].value.id == TEST_INSERT_FAIL)) {
 615                        pr_err("  found unexpected object %d-%d\n", key.tid, key.id);
 616                        err++;
 617                } else if (!obj && (tdata->objs[i].value.id != TEST_INSERT_FAIL)) {
 618                        pr_err("  object %d-%d not found!\n", key.tid, key.id);
 619                        err++;
 620                } else if (obj && memcmp(&obj->value, &key, sizeof(key))) {
 621                        pr_err("  wrong object returned (got %d-%d, expected %d-%d)\n",
 622                               obj->value.tid, obj->value.id, key.tid, key.id);
 623                        err++;
 624                }
 625
 626                cond_resched();
 627        }
 628        return err;
 629}
 630
 631static int threadfunc(void *data)
 632{
 633        int i, step, err = 0, insert_retries = 0;
 634        struct thread_data *tdata = data;
 635
 636        if (atomic_dec_and_test(&startup_count))
 637                wake_up(&startup_wait);
 638        if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == -1)) {
 639                pr_err("  thread[%d]: interrupted\n", tdata->id);
 640                goto out;
 641        }
 642
 643        for (i = 0; i < tdata->entries; i++) {
 644                tdata->objs[i].value.id = i;
 645                tdata->objs[i].value.tid = tdata->id;
 646                err = insert_retry(&ht, &tdata->objs[i], test_rht_params);
 647                if (err > 0) {
 648                        insert_retries += err;
 649                } else if (err) {
 650                        pr_err("  thread[%d]: rhashtable_insert_fast failed\n",
 651                               tdata->id);
 652                        goto out;
 653                }
 654        }
 655        if (insert_retries)
 656                pr_info("  thread[%d]: %u insertions retried due to memory pressure\n",
 657                        tdata->id, insert_retries);
 658
 659        err = thread_lookup_test(tdata);
 660        if (err) {
 661                pr_err("  thread[%d]: rhashtable_lookup_test failed\n",
 662                       tdata->id);
 663                goto out;
 664        }
 665
 666        for (step = 10; step > 0; step--) {
 667                for (i = 0; i < tdata->entries; i += step) {
 668                        if (tdata->objs[i].value.id == TEST_INSERT_FAIL)
 669                                continue;
 670                        err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
 671                                                     test_rht_params);
 672                        if (err) {
 673                                pr_err("  thread[%d]: rhashtable_remove_fast failed\n",
 674                                       tdata->id);
 675                                goto out;
 676                        }
 677                        tdata->objs[i].value.id = TEST_INSERT_FAIL;
 678
 679                        cond_resched();
 680                }
 681                err = thread_lookup_test(tdata);
 682                if (err) {
 683                        pr_err("  thread[%d]: rhashtable_lookup_test (2) failed\n",
 684                               tdata->id);
 685                        goto out;
 686                }
 687        }
 688out:
 689        while (!kthread_should_stop()) {
 690                set_current_state(TASK_INTERRUPTIBLE);
 691                schedule();
 692        }
 693        return err;
 694}
 695
 696static int __init test_rht_init(void)
 697{
 698        unsigned int entries;
 699        int i, err, started_threads = 0, failed_threads = 0;
 700        u64 total_time = 0;
 701        struct thread_data *tdata;
 702        struct test_obj *objs;
 703
 704        if (parm_entries < 0)
 705                parm_entries = 1;
 706
 707        entries = min(parm_entries, MAX_ENTRIES);
 708
 709        test_rht_params.automatic_shrinking = shrinking;
 710        test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
 711        test_rht_params.nelem_hint = size;
 712
 713        objs = vzalloc(array_size(sizeof(struct test_obj),
 714                                  test_rht_params.max_size + 1));
 715        if (!objs)
 716                return -ENOMEM;
 717
 718        pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
 719                size, max_size, shrinking);
 720
 721        for (i = 0; i < runs; i++) {
 722                s64 time;
 723
 724                pr_info("Test %02d:\n", i);
 725                memset(objs, 0, test_rht_params.max_size * sizeof(struct test_obj));
 726
 727                err = rhashtable_init(&ht, &test_rht_params);
 728                if (err < 0) {
 729                        pr_warn("Test failed: Unable to initialize hashtable: %d\n",
 730                                err);
 731                        continue;
 732                }
 733
 734                time = test_rhashtable(&ht, objs, entries);
 735                rhashtable_destroy(&ht);
 736                if (time < 0) {
 737                        vfree(objs);
 738                        pr_warn("Test failed: return code %lld\n", time);
 739                        return -EINVAL;
 740                }
 741
 742                total_time += time;
 743        }
 744
 745        pr_info("test if its possible to exceed max_size %d: %s\n",
 746                        test_rht_params.max_size, test_rhashtable_max(objs, entries) == 0 ?
 747                        "no, ok" : "YES, failed");
 748        vfree(objs);
 749
 750        do_div(total_time, runs);
 751        pr_info("Average test time: %llu\n", total_time);
 752
 753        test_insert_duplicates_run();
 754
 755        if (!tcount)
 756                return 0;
 757
 758        pr_info("Testing concurrent rhashtable access from %d threads\n",
 759                tcount);
 760        atomic_set(&startup_count, tcount);
 761        tdata = vzalloc(array_size(tcount, sizeof(struct thread_data)));
 762        if (!tdata)
 763                return -ENOMEM;
 764        objs  = vzalloc(array3_size(sizeof(struct test_obj), tcount, entries));
 765        if (!objs) {
 766                vfree(tdata);
 767                return -ENOMEM;
 768        }
 769
 770        test_rht_params.max_size = max_size ? :
 771                                   roundup_pow_of_two(tcount * entries);
 772        err = rhashtable_init(&ht, &test_rht_params);
 773        if (err < 0) {
 774                pr_warn("Test failed: Unable to initialize hashtable: %d\n",
 775                        err);
 776                vfree(tdata);
 777                vfree(objs);
 778                return -EINVAL;
 779        }
 780        for (i = 0; i < tcount; i++) {
 781                tdata[i].id = i;
 782                tdata[i].entries = entries;
 783                tdata[i].objs = objs + i * entries;
 784                tdata[i].task = kthread_run(threadfunc, &tdata[i],
 785                                            "rhashtable_thrad[%d]", i);
 786                if (IS_ERR(tdata[i].task)) {
 787                        pr_err(" kthread_run failed for thread %d\n", i);
 788                        atomic_dec(&startup_count);
 789                } else {
 790                        started_threads++;
 791                }
 792        }
 793        if (wait_event_interruptible(startup_wait, atomic_read(&startup_count) == 0))
 794                pr_err("  wait_event interruptible failed\n");
 795        /* count is 0 now, set it to -1 and wake up all threads together */
 796        atomic_dec(&startup_count);
 797        wake_up_all(&startup_wait);
 798        for (i = 0; i < tcount; i++) {
 799                if (IS_ERR(tdata[i].task))
 800                        continue;
 801                if ((err = kthread_stop(tdata[i].task))) {
 802                        pr_warn("Test failed: thread %d returned: %d\n",
 803                                i, err);
 804                        failed_threads++;
 805                }
 806        }
 807        rhashtable_destroy(&ht);
 808        vfree(tdata);
 809        vfree(objs);
 810
 811        /*
 812         * rhltable_remove is very expensive, default values can cause test
 813         * to run for 2 minutes or more,  use a smaller number instead.
 814         */
 815        err = test_rhltable(entries / 16);
 816        pr_info("Started %d threads, %d failed, rhltable test returns %d\n",
 817                started_threads, failed_threads, err);
 818        return 0;
 819}
 820
 821static void __exit test_rht_exit(void)
 822{
 823}
 824
 825module_init(test_rht_init);
 826module_exit(test_rht_exit);
 827
 828MODULE_LICENSE("GPL v2");
 829