Commit b772e1dd4b1e60a7a160f7bd4ea08e28394ceb54

Authored by Josh Triplett
Committed by Linus Torvalds
1 parent 75cfef32f2

[PATCH] RCU: add fake writers to rcutorture

rcutorture currently has one writer and an arbitrary number of readers.  To
better exercise some of the code paths in RCU implementations, add fake
writer threads which call the synchronize function for the RCU variant in a
loop, with a delay between calls to arrange for different numbers of
writers running in parallel.

[bunk@stusta.de: cleanup]
Acked-by: Paul McKenney <paulmck@us.ibm.com>
Cc: Dipkanar Sarma <dipankar@in.ibm.com>
Signed-off-by: Josh Triplett <josh@freedesktop.org>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 2 changed files with 113 additions and 5 deletions Side-by-side Diff

Documentation/RCU/torture.txt
... ... @@ -28,6 +28,15 @@
28 28 To properly exercise RCU implementations with preemptible
29 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 40 stat_interval The number of seconds between output of torture
32 41 statistics (via printk()). Regardless of the interval,
33 42 statistics are printed when the module is unloaded.
... ... @@ -15,9 +15,10 @@
15 15 * along with this program; if not, write to the Free Software
16 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 20 * Authors: Paul E. McKenney <paulmck@us.ibm.com>
  21 + * Josh Triplett <josh@freedesktop.org>
21 22 *
22 23 * See also: Documentation/RCU/torture.txt
23 24 */
24 25  
... ... @@ -47,9 +48,11 @@
47 48 #include <linux/srcu.h>
48 49  
49 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 54 static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */
  55 +static int nfakewriters = 4; /* # fake writer threads */
53 56 static int stat_interval; /* Interval between stats, in seconds. */
54 57 /* Defaults to "only at end of test". */
55 58 static int verbose; /* Print more debug info. */
... ... @@ -59,6 +62,8 @@
59 62  
60 63 module_param(nreaders, int, 0);
61 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 67 module_param(stat_interval, int, 0);
63 68 MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s");
64 69 module_param(verbose, bool, 0);
... ... @@ -82,6 +87,7 @@
82 87  
83 88 static int nrealreaders;
84 89 static struct task_struct *writer_task;
  90 +static struct task_struct **fakewriter_tasks;
85 91 static struct task_struct **reader_tasks;
86 92 static struct task_struct *stats_task;
87 93 static struct task_struct *shuffler_task;
... ... @@ -186,6 +192,7 @@
186 192 void (*readunlock)(int idx);
187 193 int (*completed)(void);
188 194 void (*deferredfree)(struct rcu_torture *p);
  195 + void (*sync)(void);
189 196 int (*stats)(char *page);
190 197 char *name;
191 198 };
... ... @@ -258,6 +265,7 @@
258 265 .readunlock = rcu_torture_read_unlock,
259 266 .completed = rcu_torture_completed,
260 267 .deferredfree = rcu_torture_deferred_free,
  268 + .sync = synchronize_rcu,
261 269 .stats = NULL,
262 270 .name = "rcu"
263 271 };
... ... @@ -287,6 +295,28 @@
287 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 320 static struct rcu_torture_ops rcu_bh_ops = {
291 321 .init = NULL,
292 322 .cleanup = NULL,
... ... @@ -295,6 +325,7 @@
295 325 .readunlock = rcu_bh_torture_read_unlock,
296 326 .completed = rcu_bh_torture_completed,
297 327 .deferredfree = rcu_bh_torture_deferred_free,
  328 + .sync = rcu_bh_torture_synchronize,
298 329 .stats = NULL,
299 330 .name = "rcu_bh"
300 331 };
... ... @@ -367,6 +398,11 @@
367 398 }
368 399 }
369 400  
  401 +static void srcu_torture_synchronize(void)
  402 +{
  403 + synchronize_srcu(&srcu_ctl);
  404 +}
  405 +
370 406 static int srcu_torture_stats(char *page)
371 407 {
372 408 int cnt = 0;
... ... @@ -392,6 +428,7 @@
392 428 .readunlock = srcu_torture_read_unlock,
393 429 .completed = srcu_torture_completed,
394 430 .deferredfree = srcu_torture_deferred_free,
  431 + .sync = srcu_torture_synchronize,
395 432 .stats = srcu_torture_stats,
396 433 .name = "srcu"
397 434 };
... ... @@ -444,6 +481,30 @@
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 508 * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current,
448 509 * incrementing the corresponding element of the pipeline array. The
449 510 * counter in the element should never be greater than 1, otherwise, the
... ... @@ -621,6 +682,12 @@
621 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 691 if (writer_task)
625 692 set_cpus_allowed(writer_task, tmp_mask);
626 693  
627 694  
... ... @@ -654,11 +721,12 @@
654 721 static inline void
655 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 726 "stat_interval=%d verbose=%d test_no_idle_hz=%d "
659 727 "shuffle_interval = %d\n",
660   - torture_type, tag, nrealreaders, stat_interval, verbose,
661   - test_no_idle_hz, shuffle_interval);
  728 + torture_type, tag, nrealreaders, nfakewriters,
  729 + stat_interval, verbose, test_no_idle_hz, shuffle_interval);
662 730 }
663 731  
664 732 static void
... ... @@ -693,6 +761,19 @@
693 761 }
694 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 777 if (stats_task != NULL) {
697 778 VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task");
698 779 kthread_stop(stats_task);
... ... @@ -779,6 +860,24 @@
779 860 VERBOSE_PRINTK_ERRSTRING("Failed to create writer");
780 861 writer_task = NULL;
781 862 goto unwind;
  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 + }
782 881 }
783 882 reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]),
784 883 GFP_KERNEL);