diff options
Diffstat (limited to 'kernel/rcutorture.c')
-rw-r--r-- | kernel/rcutorture.c | 174 |
1 files changed, 166 insertions, 8 deletions
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 33acc424667e..90b5b123f7a1 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
@@ -57,7 +57,9 @@ static int stat_interval; /* Interval between stats, in seconds. */ | |||
57 | /* Defaults to "only at end of test". */ | 57 | /* Defaults to "only at end of test". */ |
58 | static int verbose; /* Print more debug info. */ | 58 | static int verbose; /* Print more debug info. */ |
59 | static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ | 59 | static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ |
60 | static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ | 60 | static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/ |
61 | static int stutter = 5; /* Start/stop testing interval (in sec) */ | ||
62 | static int irqreader = 1; /* RCU readers from irq (timers). */ | ||
61 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ | 63 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ |
62 | 64 | ||
63 | module_param(nreaders, int, 0444); | 65 | module_param(nreaders, int, 0444); |
@@ -72,6 +74,10 @@ module_param(test_no_idle_hz, bool, 0444); | |||
72 | MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); | 74 | MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); |
73 | module_param(shuffle_interval, int, 0444); | 75 | module_param(shuffle_interval, int, 0444); |
74 | MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); | 76 | MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); |
77 | module_param(stutter, int, 0444); | ||
78 | MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test"); | ||
79 | module_param(irqreader, int, 0444); | ||
80 | MODULE_PARM_DESC(irqreader, "Allow RCU readers from irq handlers"); | ||
75 | module_param(torture_type, charp, 0444); | 81 | module_param(torture_type, charp, 0444); |
76 | MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); | 82 | MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); |
77 | 83 | ||
@@ -91,6 +97,7 @@ static struct task_struct **fakewriter_tasks; | |||
91 | static struct task_struct **reader_tasks; | 97 | static struct task_struct **reader_tasks; |
92 | static struct task_struct *stats_task; | 98 | static struct task_struct *stats_task; |
93 | static struct task_struct *shuffler_task; | 99 | static struct task_struct *shuffler_task; |
100 | static struct task_struct *stutter_task; | ||
94 | 101 | ||
95 | #define RCU_TORTURE_PIPE_LEN 10 | 102 | #define RCU_TORTURE_PIPE_LEN 10 |
96 | 103 | ||
@@ -117,8 +124,18 @@ static atomic_t n_rcu_torture_alloc_fail; | |||
117 | static atomic_t n_rcu_torture_free; | 124 | static atomic_t n_rcu_torture_free; |
118 | static atomic_t n_rcu_torture_mberror; | 125 | static atomic_t n_rcu_torture_mberror; |
119 | static atomic_t n_rcu_torture_error; | 126 | static atomic_t n_rcu_torture_error; |
127 | static long n_rcu_torture_timers = 0; | ||
120 | static struct list_head rcu_torture_removed; | 128 | static struct list_head rcu_torture_removed; |
121 | 129 | ||
130 | static int stutter_pause_test = 0; | ||
131 | |||
132 | #if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) | ||
133 | #define RCUTORTURE_RUNNABLE_INIT 1 | ||
134 | #else | ||
135 | #define RCUTORTURE_RUNNABLE_INIT 0 | ||
136 | #endif | ||
137 | int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; | ||
138 | |||
122 | /* | 139 | /* |
123 | * Allocate an element from the rcu_tortures pool. | 140 | * Allocate an element from the rcu_tortures pool. |
124 | */ | 141 | */ |
@@ -179,6 +196,16 @@ rcu_random(struct rcu_random_state *rrsp) | |||
179 | return swahw32(rrsp->rrs_state); | 196 | return swahw32(rrsp->rrs_state); |
180 | } | 197 | } |
181 | 198 | ||
199 | static void | ||
200 | rcu_stutter_wait(void) | ||
201 | { | ||
202 | while (stutter_pause_test || !rcutorture_runnable) | ||
203 | if (rcutorture_runnable) | ||
204 | schedule_timeout_interruptible(1); | ||
205 | else | ||
206 | schedule_timeout_interruptible(round_jiffies_relative(HZ)); | ||
207 | } | ||
208 | |||
182 | /* | 209 | /* |
183 | * Operations vector for selecting different types of tests. | 210 | * Operations vector for selecting different types of tests. |
184 | */ | 211 | */ |
@@ -192,7 +219,9 @@ struct rcu_torture_ops { | |||
192 | int (*completed)(void); | 219 | int (*completed)(void); |
193 | void (*deferredfree)(struct rcu_torture *p); | 220 | void (*deferredfree)(struct rcu_torture *p); |
194 | void (*sync)(void); | 221 | void (*sync)(void); |
222 | void (*cb_barrier)(void); | ||
195 | int (*stats)(char *page); | 223 | int (*stats)(char *page); |
224 | int irqcapable; | ||
196 | char *name; | 225 | char *name; |
197 | }; | 226 | }; |
198 | static struct rcu_torture_ops *cur_ops = NULL; | 227 | static struct rcu_torture_ops *cur_ops = NULL; |
@@ -265,7 +294,9 @@ static struct rcu_torture_ops rcu_ops = { | |||
265 | .completed = rcu_torture_completed, | 294 | .completed = rcu_torture_completed, |
266 | .deferredfree = rcu_torture_deferred_free, | 295 | .deferredfree = rcu_torture_deferred_free, |
267 | .sync = synchronize_rcu, | 296 | .sync = synchronize_rcu, |
297 | .cb_barrier = rcu_barrier, | ||
268 | .stats = NULL, | 298 | .stats = NULL, |
299 | .irqcapable = 1, | ||
269 | .name = "rcu" | 300 | .name = "rcu" |
270 | }; | 301 | }; |
271 | 302 | ||
@@ -304,7 +335,9 @@ static struct rcu_torture_ops rcu_sync_ops = { | |||
304 | .completed = rcu_torture_completed, | 335 | .completed = rcu_torture_completed, |
305 | .deferredfree = rcu_sync_torture_deferred_free, | 336 | .deferredfree = rcu_sync_torture_deferred_free, |
306 | .sync = synchronize_rcu, | 337 | .sync = synchronize_rcu, |
338 | .cb_barrier = NULL, | ||
307 | .stats = NULL, | 339 | .stats = NULL, |
340 | .irqcapable = 1, | ||
308 | .name = "rcu_sync" | 341 | .name = "rcu_sync" |
309 | }; | 342 | }; |
310 | 343 | ||
@@ -364,7 +397,9 @@ static struct rcu_torture_ops rcu_bh_ops = { | |||
364 | .completed = rcu_bh_torture_completed, | 397 | .completed = rcu_bh_torture_completed, |
365 | .deferredfree = rcu_bh_torture_deferred_free, | 398 | .deferredfree = rcu_bh_torture_deferred_free, |
366 | .sync = rcu_bh_torture_synchronize, | 399 | .sync = rcu_bh_torture_synchronize, |
400 | .cb_barrier = rcu_barrier_bh, | ||
367 | .stats = NULL, | 401 | .stats = NULL, |
402 | .irqcapable = 1, | ||
368 | .name = "rcu_bh" | 403 | .name = "rcu_bh" |
369 | }; | 404 | }; |
370 | 405 | ||
@@ -377,7 +412,9 @@ static struct rcu_torture_ops rcu_bh_sync_ops = { | |||
377 | .completed = rcu_bh_torture_completed, | 412 | .completed = rcu_bh_torture_completed, |
378 | .deferredfree = rcu_sync_torture_deferred_free, | 413 | .deferredfree = rcu_sync_torture_deferred_free, |
379 | .sync = rcu_bh_torture_synchronize, | 414 | .sync = rcu_bh_torture_synchronize, |
415 | .cb_barrier = NULL, | ||
380 | .stats = NULL, | 416 | .stats = NULL, |
417 | .irqcapable = 1, | ||
381 | .name = "rcu_bh_sync" | 418 | .name = "rcu_bh_sync" |
382 | }; | 419 | }; |
383 | 420 | ||
@@ -458,6 +495,7 @@ static struct rcu_torture_ops srcu_ops = { | |||
458 | .completed = srcu_torture_completed, | 495 | .completed = srcu_torture_completed, |
459 | .deferredfree = rcu_sync_torture_deferred_free, | 496 | .deferredfree = rcu_sync_torture_deferred_free, |
460 | .sync = srcu_torture_synchronize, | 497 | .sync = srcu_torture_synchronize, |
498 | .cb_barrier = NULL, | ||
461 | .stats = srcu_torture_stats, | 499 | .stats = srcu_torture_stats, |
462 | .name = "srcu" | 500 | .name = "srcu" |
463 | }; | 501 | }; |
@@ -482,6 +520,11 @@ static int sched_torture_completed(void) | |||
482 | return 0; | 520 | return 0; |
483 | } | 521 | } |
484 | 522 | ||
523 | static void rcu_sched_torture_deferred_free(struct rcu_torture *p) | ||
524 | { | ||
525 | call_rcu_sched(&p->rtort_rcu, rcu_torture_cb); | ||
526 | } | ||
527 | |||
485 | static void sched_torture_synchronize(void) | 528 | static void sched_torture_synchronize(void) |
486 | { | 529 | { |
487 | synchronize_sched(); | 530 | synchronize_sched(); |
@@ -494,12 +537,28 @@ static struct rcu_torture_ops sched_ops = { | |||
494 | .readdelay = rcu_read_delay, /* just reuse rcu's version. */ | 537 | .readdelay = rcu_read_delay, /* just reuse rcu's version. */ |
495 | .readunlock = sched_torture_read_unlock, | 538 | .readunlock = sched_torture_read_unlock, |
496 | .completed = sched_torture_completed, | 539 | .completed = sched_torture_completed, |
497 | .deferredfree = rcu_sync_torture_deferred_free, | 540 | .deferredfree = rcu_sched_torture_deferred_free, |
498 | .sync = sched_torture_synchronize, | 541 | .sync = sched_torture_synchronize, |
542 | .cb_barrier = rcu_barrier_sched, | ||
499 | .stats = NULL, | 543 | .stats = NULL, |
544 | .irqcapable = 1, | ||
500 | .name = "sched" | 545 | .name = "sched" |
501 | }; | 546 | }; |
502 | 547 | ||
548 | static struct rcu_torture_ops sched_ops_sync = { | ||
549 | .init = rcu_sync_torture_init, | ||
550 | .cleanup = NULL, | ||
551 | .readlock = sched_torture_read_lock, | ||
552 | .readdelay = rcu_read_delay, /* just reuse rcu's version. */ | ||
553 | .readunlock = sched_torture_read_unlock, | ||
554 | .completed = sched_torture_completed, | ||
555 | .deferredfree = rcu_sync_torture_deferred_free, | ||
556 | .sync = sched_torture_synchronize, | ||
557 | .cb_barrier = NULL, | ||
558 | .stats = NULL, | ||
559 | .name = "sched_sync" | ||
560 | }; | ||
561 | |||
503 | /* | 562 | /* |
504 | * RCU torture writer kthread. Repeatedly substitutes a new structure | 563 | * RCU torture writer kthread. Repeatedly substitutes a new structure |
505 | * for that pointed to by rcu_torture_current, freeing the old structure | 564 | * for that pointed to by rcu_torture_current, freeing the old structure |
@@ -537,6 +596,7 @@ rcu_torture_writer(void *arg) | |||
537 | } | 596 | } |
538 | rcu_torture_current_version++; | 597 | rcu_torture_current_version++; |
539 | oldbatch = cur_ops->completed(); | 598 | oldbatch = cur_ops->completed(); |
599 | rcu_stutter_wait(); | ||
540 | } while (!kthread_should_stop() && !fullstop); | 600 | } while (!kthread_should_stop() && !fullstop); |
541 | VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); | 601 | VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); |
542 | while (!kthread_should_stop()) | 602 | while (!kthread_should_stop()) |
@@ -560,6 +620,7 @@ rcu_torture_fakewriter(void *arg) | |||
560 | schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10); | 620 | schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10); |
561 | udelay(rcu_random(&rand) & 0x3ff); | 621 | udelay(rcu_random(&rand) & 0x3ff); |
562 | cur_ops->sync(); | 622 | cur_ops->sync(); |
623 | rcu_stutter_wait(); | ||
563 | } while (!kthread_should_stop() && !fullstop); | 624 | } while (!kthread_should_stop() && !fullstop); |
564 | 625 | ||
565 | VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); | 626 | VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); |
@@ -569,6 +630,52 @@ rcu_torture_fakewriter(void *arg) | |||
569 | } | 630 | } |
570 | 631 | ||
571 | /* | 632 | /* |
633 | * RCU torture reader from timer handler. Dereferences rcu_torture_current, | ||
634 | * incrementing the corresponding element of the pipeline array. The | ||
635 | * counter in the element should never be greater than 1, otherwise, the | ||
636 | * RCU implementation is broken. | ||
637 | */ | ||
638 | static void rcu_torture_timer(unsigned long unused) | ||
639 | { | ||
640 | int idx; | ||
641 | int completed; | ||
642 | static DEFINE_RCU_RANDOM(rand); | ||
643 | static DEFINE_SPINLOCK(rand_lock); | ||
644 | struct rcu_torture *p; | ||
645 | int pipe_count; | ||
646 | |||
647 | idx = cur_ops->readlock(); | ||
648 | completed = cur_ops->completed(); | ||
649 | p = rcu_dereference(rcu_torture_current); | ||
650 | if (p == NULL) { | ||
651 | /* Leave because rcu_torture_writer is not yet underway */ | ||
652 | cur_ops->readunlock(idx); | ||
653 | return; | ||
654 | } | ||
655 | if (p->rtort_mbtest == 0) | ||
656 | atomic_inc(&n_rcu_torture_mberror); | ||
657 | spin_lock(&rand_lock); | ||
658 | cur_ops->readdelay(&rand); | ||
659 | n_rcu_torture_timers++; | ||
660 | spin_unlock(&rand_lock); | ||
661 | preempt_disable(); | ||
662 | pipe_count = p->rtort_pipe_count; | ||
663 | if (pipe_count > RCU_TORTURE_PIPE_LEN) { | ||
664 | /* Should not happen, but... */ | ||
665 | pipe_count = RCU_TORTURE_PIPE_LEN; | ||
666 | } | ||
667 | ++__get_cpu_var(rcu_torture_count)[pipe_count]; | ||
668 | completed = cur_ops->completed() - completed; | ||
669 | if (completed > RCU_TORTURE_PIPE_LEN) { | ||
670 | /* Should not happen, but... */ | ||
671 | completed = RCU_TORTURE_PIPE_LEN; | ||
672 | } | ||
673 | ++__get_cpu_var(rcu_torture_batch)[completed]; | ||
674 | preempt_enable(); | ||
675 | cur_ops->readunlock(idx); | ||
676 | } | ||
677 | |||
678 | /* | ||
572 | * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, | 679 | * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, |
573 | * incrementing the corresponding element of the pipeline array. The | 680 | * incrementing the corresponding element of the pipeline array. The |
574 | * counter in the element should never be greater than 1, otherwise, the | 681 | * counter in the element should never be greater than 1, otherwise, the |
@@ -582,11 +689,18 @@ rcu_torture_reader(void *arg) | |||
582 | DEFINE_RCU_RANDOM(rand); | 689 | DEFINE_RCU_RANDOM(rand); |
583 | struct rcu_torture *p; | 690 | struct rcu_torture *p; |
584 | int pipe_count; | 691 | int pipe_count; |
692 | struct timer_list t; | ||
585 | 693 | ||
586 | VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); | 694 | VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); |
587 | set_user_nice(current, 19); | 695 | set_user_nice(current, 19); |
696 | if (irqreader && cur_ops->irqcapable) | ||
697 | setup_timer_on_stack(&t, rcu_torture_timer, 0); | ||
588 | 698 | ||
589 | do { | 699 | do { |
700 | if (irqreader && cur_ops->irqcapable) { | ||
701 | if (!timer_pending(&t)) | ||
702 | mod_timer(&t, 1); | ||
703 | } | ||
590 | idx = cur_ops->readlock(); | 704 | idx = cur_ops->readlock(); |
591 | completed = cur_ops->completed(); | 705 | completed = cur_ops->completed(); |
592 | p = rcu_dereference(rcu_torture_current); | 706 | p = rcu_dereference(rcu_torture_current); |
@@ -615,8 +729,11 @@ rcu_torture_reader(void *arg) | |||
615 | preempt_enable(); | 729 | preempt_enable(); |
616 | cur_ops->readunlock(idx); | 730 | cur_ops->readunlock(idx); |
617 | schedule(); | 731 | schedule(); |
732 | rcu_stutter_wait(); | ||
618 | } while (!kthread_should_stop() && !fullstop); | 733 | } while (!kthread_should_stop() && !fullstop); |
619 | VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); | 734 | VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); |
735 | if (irqreader && cur_ops->irqcapable) | ||
736 | del_timer_sync(&t); | ||
620 | while (!kthread_should_stop()) | 737 | while (!kthread_should_stop()) |
621 | schedule_timeout_uninterruptible(1); | 738 | schedule_timeout_uninterruptible(1); |
622 | return 0; | 739 | return 0; |
@@ -647,20 +764,22 @@ rcu_torture_printk(char *page) | |||
647 | cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); | 764 | cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); |
648 | cnt += sprintf(&page[cnt], | 765 | cnt += sprintf(&page[cnt], |
649 | "rtc: %p ver: %ld tfle: %d rta: %d rtaf: %d rtf: %d " | 766 | "rtc: %p ver: %ld tfle: %d rta: %d rtaf: %d rtf: %d " |
650 | "rtmbe: %d", | 767 | "rtmbe: %d nt: %ld", |
651 | rcu_torture_current, | 768 | rcu_torture_current, |
652 | rcu_torture_current_version, | 769 | rcu_torture_current_version, |
653 | list_empty(&rcu_torture_freelist), | 770 | list_empty(&rcu_torture_freelist), |
654 | atomic_read(&n_rcu_torture_alloc), | 771 | atomic_read(&n_rcu_torture_alloc), |
655 | atomic_read(&n_rcu_torture_alloc_fail), | 772 | atomic_read(&n_rcu_torture_alloc_fail), |
656 | atomic_read(&n_rcu_torture_free), | 773 | atomic_read(&n_rcu_torture_free), |
657 | atomic_read(&n_rcu_torture_mberror)); | 774 | atomic_read(&n_rcu_torture_mberror), |
775 | n_rcu_torture_timers); | ||
658 | if (atomic_read(&n_rcu_torture_mberror) != 0) | 776 | if (atomic_read(&n_rcu_torture_mberror) != 0) |
659 | cnt += sprintf(&page[cnt], " !!!"); | 777 | cnt += sprintf(&page[cnt], " !!!"); |
660 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); | 778 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); |
661 | if (i > 1) { | 779 | if (i > 1) { |
662 | cnt += sprintf(&page[cnt], "!!! "); | 780 | cnt += sprintf(&page[cnt], "!!! "); |
663 | atomic_inc(&n_rcu_torture_error); | 781 | atomic_inc(&n_rcu_torture_error); |
782 | WARN_ON_ONCE(1); | ||
664 | } | 783 | } |
665 | cnt += sprintf(&page[cnt], "Reader Pipe: "); | 784 | cnt += sprintf(&page[cnt], "Reader Pipe: "); |
666 | for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) | 785 | for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) |
@@ -785,15 +904,34 @@ rcu_torture_shuffle(void *arg) | |||
785 | return 0; | 904 | return 0; |
786 | } | 905 | } |
787 | 906 | ||
907 | /* Cause the rcutorture test to "stutter", starting and stopping all | ||
908 | * threads periodically. | ||
909 | */ | ||
910 | static int | ||
911 | rcu_torture_stutter(void *arg) | ||
912 | { | ||
913 | VERBOSE_PRINTK_STRING("rcu_torture_stutter task started"); | ||
914 | do { | ||
915 | schedule_timeout_interruptible(stutter * HZ); | ||
916 | stutter_pause_test = 1; | ||
917 | if (!kthread_should_stop()) | ||
918 | schedule_timeout_interruptible(stutter * HZ); | ||
919 | stutter_pause_test = 0; | ||
920 | } while (!kthread_should_stop()); | ||
921 | VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping"); | ||
922 | return 0; | ||
923 | } | ||
924 | |||
788 | static inline void | 925 | static inline void |
789 | rcu_torture_print_module_parms(char *tag) | 926 | rcu_torture_print_module_parms(char *tag) |
790 | { | 927 | { |
791 | printk(KERN_ALERT "%s" TORTURE_FLAG | 928 | printk(KERN_ALERT "%s" TORTURE_FLAG |
792 | "--- %s: nreaders=%d nfakewriters=%d " | 929 | "--- %s: nreaders=%d nfakewriters=%d " |
793 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " | 930 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " |
794 | "shuffle_interval = %d\n", | 931 | "shuffle_interval=%d stutter=%d irqreader=%d\n", |
795 | torture_type, tag, nrealreaders, nfakewriters, | 932 | torture_type, tag, nrealreaders, nfakewriters, |
796 | stat_interval, verbose, test_no_idle_hz, shuffle_interval); | 933 | stat_interval, verbose, test_no_idle_hz, shuffle_interval, |
934 | stutter, irqreader); | ||
797 | } | 935 | } |
798 | 936 | ||
799 | static void | 937 | static void |
@@ -802,6 +940,11 @@ rcu_torture_cleanup(void) | |||
802 | int i; | 940 | int i; |
803 | 941 | ||
804 | fullstop = 1; | 942 | fullstop = 1; |
943 | if (stutter_task) { | ||
944 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); | ||
945 | kthread_stop(stutter_task); | ||
946 | } | ||
947 | stutter_task = NULL; | ||
805 | if (shuffler_task) { | 948 | if (shuffler_task) { |
806 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_shuffle task"); | 949 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_shuffle task"); |
807 | kthread_stop(shuffler_task); | 950 | kthread_stop(shuffler_task); |
@@ -848,7 +991,9 @@ rcu_torture_cleanup(void) | |||
848 | stats_task = NULL; | 991 | stats_task = NULL; |
849 | 992 | ||
850 | /* Wait for all RCU callbacks to fire. */ | 993 | /* Wait for all RCU callbacks to fire. */ |
851 | rcu_barrier(); | 994 | |
995 | if (cur_ops->cb_barrier != NULL) | ||
996 | cur_ops->cb_barrier(); | ||
852 | 997 | ||
853 | rcu_torture_stats_print(); /* -After- the stats thread is stopped! */ | 998 | rcu_torture_stats_print(); /* -After- the stats thread is stopped! */ |
854 | 999 | ||
@@ -868,7 +1013,7 @@ rcu_torture_init(void) | |||
868 | int firsterr = 0; | 1013 | int firsterr = 0; |
869 | static struct rcu_torture_ops *torture_ops[] = | 1014 | static struct rcu_torture_ops *torture_ops[] = |
870 | { &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops, | 1015 | { &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops, |
871 | &srcu_ops, &sched_ops, }; | 1016 | &srcu_ops, &sched_ops, &sched_ops_sync, }; |
872 | 1017 | ||
873 | /* Process args and tell the world that the torturer is on the job. */ | 1018 | /* Process args and tell the world that the torturer is on the job. */ |
874 | for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { | 1019 | for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { |
@@ -988,6 +1133,19 @@ rcu_torture_init(void) | |||
988 | goto unwind; | 1133 | goto unwind; |
989 | } | 1134 | } |
990 | } | 1135 | } |
1136 | if (stutter < 0) | ||
1137 | stutter = 0; | ||
1138 | if (stutter) { | ||
1139 | /* Create the stutter thread */ | ||
1140 | stutter_task = kthread_run(rcu_torture_stutter, NULL, | ||
1141 | "rcu_torture_stutter"); | ||
1142 | if (IS_ERR(stutter_task)) { | ||
1143 | firsterr = PTR_ERR(stutter_task); | ||
1144 | VERBOSE_PRINTK_ERRSTRING("Failed to create stutter"); | ||
1145 | stutter_task = NULL; | ||
1146 | goto unwind; | ||
1147 | } | ||
1148 | } | ||
991 | return 0; | 1149 | return 0; |
992 | 1150 | ||
993 | unwind: | 1151 | unwind: |