blob: 49fffb9bce77c08b58b6c95c9a0ce8d0d02dd37f [file] [log] [blame]
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -04001// SPDX-License-Identifier: GPL-2.0+
2//
3// Performance test comparing RCU vs other mechanisms
4// for acquiring references on objects.
5//
6// Copyright (C) Google, 2020.
7//
8// Author: Joel Fernandes <joel@joelfernandes.org>
9
10#define pr_fmt(fmt) fmt
11
12#include <linux/atomic.h>
13#include <linux/bitops.h>
14#include <linux/completion.h>
15#include <linux/cpu.h>
16#include <linux/delay.h>
17#include <linux/err.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/kthread.h>
21#include <linux/kernel.h>
22#include <linux/mm.h>
23#include <linux/module.h>
24#include <linux/moduleparam.h>
25#include <linux/notifier.h>
26#include <linux/percpu.h>
27#include <linux/rcupdate.h>
28#include <linux/reboot.h>
29#include <linux/sched.h>
30#include <linux/spinlock.h>
31#include <linux/smp.h>
32#include <linux/stat.h>
33#include <linux/srcu.h>
34#include <linux/slab.h>
35#include <linux/torture.h>
36#include <linux/types.h>
37
38#include "rcu.h"
39
40#define PERF_FLAG "-ref-perf: "
41
42#define PERFOUT(s, x...) \
43 pr_alert("%s" PERF_FLAG s, perf_type, ## x)
44
45#define VERBOSE_PERFOUT(s, x...) \
46 do { if (verbose) pr_alert("%s" PERF_FLAG s, perf_type, ## x); } while (0)
47
48#define VERBOSE_PERFOUT_ERRSTRING(s, x...) \
49 do { if (verbose) pr_alert("%s" PERF_FLAG "!!! " s, perf_type, ## x); } while (0)
50
51MODULE_LICENSE("GPL");
52MODULE_AUTHOR("Joel Fernandes (Google) <joel@joelfernandes.org>");
53
54static char *perf_type = "rcu";
55module_param(perf_type, charp, 0444);
56MODULE_PARM_DESC(perf_type, "Type of test (rcu, srcu, refcnt, rwsem, rwlock.");
57
58torture_param(int, verbose, 0, "Enable verbose debugging printk()s");
59
Paul E. McKenney777a54c2020-05-25 14:16:44 -070060// Wait until there are multiple CPUs before starting test.
61torture_param(int, holdoff, IS_BUILTIN(CONFIG_RCU_REF_PERF_TEST) ? 10 : 0,
62 "Holdoff time before test start (s)");
63// Number of loops per experiment, all readers execute operations concurrently.
Paul E. McKenney4dd72a32020-05-29 13:11:26 -070064torture_param(long, loops, 10000, "Number of loops per experiment.");
Paul E. McKenney8fc28782020-05-25 15:48:38 -070065// Number of readers, with -1 defaulting to about 75% of the CPUs.
66torture_param(int, nreaders, -1, "Number of readers, -1 for 75% of CPUs.");
67// Number of runs.
68torture_param(int, nruns, 30, "Number of experiments to run.");
Paul E. McKenney918b3512020-05-31 18:14:57 -070069// Reader delay in nanoseconds, 0 for no delay.
70torture_param(int, readdelay, 0, "Read-side delay in nanoseconds.");
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -040071
72#ifdef MODULE
73# define REFPERF_SHUTDOWN 0
74#else
75# define REFPERF_SHUTDOWN 1
76#endif
77
78torture_param(bool, shutdown, REFPERF_SHUTDOWN,
79 "Shutdown at end of performance tests.");
80
81struct reader_task {
82 struct task_struct *task;
Paul E. McKenneyaf2789d2020-05-26 11:22:03 -070083 int start_reader;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -040084 wait_queue_head_t wq;
85 u64 last_duration_ns;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -040086};
87
88static struct task_struct *shutdown_task;
89static wait_queue_head_t shutdown_wq;
90
91static struct task_struct *main_task;
92static wait_queue_head_t main_wq;
93static int shutdown_start;
94
95static struct reader_task *reader_tasks;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -040096
97// Number of readers that are part of the current experiment.
98static atomic_t nreaders_exp;
99
100// Use to wait for all threads to start.
101static atomic_t n_init;
Paul E. McKenney86e0da22020-05-26 11:40:52 -0700102static atomic_t n_started;
Paul E. McKenney2db0bda2020-05-26 12:34:57 -0700103static atomic_t n_warmedup;
104static atomic_t n_cooleddown;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400105
106// Track which experiment is currently running.
107static int exp_idx;
108
109// Operations vector for selecting different types of tests.
110struct ref_perf_ops {
111 void (*init)(void);
112 void (*cleanup)(void);
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700113 void (*readsection)(const int nloops);
Paul E. McKenney918b3512020-05-31 18:14:57 -0700114 void (*delaysection)(const int nloops, const int udl, const int ndl);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400115 const char *name;
116};
117
118static struct ref_perf_ops *cur_ops;
119
Paul E. McKenney918b3512020-05-31 18:14:57 -0700120static void un_delay(const int udl, const int ndl)
121{
122 if (udl)
123 udelay(udl);
124 if (ndl)
125 ndelay(ndl);
126}
127
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700128static void ref_rcu_read_section(const int nloops)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400129{
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700130 int i;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400131
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700132 for (i = nloops; i >= 0; i--) {
133 rcu_read_lock();
134 rcu_read_unlock();
135 }
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400136}
137
Paul E. McKenney918b3512020-05-31 18:14:57 -0700138static void ref_rcu_delay_section(const int nloops, const int udl, const int ndl)
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700139{
140 int i;
141
142 for (i = nloops; i >= 0; i--) {
143 rcu_read_lock();
Paul E. McKenney918b3512020-05-31 18:14:57 -0700144 un_delay(udl, ndl);
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700145 rcu_read_unlock();
146 }
147}
148
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400149static void rcu_sync_perf_init(void)
150{
151}
152
153static struct ref_perf_ops rcu_ops = {
154 .init = rcu_sync_perf_init,
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700155 .readsection = ref_rcu_read_section,
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700156 .delaysection = ref_rcu_delay_section,
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400157 .name = "rcu"
158};
159
160
161// Definitions for SRCU ref perf testing.
162DEFINE_STATIC_SRCU(srcu_refctl_perf);
163static struct srcu_struct *srcu_ctlp = &srcu_refctl_perf;
164
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700165static void srcu_ref_perf_read_section(const int nloops)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400166{
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700167 int i;
168 int idx;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400169
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700170 for (i = nloops; i >= 0; i--) {
171 idx = srcu_read_lock(srcu_ctlp);
172 srcu_read_unlock(srcu_ctlp, idx);
173 }
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400174}
175
Paul E. McKenney918b3512020-05-31 18:14:57 -0700176static void srcu_ref_perf_delay_section(const int nloops, const int udl, const int ndl)
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700177{
178 int i;
179 int idx;
180
181 for (i = nloops; i >= 0; i--) {
182 idx = srcu_read_lock(srcu_ctlp);
Paul E. McKenney918b3512020-05-31 18:14:57 -0700183 un_delay(udl, ndl);
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700184 srcu_read_unlock(srcu_ctlp, idx);
185 }
186}
187
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400188static struct ref_perf_ops srcu_ops = {
189 .init = rcu_sync_perf_init,
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700190 .readsection = srcu_ref_perf_read_section,
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700191 .delaysection = srcu_ref_perf_delay_section,
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400192 .name = "srcu"
193};
194
195// Definitions for reference count
196static atomic_t refcnt;
197
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700198static void ref_refcnt_section(const int nloops)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400199{
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700200 int i;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400201
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700202 for (i = nloops; i >= 0; i--) {
203 atomic_inc(&refcnt);
204 atomic_dec(&refcnt);
205 }
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400206}
207
Paul E. McKenney918b3512020-05-31 18:14:57 -0700208static void ref_refcnt_delay_section(const int nloops, const int udl, const int ndl)
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700209{
210 int i;
211
212 for (i = nloops; i >= 0; i--) {
213 atomic_inc(&refcnt);
Paul E. McKenney918b3512020-05-31 18:14:57 -0700214 un_delay(udl, ndl);
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700215 atomic_dec(&refcnt);
216 }
217}
218
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400219static struct ref_perf_ops refcnt_ops = {
220 .init = rcu_sync_perf_init,
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700221 .readsection = ref_refcnt_section,
222 .delaysection = ref_refcnt_delay_section,
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400223 .name = "refcnt"
224};
225
226// Definitions for rwlock
227static rwlock_t test_rwlock;
228
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700229static void ref_rwlock_init(void)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400230{
231 rwlock_init(&test_rwlock);
232}
233
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700234static void ref_rwlock_section(const int nloops)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400235{
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700236 int i;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400237
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700238 for (i = nloops; i >= 0; i--) {
239 read_lock(&test_rwlock);
240 read_unlock(&test_rwlock);
241 }
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400242}
243
Paul E. McKenney918b3512020-05-31 18:14:57 -0700244static void ref_rwlock_delay_section(const int nloops, const int udl, const int ndl)
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700245{
246 int i;
247
248 for (i = nloops; i >= 0; i--) {
249 read_lock(&test_rwlock);
Paul E. McKenney918b3512020-05-31 18:14:57 -0700250 un_delay(udl, ndl);
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700251 read_unlock(&test_rwlock);
252 }
253}
254
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400255static struct ref_perf_ops rwlock_ops = {
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700256 .init = ref_rwlock_init,
257 .readsection = ref_rwlock_section,
258 .delaysection = ref_rwlock_delay_section,
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400259 .name = "rwlock"
260};
261
262// Definitions for rwsem
263static struct rw_semaphore test_rwsem;
264
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700265static void ref_rwsem_init(void)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400266{
267 init_rwsem(&test_rwsem);
268}
269
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700270static void ref_rwsem_section(const int nloops)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400271{
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700272 int i;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400273
Paul E. McKenney75dd8ef2020-05-25 14:59:06 -0700274 for (i = nloops; i >= 0; i--) {
275 down_read(&test_rwsem);
276 up_read(&test_rwsem);
277 }
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400278}
279
Paul E. McKenney918b3512020-05-31 18:14:57 -0700280static void ref_rwsem_delay_section(const int nloops, const int udl, const int ndl)
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700281{
282 int i;
283
284 for (i = nloops; i >= 0; i--) {
285 down_read(&test_rwsem);
Paul E. McKenney918b3512020-05-31 18:14:57 -0700286 un_delay(udl, ndl);
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700287 up_read(&test_rwsem);
288 }
289}
290
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400291static struct ref_perf_ops rwsem_ops = {
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700292 .init = ref_rwsem_init,
293 .readsection = ref_rwsem_section,
294 .delaysection = ref_rwsem_delay_section,
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400295 .name = "rwsem"
296};
297
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700298static void rcu_perf_one_reader(void)
299{
300 if (readdelay <= 0)
301 cur_ops->readsection(loops);
302 else
Paul E. McKenney918b3512020-05-31 18:14:57 -0700303 cur_ops->delaysection(loops, readdelay / 1000, readdelay % 1000);
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700304}
305
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400306// Reader kthread. Repeatedly does empty RCU read-side
307// critical section, minimizing update-side interference.
308static int
309ref_perf_reader(void *arg)
310{
311 unsigned long flags;
312 long me = (long)arg;
313 struct reader_task *rt = &(reader_tasks[me]);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400314 u64 start;
315 s64 duration;
316
317 VERBOSE_PERFOUT("ref_perf_reader %ld: task started", me);
318 set_cpus_allowed_ptr(current, cpumask_of(me % nr_cpu_ids));
319 set_user_nice(current, MAX_NICE);
320 atomic_inc(&n_init);
Paul E. McKenney777a54c2020-05-25 14:16:44 -0700321 if (holdoff)
322 schedule_timeout_interruptible(holdoff * HZ);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400323repeat:
324 VERBOSE_PERFOUT("ref_perf_reader %ld: waiting to start next experiment on cpu %d", me, smp_processor_id());
325
326 // Wait for signal that this reader can start.
Paul E. McKenneyaf2789d2020-05-26 11:22:03 -0700327 wait_event(rt->wq, (atomic_read(&nreaders_exp) && smp_load_acquire(&rt->start_reader)) ||
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400328 torture_must_stop());
329
330 if (torture_must_stop())
331 goto end;
332
333 // Make sure that the CPU is affinitized appropriately during testing.
334 WARN_ON_ONCE(smp_processor_id() != me);
335
Paul E. McKenneyaf2789d2020-05-26 11:22:03 -0700336 WRITE_ONCE(rt->start_reader, 0);
Paul E. McKenney86e0da22020-05-26 11:40:52 -0700337 if (!atomic_dec_return(&n_started))
338 while (atomic_read_acquire(&n_started))
339 cpu_relax();
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400340
Paul E. McKenneyb864f892020-05-26 10:57:34 -0700341 VERBOSE_PERFOUT("ref_perf_reader %ld: experiment %d started", me, exp_idx);
342
Paul E. McKenney2db0bda2020-05-26 12:34:57 -0700343
344 // To reduce noise, do an initial cache-warming invocation, check
345 // in, and then keep warming until everyone has checked in.
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700346 rcu_perf_one_reader();
Paul E. McKenney2db0bda2020-05-26 12:34:57 -0700347 if (!atomic_dec_return(&n_warmedup))
348 while (atomic_read_acquire(&n_warmedup))
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700349 rcu_perf_one_reader();
Paul E. McKenney2db0bda2020-05-26 12:34:57 -0700350 // Also keep interrupts disabled. This also has the effect
351 // of preventing entries into slow path for rcu_read_unlock().
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400352 local_irq_save(flags);
353 start = ktime_get_mono_fast_ns();
354
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700355 rcu_perf_one_reader();
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400356
357 duration = ktime_get_mono_fast_ns() - start;
358 local_irq_restore(flags);
359
360 rt->last_duration_ns = WARN_ON_ONCE(duration < 0) ? 0 : duration;
Paul E. McKenney2db0bda2020-05-26 12:34:57 -0700361 // To reduce runtime-skew noise, do maintain-load invocations until
362 // everyone is done.
363 if (!atomic_dec_return(&n_cooleddown))
364 while (atomic_read_acquire(&n_cooleddown))
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700365 rcu_perf_one_reader();
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400366
Paul E. McKenneyb864f892020-05-26 10:57:34 -0700367 if (atomic_dec_and_test(&nreaders_exp))
368 wake_up(&main_wq);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400369
370 VERBOSE_PERFOUT("ref_perf_reader %ld: experiment %d ended, (readers remaining=%d)",
371 me, exp_idx, atomic_read(&nreaders_exp));
372
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400373 if (!torture_must_stop())
374 goto repeat;
375end:
376 torture_kthread_stopping("ref_perf_reader");
377 return 0;
378}
379
Paul E. McKenney29907502020-05-26 09:32:57 -0700380static void reset_readers(void)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400381{
382 int i;
383 struct reader_task *rt;
384
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700385 for (i = 0; i < nreaders; i++) {
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400386 rt = &(reader_tasks[i]);
387
388 rt->last_duration_ns = 0;
389 }
390}
391
392// Print the results of each reader and return the sum of all their durations.
Paul E. McKenney29907502020-05-26 09:32:57 -0700393static u64 process_durations(int n)
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400394{
395 int i;
396 struct reader_task *rt;
397 char buf1[64];
Paul E. McKenney2e90de72020-05-25 17:45:03 -0700398 char *buf;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400399 u64 sum = 0;
400
Paul E. McKenney2e90de72020-05-25 17:45:03 -0700401 buf = kmalloc(128 + nreaders * 32, GFP_KERNEL);
402 if (!buf)
403 return 0;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400404 buf[0] = 0;
405 sprintf(buf, "Experiment #%d (Format: <THREAD-NUM>:<Total loop time in ns>)",
406 exp_idx);
407
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700408 for (i = 0; i < n && !torture_must_stop(); i++) {
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400409 rt = &(reader_tasks[i]);
410 sprintf(buf1, "%d: %llu\t", i, rt->last_duration_ns);
411
412 if (i % 5 == 0)
413 strcat(buf, "\n");
414 strcat(buf, buf1);
415
416 sum += rt->last_duration_ns;
417 }
418 strcat(buf, "\n");
419
420 PERFOUT("%s\n", buf);
421
Paul E. McKenney2e90de72020-05-25 17:45:03 -0700422 kfree(buf);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400423 return sum;
424}
425
426// The main_func is the main orchestrator, it performs a bunch of
427// experiments. For every experiment, it orders all the readers
428// involved to start and waits for them to finish the experiment. It
429// then reads their timestamps and starts the next experiment. Each
430// experiment progresses from 1 concurrent reader to N of them at which
431// point all the timestamps are printed.
432static int main_func(void *arg)
433{
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700434 bool errexit = false;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400435 int exp, r;
436 char buf1[64];
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700437 char *buf;
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700438 u64 *result_avg;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400439
440 set_cpus_allowed_ptr(current, cpumask_of(nreaders % nr_cpu_ids));
441 set_user_nice(current, MAX_NICE);
442
443 VERBOSE_PERFOUT("main_func task started");
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700444 result_avg = kzalloc(nruns * sizeof(*result_avg), GFP_KERNEL);
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700445 buf = kzalloc(64 + nruns * 32, GFP_KERNEL);
446 if (!result_avg || !buf) {
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700447 VERBOSE_PERFOUT_ERRSTRING("out of memory");
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700448 errexit = true;
449 }
Paul E. McKenney777a54c2020-05-25 14:16:44 -0700450 if (holdoff)
451 schedule_timeout_interruptible(holdoff * HZ);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400452
Paul E. McKenney96af8662020-05-27 16:46:56 -0700453 // Wait for all threads to start.
454 atomic_inc(&n_init);
455 while (atomic_read(&n_init) < nreaders + 1)
456 schedule_timeout_uninterruptible(1);
457
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400458 // Start exp readers up per experiment
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700459 for (exp = 0; exp < nruns && !torture_must_stop(); exp++) {
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700460 if (errexit)
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700461 break;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400462 if (torture_must_stop())
463 goto end;
464
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700465 reset_readers();
466 atomic_set(&nreaders_exp, nreaders);
Paul E. McKenney86e0da22020-05-26 11:40:52 -0700467 atomic_set(&n_started, nreaders);
Paul E. McKenney2db0bda2020-05-26 12:34:57 -0700468 atomic_set(&n_warmedup, nreaders);
469 atomic_set(&n_cooleddown, nreaders);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400470
471 exp_idx = exp;
472
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700473 for (r = 0; r < nreaders; r++) {
Paul E. McKenneyaf2789d2020-05-26 11:22:03 -0700474 smp_store_release(&reader_tasks[r].start_reader, 1);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400475 wake_up(&reader_tasks[r].wq);
476 }
477
478 VERBOSE_PERFOUT("main_func: experiment started, waiting for %d readers",
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700479 nreaders);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400480
481 wait_event(main_wq,
482 !atomic_read(&nreaders_exp) || torture_must_stop());
483
484 VERBOSE_PERFOUT("main_func: experiment ended");
485
486 if (torture_must_stop())
487 goto end;
488
Arnd Bergmann7c944d72020-05-29 14:36:26 -0700489 result_avg[exp] = div_u64(1000 * process_durations(nreaders), nreaders * loops);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400490 }
491
492 // Print the average of all experiments
493 PERFOUT("END OF TEST. Calculating average duration per loop (nanoseconds)...\n");
494
495 buf[0] = 0;
496 strcat(buf, "\n");
Paul E. McKenney6efb0632020-05-26 14:26:25 -0700497 strcat(buf, "Runs\tTime(ns)\n");
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400498
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700499 for (exp = 0; exp < nruns; exp++) {
Arnd Bergmann7c944d72020-05-29 14:36:26 -0700500 u64 avg;
501 u32 rem;
502
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700503 if (errexit)
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700504 break;
Arnd Bergmann7c944d72020-05-29 14:36:26 -0700505 avg = div_u64_rem(result_avg[exp], 1000, &rem);
506 sprintf(buf1, "%d\t%llu.%03u\n", exp + 1, avg, rem);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400507 strcat(buf, buf1);
508 }
509
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700510 if (!errexit)
Paul E. McKenneydbf28ef2020-05-25 17:22:24 -0700511 PERFOUT("%s", buf);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400512
513 // This will shutdown everything including us.
514 if (shutdown) {
515 shutdown_start = 1;
516 wake_up(&shutdown_wq);
517 }
518
519 // Wait for torture to stop us
520 while (!torture_must_stop())
521 schedule_timeout_uninterruptible(1);
522
523end:
524 torture_kthread_stopping("main_func");
Paul E. McKenneyf518f152020-05-25 17:32:56 -0700525 kfree(result_avg);
526 kfree(buf);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400527 return 0;
528}
529
530static void
531ref_perf_print_module_parms(struct ref_perf_ops *cur_ops, const char *tag)
532{
533 pr_alert("%s" PERF_FLAG
Paul E. McKenneyb4d1e342020-05-28 16:37:35 -0700534 "--- %s: verbose=%d shutdown=%d holdoff=%d loops=%ld nreaders=%d nruns=%d readdelay=%d\n", perf_type, tag,
535 verbose, shutdown, holdoff, loops, nreaders, nruns, readdelay);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400536}
537
538static void
539ref_perf_cleanup(void)
540{
541 int i;
542
543 if (torture_cleanup_begin())
544 return;
545
546 if (!cur_ops) {
547 torture_cleanup_end();
548 return;
549 }
550
551 if (reader_tasks) {
552 for (i = 0; i < nreaders; i++)
553 torture_stop_kthread("ref_perf_reader",
554 reader_tasks[i].task);
555 }
556 kfree(reader_tasks);
557
558 torture_stop_kthread("main_task", main_task);
559 kfree(main_task);
560
561 // Do perf-type-specific cleanup operations.
562 if (cur_ops->cleanup != NULL)
563 cur_ops->cleanup();
564
565 torture_cleanup_end();
566}
567
568// Shutdown kthread. Just waits to be awakened, then shuts down system.
569static int
570ref_perf_shutdown(void *arg)
571{
572 wait_event(shutdown_wq, shutdown_start);
573
574 smp_mb(); // Wake before output.
575 ref_perf_cleanup();
576 kernel_power_off();
577
578 return -EINVAL;
579}
580
581static int __init
582ref_perf_init(void)
583{
584 long i;
585 int firsterr = 0;
586 static struct ref_perf_ops *perf_ops[] = {
587 &rcu_ops, &srcu_ops, &refcnt_ops, &rwlock_ops, &rwsem_ops,
588 };
589
590 if (!torture_init_begin(perf_type, verbose))
591 return -EBUSY;
592
593 for (i = 0; i < ARRAY_SIZE(perf_ops); i++) {
594 cur_ops = perf_ops[i];
595 if (strcmp(perf_type, cur_ops->name) == 0)
596 break;
597 }
598 if (i == ARRAY_SIZE(perf_ops)) {
599 pr_alert("rcu-perf: invalid perf type: \"%s\"\n", perf_type);
600 pr_alert("rcu-perf types:");
601 for (i = 0; i < ARRAY_SIZE(perf_ops); i++)
602 pr_cont(" %s", perf_ops[i]->name);
603 pr_cont("\n");
604 WARN_ON(!IS_MODULE(CONFIG_RCU_REF_PERF_TEST));
605 firsterr = -EINVAL;
606 cur_ops = NULL;
607 goto unwind;
608 }
609 if (cur_ops->init)
610 cur_ops->init();
611
612 ref_perf_print_module_parms(cur_ops, "Start of test");
613
614 // Shutdown task
615 if (shutdown) {
616 init_waitqueue_head(&shutdown_wq);
617 firsterr = torture_create_kthread(ref_perf_shutdown, NULL,
618 shutdown_task);
619 if (firsterr)
620 goto unwind;
621 schedule_timeout_uninterruptible(1);
622 }
623
Paul E. McKenney8fc28782020-05-25 15:48:38 -0700624 // Reader tasks (default to ~75% of online CPUs).
625 if (nreaders < 0)
626 nreaders = (num_online_cpus() >> 1) + (num_online_cpus() >> 2);
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400627 reader_tasks = kcalloc(nreaders, sizeof(reader_tasks[0]),
628 GFP_KERNEL);
629 if (!reader_tasks) {
630 VERBOSE_PERFOUT_ERRSTRING("out of memory");
631 firsterr = -ENOMEM;
632 goto unwind;
633 }
634
635 VERBOSE_PERFOUT("Starting %d reader threads\n", nreaders);
636
637 for (i = 0; i < nreaders; i++) {
638 firsterr = torture_create_kthread(ref_perf_reader, (void *)i,
639 reader_tasks[i].task);
640 if (firsterr)
641 goto unwind;
642
643 init_waitqueue_head(&(reader_tasks[i].wq));
644 }
645
646 // Main Task
647 init_waitqueue_head(&main_wq);
648 firsterr = torture_create_kthread(main_func, NULL, main_task);
649 if (firsterr)
650 goto unwind;
Joel Fernandes (Google)653ed642020-05-25 00:36:48 -0400651
652 torture_init_end();
653 return 0;
654
655unwind:
656 torture_init_end();
657 ref_perf_cleanup();
658 return firsterr;
659}
660
661module_init(ref_perf_init);
662module_exit(ref_perf_cleanup);