.. | .. |
---|
| 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; |
---|