| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Resizable, Scalable, Concurrent Hash Table |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch> |
|---|
| 5 | 6 | * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 9 | | - * published by the Free Software Foundation. |
|---|
| 10 | 7 | */ |
|---|
| 11 | 8 | |
|---|
| 12 | 9 | /************************************************************************** |
|---|
| .. | .. |
|---|
| 20 | 17 | #include <linux/module.h> |
|---|
| 21 | 18 | #include <linux/rcupdate.h> |
|---|
| 22 | 19 | #include <linux/rhashtable.h> |
|---|
| 23 | | -#include <linux/semaphore.h> |
|---|
| 24 | 20 | #include <linux/slab.h> |
|---|
| 25 | 21 | #include <linux/sched.h> |
|---|
| 26 | 22 | #include <linux/random.h> |
|---|
| 27 | 23 | #include <linux/vmalloc.h> |
|---|
| 24 | +#include <linux/wait.h> |
|---|
| 28 | 25 | |
|---|
| 29 | 26 | #define MAX_ENTRIES 1000000 |
|---|
| 30 | 27 | #define TEST_INSERT_FAIL INT_MAX |
|---|
| .. | .. |
|---|
| 112 | 109 | .automatic_shrinking = false, |
|---|
| 113 | 110 | }; |
|---|
| 114 | 111 | |
|---|
| 115 | | -static struct semaphore prestart_sem; |
|---|
| 116 | | -static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0); |
|---|
| 112 | +static atomic_t startup_count; |
|---|
| 113 | +static DECLARE_WAIT_QUEUE_HEAD(startup_wait); |
|---|
| 117 | 114 | |
|---|
| 118 | 115 | static int insert_retry(struct rhashtable *ht, struct test_obj *obj, |
|---|
| 119 | 116 | const struct rhashtable_params params) |
|---|
| .. | .. |
|---|
| 177 | 174 | |
|---|
| 178 | 175 | static void test_bucket_stats(struct rhashtable *ht, unsigned int entries) |
|---|
| 179 | 176 | { |
|---|
| 180 | | - unsigned int err, total = 0, chain_len = 0; |
|---|
| 177 | + unsigned int total = 0, chain_len = 0; |
|---|
| 181 | 178 | struct rhashtable_iter hti; |
|---|
| 182 | 179 | struct rhash_head *pos; |
|---|
| 183 | 180 | |
|---|
| 184 | | - err = rhashtable_walk_init(ht, &hti, GFP_KERNEL); |
|---|
| 185 | | - if (err) { |
|---|
| 186 | | - pr_warn("Test failed: allocation error"); |
|---|
| 187 | | - return; |
|---|
| 188 | | - } |
|---|
| 189 | | - |
|---|
| 181 | + rhashtable_walk_enter(ht, &hti); |
|---|
| 190 | 182 | rhashtable_walk_start(&hti); |
|---|
| 191 | 183 | |
|---|
| 192 | 184 | while ((pos = rhashtable_walk_next(&hti))) { |
|---|
| .. | .. |
|---|
| 395 | 387 | if (WARN(err, "cannot remove element at slot %d", i)) |
|---|
| 396 | 388 | continue; |
|---|
| 397 | 389 | } else { |
|---|
| 398 | | - if (WARN(err != -ENOENT, "removed non-existant element %d, error %d not %d", |
|---|
| 390 | + if (WARN(err != -ENOENT, "removed non-existent element %d, error %d not %d", |
|---|
| 399 | 391 | i, err, -ENOENT)) |
|---|
| 400 | 392 | continue; |
|---|
| 401 | 393 | } |
|---|
| .. | .. |
|---|
| 440 | 432 | if (WARN(err, "cannot remove element at slot %d", i)) |
|---|
| 441 | 433 | continue; |
|---|
| 442 | 434 | } else { |
|---|
| 443 | | - if (WARN(err != -ENOENT, "removed non-existant element, error %d not %d", |
|---|
| 435 | + if (WARN(err != -ENOENT, "removed non-existent element, error %d not %d", |
|---|
| 444 | 436 | err, -ENOENT)) |
|---|
| 445 | | - continue; |
|---|
| 437 | + continue; |
|---|
| 446 | 438 | } |
|---|
| 447 | 439 | } |
|---|
| 448 | 440 | |
|---|
| .. | .. |
|---|
| 505 | 497 | struct rhash_head *pos, *next; |
|---|
| 506 | 498 | struct test_obj_rhl *p; |
|---|
| 507 | 499 | |
|---|
| 508 | | - pos = rht_dereference(tbl->buckets[i], ht); |
|---|
| 500 | + pos = rht_ptr_exclusive(tbl->buckets + i); |
|---|
| 509 | 501 | next = !rht_is_a_nulls(pos) ? rht_dereference(pos->next, ht) : NULL; |
|---|
| 510 | 502 | |
|---|
| 511 | 503 | if (!rht_is_a_nulls(pos)) { |
|---|
| .. | .. |
|---|
| 641 | 633 | int i, step, err = 0, insert_retries = 0; |
|---|
| 642 | 634 | struct thread_data *tdata = data; |
|---|
| 643 | 635 | |
|---|
| 644 | | - up(&prestart_sem); |
|---|
| 645 | | - if (down_interruptible(&startup_sem)) |
|---|
| 646 | | - pr_err(" thread[%d]: down_interruptible failed\n", tdata->id); |
|---|
| 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 | + } |
|---|
| 647 | 642 | |
|---|
| 648 | 643 | for (i = 0; i < tdata->entries; i++) { |
|---|
| 649 | 644 | tdata->objs[i].value.id = i; |
|---|
| .. | .. |
|---|
| 762 | 757 | |
|---|
| 763 | 758 | pr_info("Testing concurrent rhashtable access from %d threads\n", |
|---|
| 764 | 759 | tcount); |
|---|
| 765 | | - sema_init(&prestart_sem, 1 - tcount); |
|---|
| 760 | + atomic_set(&startup_count, tcount); |
|---|
| 766 | 761 | tdata = vzalloc(array_size(tcount, sizeof(struct thread_data))); |
|---|
| 767 | 762 | if (!tdata) |
|---|
| 768 | 763 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 788 | 783 | tdata[i].objs = objs + i * entries; |
|---|
| 789 | 784 | tdata[i].task = kthread_run(threadfunc, &tdata[i], |
|---|
| 790 | 785 | "rhashtable_thrad[%d]", i); |
|---|
| 791 | | - if (IS_ERR(tdata[i].task)) |
|---|
| 786 | + if (IS_ERR(tdata[i].task)) { |
|---|
| 792 | 787 | pr_err(" kthread_run failed for thread %d\n", i); |
|---|
| 793 | | - else |
|---|
| 788 | + atomic_dec(&startup_count); |
|---|
| 789 | + } else { |
|---|
| 794 | 790 | started_threads++; |
|---|
| 791 | + } |
|---|
| 795 | 792 | } |
|---|
| 796 | | - if (down_interruptible(&prestart_sem)) |
|---|
| 797 | | - pr_err(" down interruptible failed\n"); |
|---|
| 798 | | - for (i = 0; i < tcount; i++) |
|---|
| 799 | | - up(&startup_sem); |
|---|
| 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); |
|---|
| 800 | 798 | for (i = 0; i < tcount; i++) { |
|---|
| 801 | 799 | if (IS_ERR(tdata[i].task)) |
|---|
| 802 | 800 | continue; |
|---|