Blame view

kernel/rcu/rcuperf.c 18.4 KB
8704baab9   Paul E. McKenney   rcutorture: Add R...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  /*
   * Read-Copy Update module-based performance-test facility
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, you can access it online at
   * http://www.gnu.org/licenses/gpl-2.0.html.
   *
   * Copyright (C) IBM Corporation, 2015
   *
   * Authors: Paul E. McKenney <paulmck@us.ibm.com>
   */
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/kthread.h>
  #include <linux/err.h>
  #include <linux/spinlock.h>
  #include <linux/smp.h>
  #include <linux/rcupdate.h>
  #include <linux/interrupt.h>
  #include <linux/sched.h>
ae7e81c07   Ingo Molnar   sched/headers: Pr...
33
  #include <uapi/linux/sched/types.h>
8704baab9   Paul E. McKenney   rcutorture: Add R...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  #include <linux/atomic.h>
  #include <linux/bitops.h>
  #include <linux/completion.h>
  #include <linux/moduleparam.h>
  #include <linux/percpu.h>
  #include <linux/notifier.h>
  #include <linux/reboot.h>
  #include <linux/freezer.h>
  #include <linux/cpu.h>
  #include <linux/delay.h>
  #include <linux/stat.h>
  #include <linux/srcu.h>
  #include <linux/slab.h>
  #include <asm/byteorder.h>
  #include <linux/torture.h>
  #include <linux/vmalloc.h>
25c36329a   Paul E. McKenney   rcu: Move expedit...
50
  #include "rcu.h"
8704baab9   Paul E. McKenney   rcutorture: Add R...
51
52
53
54
55
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.vnet.ibm.com>");
  
  #define PERF_FLAG "-perf:"
  #define PERFOUT_STRING(s) \
