xref: /linux-6.15/kernel/rcu/refscale.c (revision 1fbeb3a8)
1*1fbeb3a8SPaul E. McKenney // SPDX-License-Identifier: GPL-2.0+
2*1fbeb3a8SPaul E. McKenney //
3*1fbeb3a8SPaul E. McKenney // Scalability test comparing RCU vs other mechanisms
4*1fbeb3a8SPaul E. McKenney // for acquiring references on objects.
5*1fbeb3a8SPaul E. McKenney //
6*1fbeb3a8SPaul E. McKenney // Copyright (C) Google, 2020.
7*1fbeb3a8SPaul E. McKenney //
8*1fbeb3a8SPaul E. McKenney // Author: Joel Fernandes <[email protected]>
9*1fbeb3a8SPaul E. McKenney 
10*1fbeb3a8SPaul E. McKenney #define pr_fmt(fmt) fmt
11*1fbeb3a8SPaul E. McKenney 
12*1fbeb3a8SPaul E. McKenney #include <linux/atomic.h>
13*1fbeb3a8SPaul E. McKenney #include <linux/bitops.h>
14*1fbeb3a8SPaul E. McKenney #include <linux/completion.h>
15*1fbeb3a8SPaul E. McKenney #include <linux/cpu.h>
16*1fbeb3a8SPaul E. McKenney #include <linux/delay.h>
17*1fbeb3a8SPaul E. McKenney #include <linux/err.h>
18*1fbeb3a8SPaul E. McKenney #include <linux/init.h>
19*1fbeb3a8SPaul E. McKenney #include <linux/interrupt.h>
20*1fbeb3a8SPaul E. McKenney #include <linux/kthread.h>
21*1fbeb3a8SPaul E. McKenney #include <linux/kernel.h>
22*1fbeb3a8SPaul E. McKenney #include <linux/mm.h>
23*1fbeb3a8SPaul E. McKenney #include <linux/module.h>
24*1fbeb3a8SPaul E. McKenney #include <linux/moduleparam.h>
25*1fbeb3a8SPaul E. McKenney #include <linux/notifier.h>
26*1fbeb3a8SPaul E. McKenney #include <linux/percpu.h>
27*1fbeb3a8SPaul E. McKenney #include <linux/rcupdate.h>
28*1fbeb3a8SPaul E. McKenney #include <linux/rcupdate_trace.h>
29*1fbeb3a8SPaul E. McKenney #include <linux/reboot.h>
30*1fbeb3a8SPaul E. McKenney #include <linux/sched.h>
31*1fbeb3a8SPaul E. McKenney #include <linux/spinlock.h>
32*1fbeb3a8SPaul E. McKenney #include <linux/smp.h>
33*1fbeb3a8SPaul E. McKenney #include <linux/stat.h>
34*1fbeb3a8SPaul E. McKenney #include <linux/srcu.h>
35*1fbeb3a8SPaul E. McKenney #include <linux/slab.h>
36*1fbeb3a8SPaul E. McKenney #include <linux/torture.h>
37*1fbeb3a8SPaul E. McKenney #include <linux/types.h>
38*1fbeb3a8SPaul E. McKenney 
39*1fbeb3a8SPaul E. McKenney #include "rcu.h"
40*1fbeb3a8SPaul E. McKenney 
41*1fbeb3a8SPaul E. McKenney #define SCALE_FLAG "-ref-scale: "
42*1fbeb3a8SPaul E. McKenney 
43*1fbeb3a8SPaul E. McKenney #define SCALEOUT(s, x...) \
44*1fbeb3a8SPaul E. McKenney 	pr_alert("%s" SCALE_FLAG s, scale_type, ## x)
45*1fbeb3a8SPaul E. McKenney 
46*1fbeb3a8SPaul E. McKenney #define VERBOSE_SCALEOUT(s, x...) \
47*1fbeb3a8SPaul E. McKenney 	do { if (verbose) pr_alert("%s" SCALE_FLAG s, scale_type, ## x); } while (0)
48*1fbeb3a8SPaul E. McKenney 
49*1fbeb3a8SPaul E. McKenney #define VERBOSE_SCALEOUT_ERRSTRING(s, x...) \
50*1fbeb3a8SPaul E. McKenney 	do { if (verbose) pr_alert("%s" SCALE_FLAG "!!! " s, scale_type, ## x); } while (0)
51*1fbeb3a8SPaul E. McKenney 
52*1fbeb3a8SPaul E. McKenney MODULE_LICENSE("GPL");
53*1fbeb3a8SPaul E. McKenney MODULE_AUTHOR("Joel Fernandes (Google) <[email protected]>");
54*1fbeb3a8SPaul E. McKenney 
55*1fbeb3a8SPaul E. McKenney static char *scale_type = "rcu";
56*1fbeb3a8SPaul E. McKenney module_param(scale_type, charp, 0444);
57*1fbeb3a8SPaul E. McKenney MODULE_PARM_DESC(scale_type, "Type of test (rcu, srcu, refcnt, rwsem, rwlock.");
58*1fbeb3a8SPaul E. McKenney 
59*1fbeb3a8SPaul E. McKenney torture_param(int, verbose, 0, "Enable verbose debugging printk()s");
60*1fbeb3a8SPaul E. McKenney 
61*1fbeb3a8SPaul E. McKenney // Wait until there are multiple CPUs before starting test.
62*1fbeb3a8SPaul E. McKenney torture_param(int, holdoff, IS_BUILTIN(CONFIG_RCU_REF_SCALE_TEST) ? 10 : 0,
63*1fbeb3a8SPaul E. McKenney 	      "Holdoff time before test start (s)");
64*1fbeb3a8SPaul E. McKenney // Number of loops per experiment, all readers execute operations concurrently.
65*1fbeb3a8SPaul E. McKenney torture_param(long, loops, 10000, "Number of loops per experiment.");
66*1fbeb3a8SPaul E. McKenney // Number of readers, with -1 defaulting to about 75% of the CPUs.
67*1fbeb3a8SPaul E. McKenney torture_param(int, nreaders, -1, "Number of readers, -1 for 75% of CPUs.");
68*1fbeb3a8SPaul E. McKenney // Number of runs.
69*1fbeb3a8SPaul E. McKenney torture_param(int, nruns, 30, "Number of experiments to run.");
70*1fbeb3a8SPaul E. McKenney // Reader delay in nanoseconds, 0 for no delay.
71*1fbeb3a8SPaul E. McKenney torture_param(int, readdelay, 0, "Read-side delay in nanoseconds.");
72*1fbeb3a8SPaul E. McKenney 
73*1fbeb3a8SPaul E. McKenney #ifdef MODULE
74*1fbeb3a8SPaul E. McKenney # define REFSCALE_SHUTDOWN 0
75*1fbeb3a8SPaul E. McKenney #else
76*1fbeb3a8SPaul E. McKenney # define REFSCALE_SHUTDOWN 1
77*1fbeb3a8SPaul E. McKenney #endif
78*1fbeb3a8SPaul E. McKenney 
79*1fbeb3a8SPaul E. McKenney torture_param(bool, shutdown, REFSCALE_SHUTDOWN,
80*1fbeb3a8SPaul E. McKenney 	      "Shutdown at end of scalability tests.");
81*1fbeb3a8SPaul E. McKenney 
82*1fbeb3a8SPaul E. McKenney struct reader_task {
83*1fbeb3a8SPaul E. McKenney 	struct task_struct *task;
84*1fbeb3a8SPaul E. McKenney 	int start_reader;
85*1fbeb3a8SPaul E. McKenney 	wait_queue_head_t wq;
86*1fbeb3a8SPaul E. McKenney 	u64 last_duration_ns;
87*1fbeb3a8SPaul E. McKenney };
88*1fbeb3a8SPaul E. McKenney 
89*1fbeb3a8SPaul E. McKenney static struct task_struct *shutdown_task;
90*1fbeb3a8SPaul E. McKenney static wait_queue_head_t shutdown_wq;
91*1fbeb3a8SPaul E. McKenney 
92*1fbeb3a8SPaul E. McKenney static struct task_struct *main_task;
93*1fbeb3a8SPaul E. McKenney static wait_queue_head_t main_wq;
94*1fbeb3a8SPaul E. McKenney static int shutdown_start;
95*1fbeb3a8SPaul E. McKenney 
96*1fbeb3a8SPaul E. McKenney static struct reader_task *reader_tasks;
97*1fbeb3a8SPaul E. McKenney 
98*1fbeb3a8SPaul E. McKenney // Number of readers that are part of the current experiment.
99*1fbeb3a8SPaul E. McKenney static atomic_t nreaders_exp;
100*1fbeb3a8SPaul E. McKenney 
101*1fbeb3a8SPaul E. McKenney // Use to wait for all threads to start.
102*1fbeb3a8SPaul E. McKenney static atomic_t n_init;
103*1fbeb3a8SPaul E. McKenney static atomic_t n_started;
104*1fbeb3a8SPaul E. McKenney static atomic_t n_warmedup;
105*1fbeb3a8SPaul E. McKenney static atomic_t n_cooleddown;
106*1fbeb3a8SPaul E. McKenney 
107*1fbeb3a8SPaul E. McKenney // Track which experiment is currently running.
108*1fbeb3a8SPaul E. McKenney static int exp_idx;
109*1fbeb3a8SPaul E. McKenney 
110*1fbeb3a8SPaul E. McKenney // Operations vector for selecting different types of tests.
111*1fbeb3a8SPaul E. McKenney struct ref_scale_ops {
112*1fbeb3a8SPaul E. McKenney 	void (*init)(void);
113*1fbeb3a8SPaul E. McKenney 	void (*cleanup)(void);
114*1fbeb3a8SPaul E. McKenney 	void (*readsection)(const int nloops);
115*1fbeb3a8SPaul E. McKenney 	void (*delaysection)(const int nloops, const int udl, const int ndl);
116*1fbeb3a8SPaul E. McKenney 	const char *name;
117*1fbeb3a8SPaul E. McKenney };
118*1fbeb3a8SPaul E. McKenney 
119*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops *cur_ops;
120*1fbeb3a8SPaul E. McKenney 
121*1fbeb3a8SPaul E. McKenney static void un_delay(const int udl, const int ndl)
122*1fbeb3a8SPaul E. McKenney {
123*1fbeb3a8SPaul E. McKenney 	if (udl)
124*1fbeb3a8SPaul E. McKenney 		udelay(udl);
125*1fbeb3a8SPaul E. McKenney 	if (ndl)
126*1fbeb3a8SPaul E. McKenney 		ndelay(ndl);
127*1fbeb3a8SPaul E. McKenney }
128*1fbeb3a8SPaul E. McKenney 
129*1fbeb3a8SPaul E. McKenney static void ref_rcu_read_section(const int nloops)
130*1fbeb3a8SPaul E. McKenney {
131*1fbeb3a8SPaul E. McKenney 	int i;
132*1fbeb3a8SPaul E. McKenney 
133*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
134*1fbeb3a8SPaul E. McKenney 		rcu_read_lock();
135*1fbeb3a8SPaul E. McKenney 		rcu_read_unlock();
136*1fbeb3a8SPaul E. McKenney 	}
137*1fbeb3a8SPaul E. McKenney }
138*1fbeb3a8SPaul E. McKenney 
139*1fbeb3a8SPaul E. McKenney static void ref_rcu_delay_section(const int nloops, const int udl, const int ndl)
140*1fbeb3a8SPaul E. McKenney {
141*1fbeb3a8SPaul E. McKenney 	int i;
142*1fbeb3a8SPaul E. McKenney 
143*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
144*1fbeb3a8SPaul E. McKenney 		rcu_read_lock();
145*1fbeb3a8SPaul E. McKenney 		un_delay(udl, ndl);
146*1fbeb3a8SPaul E. McKenney 		rcu_read_unlock();
147*1fbeb3a8SPaul E. McKenney 	}
148*1fbeb3a8SPaul E. McKenney }
149*1fbeb3a8SPaul E. McKenney 
150*1fbeb3a8SPaul E. McKenney static void rcu_sync_scale_init(void)
151*1fbeb3a8SPaul E. McKenney {
152*1fbeb3a8SPaul E. McKenney }
153*1fbeb3a8SPaul E. McKenney 
154*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rcu_ops = {
155*1fbeb3a8SPaul E. McKenney 	.init		= rcu_sync_scale_init,
156*1fbeb3a8SPaul E. McKenney 	.readsection	= ref_rcu_read_section,
157*1fbeb3a8SPaul E. McKenney 	.delaysection	= ref_rcu_delay_section,
158*1fbeb3a8SPaul E. McKenney 	.name		= "rcu"
159*1fbeb3a8SPaul E. McKenney };
160*1fbeb3a8SPaul E. McKenney 
161*1fbeb3a8SPaul E. McKenney // Definitions for SRCU ref scale testing.
162*1fbeb3a8SPaul E. McKenney DEFINE_STATIC_SRCU(srcu_refctl_scale);
163*1fbeb3a8SPaul E. McKenney static struct srcu_struct *srcu_ctlp = &srcu_refctl_scale;
164*1fbeb3a8SPaul E. McKenney 
165*1fbeb3a8SPaul E. McKenney static void srcu_ref_scale_read_section(const int nloops)
166*1fbeb3a8SPaul E. McKenney {
167*1fbeb3a8SPaul E. McKenney 	int i;
168*1fbeb3a8SPaul E. McKenney 	int idx;
169*1fbeb3a8SPaul E. McKenney 
170*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
171*1fbeb3a8SPaul E. McKenney 		idx = srcu_read_lock(srcu_ctlp);
172*1fbeb3a8SPaul E. McKenney 		srcu_read_unlock(srcu_ctlp, idx);
173*1fbeb3a8SPaul E. McKenney 	}
174*1fbeb3a8SPaul E. McKenney }
175*1fbeb3a8SPaul E. McKenney 
176*1fbeb3a8SPaul E. McKenney static void srcu_ref_scale_delay_section(const int nloops, const int udl, const int ndl)
177*1fbeb3a8SPaul E. McKenney {
178*1fbeb3a8SPaul E. McKenney 	int i;
179*1fbeb3a8SPaul E. McKenney 	int idx;
180*1fbeb3a8SPaul E. McKenney 
181*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
182*1fbeb3a8SPaul E. McKenney 		idx = srcu_read_lock(srcu_ctlp);
183*1fbeb3a8SPaul E. McKenney 		un_delay(udl, ndl);
184*1fbeb3a8SPaul E. McKenney 		srcu_read_unlock(srcu_ctlp, idx);
185*1fbeb3a8SPaul E. McKenney 	}
186*1fbeb3a8SPaul E. McKenney }
187*1fbeb3a8SPaul E. McKenney 
188*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops srcu_ops = {
189*1fbeb3a8SPaul E. McKenney 	.init		= rcu_sync_scale_init,
190*1fbeb3a8SPaul E. McKenney 	.readsection	= srcu_ref_scale_read_section,
191*1fbeb3a8SPaul E. McKenney 	.delaysection	= srcu_ref_scale_delay_section,
192*1fbeb3a8SPaul E. McKenney 	.name		= "srcu"
193*1fbeb3a8SPaul E. McKenney };
194*1fbeb3a8SPaul E. McKenney 
195*1fbeb3a8SPaul E. McKenney // Definitions for RCU Tasks ref scale testing: Empty read markers.
196*1fbeb3a8SPaul E. McKenney // These definitions also work for RCU Rude readers.
197*1fbeb3a8SPaul E. McKenney static void rcu_tasks_ref_scale_read_section(const int nloops)
198*1fbeb3a8SPaul E. McKenney {
199*1fbeb3a8SPaul E. McKenney 	int i;
200*1fbeb3a8SPaul E. McKenney 
201*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--)
202*1fbeb3a8SPaul E. McKenney 		continue;
203*1fbeb3a8SPaul E. McKenney }
204*1fbeb3a8SPaul E. McKenney 
205*1fbeb3a8SPaul E. McKenney static void rcu_tasks_ref_scale_delay_section(const int nloops, const int udl, const int ndl)
206*1fbeb3a8SPaul E. McKenney {
207*1fbeb3a8SPaul E. McKenney 	int i;
208*1fbeb3a8SPaul E. McKenney 
209*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--)
210*1fbeb3a8SPaul E. McKenney 		un_delay(udl, ndl);
211*1fbeb3a8SPaul E. McKenney }
212*1fbeb3a8SPaul E. McKenney 
213*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rcu_tasks_ops = {
214*1fbeb3a8SPaul E. McKenney 	.init		= rcu_sync_scale_init,
215*1fbeb3a8SPaul E. McKenney 	.readsection	= rcu_tasks_ref_scale_read_section,
216*1fbeb3a8SPaul E. McKenney 	.delaysection	= rcu_tasks_ref_scale_delay_section,
217*1fbeb3a8SPaul E. McKenney 	.name		= "rcu-tasks"
218*1fbeb3a8SPaul E. McKenney };
219*1fbeb3a8SPaul E. McKenney 
220*1fbeb3a8SPaul E. McKenney // Definitions for RCU Tasks Trace ref scale testing.
221*1fbeb3a8SPaul E. McKenney static void rcu_trace_ref_scale_read_section(const int nloops)
222*1fbeb3a8SPaul E. McKenney {
223*1fbeb3a8SPaul E. McKenney 	int i;
224*1fbeb3a8SPaul E. McKenney 
225*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
226*1fbeb3a8SPaul E. McKenney 		rcu_read_lock_trace();
227*1fbeb3a8SPaul E. McKenney 		rcu_read_unlock_trace();
228*1fbeb3a8SPaul E. McKenney 	}
229*1fbeb3a8SPaul E. McKenney }
230*1fbeb3a8SPaul E. McKenney 
231*1fbeb3a8SPaul E. McKenney static void rcu_trace_ref_scale_delay_section(const int nloops, const int udl, const int ndl)
232*1fbeb3a8SPaul E. McKenney {
233*1fbeb3a8SPaul E. McKenney 	int i;
234*1fbeb3a8SPaul E. McKenney 
235*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
236*1fbeb3a8SPaul E. McKenney 		rcu_read_lock_trace();
237*1fbeb3a8SPaul E. McKenney 		un_delay(udl, ndl);
238*1fbeb3a8SPaul E. McKenney 		rcu_read_unlock_trace();
239*1fbeb3a8SPaul E. McKenney 	}
240*1fbeb3a8SPaul E. McKenney }
241*1fbeb3a8SPaul E. McKenney 
242*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rcu_trace_ops = {
243*1fbeb3a8SPaul E. McKenney 	.init		= rcu_sync_scale_init,
244*1fbeb3a8SPaul E. McKenney 	.readsection	= rcu_trace_ref_scale_read_section,
245*1fbeb3a8SPaul E. McKenney 	.delaysection	= rcu_trace_ref_scale_delay_section,
246*1fbeb3a8SPaul E. McKenney 	.name		= "rcu-trace"
247*1fbeb3a8SPaul E. McKenney };
248*1fbeb3a8SPaul E. McKenney 
249*1fbeb3a8SPaul E. McKenney // Definitions for reference count
250*1fbeb3a8SPaul E. McKenney static atomic_t refcnt;
251*1fbeb3a8SPaul E. McKenney 
252*1fbeb3a8SPaul E. McKenney static void ref_refcnt_section(const int nloops)
253*1fbeb3a8SPaul E. McKenney {
254*1fbeb3a8SPaul E. McKenney 	int i;
255*1fbeb3a8SPaul E. McKenney 
256*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
257*1fbeb3a8SPaul E. McKenney 		atomic_inc(&refcnt);
258*1fbeb3a8SPaul E. McKenney 		atomic_dec(&refcnt);
259*1fbeb3a8SPaul E. McKenney 	}
260*1fbeb3a8SPaul E. McKenney }
261*1fbeb3a8SPaul E. McKenney 
262*1fbeb3a8SPaul E. McKenney static void ref_refcnt_delay_section(const int nloops, const int udl, const int ndl)
263*1fbeb3a8SPaul E. McKenney {
264*1fbeb3a8SPaul E. McKenney 	int i;
265*1fbeb3a8SPaul E. McKenney 
266*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
267*1fbeb3a8SPaul E. McKenney 		atomic_inc(&refcnt);
268*1fbeb3a8SPaul E. McKenney 		un_delay(udl, ndl);
269*1fbeb3a8SPaul E. McKenney 		atomic_dec(&refcnt);
270*1fbeb3a8SPaul E. McKenney 	}
271*1fbeb3a8SPaul E. McKenney }
272*1fbeb3a8SPaul E. McKenney 
273*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops refcnt_ops = {
274*1fbeb3a8SPaul E. McKenney 	.init		= rcu_sync_scale_init,
275*1fbeb3a8SPaul E. McKenney 	.readsection	= ref_refcnt_section,
276*1fbeb3a8SPaul E. McKenney 	.delaysection	= ref_refcnt_delay_section,
277*1fbeb3a8SPaul E. McKenney 	.name		= "refcnt"
278*1fbeb3a8SPaul E. McKenney };
279*1fbeb3a8SPaul E. McKenney 
280*1fbeb3a8SPaul E. McKenney // Definitions for rwlock
281*1fbeb3a8SPaul E. McKenney static rwlock_t test_rwlock;
282*1fbeb3a8SPaul E. McKenney 
283*1fbeb3a8SPaul E. McKenney static void ref_rwlock_init(void)
284*1fbeb3a8SPaul E. McKenney {
285*1fbeb3a8SPaul E. McKenney 	rwlock_init(&test_rwlock);
286*1fbeb3a8SPaul E. McKenney }
287*1fbeb3a8SPaul E. McKenney 
288*1fbeb3a8SPaul E. McKenney static void ref_rwlock_section(const int nloops)
289*1fbeb3a8SPaul E. McKenney {
290*1fbeb3a8SPaul E. McKenney 	int i;
291*1fbeb3a8SPaul E. McKenney 
292*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
293*1fbeb3a8SPaul E. McKenney 		read_lock(&test_rwlock);
294*1fbeb3a8SPaul E. McKenney 		read_unlock(&test_rwlock);
295*1fbeb3a8SPaul E. McKenney 	}
296*1fbeb3a8SPaul E. McKenney }
297*1fbeb3a8SPaul E. McKenney 
298*1fbeb3a8SPaul E. McKenney static void ref_rwlock_delay_section(const int nloops, const int udl, const int ndl)
299*1fbeb3a8SPaul E. McKenney {
300*1fbeb3a8SPaul E. McKenney 	int i;
301*1fbeb3a8SPaul E. McKenney 
302*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
303*1fbeb3a8SPaul E. McKenney 		read_lock(&test_rwlock);
304*1fbeb3a8SPaul E. McKenney 		un_delay(udl, ndl);
305*1fbeb3a8SPaul E. McKenney 		read_unlock(&test_rwlock);
306*1fbeb3a8SPaul E. McKenney 	}
307*1fbeb3a8SPaul E. McKenney }
308*1fbeb3a8SPaul E. McKenney 
309*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rwlock_ops = {
310*1fbeb3a8SPaul E. McKenney 	.init		= ref_rwlock_init,
311*1fbeb3a8SPaul E. McKenney 	.readsection	= ref_rwlock_section,
312*1fbeb3a8SPaul E. McKenney 	.delaysection	= ref_rwlock_delay_section,
313*1fbeb3a8SPaul E. McKenney 	.name		= "rwlock"
314*1fbeb3a8SPaul E. McKenney };
315*1fbeb3a8SPaul E. McKenney 
316*1fbeb3a8SPaul E. McKenney // Definitions for rwsem
317*1fbeb3a8SPaul E. McKenney static struct rw_semaphore test_rwsem;
318*1fbeb3a8SPaul E. McKenney 
319*1fbeb3a8SPaul E. McKenney static void ref_rwsem_init(void)
320*1fbeb3a8SPaul E. McKenney {
321*1fbeb3a8SPaul E. McKenney 	init_rwsem(&test_rwsem);
322*1fbeb3a8SPaul E. McKenney }
323*1fbeb3a8SPaul E. McKenney 
324*1fbeb3a8SPaul E. McKenney static void ref_rwsem_section(const int nloops)
325*1fbeb3a8SPaul E. McKenney {
326*1fbeb3a8SPaul E. McKenney 	int i;
327*1fbeb3a8SPaul E. McKenney 
328*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
329*1fbeb3a8SPaul E. McKenney 		down_read(&test_rwsem);
330*1fbeb3a8SPaul E. McKenney 		up_read(&test_rwsem);
331*1fbeb3a8SPaul E. McKenney 	}
332*1fbeb3a8SPaul E. McKenney }
333*1fbeb3a8SPaul E. McKenney 
334*1fbeb3a8SPaul E. McKenney static void ref_rwsem_delay_section(const int nloops, const int udl, const int ndl)
335*1fbeb3a8SPaul E. McKenney {
336*1fbeb3a8SPaul E. McKenney 	int i;
337*1fbeb3a8SPaul E. McKenney 
338*1fbeb3a8SPaul E. McKenney 	for (i = nloops; i >= 0; i--) {
339*1fbeb3a8SPaul E. McKenney 		down_read(&test_rwsem);
340*1fbeb3a8SPaul E. McKenney 		un_delay(udl, ndl);
341*1fbeb3a8SPaul E. McKenney 		up_read(&test_rwsem);
342*1fbeb3a8SPaul E. McKenney 	}
343*1fbeb3a8SPaul E. McKenney }
344*1fbeb3a8SPaul E. McKenney 
345*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rwsem_ops = {
346*1fbeb3a8SPaul E. McKenney 	.init		= ref_rwsem_init,
347*1fbeb3a8SPaul E. McKenney 	.readsection	= ref_rwsem_section,
348*1fbeb3a8SPaul E. McKenney 	.delaysection	= ref_rwsem_delay_section,
349*1fbeb3a8SPaul E. McKenney 	.name		= "rwsem"
350*1fbeb3a8SPaul E. McKenney };
351*1fbeb3a8SPaul E. McKenney 
352*1fbeb3a8SPaul E. McKenney static void rcu_scale_one_reader(void)
353*1fbeb3a8SPaul E. McKenney {
354*1fbeb3a8SPaul E. McKenney 	if (readdelay <= 0)
355*1fbeb3a8SPaul E. McKenney 		cur_ops->readsection(loops);
356*1fbeb3a8SPaul E. McKenney 	else
357*1fbeb3a8SPaul E. McKenney 		cur_ops->delaysection(loops, readdelay / 1000, readdelay % 1000);
358*1fbeb3a8SPaul E. McKenney }
359*1fbeb3a8SPaul E. McKenney 
360*1fbeb3a8SPaul E. McKenney // Reader kthread.  Repeatedly does empty RCU read-side
361*1fbeb3a8SPaul E. McKenney // critical section, minimizing update-side interference.
362*1fbeb3a8SPaul E. McKenney static int
363*1fbeb3a8SPaul E. McKenney ref_scale_reader(void *arg)
364*1fbeb3a8SPaul E. McKenney {
365*1fbeb3a8SPaul E. McKenney 	unsigned long flags;
366*1fbeb3a8SPaul E. McKenney 	long me = (long)arg;
367*1fbeb3a8SPaul E. McKenney 	struct reader_task *rt = &(reader_tasks[me]);
368*1fbeb3a8SPaul E. McKenney 	u64 start;
369*1fbeb3a8SPaul E. McKenney 	s64 duration;
370*1fbeb3a8SPaul E. McKenney 
371*1fbeb3a8SPaul E. McKenney 	VERBOSE_SCALEOUT("ref_scale_reader %ld: task started", me);
372*1fbeb3a8SPaul E. McKenney 	set_cpus_allowed_ptr(current, cpumask_of(me % nr_cpu_ids));
373*1fbeb3a8SPaul E. McKenney 	set_user_nice(current, MAX_NICE);
374*1fbeb3a8SPaul E. McKenney 	atomic_inc(&n_init);
375*1fbeb3a8SPaul E. McKenney 	if (holdoff)
376*1fbeb3a8SPaul E. McKenney 		schedule_timeout_interruptible(holdoff * HZ);
377*1fbeb3a8SPaul E. McKenney repeat:
378*1fbeb3a8SPaul E. McKenney 	VERBOSE_SCALEOUT("ref_scale_reader %ld: waiting to start next experiment on cpu %d", me, smp_processor_id());
379*1fbeb3a8SPaul E. McKenney 
380*1fbeb3a8SPaul E. McKenney 	// Wait for signal that this reader can start.
381*1fbeb3a8SPaul E. McKenney 	wait_event(rt->wq, (atomic_read(&nreaders_exp) && smp_load_acquire(&rt->start_reader)) ||
382*1fbeb3a8SPaul E. McKenney 			   torture_must_stop());
383*1fbeb3a8SPaul E. McKenney 
384*1fbeb3a8SPaul E. McKenney 	if (torture_must_stop())
385*1fbeb3a8SPaul E. McKenney 		goto end;
386*1fbeb3a8SPaul E. McKenney 
387*1fbeb3a8SPaul E. McKenney 	// Make sure that the CPU is affinitized appropriately during testing.
388*1fbeb3a8SPaul E. McKenney 	WARN_ON_ONCE(smp_processor_id() != me);
389*1fbeb3a8SPaul E. McKenney 
390*1fbeb3a8SPaul E. McKenney 	WRITE_ONCE(rt->start_reader, 0);
391*1fbeb3a8SPaul E. McKenney 	if (!atomic_dec_return(&n_started))
392*1fbeb3a8SPaul E. McKenney 		while (atomic_read_acquire(&n_started))
393*1fbeb3a8SPaul E. McKenney 			cpu_relax();
394*1fbeb3a8SPaul E. McKenney 
395*1fbeb3a8SPaul E. McKenney 	VERBOSE_SCALEOUT("ref_scale_reader %ld: experiment %d started", me, exp_idx);
396*1fbeb3a8SPaul E. McKenney 
397*1fbeb3a8SPaul E. McKenney 
398*1fbeb3a8SPaul E. McKenney 	// To reduce noise, do an initial cache-warming invocation, check
399*1fbeb3a8SPaul E. McKenney 	// in, and then keep warming until everyone has checked in.
400*1fbeb3a8SPaul E. McKenney 	rcu_scale_one_reader();
401*1fbeb3a8SPaul E. McKenney 	if (!atomic_dec_return(&n_warmedup))
402*1fbeb3a8SPaul E. McKenney 		while (atomic_read_acquire(&n_warmedup))
403*1fbeb3a8SPaul E. McKenney 			rcu_scale_one_reader();
404*1fbeb3a8SPaul E. McKenney 	// Also keep interrupts disabled.  This also has the effect
405*1fbeb3a8SPaul E. McKenney 	// of preventing entries into slow path for rcu_read_unlock().
406*1fbeb3a8SPaul E. McKenney 	local_irq_save(flags);
407*1fbeb3a8SPaul E. McKenney 	start = ktime_get_mono_fast_ns();
408*1fbeb3a8SPaul E. McKenney 
409*1fbeb3a8SPaul E. McKenney 	rcu_scale_one_reader();
410*1fbeb3a8SPaul E. McKenney 
411*1fbeb3a8SPaul E. McKenney 	duration = ktime_get_mono_fast_ns() - start;
412*1fbeb3a8SPaul E. McKenney 	local_irq_restore(flags);
413*1fbeb3a8SPaul E. McKenney 
414*1fbeb3a8SPaul E. McKenney 	rt->last_duration_ns = WARN_ON_ONCE(duration < 0) ? 0 : duration;
415*1fbeb3a8SPaul E. McKenney 	// To reduce runtime-skew noise, do maintain-load invocations until
416*1fbeb3a8SPaul E. McKenney 	// everyone is done.
417*1fbeb3a8SPaul E. McKenney 	if (!atomic_dec_return(&n_cooleddown))
418*1fbeb3a8SPaul E. McKenney 		while (atomic_read_acquire(&n_cooleddown))
419*1fbeb3a8SPaul E. McKenney 			rcu_scale_one_reader();
420*1fbeb3a8SPaul E. McKenney 
421*1fbeb3a8SPaul E. McKenney 	if (atomic_dec_and_test(&nreaders_exp))
422*1fbeb3a8SPaul E. McKenney 		wake_up(&main_wq);
423*1fbeb3a8SPaul E. McKenney 
424*1fbeb3a8SPaul E. McKenney 	VERBOSE_SCALEOUT("ref_scale_reader %ld: experiment %d ended, (readers remaining=%d)",
425*1fbeb3a8SPaul E. McKenney 			me, exp_idx, atomic_read(&nreaders_exp));
426*1fbeb3a8SPaul E. McKenney 
427*1fbeb3a8SPaul E. McKenney 	if (!torture_must_stop())
428*1fbeb3a8SPaul E. McKenney 		goto repeat;
429*1fbeb3a8SPaul E. McKenney end:
430*1fbeb3a8SPaul E. McKenney 	torture_kthread_stopping("ref_scale_reader");
431*1fbeb3a8SPaul E. McKenney 	return 0;
432*1fbeb3a8SPaul E. McKenney }
433*1fbeb3a8SPaul E. McKenney 
434*1fbeb3a8SPaul E. McKenney static void reset_readers(void)
435*1fbeb3a8SPaul E. McKenney {
436*1fbeb3a8SPaul E. McKenney 	int i;
437*1fbeb3a8SPaul E. McKenney 	struct reader_task *rt;
438*1fbeb3a8SPaul E. McKenney 
439*1fbeb3a8SPaul E. McKenney 	for (i = 0; i < nreaders; i++) {
440*1fbeb3a8SPaul E. McKenney 		rt = &(reader_tasks[i]);
441*1fbeb3a8SPaul E. McKenney 
442*1fbeb3a8SPaul E. McKenney 		rt->last_duration_ns = 0;
443*1fbeb3a8SPaul E. McKenney 	}
444*1fbeb3a8SPaul E. McKenney }
445*1fbeb3a8SPaul E. McKenney 
446*1fbeb3a8SPaul E. McKenney // Print the results of each reader and return the sum of all their durations.
447*1fbeb3a8SPaul E. McKenney static u64 process_durations(int n)
448*1fbeb3a8SPaul E. McKenney {
449*1fbeb3a8SPaul E. McKenney 	int i;
450*1fbeb3a8SPaul E. McKenney 	struct reader_task *rt;
451*1fbeb3a8SPaul E. McKenney 	char buf1[64];
452*1fbeb3a8SPaul E. McKenney 	char *buf;
453*1fbeb3a8SPaul E. McKenney 	u64 sum = 0;
454*1fbeb3a8SPaul E. McKenney 
455*1fbeb3a8SPaul E. McKenney 	buf = kmalloc(128 + nreaders * 32, GFP_KERNEL);
456*1fbeb3a8SPaul E. McKenney 	if (!buf)
457*1fbeb3a8SPaul E. McKenney 		return 0;
458*1fbeb3a8SPaul E. McKenney 	buf[0] = 0;
459*1fbeb3a8SPaul E. McKenney 	sprintf(buf, "Experiment #%d (Format: <THREAD-NUM>:<Total loop time in ns>)",
460*1fbeb3a8SPaul E. McKenney 		exp_idx);
461*1fbeb3a8SPaul E. McKenney 
462*1fbeb3a8SPaul E. McKenney 	for (i = 0; i < n && !torture_must_stop(); i++) {
463*1fbeb3a8SPaul E. McKenney 		rt = &(reader_tasks[i]);
464*1fbeb3a8SPaul E. McKenney 		sprintf(buf1, "%d: %llu\t", i, rt->last_duration_ns);
465*1fbeb3a8SPaul E. McKenney 
466*1fbeb3a8SPaul E. McKenney 		if (i % 5 == 0)
467*1fbeb3a8SPaul E. McKenney 			strcat(buf, "\n");
468*1fbeb3a8SPaul E. McKenney 		strcat(buf, buf1);
469*1fbeb3a8SPaul E. McKenney 
470*1fbeb3a8SPaul E. McKenney 		sum += rt->last_duration_ns;
471*1fbeb3a8SPaul E. McKenney 	}
472*1fbeb3a8SPaul E. McKenney 	strcat(buf, "\n");
473*1fbeb3a8SPaul E. McKenney 
474*1fbeb3a8SPaul E. McKenney 	SCALEOUT("%s\n", buf);
475*1fbeb3a8SPaul E. McKenney 
476*1fbeb3a8SPaul E. McKenney 	kfree(buf);
477*1fbeb3a8SPaul E. McKenney 	return sum;
478*1fbeb3a8SPaul E. McKenney }
479*1fbeb3a8SPaul E. McKenney 
480*1fbeb3a8SPaul E. McKenney // The main_func is the main orchestrator, it performs a bunch of
481*1fbeb3a8SPaul E. McKenney // experiments.  For every experiment, it orders all the readers
482*1fbeb3a8SPaul E. McKenney // involved to start and waits for them to finish the experiment. It
483*1fbeb3a8SPaul E. McKenney // then reads their timestamps and starts the next experiment. Each
484*1fbeb3a8SPaul E. McKenney // experiment progresses from 1 concurrent reader to N of them at which
485*1fbeb3a8SPaul E. McKenney // point all the timestamps are printed.
486*1fbeb3a8SPaul E. McKenney static int main_func(void *arg)
487*1fbeb3a8SPaul E. McKenney {
488*1fbeb3a8SPaul E. McKenney 	bool errexit = false;
489*1fbeb3a8SPaul E. McKenney 	int exp, r;
490*1fbeb3a8SPaul E. McKenney 	char buf1[64];
491*1fbeb3a8SPaul E. McKenney 	char *buf;
492*1fbeb3a8SPaul E. McKenney 	u64 *result_avg;
493*1fbeb3a8SPaul E. McKenney 
494*1fbeb3a8SPaul E. McKenney 	set_cpus_allowed_ptr(current, cpumask_of(nreaders % nr_cpu_ids));
495*1fbeb3a8SPaul E. McKenney 	set_user_nice(current, MAX_NICE);
496*1fbeb3a8SPaul E. McKenney 
497*1fbeb3a8SPaul E. McKenney 	VERBOSE_SCALEOUT("main_func task started");
498*1fbeb3a8SPaul E. McKenney 	result_avg = kzalloc(nruns * sizeof(*result_avg), GFP_KERNEL);
499*1fbeb3a8SPaul E. McKenney 	buf = kzalloc(64 + nruns * 32, GFP_KERNEL);
500*1fbeb3a8SPaul E. McKenney 	if (!result_avg || !buf) {
501*1fbeb3a8SPaul E. McKenney 		VERBOSE_SCALEOUT_ERRSTRING("out of memory");
502*1fbeb3a8SPaul E. McKenney 		errexit = true;
503*1fbeb3a8SPaul E. McKenney 	}
504*1fbeb3a8SPaul E. McKenney 	if (holdoff)
505*1fbeb3a8SPaul E. McKenney 		schedule_timeout_interruptible(holdoff * HZ);
506*1fbeb3a8SPaul E. McKenney 
507*1fbeb3a8SPaul E. McKenney 	// Wait for all threads to start.
508*1fbeb3a8SPaul E. McKenney 	atomic_inc(&n_init);
509*1fbeb3a8SPaul E. McKenney 	while (atomic_read(&n_init) < nreaders + 1)
510*1fbeb3a8SPaul E. McKenney 		schedule_timeout_uninterruptible(1);
511*1fbeb3a8SPaul E. McKenney 
512*1fbeb3a8SPaul E. McKenney 	// Start exp readers up per experiment
513*1fbeb3a8SPaul E. McKenney 	for (exp = 0; exp < nruns && !torture_must_stop(); exp++) {
514*1fbeb3a8SPaul E. McKenney 		if (errexit)
515*1fbeb3a8SPaul E. McKenney 			break;
516*1fbeb3a8SPaul E. McKenney 		if (torture_must_stop())
517*1fbeb3a8SPaul E. McKenney 			goto end;
518*1fbeb3a8SPaul E. McKenney 
519*1fbeb3a8SPaul E. McKenney 		reset_readers();
520*1fbeb3a8SPaul E. McKenney 		atomic_set(&nreaders_exp, nreaders);
521*1fbeb3a8SPaul E. McKenney 		atomic_set(&n_started, nreaders);
522*1fbeb3a8SPaul E. McKenney 		atomic_set(&n_warmedup, nreaders);
523*1fbeb3a8SPaul E. McKenney 		atomic_set(&n_cooleddown, nreaders);
524*1fbeb3a8SPaul E. McKenney 
525*1fbeb3a8SPaul E. McKenney 		exp_idx = exp;
526*1fbeb3a8SPaul E. McKenney 
527*1fbeb3a8SPaul E. McKenney 		for (r = 0; r < nreaders; r++) {
528*1fbeb3a8SPaul E. McKenney 			smp_store_release(&reader_tasks[r].start_reader, 1);
529*1fbeb3a8SPaul E. McKenney 			wake_up(&reader_tasks[r].wq);
530*1fbeb3a8SPaul E. McKenney 		}
531*1fbeb3a8SPaul E. McKenney 
532*1fbeb3a8SPaul E. McKenney 		VERBOSE_SCALEOUT("main_func: experiment started, waiting for %d readers",
533*1fbeb3a8SPaul E. McKenney 				nreaders);
534*1fbeb3a8SPaul E. McKenney 
535*1fbeb3a8SPaul E. McKenney 		wait_event(main_wq,
536*1fbeb3a8SPaul E. McKenney 			   !atomic_read(&nreaders_exp) || torture_must_stop());
537*1fbeb3a8SPaul E. McKenney 
538*1fbeb3a8SPaul E. McKenney 		VERBOSE_SCALEOUT("main_func: experiment ended");
539*1fbeb3a8SPaul E. McKenney 
540*1fbeb3a8SPaul E. McKenney 		if (torture_must_stop())
541*1fbeb3a8SPaul E. McKenney 			goto end;
542*1fbeb3a8SPaul E. McKenney 
543*1fbeb3a8SPaul E. McKenney 		result_avg[exp] = div_u64(1000 * process_durations(nreaders), nreaders * loops);
544*1fbeb3a8SPaul E. McKenney 	}
545*1fbeb3a8SPaul E. McKenney 
546*1fbeb3a8SPaul E. McKenney 	// Print the average of all experiments
547*1fbeb3a8SPaul E. McKenney 	SCALEOUT("END OF TEST. Calculating average duration per loop (nanoseconds)...\n");
548*1fbeb3a8SPaul E. McKenney 
549*1fbeb3a8SPaul E. McKenney 	buf[0] = 0;
550*1fbeb3a8SPaul E. McKenney 	strcat(buf, "\n");
551*1fbeb3a8SPaul E. McKenney 	strcat(buf, "Runs\tTime(ns)\n");
552*1fbeb3a8SPaul E. McKenney 
553*1fbeb3a8SPaul E. McKenney 	for (exp = 0; exp < nruns; exp++) {
554*1fbeb3a8SPaul E. McKenney 		u64 avg;
555*1fbeb3a8SPaul E. McKenney 		u32 rem;
556*1fbeb3a8SPaul E. McKenney 
557*1fbeb3a8SPaul E. McKenney 		if (errexit)
558*1fbeb3a8SPaul E. McKenney 			break;
559*1fbeb3a8SPaul E. McKenney 		avg = div_u64_rem(result_avg[exp], 1000, &rem);
560*1fbeb3a8SPaul E. McKenney 		sprintf(buf1, "%d\t%llu.%03u\n", exp + 1, avg, rem);
561*1fbeb3a8SPaul E. McKenney 		strcat(buf, buf1);
562*1fbeb3a8SPaul E. McKenney 	}
563*1fbeb3a8SPaul E. McKenney 
564*1fbeb3a8SPaul E. McKenney 	if (!errexit)
565*1fbeb3a8SPaul E. McKenney 		SCALEOUT("%s", buf);
566*1fbeb3a8SPaul E. McKenney 
567*1fbeb3a8SPaul E. McKenney 	// This will shutdown everything including us.
568*1fbeb3a8SPaul E. McKenney 	if (shutdown) {
569*1fbeb3a8SPaul E. McKenney 		shutdown_start = 1;
570*1fbeb3a8SPaul E. McKenney 		wake_up(&shutdown_wq);
571*1fbeb3a8SPaul E. McKenney 	}
572*1fbeb3a8SPaul E. McKenney 
573*1fbeb3a8SPaul E. McKenney 	// Wait for torture to stop us
574*1fbeb3a8SPaul E. McKenney 	while (!torture_must_stop())
575*1fbeb3a8SPaul E. McKenney 		schedule_timeout_uninterruptible(1);
576*1fbeb3a8SPaul E. McKenney 
577*1fbeb3a8SPaul E. McKenney end:
578*1fbeb3a8SPaul E. McKenney 	torture_kthread_stopping("main_func");
579*1fbeb3a8SPaul E. McKenney 	kfree(result_avg);
580*1fbeb3a8SPaul E. McKenney 	kfree(buf);
581*1fbeb3a8SPaul E. McKenney 	return 0;
582*1fbeb3a8SPaul E. McKenney }
583*1fbeb3a8SPaul E. McKenney 
584*1fbeb3a8SPaul E. McKenney static void
585*1fbeb3a8SPaul E. McKenney ref_scale_print_module_parms(struct ref_scale_ops *cur_ops, const char *tag)
586*1fbeb3a8SPaul E. McKenney {
587*1fbeb3a8SPaul E. McKenney 	pr_alert("%s" SCALE_FLAG
588*1fbeb3a8SPaul E. McKenney 		 "--- %s:  verbose=%d shutdown=%d holdoff=%d loops=%ld nreaders=%d nruns=%d readdelay=%d\n", scale_type, tag,
589*1fbeb3a8SPaul E. McKenney 		 verbose, shutdown, holdoff, loops, nreaders, nruns, readdelay);
590*1fbeb3a8SPaul E. McKenney }
591*1fbeb3a8SPaul E. McKenney 
592*1fbeb3a8SPaul E. McKenney static void
593*1fbeb3a8SPaul E. McKenney ref_scale_cleanup(void)
594*1fbeb3a8SPaul E. McKenney {
595*1fbeb3a8SPaul E. McKenney 	int i;
596*1fbeb3a8SPaul E. McKenney 
597*1fbeb3a8SPaul E. McKenney 	if (torture_cleanup_begin())
598*1fbeb3a8SPaul E. McKenney 		return;
599*1fbeb3a8SPaul E. McKenney 
600*1fbeb3a8SPaul E. McKenney 	if (!cur_ops) {
601*1fbeb3a8SPaul E. McKenney 		torture_cleanup_end();
602*1fbeb3a8SPaul E. McKenney 		return;
603*1fbeb3a8SPaul E. McKenney 	}
604*1fbeb3a8SPaul E. McKenney 
605*1fbeb3a8SPaul E. McKenney 	if (reader_tasks) {
606*1fbeb3a8SPaul E. McKenney 		for (i = 0; i < nreaders; i++)
607*1fbeb3a8SPaul E. McKenney 			torture_stop_kthread("ref_scale_reader",
608*1fbeb3a8SPaul E. McKenney 					     reader_tasks[i].task);
609*1fbeb3a8SPaul E. McKenney 	}
610*1fbeb3a8SPaul E. McKenney 	kfree(reader_tasks);
611*1fbeb3a8SPaul E. McKenney 
612*1fbeb3a8SPaul E. McKenney 	torture_stop_kthread("main_task", main_task);
613*1fbeb3a8SPaul E. McKenney 	kfree(main_task);
614*1fbeb3a8SPaul E. McKenney 
615*1fbeb3a8SPaul E. McKenney 	// Do scale-type-specific cleanup operations.
616*1fbeb3a8SPaul E. McKenney 	if (cur_ops->cleanup != NULL)
617*1fbeb3a8SPaul E. McKenney 		cur_ops->cleanup();
618*1fbeb3a8SPaul E. McKenney 
619*1fbeb3a8SPaul E. McKenney 	torture_cleanup_end();
620*1fbeb3a8SPaul E. McKenney }
621*1fbeb3a8SPaul E. McKenney 
622*1fbeb3a8SPaul E. McKenney // Shutdown kthread.  Just waits to be awakened, then shuts down system.
623*1fbeb3a8SPaul E. McKenney static int
624*1fbeb3a8SPaul E. McKenney ref_scale_shutdown(void *arg)
625*1fbeb3a8SPaul E. McKenney {
626*1fbeb3a8SPaul E. McKenney 	wait_event(shutdown_wq, shutdown_start);
627*1fbeb3a8SPaul E. McKenney 
628*1fbeb3a8SPaul E. McKenney 	smp_mb(); // Wake before output.
629*1fbeb3a8SPaul E. McKenney 	ref_scale_cleanup();
630*1fbeb3a8SPaul E. McKenney 	kernel_power_off();
631*1fbeb3a8SPaul E. McKenney 
632*1fbeb3a8SPaul E. McKenney 	return -EINVAL;
633*1fbeb3a8SPaul E. McKenney }
634*1fbeb3a8SPaul E. McKenney 
635*1fbeb3a8SPaul E. McKenney static int __init
636*1fbeb3a8SPaul E. McKenney ref_scale_init(void)
637*1fbeb3a8SPaul E. McKenney {
638*1fbeb3a8SPaul E. McKenney 	long i;
639*1fbeb3a8SPaul E. McKenney 	int firsterr = 0;
640*1fbeb3a8SPaul E. McKenney 	static struct ref_scale_ops *scale_ops[] = {
641*1fbeb3a8SPaul E. McKenney 		&rcu_ops, &srcu_ops, &rcu_trace_ops, &rcu_tasks_ops,
642*1fbeb3a8SPaul E. McKenney 		&refcnt_ops, &rwlock_ops, &rwsem_ops,
643*1fbeb3a8SPaul E. McKenney 	};
644*1fbeb3a8SPaul E. McKenney 
645*1fbeb3a8SPaul E. McKenney 	if (!torture_init_begin(scale_type, verbose))
646*1fbeb3a8SPaul E. McKenney 		return -EBUSY;
647*1fbeb3a8SPaul E. McKenney 
648*1fbeb3a8SPaul E. McKenney 	for (i = 0; i < ARRAY_SIZE(scale_ops); i++) {
649*1fbeb3a8SPaul E. McKenney 		cur_ops = scale_ops[i];
650*1fbeb3a8SPaul E. McKenney 		if (strcmp(scale_type, cur_ops->name) == 0)
651*1fbeb3a8SPaul E. McKenney 			break;
652*1fbeb3a8SPaul E. McKenney 	}
653*1fbeb3a8SPaul E. McKenney 	if (i == ARRAY_SIZE(scale_ops)) {
654*1fbeb3a8SPaul E. McKenney 		pr_alert("rcu-scale: invalid scale type: \"%s\"\n", scale_type);
655*1fbeb3a8SPaul E. McKenney 		pr_alert("rcu-scale types:");
656*1fbeb3a8SPaul E. McKenney 		for (i = 0; i < ARRAY_SIZE(scale_ops); i++)
657*1fbeb3a8SPaul E. McKenney 			pr_cont(" %s", scale_ops[i]->name);
658*1fbeb3a8SPaul E. McKenney 		pr_cont("\n");
659*1fbeb3a8SPaul E. McKenney 		WARN_ON(!IS_MODULE(CONFIG_RCU_REF_SCALE_TEST));
660*1fbeb3a8SPaul E. McKenney 		firsterr = -EINVAL;
661*1fbeb3a8SPaul E. McKenney 		cur_ops = NULL;
662*1fbeb3a8SPaul E. McKenney 		goto unwind;
663*1fbeb3a8SPaul E. McKenney 	}
664*1fbeb3a8SPaul E. McKenney 	if (cur_ops->init)
665*1fbeb3a8SPaul E. McKenney 		cur_ops->init();
666*1fbeb3a8SPaul E. McKenney 
667*1fbeb3a8SPaul E. McKenney 	ref_scale_print_module_parms(cur_ops, "Start of test");
668*1fbeb3a8SPaul E. McKenney 
669*1fbeb3a8SPaul E. McKenney 	// Shutdown task
670*1fbeb3a8SPaul E. McKenney 	if (shutdown) {
671*1fbeb3a8SPaul E. McKenney 		init_waitqueue_head(&shutdown_wq);
672*1fbeb3a8SPaul E. McKenney 		firsterr = torture_create_kthread(ref_scale_shutdown, NULL,
673*1fbeb3a8SPaul E. McKenney 						  shutdown_task);
674*1fbeb3a8SPaul E. McKenney 		if (firsterr)
675*1fbeb3a8SPaul E. McKenney 			goto unwind;
676*1fbeb3a8SPaul E. McKenney 		schedule_timeout_uninterruptible(1);
677*1fbeb3a8SPaul E. McKenney 	}
678*1fbeb3a8SPaul E. McKenney 
679*1fbeb3a8SPaul E. McKenney 	// Reader tasks (default to ~75% of online CPUs).
680*1fbeb3a8SPaul E. McKenney 	if (nreaders < 0)
681*1fbeb3a8SPaul E. McKenney 		nreaders = (num_online_cpus() >> 1) + (num_online_cpus() >> 2);
682*1fbeb3a8SPaul E. McKenney 	reader_tasks = kcalloc(nreaders, sizeof(reader_tasks[0]),
683*1fbeb3a8SPaul E. McKenney 			       GFP_KERNEL);
684*1fbeb3a8SPaul E. McKenney 	if (!reader_tasks) {
685*1fbeb3a8SPaul E. McKenney 		VERBOSE_SCALEOUT_ERRSTRING("out of memory");
686*1fbeb3a8SPaul E. McKenney 		firsterr = -ENOMEM;
687*1fbeb3a8SPaul E. McKenney 		goto unwind;
688*1fbeb3a8SPaul E. McKenney 	}
689*1fbeb3a8SPaul E. McKenney 
690*1fbeb3a8SPaul E. McKenney 	VERBOSE_SCALEOUT("Starting %d reader threads\n", nreaders);
691*1fbeb3a8SPaul E. McKenney 
692*1fbeb3a8SPaul E. McKenney 	for (i = 0; i < nreaders; i++) {
693*1fbeb3a8SPaul E. McKenney 		firsterr = torture_create_kthread(ref_scale_reader, (void *)i,
694*1fbeb3a8SPaul E. McKenney 						  reader_tasks[i].task);
695*1fbeb3a8SPaul E. McKenney 		if (firsterr)
696*1fbeb3a8SPaul E. McKenney 			goto unwind;
697*1fbeb3a8SPaul E. McKenney 
698*1fbeb3a8SPaul E. McKenney 		init_waitqueue_head(&(reader_tasks[i].wq));
699*1fbeb3a8SPaul E. McKenney 	}
700*1fbeb3a8SPaul E. McKenney 
701*1fbeb3a8SPaul E. McKenney 	// Main Task
702*1fbeb3a8SPaul E. McKenney 	init_waitqueue_head(&main_wq);
703*1fbeb3a8SPaul E. McKenney 	firsterr = torture_create_kthread(main_func, NULL, main_task);
704*1fbeb3a8SPaul E. McKenney 	if (firsterr)
705*1fbeb3a8SPaul E. McKenney 		goto unwind;
706*1fbeb3a8SPaul E. McKenney 
707*1fbeb3a8SPaul E. McKenney 	torture_init_end();
708*1fbeb3a8SPaul E. McKenney 	return 0;
709*1fbeb3a8SPaul E. McKenney 
710*1fbeb3a8SPaul E. McKenney unwind:
711*1fbeb3a8SPaul E. McKenney 	torture_init_end();
712*1fbeb3a8SPaul E. McKenney 	ref_scale_cleanup();
713*1fbeb3a8SPaul E. McKenney 	return firsterr;
714*1fbeb3a8SPaul E. McKenney }
715*1fbeb3a8SPaul E. McKenney 
716*1fbeb3a8SPaul E. McKenney module_init(ref_scale_init);
717*1fbeb3a8SPaul E. McKenney module_exit(ref_scale_cleanup);
718