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) { |