a56fefa26   SeongJae Park   rcuperf: Consiste...
56
57
  	pr_alert("%s" PERF_FLAG " %s
  ", perf_type, s)
8704baab9   Paul E. McKenney   rcutorture: Add R...
58
59
60
61
62
63
  #define VERBOSE_PERFOUT_STRING(s) \
  	do { if (verbose) pr_alert("%s" PERF_FLAG " %s
  ", perf_type, s); } while (0)
  #define VERBOSE_PERFOUT_ERRSTRING(s) \
  	do { if (verbose) pr_alert("%s" PERF_FLAG "!!! %s
  ", perf_type, s); } while (0)
881ed593a   Paul E. McKenney   rcuperf: Add abil...
64
65
  torture_param(bool, gp_async, false, "Use asynchronous GP wait primitives");
  torture_param(int, gp_async_max, 1000, "Max # outstanding waits per reader");
af06d4f74   Boqun Feng   rcuperf: Don't tr...
66
  torture_param(bool, gp_exp, false, "Use expedited GP wait primitives");
df37e66bf   Paul E. McKenney   rcutorture: Add r...
67
  torture_param(int, holdoff, 10, "Holdoff time before test start (s)");
492b95e59   Paul E. McKenney   rcuperf: Set more...
68
  torture_param(int, nreaders, 0, "Number of RCU reader threads");
8704baab9   Paul E. McKenney   rcutorture: Add R...
69
  torture_param(int, nwriters, -1, "Number of RCU updater threads");
492b95e59   Paul E. McKenney   rcuperf: Set more...
70
71
  torture_param(bool, shutdown, !IS_ENABLED(MODULE),
  	      "Shutdown at end of performance tests.");
8704baab9   Paul E. McKenney   rcutorture: Add R...
72
  torture_param(bool, verbose, true, "Enable verbose debugging printk()s");
820687a7b   Paul E. McKenney   rcuperf: Add writ...
73
  torture_param(int, writer_holdoff, 0, "Holdoff (us) between GPs, zero to disable");
8704baab9   Paul E. McKenney   rcutorture: Add R...
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  
  static char *perf_type = "rcu";
  module_param(perf_type, charp, 0444);
  MODULE_PARM_DESC(perf_type, "Type of RCU to performance-test (rcu, rcu_bh, ...)");
  
  static int nrealreaders;
  static int nrealwriters;
  static struct task_struct **writer_tasks;
  static struct task_struct **reader_tasks;
  static struct task_struct *shutdown_task;
  
  static u64 **writer_durations;
  static int *writer_n_durations;
  static atomic_t n_rcu_perf_reader_started;
  static atomic_t n_rcu_perf_writer_started;
  static atomic_t n_rcu_perf_writer_finished;
  static wait_queue_head_t shutdown_wq;
  static u64 t_rcu_perf_writer_started;
  static u64 t_rcu_perf_writer_finished;
  static unsigned long b_rcu_perf_writer_started;
  static unsigned long b_rcu_perf_writer_finished;
881ed593a   Paul E. McKenney   rcuperf: Add abil...
95
  static DEFINE_PER_CPU(atomic_t, n_async_inflight);
8704baab9   Paul E. McKenney   rcutorture: Add R...
96
97
98
  
  static int rcu_perf_writer_state;
  #define RTWS_INIT		0
881ed593a   Paul E. McKenney   rcuperf: Add abil...
99
100
101
102
103
104
  #define RTWS_ASYNC		1
  #define RTWS_BARRIER		2
  #define RTWS_EXP_SYNC		3
  #define RTWS_SYNC		4
  #define RTWS_IDLE		5
  #define RTWS_STOPPING		6
8704baab9   Paul E. McKenney   rcutorture: Add R...
105
106
107
  
  #define MAX_MEAS 10000
  #define MIN_MEAS 100
f8cbdee99   Paul E. McKenney   torture: Simplify...
108
  static int perf_runnable = IS_ENABLED(MODULE);
8704baab9   Paul E. McKenney   rcutorture: Add R...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
  module_param(perf_runnable, int, 0444);
  MODULE_PARM_DESC(perf_runnable, "Start rcuperf at boot");
  
  /*
   * Operations vector for selecting different types of tests.
   */
  
  struct rcu_perf_ops {
  	int ptype;
  	void (*init)(void);
  	void (*cleanup)(void);
  	int (*readlock)(void);
  	void (*readunlock)(int idx);
  	unsigned long (*started)(void);
  	unsigned long (*completed)(void);
  	unsigned long (*exp_completed)(void);
881ed593a   Paul E. McKenney   rcuperf: Add abil...
125
126
  	void (*async)(struct rcu_head *head, rcu_callback_t func);
  	void (*gp_barrier)(void);
8704baab9   Paul E. McKenney   rcutorture: Add R...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  	void (*sync)(void);
  	void (*exp_sync)(void);
  	const char *name;
  };
  
  static struct rcu_perf_ops *cur_ops;
  
  /*
   * Definitions for rcu perf testing.
   */
  
  static int rcu_perf_read_lock(void) __acquires(RCU)
  {
  	rcu_read_lock();
  	return 0;
  }
  
  static void rcu_perf_read_unlock(int idx) __releases(RCU)
  {
  	rcu_read_unlock();
  }
  
  static unsigned long __maybe_unused rcu_no_completed(void)
  {
  	return 0;
  }
  
  static void rcu_sync_perf_init(void)
  {
  }
  
  static struct rcu_perf_ops rcu_ops = {
  	.ptype		= RCU_FLAVOR,
  	.init		= rcu_sync_perf_init,
  	.readlock	= rcu_perf_read_lock,
  	.readunlock	= rcu_perf_read_unlock,
  	.started	= rcu_batches_started,
  	.completed	= rcu_batches_completed,
  	.exp_completed	= rcu_exp_batches_completed,
881ed593a   Paul E. McKenney   rcuperf: Add abil...
166
167
  	.async		= call_rcu,
  	.gp_barrier	= rcu_barrier,
8704baab9   Paul E. McKenney   rcutorture: Add R...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  	.sync		= synchronize_rcu,
  	.exp_sync	= synchronize_rcu_expedited,
  	.name		= "rcu"
  };
  
  /*
   * Definitions for rcu_bh perf testing.
   */
  
  static int rcu_bh_perf_read_lock(void) __acquires(RCU_BH)
  {
  	rcu_read_lock_bh();
  	return 0;
  }
  
  static void rcu_bh_perf_read_unlock(int idx) __releases(RCU_BH)
  {
  	rcu_read_unlock_bh();
  }
  
  static struct rcu_perf_ops rcu_bh_ops = {
  	.ptype		= RCU_BH_FLAVOR,
  	.init		= rcu_sync_perf_init,
  	.readlock	= rcu_bh_perf_read_lock,
  	.readunlock	= rcu_bh_perf_read_unlock,
  	.started	= rcu_batches_started_bh,
  	.completed	= rcu_batches_completed_bh,
  	.exp_completed	= rcu_exp_batches_completed_sched,
881ed593a   Paul E. McKenney   rcuperf: Add abil...
196
197
  	.async		= call_rcu_bh,
  	.gp_barrier	= rcu_barrier_bh,
8704baab9   Paul E. McKenney   rcutorture: Add R...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  	.sync		= synchronize_rcu_bh,
  	.exp_sync	= synchronize_rcu_bh_expedited,
  	.name		= "rcu_bh"
  };
  
  /*
   * Definitions for srcu perf testing.
   */
  
  DEFINE_STATIC_SRCU(srcu_ctl_perf);
  static struct srcu_struct *srcu_ctlp = &srcu_ctl_perf;
  
  static int srcu_perf_read_lock(void) __acquires(srcu_ctlp)
  {
  	return srcu_read_lock(srcu_ctlp);
  }
  
  static void srcu_perf_read_unlock(int idx) __releases(srcu_ctlp)
  {
  	srcu_read_unlock(srcu_ctlp, idx);
  }
  
  static unsigned long srcu_perf_completed(void)
  {
  	return srcu_batches_completed(srcu_ctlp);
  }
881ed593a   Paul E. McKenney   rcuperf: Add abil...
224
225
226
227
228
229
230
231
232
  static void srcu_call_rcu(struct rcu_head *head, rcu_callback_t func)
  {
  	call_srcu(srcu_ctlp, head, func);
  }
  
  static void srcu_rcu_barrier(void)
  {
  	srcu_barrier(srcu_ctlp);
  }
8704baab9   Paul E. McKenney   rcutorture: Add R...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  static void srcu_perf_synchronize(void)
  {
  	synchronize_srcu(srcu_ctlp);
  }
  
  static void srcu_perf_synchronize_expedited(void)
  {
  	synchronize_srcu_expedited(srcu_ctlp);
  }
  
  static struct rcu_perf_ops srcu_ops = {
  	.ptype		= SRCU_FLAVOR,
  	.init		= rcu_sync_perf_init,
  	.readlock	= srcu_perf_read_lock,
  	.readunlock	= srcu_perf_read_unlock,
  	.started	= NULL,
  	.completed	= srcu_perf_completed,
  	.exp_completed	= srcu_perf_completed,
881ed593a   Paul E. McKenney   rcuperf: Add abil...
251
252
  	.async		= srcu_call_rcu,
  	.gp_barrier	= srcu_rcu_barrier,
8704baab9   Paul E. McKenney   rcutorture: Add R...
253
254
255
256
  	.sync		= srcu_perf_synchronize,
  	.exp_sync	= srcu_perf_synchronize_expedited,
  	.name		= "srcu"
  };
f60cb4d4c   Paul E. McKenney   rcuperf: Add test...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
  static struct srcu_struct srcud;
  
  static void srcu_sync_perf_init(void)
  {
  	srcu_ctlp = &srcud;
  	init_srcu_struct(srcu_ctlp);
  }
  
  static void srcu_sync_perf_cleanup(void)
  {
  	cleanup_srcu_struct(srcu_ctlp);
  }
  
  static struct rcu_perf_ops srcud_ops = {
  	.ptype		= SRCU_FLAVOR,
  	.init		= srcu_sync_perf_init,
  	.cleanup	= srcu_sync_perf_cleanup,
  	.readlock	= srcu_perf_read_lock,
  	.readunlock	= srcu_perf_read_unlock,
  	.started	= NULL,
  	.completed	= srcu_perf_completed,
  	.exp_completed	= srcu_perf_completed,
  	.async		= srcu_call_rcu,
  	.gp_barrier	= srcu_rcu_barrier,
  	.sync		= srcu_perf_synchronize,
  	.exp_sync	= srcu_perf_synchronize_expedited,
  	.name		= "srcud"
  };
8704baab9   Paul E. McKenney   rcutorture: Add R...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  /*
   * Definitions for sched perf testing.
   */
  
  static int sched_perf_read_lock(void)
  {
  	preempt_disable();
  	return 0;
  }
  
  static void sched_perf_read_unlock(int idx)
  {
  	preempt_enable();
  }
  
  static struct rcu_perf_ops sched_ops = {
  	.ptype		= RCU_SCHED_FLAVOR,
  	.init		= rcu_sync_perf_init,
  	.readlock	= sched_perf_read_lock,
  	.readunlock	= sched_perf_read_unlock,
  	.started	= rcu_batches_started_sched,
  	.completed	= rcu_batches_completed_sched,
  	.exp_completed	= rcu_exp_batches_completed_sched,
881ed593a   Paul E. McKenney   rcuperf: Add abil...
308
309
  	.async		= call_rcu_sched,
  	.gp_barrier	= rcu_barrier_sched,
8704baab9   Paul E. McKenney   rcutorture: Add R...
310
311
312
313
  	.sync		= synchronize_sched,
  	.exp_sync	= synchronize_sched_expedited,
  	.name		= "sched"
  };
8704baab9   Paul E. McKenney   rcutorture: Add R...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  /*
   * Definitions for RCU-tasks perf testing.
   */
  
  static int tasks_perf_read_lock(void)
  {
  	return 0;
  }
  
  static void tasks_perf_read_unlock(int idx)
  {
  }
  
  static struct rcu_perf_ops tasks_ops = {
  	.ptype		= RCU_TASKS_FLAVOR,
  	.init		= rcu_sync_perf_init,
  	.readlock	= tasks_perf_read_lock,
  	.readunlock	= tasks_perf_read_unlock,
  	.started	= rcu_no_completed,
  	.completed	= rcu_no_completed,
881ed593a   Paul E. McKenney   rcuperf: Add abil...
334
335
  	.async		= call_rcu_tasks,
  	.gp_barrier	= rcu_barrier_tasks,
8704baab9   Paul E. McKenney   rcutorture: Add R...
336
337
338
339
  	.sync		= synchronize_rcu_tasks,
  	.exp_sync	= synchronize_rcu_tasks,
  	.name		= "tasks"
  };
8704baab9   Paul E. McKenney   rcutorture: Add R...
340
341
342
343
  static bool __maybe_unused torturing_tasks(void)
  {
  	return cur_ops == &tasks_ops;
  }
8704baab9   Paul E. McKenney   rcutorture: Add R...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  /*
   * If performance tests complete, wait for shutdown to commence.
   */
  static void rcu_perf_wait_shutdown(void)
  {
  	cond_resched_rcu_qs();
  	if (atomic_read(&n_rcu_perf_writer_finished) < nrealwriters)
  		return;
  	while (!torture_must_stop())
  		schedule_timeout_uninterruptible(1);
  }
  
  /*
   * RCU perf reader kthread.  Repeatedly does empty RCU read-side
   * critical section, minimizing update-side interference.
   */
  static int
  rcu_perf_reader(void *arg)
  {
  	unsigned long flags;
  	int idx;
6b558c4c7   Paul E. McKenney   rcutorture: Bind ...
365
  	long me = (long)arg;
8704baab9   Paul E. McKenney   rcutorture: Add R...
366
367
  
  	VERBOSE_PERFOUT_STRING("rcu_perf_reader task started");
6b558c4c7   Paul E. McKenney   rcutorture: Bind ...
368
  	set_cpus_allowed_ptr(current, cpumask_of(me % nr_cpu_ids));
8704baab9   Paul E. McKenney   rcutorture: Add R...
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
  	set_user_nice(current, MAX_NICE);
  	atomic_inc(&n_rcu_perf_reader_started);
  
  	do {
  		local_irq_save(flags);
  		idx = cur_ops->readlock();
  		cur_ops->readunlock(idx);
  		local_irq_restore(flags);
  		rcu_perf_wait_shutdown();
  	} while (!torture_must_stop());
  	torture_kthread_stopping("rcu_perf_reader");
  	return 0;
  }
  
  /*
881ed593a   Paul E. McKenney   rcuperf: Add abil...
384
385
386
387
388
389
390
391
392
   * Callback function for asynchronous grace periods from rcu_perf_writer().
   */
  static void rcu_perf_async_cb(struct rcu_head *rhp)
  {
  	atomic_dec(this_cpu_ptr(&n_async_inflight));
  	kfree(rhp);
  }
  
  /*
8704baab9   Paul E. McKenney   rcutorture: Add R...
393
394
395
396
397
398
399
400
   * RCU perf writer kthread.  Repeatedly does a grace period.
   */
  static int
  rcu_perf_writer(void *arg)
  {
  	int i = 0;
  	int i_max;
  	long me = (long)arg;
881ed593a   Paul E. McKenney   rcuperf: Add abil...
401
  	struct rcu_head *rhp = NULL;
2094c9955   Paul E. McKenney   rcutorture: Set r...
402
  	struct sched_param sp;
8704baab9   Paul E. McKenney   rcutorture: Add R...
403
404
405
406
407
408
  	bool started = false, done = false, alldone = false;
  	u64 t;
  	u64 *wdp;
  	u64 *wdpp = writer_durations[me];
  
  	VERBOSE_PERFOUT_STRING("rcu_perf_writer task started");
8704baab9   Paul E. McKenney   rcutorture: Add R...
409
  	WARN_ON(!wdpp);
6b558c4c7   Paul E. McKenney   rcutorture: Bind ...
410
  	set_cpus_allowed_ptr(current, cpumask_of(me % nr_cpu_ids));
2094c9955   Paul E. McKenney   rcutorture: Set r...
411
412
  	sp.sched_priority = 1;
  	sched_setscheduler_nocheck(current, SCHED_FIFO, &sp);
df37e66bf   Paul E. McKenney   rcutorture: Add r...
413
414
415
  
  	if (holdoff)
  		schedule_timeout_uninterruptible(holdoff * HZ);
8704baab9   Paul E. McKenney   rcutorture: Add R...
416
417
418
419
420
421
422
423
424
425
426
427
428
  	t = ktime_get_mono_fast_ns();
  	if (atomic_inc_return(&n_rcu_perf_writer_started) >= nrealwriters) {
  		t_rcu_perf_writer_started = t;
  		if (gp_exp) {
  			b_rcu_perf_writer_started =
  				cur_ops->exp_completed() / 2;
  		} else {
  			b_rcu_perf_writer_started =
  				cur_ops->completed();
  		}
  	}
  
  	do {
820687a7b   Paul E. McKenney   rcuperf: Add writ...
429
430
  		if (writer_holdoff)
  			udelay(writer_holdoff);
8704baab9   Paul E. McKenney   rcutorture: Add R...
431
432
  		wdp = &wdpp[i];
  		*wdp = ktime_get_mono_fast_ns();
881ed593a   Paul E. McKenney   rcuperf: Add abil...
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
  		if (gp_async) {
  retry:
  			if (!rhp)
  				rhp = kmalloc(sizeof(*rhp), GFP_KERNEL);
  			if (rhp && atomic_read(this_cpu_ptr(&n_async_inflight)) < gp_async_max) {
  				rcu_perf_writer_state = RTWS_ASYNC;
  				atomic_inc(this_cpu_ptr(&n_async_inflight));
  				cur_ops->async(rhp, rcu_perf_async_cb);
  				rhp = NULL;
  			} else if (!kthread_should_stop()) {
  				rcu_perf_writer_state = RTWS_BARRIER;
  				cur_ops->gp_barrier();
  				goto retry;
  			} else {
  				kfree(rhp); /* Because we are stopping. */
  			}
  		} else if (gp_exp) {
8704baab9   Paul E. McKenney   rcutorture: Add R...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
  			rcu_perf_writer_state = RTWS_EXP_SYNC;
  			cur_ops->exp_sync();
  		} else {
  			rcu_perf_writer_state = RTWS_SYNC;
  			cur_ops->sync();
  		}
  		rcu_perf_writer_state = RTWS_IDLE;
  		t = ktime_get_mono_fast_ns();
  		*wdp = t - *wdp;
  		i_max = i;
  		if (!started &&
  		    atomic_read(&n_rcu_perf_writer_started) >= nrealwriters)
  			started = true;
  		if (!done && i >= MIN_MEAS) {
  			done = true;
620316e52   Paul E. McKenney   rcutorture: Avoid...
465
466
467
  			sp.sched_priority = 0;
  			sched_setscheduler_nocheck(current,
  						   SCHED_NORMAL, &sp);
a56fefa26   SeongJae Park   rcuperf: Consiste...
468
469
470
  			pr_alert("%s%s rcu_perf_writer %ld has %d measurements
  ",
  				 perf_type, PERF_FLAG, me, MIN_MEAS);
8704baab9   Paul E. McKenney   rcutorture: Add R...
471
472
  			if (atomic_inc_return(&n_rcu_perf_writer_finished) >=
  			    nrealwriters) {
620316e52   Paul E. McKenney   rcutorture: Avoid...
473
  				schedule_timeout_interruptible(10);
ac2bb275e   Paul E. McKenney   rcutorture: Make ...
474
  				rcu_ftrace_dump(DUMP_ALL);
8704baab9   Paul E. McKenney   rcutorture: Add R...
475
476
477
478
479
480
481
482
483
  				PERFOUT_STRING("Test complete");
  				t_rcu_perf_writer_finished = t;
  				if (gp_exp) {
  					b_rcu_perf_writer_finished =
  						cur_ops->exp_completed() / 2;
  				} else {
  					b_rcu_perf_writer_finished =
  						cur_ops->completed();
  				}
e6fb1fc10   Artem Savkov   rcuperf: Do not w...
484
485
486
487
  				if (shutdown) {
  					smp_mb(); /* Assign before wake. */
  					wake_up(&shutdown_wq);
  				}
8704baab9   Paul E. McKenney   rcutorture: Add R...
488
489
490
491
492
493
494
495
496
  			}
  		}
  		if (done && !alldone &&
  		    atomic_read(&n_rcu_perf_writer_finished) >= nrealwriters)
  			alldone = true;
  		if (started && !alldone && i < MAX_MEAS - 1)
  			i++;
  		rcu_perf_wait_shutdown();
  	} while (!torture_must_stop());
881ed593a   Paul E. McKenney   rcuperf: Add abil...
497
498
499
500
  	if (gp_async) {
  		rcu_perf_writer_state = RTWS_BARRIER;
  		cur_ops->gp_barrier();
  	}
8704baab9   Paul E. McKenney   rcutorture: Add R...
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  	rcu_perf_writer_state = RTWS_STOPPING;
  	writer_n_durations[me] = i_max;
  	torture_kthread_stopping("rcu_perf_writer");
  	return 0;
  }
  
  static inline void
  rcu_perf_print_module_parms(struct rcu_perf_ops *cur_ops, const char *tag)
  {
  	pr_alert("%s" PERF_FLAG
  		 "--- %s: nreaders=%d nwriters=%d verbose=%d shutdown=%d
  ",
  		 perf_type, tag, nrealreaders, nrealwriters, verbose, shutdown);
  }
  
  static void
  rcu_perf_cleanup(void)
  {
  	int i;
  	int j;
  	int ngps = 0;
  	u64 *wdp;
  	u64 *wdpp;
9683937df   Paul E. McKenney   rcuperf: Defer ex...
524
525
526
527
528
529
530
531
  	/*
  	 * Would like warning at start, but everything is expedited
  	 * during the mid-boot phase, so have to wait till the end.
  	 */
  	if (rcu_gp_is_expedited() && !rcu_gp_is_normal() && !gp_exp)
  		VERBOSE_PERFOUT_ERRSTRING("All grace periods expedited, no normal ones to measure!");
  	if (rcu_gp_is_normal() && gp_exp)
  		VERBOSE_PERFOUT_ERRSTRING("All grace periods normal, no expedited ones to measure!");
881ed593a   Paul E. McKenney   rcuperf: Add abil...
532
533
  	if (gp_exp && gp_async)
  		VERBOSE_PERFOUT_ERRSTRING("No expedited async GPs, so went with async!");
9683937df   Paul E. McKenney   rcuperf: Defer ex...
534

8704baab9   Paul E. McKenney   rcutorture: Add R...
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
  	if (torture_cleanup_begin())
  		return;
  
  	if (reader_tasks) {
  		for (i = 0; i < nrealreaders; i++)
  			torture_stop_kthread(rcu_perf_reader,
  					     reader_tasks[i]);
  		kfree(reader_tasks);
  	}
  
  	if (writer_tasks) {
  		for (i = 0; i < nrealwriters; i++) {
  			torture_stop_kthread(rcu_perf_writer,
  					     writer_tasks[i]);
  			if (!writer_n_durations)
  				continue;
  			j = writer_n_durations[i];
  			pr_alert("%s%s writer %d gps: %d
  ",
  				 perf_type, PERF_FLAG, i, j);
  			ngps += j;
  		}
  		pr_alert("%s%s start: %llu end: %llu duration: %llu gps: %d batches: %ld
  ",
  			 perf_type, PERF_FLAG,
  			 t_rcu_perf_writer_started, t_rcu_perf_writer_finished,
  			 t_rcu_perf_writer_finished -
  			 t_rcu_perf_writer_started,
  			 ngps,
  			 b_rcu_perf_writer_finished -
  			 b_rcu_perf_writer_started);
  		for (i = 0; i < nrealwriters; i++) {
  			if (!writer_durations)
  				break;
  			if (!writer_n_durations)
  				continue;
  			wdpp = writer_durations[i];
  			if (!wdpp)
  				continue;
  			for (j = 0; j <= writer_n_durations[i]; j++) {
  				wdp = &wdpp[j];
  				pr_alert("%s%s %4d writer-duration: %5d %llu
  ",
  					perf_type, PERF_FLAG,
  					i, j, *wdp);
  				if (j % 100 == 0)
  					schedule_timeout_uninterruptible(1);
  			}
  			kfree(writer_durations[i]);
  		}
  		kfree(writer_tasks);
  		kfree(writer_durations);
  		kfree(writer_n_durations);
  	}
  
  	/* Do flavor-specific cleanup operations.  */
  	if (cur_ops->cleanup != NULL)
  		cur_ops->cleanup();
  
  	torture_cleanup_end();
  }
  
  /*
   * Return the number if non-negative.  If -1, the number of CPUs.
   * If less than -1, that much less than the number of CPUs, but
   * at least one.
   */
  static int compute_real(int n)
  {
  	int nr;
  
  	if (n >= 0) {
  		nr = n;
  	} else {
  		nr = num_online_cpus() + 1 + n;
  		if (nr <= 0)
  			nr = 1;
  	}
  	return nr;
  }
  
  /*
   * RCU perf shutdown kthread.  Just waits to be awakened, then shuts
   * down system.
   */
  static int
  rcu_perf_shutdown(void *arg)
  {
  	do {
  		wait_event(shutdown_wq,
  			   atomic_read(&n_rcu_perf_writer_finished) >=
  			   nrealwriters);
  	} while (atomic_read(&n_rcu_perf_writer_finished) < nrealwriters);
  	smp_mb(); /* Wake before output. */
  	rcu_perf_cleanup();
  	kernel_power_off();
  	return -EINVAL;
  }
  
  static int __init
  rcu_perf_init(void)
  {
  	long i;
  	int firsterr = 0;
  	static struct rcu_perf_ops *perf_ops[] = {
f60cb4d4c   Paul E. McKenney   rcuperf: Add test...
640
  		&rcu_ops, &rcu_bh_ops, &srcu_ops, &srcud_ops, &sched_ops,
f1dbc54b9   Paul E. McKenney   rcu: Remove CONFI...
641
  		&tasks_ops,
8704baab9   Paul E. McKenney   rcutorture: Add R...
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
  	};
  
  	if (!torture_init_begin(perf_type, verbose, &perf_runnable))
  		return -EBUSY;
  
  	/* Process args and tell the world that the perf'er is on the job. */
  	for (i = 0; i < ARRAY_SIZE(perf_ops); i++) {
  		cur_ops = perf_ops[i];
  		if (strcmp(perf_type, cur_ops->name) == 0)
  			break;
  	}
  	if (i == ARRAY_SIZE(perf_ops)) {
  		pr_alert("rcu-perf: invalid perf type: \"%s\"
  ",
  			 perf_type);
  		pr_alert("rcu-perf types:");
  		for (i = 0; i < ARRAY_SIZE(perf_ops); i++)
  			pr_alert(" %s", perf_ops[i]->name);
  		pr_alert("
  ");
  		firsterr = -EINVAL;
  		goto unwind;
  	}
  	if (cur_ops->init)
  		cur_ops->init();
  
  	nrealwriters = compute_real(nwriters);
  	nrealreaders = compute_real(nreaders);
  	atomic_set(&n_rcu_perf_reader_started, 0);
  	atomic_set(&n_rcu_perf_writer_started, 0);
  	atomic_set(&n_rcu_perf_writer_finished, 0);
  	rcu_perf_print_module_parms(cur_ops, "Start of test");
  
  	/* Start up the kthreads. */
  
  	if (shutdown) {
  		init_waitqueue_head(&shutdown_wq);
  		firsterr = torture_create_kthread(rcu_perf_shutdown, NULL,
  						  shutdown_task);
  		if (firsterr)
  			goto unwind;
  		schedule_timeout_uninterruptible(1);
  	}
  	reader_tasks = kcalloc(nrealreaders, sizeof(reader_tasks[0]),
  			       GFP_KERNEL);
  	if (reader_tasks == NULL) {
  		VERBOSE_PERFOUT_ERRSTRING("out of memory");
  		firsterr = -ENOMEM;
  		goto unwind;
  	}
  	for (i = 0; i < nrealreaders; i++) {
6b558c4c7   Paul E. McKenney   rcutorture: Bind ...
693
  		firsterr = torture_create_kthread(rcu_perf_reader, (void *)i,
8704baab9   Paul E. McKenney   rcutorture: Add R...
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
  						  reader_tasks[i]);
  		if (firsterr)
  			goto unwind;
  	}
  	while (atomic_read(&n_rcu_perf_reader_started) < nrealreaders)
  		schedule_timeout_uninterruptible(1);
  	writer_tasks = kcalloc(nrealwriters, sizeof(reader_tasks[0]),
  			       GFP_KERNEL);
  	writer_durations = kcalloc(nrealwriters, sizeof(*writer_durations),
  				   GFP_KERNEL);
  	writer_n_durations =
  		kcalloc(nrealwriters, sizeof(*writer_n_durations),
  			GFP_KERNEL);
  	if (!writer_tasks || !writer_durations || !writer_n_durations) {
  		VERBOSE_PERFOUT_ERRSTRING("out of memory");
  		firsterr = -ENOMEM;
  		goto unwind;
  	}
  	for (i = 0; i < nrealwriters; i++) {
  		writer_durations[i] =
  			kcalloc(MAX_MEAS, sizeof(*writer_durations[i]),
  				GFP_KERNEL);
05dbbfe75   Wei Yongjun   rcutorture: Fix e...
716
717
  		if (!writer_durations[i]) {
  			firsterr = -ENOMEM;
8704baab9   Paul E. McKenney   rcutorture: Add R...
718
  			goto unwind;
05dbbfe75   Wei Yongjun   rcutorture: Fix e...
719
  		}
8704baab9   Paul E. McKenney   rcutorture: Add R...
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
  		firsterr = torture_create_kthread(rcu_perf_writer, (void *)i,
  						  writer_tasks[i]);
  		if (firsterr)
  			goto unwind;
  	}
  	torture_init_end();
  	return 0;
  
  unwind:
  	torture_init_end();
  	rcu_perf_cleanup();
  	return firsterr;
  }
  
  module_init(rcu_perf_init);
  module_exit(rcu_perf_cleanup);