aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/locking/Makefile1
-rw-r--r--kernel/locking/locktorture.c421
-rw-r--r--lib/Kconfig.debug15
3 files changed, 437 insertions, 0 deletions
diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile
index baab8e5e7f66..a28ea6d9e6e8 100644
--- a/kernel/locking/Makefile
+++ b/kernel/locking/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
23obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o 23obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
24obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o 24obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o
25obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o 25obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o
26obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c
new file mode 100644
index 000000000000..d69d20d9c9db
--- /dev/null
+++ b/kernel/locking/locktorture.c
@@ -0,0 +1,421 @@
1/*
2 * Module-based torture test facility for locking
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, you can access it online at
16 * http://www.gnu.org/licenses/gpl-2.0.html.
17 *
18 * Copyright (C) IBM Corporation, 2014
19 *
20 * Author: Paul E. McKenney <paulmck@us.ibm.com>
21 * Based on kernel/rcu/torture.c.
22 */
23#include <linux/types.h>
24#include <linux/kernel.h>
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/kthread.h>
28#include <linux/err.h>
29#include <linux/spinlock.h>
30#include <linux/smp.h>
31#include <linux/interrupt.h>
32#include <linux/sched.h>
33#include <linux/atomic.h>
34#include <linux/bitops.h>
35#include <linux/completion.h>
36#include <linux/moduleparam.h>
37#include <linux/percpu.h>
38#include <linux/notifier.h>
39#include <linux/reboot.h>
40#include <linux/freezer.h>
41#include <linux/cpu.h>
42#include <linux/delay.h>
43#include <linux/stat.h>
44#include <linux/slab.h>
45#include <linux/trace_clock.h>
46#include <asm/byteorder.h>
47#include <linux/torture.h>
48
49MODULE_LICENSE("GPL");
50MODULE_AUTHOR("Paul E. McKenney <paulmck@us.ibm.com>");
51
52torture_param(int, nwriters_stress, -1,
53 "Number of write-locking stress-test threads");
54torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)");
55torture_param(int, onoff_interval, 0,
56 "Time between CPU hotplugs (s), 0=disable");
57torture_param(int, shuffle_interval, 3,
58 "Number of jiffies between shuffles, 0=disable");
59torture_param(int, shutdown_secs, 0, "Shutdown time (j), <= zero to disable.");
60torture_param(int, stat_interval, 60,
61 "Number of seconds between stats printk()s");
62torture_param(int, stutter, 5, "Number of jiffies to run/halt test, 0=disable");
63torture_param(bool, verbose, true,
64 "Enable verbose debugging printk()s");
65
66static char *torture_type = "spin_lock";
67module_param(torture_type, charp, 0444);
68MODULE_PARM_DESC(torture_type,
69 "Type of lock to torture (spin_lock, spin_lock_irq, ...)");
70
71static atomic_t n_lock_torture_errors;
72
73static struct task_struct *stats_task;
74static struct task_struct **writer_tasks;
75
76static int nrealwriters_stress;
77static bool lock_is_write_held;
78
79struct lock_writer_stress_stats {
80 long n_write_lock_fail;
81 long n_write_lock_acquired;
82};
83static struct lock_writer_stress_stats *lwsa;
84
85#if defined(MODULE) || defined(CONFIG_LOCK_TORTURE_TEST_RUNNABLE)
86#define LOCKTORTURE_RUNNABLE_INIT 1
87#else
88#define LOCKTORTURE_RUNNABLE_INIT 0
89#endif
90int locktorture_runnable = LOCKTORTURE_RUNNABLE_INIT;
91module_param(locktorture_runnable, int, 0444);
92MODULE_PARM_DESC(locktorture_runnable, "Start locktorture at boot");
93
94/* Forward reference. */
95static void lock_torture_cleanup(void);
96
97/*
98 * Operations vector for selecting different types of tests.
99 */
100struct lock_torture_ops {
101 void (*init)(void);
102 int (*writelock)(void);
103 void (*write_delay)(struct torture_random_state *trsp);
104 void (*writeunlock)(void);
105 unsigned long flags;
106 const char *name;
107};
108
109static struct lock_torture_ops *cur_ops;
110
111/*
112 * Definitions for lock torture testing.
113 */
114
115static DEFINE_SPINLOCK(torture_spinlock);
116
117static int torture_spin_lock_write_lock(void) __acquires(torture_spinlock)
118{
119 spin_lock(&torture_spinlock);
120 return 0;
121}
122
123static void torture_spin_lock_write_delay(struct torture_random_state *trsp)
124{
125 const unsigned long shortdelay_us = 2;
126 const unsigned long longdelay_us = 100;
127
128 /* We want a short delay mostly to emulate likely code, and
129 * we want a long delay occasionally to force massive contention.
130 */
131 if (!(torture_random(trsp) %
132 (nrealwriters_stress * 2000 * longdelay_us)))
133 mdelay(longdelay_us);
134 if (!(torture_random(trsp) %
135 (nrealwriters_stress * 2 * shortdelay_us)))
136 udelay(shortdelay_us);
137#ifdef CONFIG_PREEMPT
138 if (!(torture_random(trsp) % (nrealwriters_stress * 20000)))
139 preempt_schedule(); /* Allow test to be preempted. */
140#endif
141}
142
143static void torture_spin_lock_write_unlock(void) __releases(torture_spinlock)
144{
145 spin_unlock(&torture_spinlock);
146}
147
148static struct lock_torture_ops spin_lock_ops = {
149 .writelock = torture_spin_lock_write_lock,
150 .write_delay = torture_spin_lock_write_delay,
151 .writeunlock = torture_spin_lock_write_unlock,
152 .name = "spin_lock"
153};
154
155static int torture_spin_lock_write_lock_irq(void)
156__acquires(torture_spinlock_irq)
157{
158 unsigned long flags;
159
160 spin_lock_irqsave(&torture_spinlock, flags);
161 cur_ops->flags = flags;
162 return 0;
163}
164
165static void torture_lock_spin_write_unlock_irq(void)
166__releases(torture_spinlock)
167{
168 spin_unlock_irqrestore(&torture_spinlock, cur_ops->flags);
169}
170
171static struct lock_torture_ops spin_lock_irq_ops = {
172 .writelock = torture_spin_lock_write_lock_irq,
173 .write_delay = torture_spin_lock_write_delay,
174 .writeunlock = torture_lock_spin_write_unlock_irq,
175 .name = "spin_lock_irq"
176};
177
178/*
179 * Lock torture writer kthread. Repeatedly acquires and releases
180 * the lock, checking for duplicate acquisitions.
181 */
182static int lock_torture_writer(void *arg)
183{
184 struct lock_writer_stress_stats *lwsp = arg;
185 static DEFINE_TORTURE_RANDOM(rand);
186
187 VERBOSE_TOROUT_STRING("lock_torture_writer task started");
188 set_user_nice(current, 19);
189
190 do {
191 schedule_timeout_uninterruptible(1);
192 cur_ops->writelock();
193 if (WARN_ON_ONCE(lock_is_write_held))
194 lwsp->n_write_lock_fail++;
195 lock_is_write_held = 1;
196 lwsp->n_write_lock_acquired++;
197 cur_ops->write_delay(&rand);
198 lock_is_write_held = 0;
199 cur_ops->writeunlock();
200 stutter_wait("lock_torture_writer");
201 } while (!torture_must_stop());
202 torture_kthread_stopping("lock_torture_writer");
203 return 0;
204}
205
206/*
207 * Create an lock-torture-statistics message in the specified buffer.
208 */
209static void lock_torture_printk(char *page)
210{
211 bool fail = 0;
212 int i;
213 long max = 0;
214 long min = lwsa[0].n_write_lock_acquired;
215 long long sum = 0;
216
217 for (i = 0; i < nrealwriters_stress; i++) {
218 if (lwsa[i].n_write_lock_fail)
219 fail = true;
220 sum += lwsa[i].n_write_lock_acquired;
221 if (max < lwsa[i].n_write_lock_fail)
222 max = lwsa[i].n_write_lock_fail;
223 if (min > lwsa[i].n_write_lock_fail)
224 min = lwsa[i].n_write_lock_fail;
225 }
226 page += sprintf(page, "%s%s ", torture_type, TORTURE_FLAG);
227 page += sprintf(page,
228 "Writes: Total: %lld Max/Min: %ld/%ld %s Fail: %d %s\n",
229 sum, max, min, max / 2 > min ? "???" : "",
230 fail, fail ? "!!!" : "");
231 if (fail)
232 atomic_inc(&n_lock_torture_errors);
233}
234
235/*
236 * Print torture statistics. Caller must ensure that there is only one
237 * call to this function at a given time!!! This is normally accomplished
238 * by relying on the module system to only have one copy of the module
239 * loaded, and then by giving the lock_torture_stats kthread full control
240 * (or the init/cleanup functions when lock_torture_stats thread is not
241 * running).
242 */
243static void lock_torture_stats_print(void)
244{
245 int size = nrealwriters_stress * 200 + 8192;
246 char *buf;
247
248 buf = kmalloc(size, GFP_KERNEL);
249 if (!buf) {
250 pr_err("lock_torture_stats_print: Out of memory, need: %d",
251 size);
252 return;
253 }
254 lock_torture_printk(buf);
255 pr_alert("%s", buf);
256 kfree(buf);
257}
258
259/*
260 * Periodically prints torture statistics, if periodic statistics printing
261 * was specified via the stat_interval module parameter.
262 *
263 * No need to worry about fullstop here, since this one doesn't reference
264 * volatile state or register callbacks.
265 */
266static int lock_torture_stats(void *arg)
267{
268 VERBOSE_TOROUT_STRING("lock_torture_stats task started");
269 do {
270 schedule_timeout_interruptible(stat_interval * HZ);
271 lock_torture_stats_print();
272 torture_shutdown_absorb("lock_torture_stats");
273 } while (!torture_must_stop());
274 torture_kthread_stopping("lock_torture_stats");
275 return 0;
276}
277
278static inline void
279lock_torture_print_module_parms(struct lock_torture_ops *cur_ops,
280 const char *tag)
281{
282 pr_alert("%s" TORTURE_FLAG
283 "--- %s: nwriters_stress=%d stat_interval=%d verbose=%d shuffle_interval=%d stutter=%d shutdown_secs=%d onoff_interval=%d onoff_holdoff=%d\n",
284 torture_type, tag, nrealwriters_stress, stat_interval, verbose,
285 shuffle_interval, stutter, shutdown_secs,
286 onoff_interval, onoff_holdoff);
287}
288
289static void lock_torture_cleanup(void)
290{
291 int i;
292
293 if (torture_cleanup())
294 return;
295
296 if (writer_tasks) {
297 for (i = 0; i < nrealwriters_stress; i++)
298 torture_stop_kthread(lock_torture_writer,
299 writer_tasks[i]);
300 kfree(writer_tasks);
301 writer_tasks = NULL;
302 }
303
304 torture_stop_kthread(lock_torture_stats, stats_task);
305 lock_torture_stats_print(); /* -After- the stats thread is stopped! */
306
307 if (atomic_read(&n_lock_torture_errors))
308 lock_torture_print_module_parms(cur_ops,
309 "End of test: FAILURE");
310 else if (torture_onoff_failures())
311 lock_torture_print_module_parms(cur_ops,
312 "End of test: LOCK_HOTPLUG");
313 else
314 lock_torture_print_module_parms(cur_ops,
315 "End of test: SUCCESS");
316}
317
318static int __init lock_torture_init(void)
319{
320 int i;
321 int firsterr = 0;
322 static struct lock_torture_ops *torture_ops[] = {
323 &spin_lock_ops, &spin_lock_irq_ops,
324 };
325
326 torture_init_begin(torture_type, verbose, &locktorture_runnable);
327
328 /* Process args and tell the world that the torturer is on the job. */
329 for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
330 cur_ops = torture_ops[i];
331 if (strcmp(torture_type, cur_ops->name) == 0)
332 break;
333 }
334 if (i == ARRAY_SIZE(torture_ops)) {
335 pr_alert("lock-torture: invalid torture type: \"%s\"\n",
336 torture_type);
337 pr_alert("lock-torture types:");
338 for (i = 0; i < ARRAY_SIZE(torture_ops); i++)
339 pr_alert(" %s", torture_ops[i]->name);
340 pr_alert("\n");
341 torture_init_end();
342 return -EINVAL;
343 }
344 if (cur_ops->init)
345 cur_ops->init(); /* no "goto unwind" prior to this point!!! */
346
347 if (nwriters_stress >= 0)
348 nrealwriters_stress = nwriters_stress;
349 else
350 nrealwriters_stress = 2 * num_online_cpus();
351 lock_torture_print_module_parms(cur_ops, "Start of test");
352
353 /* Initialize the statistics so that each run gets its own numbers. */
354
355 lock_is_write_held = 0;
356 lwsa = kmalloc(sizeof(*lwsa) * nrealwriters_stress, GFP_KERNEL);
357 if (lwsa == NULL) {
358 VERBOSE_TOROUT_STRING("lwsa: Out of memory");
359 firsterr = -ENOMEM;
360 goto unwind;
361 }
362 for (i = 0; i < nrealwriters_stress; i++) {
363 lwsa[i].n_write_lock_fail = 0;
364 lwsa[i].n_write_lock_acquired = 0;
365 }
366
367 /* Start up the kthreads. */
368
369 if (onoff_interval > 0) {
370 firsterr = torture_onoff_init(onoff_holdoff * HZ,
371 onoff_interval * HZ);
372 if (firsterr)
373 goto unwind;
374 }
375 if (shuffle_interval > 0) {
376 firsterr = torture_shuffle_init(shuffle_interval);
377 if (firsterr)
378 goto unwind;
379 }
380 if (shutdown_secs > 0) {
381 firsterr = torture_shutdown_init(shutdown_secs,
382 lock_torture_cleanup);
383 if (firsterr)
384 goto unwind;
385 }
386 if (stutter > 0) {
387 firsterr = torture_stutter_init(stutter);
388 if (firsterr)
389 goto unwind;
390 }
391
392 writer_tasks = kzalloc(nrealwriters_stress * sizeof(writer_tasks[0]),
393 GFP_KERNEL);
394 if (writer_tasks == NULL) {
395 VERBOSE_TOROUT_ERRSTRING("writer_tasks: Out of memory");
396 firsterr = -ENOMEM;
397 goto unwind;
398 }
399 for (i = 0; i < nrealwriters_stress; i++) {
400 firsterr = torture_create_kthread(lock_torture_writer, &lwsa[i],
401 writer_tasks[i]);
402 if (firsterr)
403 goto unwind;
404 }
405 if (stat_interval > 0) {
406 firsterr = torture_create_kthread(lock_torture_stats, NULL,
407 stats_task);
408 if (firsterr)
409 goto unwind;
410 }
411 torture_init_end();
412 return 0;
413
414unwind:
415 torture_init_end();
416 lock_torture_cleanup();
417 return firsterr;
418}
419
420module_init(lock_torture_init);
421module_exit(lock_torture_cleanup);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2bfb4e5cdf8c..dd7f8858188a 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -980,6 +980,21 @@ config DEBUG_LOCKING_API_SELFTESTS
980 The following locking APIs are covered: spinlocks, rwlocks, 980 The following locking APIs are covered: spinlocks, rwlocks,
981 mutexes and rwsems. 981 mutexes and rwsems.
982 982
983config LOCK_TORTURE_TEST
984 tristate "torture tests for locking"
985 depends on DEBUG_KERNEL
986 select TORTURE_TEST
987 default n
988 help
989 This option provides a kernel module that runs torture tests
990 on kernel locking primitives. The kernel module may be built
991 after the fact on the running kernel to be tested, if desired.
992
993 Say Y here if you want kernel locking-primitive torture tests
994 to be built into the kernel.
995 Say M if you want these torture tests to build as a module.
996 Say N if you are unsure.
997
983endmenu # lock debugging 998endmenu # lock debugging
984 999
985config TRACE_IRQFLAGS 1000config TRACE_IRQFLAGS