diff options
-rw-r--r-- | Documentation/RCU/torture.txt | 9 | ||||
-rw-r--r-- | kernel/rcutorture.c | 109 |
2 files changed, 113 insertions, 5 deletions
diff --git a/Documentation/RCU/torture.txt b/Documentation/RCU/torture.txt index 2174badd31e0..2180ef93accc 100644 --- a/Documentation/RCU/torture.txt +++ b/Documentation/RCU/torture.txt | |||
@@ -28,6 +28,15 @@ nreaders This is the number of RCU reading threads supported. | |||
28 | To properly exercise RCU implementations with preemptible | 28 | To properly exercise RCU implementations with preemptible |
29 | read-side critical sections. | 29 | read-side critical sections. |
30 | 30 | ||
31 | nfakewriters This is the number of RCU fake writer threads to run. Fake | ||
32 | writer threads repeatedly use the synchronous "wait for | ||
33 | current readers" function of the interface selected by | ||
34 | torture_type, with a delay between calls to allow for various | ||
35 | different numbers of writers running in parallel. | ||
36 | nfakewriters defaults to 4, which provides enough parallelism | ||
37 | to trigger special cases caused by multiple writers, such as | ||
38 | the synchronize_srcu() early return optimization. | ||
39 | |||
31 | stat_interval The number of seconds between output of torture | 40 | stat_interval The number of seconds between output of torture |
32 | statistics (via printk()). Regardless of the interval, | 41 | statistics (via printk()). Regardless of the interval, |
33 | statistics are printed when the module is unloaded. | 42 | statistics are printed when the module is unloaded. |
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 43d6d4f9ef09..e0450210ce4d 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
@@ -15,9 +15,10 @@ | |||
15 | * along with this program; if not, write to the Free Software | 15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | * | 17 | * |
18 | * Copyright (C) IBM Corporation, 2005 | 18 | * Copyright (C) IBM Corporation, 2005, 2006 |
19 | * | 19 | * |
20 | * Authors: Paul E. McKenney <paulmck@us.ibm.com> | 20 | * Authors: Paul E. McKenney <paulmck@us.ibm.com> |
21 | * Josh Triplett <josh@freedesktop.org> | ||
21 | * | 22 | * |
22 | * See also: Documentation/RCU/torture.txt | 23 | * See also: Documentation/RCU/torture.txt |
23 | */ | 24 | */ |
@@ -47,9 +48,11 @@ | |||
47 | #include <linux/srcu.h> | 48 | #include <linux/srcu.h> |
48 | 49 | ||
49 | MODULE_LICENSE("GPL"); | 50 | MODULE_LICENSE("GPL"); |
50 | MODULE_AUTHOR("Paul E. McKenney <paulmck@us.ibm.com>"); | 51 | MODULE_AUTHOR("Paul E. McKenney <paulmck@us.ibm.com> and " |
52 | "Josh Triplett <josh@freedesktop.org>"); | ||
51 | 53 | ||
52 | static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */ | 54 | static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */ |
55 | static int nfakewriters = 4; /* # fake writer threads */ | ||
53 | static int stat_interval; /* Interval between stats, in seconds. */ | 56 | static int stat_interval; /* Interval between stats, in seconds. */ |
54 | /* Defaults to "only at end of test". */ | 57 | /* Defaults to "only at end of test". */ |
55 | static int verbose; /* Print more debug info. */ | 58 | static int verbose; /* Print more debug info. */ |
@@ -59,6 +62,8 @@ static char *torture_type = "rcu"; /* What to torture: rcu, rcu_bh, srcu. */ | |||
59 | 62 | ||
60 | module_param(nreaders, int, 0); | 63 | module_param(nreaders, int, 0); |
61 | MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); | 64 | MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); |
65 | module_param(nfakewriters, int, 0); | ||
66 | MODULE_PARM_DESC(nfakewriters, "Number of RCU fake writer threads"); | ||
62 | module_param(stat_interval, int, 0); | 67 | module_param(stat_interval, int, 0); |
63 | MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s"); | 68 | MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s"); |
64 | module_param(verbose, bool, 0); | 69 | module_param(verbose, bool, 0); |
@@ -82,6 +87,7 @@ static char printk_buf[4096]; | |||
82 | 87 | ||
83 | static int nrealreaders; | 88 | static int nrealreaders; |
84 | static struct task_struct *writer_task; | 89 | static struct task_struct *writer_task; |
90 | static struct task_struct **fakewriter_tasks; | ||
85 | static struct task_struct **reader_tasks; | 91 | static struct task_struct **reader_tasks; |
86 | static struct task_struct *stats_task; | 92 | static struct task_struct *stats_task; |
87 | static struct task_struct *shuffler_task; | 93 | static struct task_struct *shuffler_task; |
@@ -186,6 +192,7 @@ struct rcu_torture_ops { | |||
186 | void (*readunlock)(int idx); | 192 | void (*readunlock)(int idx); |
187 | int (*completed)(void); | 193 | int (*completed)(void); |
188 | void (*deferredfree)(struct rcu_torture *p); | 194 | void (*deferredfree)(struct rcu_torture *p); |
195 | void (*sync)(void); | ||
189 | int (*stats)(char *page); | 196 | int (*stats)(char *page); |
190 | char *name; | 197 | char *name; |
191 | }; | 198 | }; |
@@ -258,6 +265,7 @@ static struct rcu_torture_ops rcu_ops = { | |||
258 | .readunlock = rcu_torture_read_unlock, | 265 | .readunlock = rcu_torture_read_unlock, |
259 | .completed = rcu_torture_completed, | 266 | .completed = rcu_torture_completed, |
260 | .deferredfree = rcu_torture_deferred_free, | 267 | .deferredfree = rcu_torture_deferred_free, |
268 | .sync = synchronize_rcu, | ||
261 | .stats = NULL, | 269 | .stats = NULL, |
262 | .name = "rcu" | 270 | .name = "rcu" |
263 | }; | 271 | }; |
@@ -287,6 +295,28 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p) | |||
287 | call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); | 295 | call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); |
288 | } | 296 | } |
289 | 297 | ||
298 | struct rcu_bh_torture_synchronize { | ||
299 | struct rcu_head head; | ||
300 | struct completion completion; | ||
301 | }; | ||
302 | |||
303 | static void rcu_bh_torture_wakeme_after_cb(struct rcu_head *head) | ||
304 | { | ||
305 | struct rcu_bh_torture_synchronize *rcu; | ||
306 | |||
307 | rcu = container_of(head, struct rcu_bh_torture_synchronize, head); | ||
308 | complete(&rcu->completion); | ||
309 | } | ||
310 | |||
311 | static void rcu_bh_torture_synchronize(void) | ||
312 | { | ||
313 | struct rcu_bh_torture_synchronize rcu; | ||
314 | |||
315 | init_completion(&rcu.completion); | ||
316 | call_rcu_bh(&rcu.head, rcu_bh_torture_wakeme_after_cb); | ||
317 | wait_for_completion(&rcu.completion); | ||
318 | } | ||
319 | |||
290 | static struct rcu_torture_ops rcu_bh_ops = { | 320 | static struct rcu_torture_ops rcu_bh_ops = { |
291 | .init = NULL, | 321 | .init = NULL, |
292 | .cleanup = NULL, | 322 | .cleanup = NULL, |
@@ -295,6 +325,7 @@ static struct rcu_torture_ops rcu_bh_ops = { | |||
295 | .readunlock = rcu_bh_torture_read_unlock, | 325 | .readunlock = rcu_bh_torture_read_unlock, |
296 | .completed = rcu_bh_torture_completed, | 326 | .completed = rcu_bh_torture_completed, |
297 | .deferredfree = rcu_bh_torture_deferred_free, | 327 | .deferredfree = rcu_bh_torture_deferred_free, |
328 | .sync = rcu_bh_torture_synchronize, | ||
298 | .stats = NULL, | 329 | .stats = NULL, |
299 | .name = "rcu_bh" | 330 | .name = "rcu_bh" |
300 | }; | 331 | }; |
@@ -367,6 +398,11 @@ static void srcu_torture_deferred_free(struct rcu_torture *p) | |||
367 | } | 398 | } |
368 | } | 399 | } |
369 | 400 | ||
401 | static void srcu_torture_synchronize(void) | ||
402 | { | ||
403 | synchronize_srcu(&srcu_ctl); | ||
404 | } | ||
405 | |||
370 | static int srcu_torture_stats(char *page) | 406 | static int srcu_torture_stats(char *page) |
371 | { | 407 | { |
372 | int cnt = 0; | 408 | int cnt = 0; |
@@ -392,6 +428,7 @@ static struct rcu_torture_ops srcu_ops = { | |||
392 | .readunlock = srcu_torture_read_unlock, | 428 | .readunlock = srcu_torture_read_unlock, |
393 | .completed = srcu_torture_completed, | 429 | .completed = srcu_torture_completed, |
394 | .deferredfree = srcu_torture_deferred_free, | 430 | .deferredfree = srcu_torture_deferred_free, |
431 | .sync = srcu_torture_synchronize, | ||
395 | .stats = srcu_torture_stats, | 432 | .stats = srcu_torture_stats, |
396 | .name = "srcu" | 433 | .name = "srcu" |
397 | }; | 434 | }; |
@@ -444,6 +481,30 @@ rcu_torture_writer(void *arg) | |||
444 | } | 481 | } |
445 | 482 | ||
446 | /* | 483 | /* |
484 | * RCU torture fake writer kthread. Repeatedly calls sync, with a random | ||
485 | * delay between calls. | ||
486 | */ | ||
487 | static int | ||
488 | rcu_torture_fakewriter(void *arg) | ||
489 | { | ||
490 | DEFINE_RCU_RANDOM(rand); | ||
491 | |||
492 | VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task started"); | ||
493 | set_user_nice(current, 19); | ||
494 | |||
495 | do { | ||
496 | schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10); | ||
497 | udelay(rcu_random(&rand) & 0x3ff); | ||
498 | cur_ops->sync(); | ||
499 | } while (!kthread_should_stop() && !fullstop); | ||
500 | |||
501 | VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); | ||
502 | while (!kthread_should_stop()) | ||
503 | schedule_timeout_uninterruptible(1); | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | /* | ||
447 | * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, | 508 | * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, |
448 | * incrementing the corresponding element of the pipeline array. The | 509 | * incrementing the corresponding element of the pipeline array. The |
449 | * counter in the element should never be greater than 1, otherwise, the | 510 | * counter in the element should never be greater than 1, otherwise, the |
@@ -621,6 +682,12 @@ static void rcu_torture_shuffle_tasks(void) | |||
621 | set_cpus_allowed(reader_tasks[i], tmp_mask); | 682 | set_cpus_allowed(reader_tasks[i], tmp_mask); |
622 | } | 683 | } |
623 | 684 | ||
685 | if (fakewriter_tasks != NULL) { | ||
686 | for (i = 0; i < nfakewriters; i++) | ||
687 | if (fakewriter_tasks[i]) | ||
688 | set_cpus_allowed(fakewriter_tasks[i], tmp_mask); | ||
689 | } | ||
690 | |||
624 | if (writer_task) | 691 | if (writer_task) |
625 | set_cpus_allowed(writer_task, tmp_mask); | 692 | set_cpus_allowed(writer_task, tmp_mask); |
626 | 693 | ||
@@ -654,11 +721,12 @@ rcu_torture_shuffle(void *arg) | |||
654 | static inline void | 721 | static inline void |
655 | rcu_torture_print_module_parms(char *tag) | 722 | rcu_torture_print_module_parms(char *tag) |
656 | { | 723 | { |
657 | printk(KERN_ALERT "%s" TORTURE_FLAG "--- %s: nreaders=%d " | 724 | printk(KERN_ALERT "%s" TORTURE_FLAG |
725 | "--- %s: nreaders=%d nfakewriters=%d " | ||
658 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " | 726 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " |
659 | "shuffle_interval = %d\n", | 727 | "shuffle_interval = %d\n", |
660 | torture_type, tag, nrealreaders, stat_interval, verbose, | 728 | torture_type, tag, nrealreaders, nfakewriters, |
661 | test_no_idle_hz, shuffle_interval); | 729 | stat_interval, verbose, test_no_idle_hz, shuffle_interval); |
662 | } | 730 | } |
663 | 731 | ||
664 | static void | 732 | static void |
@@ -693,6 +761,19 @@ rcu_torture_cleanup(void) | |||
693 | } | 761 | } |
694 | rcu_torture_current = NULL; | 762 | rcu_torture_current = NULL; |
695 | 763 | ||
764 | if (fakewriter_tasks != NULL) { | ||
765 | for (i = 0; i < nfakewriters; i++) { | ||
766 | if (fakewriter_tasks[i] != NULL) { | ||
767 | VERBOSE_PRINTK_STRING( | ||
768 | "Stopping rcu_torture_fakewriter task"); | ||
769 | kthread_stop(fakewriter_tasks[i]); | ||
770 | } | ||
771 | fakewriter_tasks[i] = NULL; | ||
772 | } | ||
773 | kfree(fakewriter_tasks); | ||
774 | fakewriter_tasks = NULL; | ||
775 | } | ||
776 | |||
696 | if (stats_task != NULL) { | 777 | if (stats_task != NULL) { |
697 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task"); | 778 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task"); |
698 | kthread_stop(stats_task); | 779 | kthread_stop(stats_task); |
@@ -780,6 +861,24 @@ rcu_torture_init(void) | |||
780 | writer_task = NULL; | 861 | writer_task = NULL; |
781 | goto unwind; | 862 | goto unwind; |
782 | } | 863 | } |
864 | fakewriter_tasks = kzalloc(nfakewriters * sizeof(fakewriter_tasks[0]), | ||
865 | GFP_KERNEL); | ||
866 | if (fakewriter_tasks == NULL) { | ||
867 | VERBOSE_PRINTK_ERRSTRING("out of memory"); | ||
868 | firsterr = -ENOMEM; | ||
869 | goto unwind; | ||
870 | } | ||
871 | for (i = 0; i < nfakewriters; i++) { | ||
872 | VERBOSE_PRINTK_STRING("Creating rcu_torture_fakewriter task"); | ||
873 | fakewriter_tasks[i] = kthread_run(rcu_torture_fakewriter, NULL, | ||
874 | "rcu_torture_fakewriter"); | ||
875 | if (IS_ERR(fakewriter_tasks[i])) { | ||
876 | firsterr = PTR_ERR(fakewriter_tasks[i]); | ||
877 | VERBOSE_PRINTK_ERRSTRING("Failed to create fakewriter"); | ||
878 | fakewriter_tasks[i] = NULL; | ||
879 | goto unwind; | ||
880 | } | ||
881 | } | ||
783 | reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]), | 882 | reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]), |
784 | GFP_KERNEL); | 883 | GFP_KERNEL); |
785 | if (reader_tasks == NULL) { | 884 | if (reader_tasks == NULL) { |