diff options
author | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2008-06-25 15:24:52 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-06-26 03:24:33 -0400 |
commit | 0729fbf3bc70870370b4f43d652f05a468dc68b8 (patch) | |
tree | 92557b04b368fac084f59d8397c32c5461ac26fd /kernel | |
parent | 9a13150109fb418c50fa400c012f90d0ce6f67c3 (diff) |
rcu: make rcutorture even more vicious: invoke RCU readers from irq handlers (timers)
This patch allows torturing RCU from irq handlers (timers, in this case).
A new module parameter irqreader enables such additional torturing,
and is enabled by default. Variants of RCU that do not tolerate readers
being called from irq handlers (e.g., SRCU) ignore irqreader.
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: josh@freedesktop.org
Cc: dvhltc@us.ibm.com
Cc: niv@us.ibm.com
Cc: dino@in.ibm.com
Cc: akpm@linux-foundation.org
Cc: torvalds@linux-foundation.org
Cc: vegard.nossum@gmail.com
Cc: adobriyan@gmail.com
Cc: oleg@tv-sign.ru
Cc: bunk@kernel.org
Cc: rjw@sisk.pl
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/rcutorture.c | 74 |
1 files changed, 70 insertions, 4 deletions
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 5e954edf0ed5..90b5b123f7a1 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
@@ -59,6 +59,7 @@ 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 = 3; /* 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) */ | 61 | static int stutter = 5; /* Start/stop testing interval (in sec) */ |
62 | static int irqreader = 1; /* RCU readers from irq (timers). */ | ||
62 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ | 63 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ |
63 | 64 | ||
64 | module_param(nreaders, int, 0444); | 65 | module_param(nreaders, int, 0444); |
@@ -75,6 +76,8 @@ module_param(shuffle_interval, int, 0444); | |||
75 | MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); | 76 | MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); |
76 | module_param(stutter, int, 0444); | 77 | module_param(stutter, int, 0444); |
77 | MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test"); | 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"); | ||
78 | module_param(torture_type, charp, 0444); | 81 | module_param(torture_type, charp, 0444); |
79 | 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)"); |
80 | 83 | ||
@@ -121,6 +124,7 @@ static atomic_t n_rcu_torture_alloc_fail; | |||
121 | static atomic_t n_rcu_torture_free; | 124 | static atomic_t n_rcu_torture_free; |
122 | static atomic_t n_rcu_torture_mberror; | 125 | static atomic_t n_rcu_torture_mberror; |
123 | static atomic_t n_rcu_torture_error; | 126 | static atomic_t n_rcu_torture_error; |
127 | static long n_rcu_torture_timers = 0; | ||
124 | static struct list_head rcu_torture_removed; | 128 | static struct list_head rcu_torture_removed; |
125 | 129 | ||
126 | static int stutter_pause_test = 0; | 130 | static int stutter_pause_test = 0; |
@@ -217,6 +221,7 @@ struct rcu_torture_ops { | |||
217 | void (*sync)(void); | 221 | void (*sync)(void); |
218 | void (*cb_barrier)(void); | 222 | void (*cb_barrier)(void); |
219 | int (*stats)(char *page); | 223 | int (*stats)(char *page); |
224 | int irqcapable; | ||
220 | char *name; | 225 | char *name; |
221 | }; | 226 | }; |
222 | static struct rcu_torture_ops *cur_ops = NULL; | 227 | static struct rcu_torture_ops *cur_ops = NULL; |
@@ -291,6 +296,7 @@ static struct rcu_torture_ops rcu_ops = { | |||
291 | .sync = synchronize_rcu, | 296 | .sync = synchronize_rcu, |
292 | .cb_barrier = rcu_barrier, | 297 | .cb_barrier = rcu_barrier, |
293 | .stats = NULL, | 298 | .stats = NULL, |
299 | .irqcapable = 1, | ||
294 | .name = "rcu" | 300 | .name = "rcu" |
295 | }; | 301 | }; |
296 | 302 | ||
@@ -331,6 +337,7 @@ static struct rcu_torture_ops rcu_sync_ops = { | |||
331 | .sync = synchronize_rcu, | 337 | .sync = synchronize_rcu, |
332 | .cb_barrier = NULL, | 338 | .cb_barrier = NULL, |
333 | .stats = NULL, | 339 | .stats = NULL, |
340 | .irqcapable = 1, | ||
334 | .name = "rcu_sync" | 341 | .name = "rcu_sync" |
335 | }; | 342 | }; |
336 | 343 | ||
@@ -392,6 +399,7 @@ static struct rcu_torture_ops rcu_bh_ops = { | |||
392 | .sync = rcu_bh_torture_synchronize, | 399 | .sync = rcu_bh_torture_synchronize, |
393 | .cb_barrier = rcu_barrier_bh, | 400 | .cb_barrier = rcu_barrier_bh, |
394 | .stats = NULL, | 401 | .stats = NULL, |
402 | .irqcapable = 1, | ||
395 | .name = "rcu_bh" | 403 | .name = "rcu_bh" |
396 | }; | 404 | }; |
397 | 405 | ||
@@ -406,6 +414,7 @@ static struct rcu_torture_ops rcu_bh_sync_ops = { | |||
406 | .sync = rcu_bh_torture_synchronize, | 414 | .sync = rcu_bh_torture_synchronize, |
407 | .cb_barrier = NULL, | 415 | .cb_barrier = NULL, |
408 | .stats = NULL, | 416 | .stats = NULL, |
417 | .irqcapable = 1, | ||
409 | .name = "rcu_bh_sync" | 418 | .name = "rcu_bh_sync" |
410 | }; | 419 | }; |
411 | 420 | ||
@@ -532,6 +541,7 @@ static struct rcu_torture_ops sched_ops = { | |||
532 | .sync = sched_torture_synchronize, | 541 | .sync = sched_torture_synchronize, |
533 | .cb_barrier = rcu_barrier_sched, | 542 | .cb_barrier = rcu_barrier_sched, |
534 | .stats = NULL, | 543 | .stats = NULL, |
544 | .irqcapable = 1, | ||
535 | .name = "sched" | 545 | .name = "sched" |
536 | }; | 546 | }; |
537 | 547 | ||
@@ -620,6 +630,52 @@ rcu_torture_fakewriter(void *arg) | |||
620 | } | 630 | } |
621 | 631 | ||
622 | /* | 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 | /* | ||
623 | * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, | 679 | * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, |
624 | * incrementing the corresponding element of the pipeline array. The | 680 | * incrementing the corresponding element of the pipeline array. The |
625 | * 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 |
@@ -633,11 +689,18 @@ rcu_torture_reader(void *arg) | |||
633 | DEFINE_RCU_RANDOM(rand); | 689 | DEFINE_RCU_RANDOM(rand); |
634 | struct rcu_torture *p; | 690 | struct rcu_torture *p; |
635 | int pipe_count; | 691 | int pipe_count; |
692 | struct timer_list t; | ||
636 | 693 | ||
637 | VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); | 694 | VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); |
638 | 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); | ||
639 | 698 | ||
640 | do { | 699 | do { |
700 | if (irqreader && cur_ops->irqcapable) { | ||
701 | if (!timer_pending(&t)) | ||
702 | mod_timer(&t, 1); | ||
703 | } | ||
641 | idx = cur_ops->readlock(); | 704 | idx = cur_ops->readlock(); |
642 | completed = cur_ops->completed(); | 705 | completed = cur_ops->completed(); |
643 | p = rcu_dereference(rcu_torture_current); | 706 | p = rcu_dereference(rcu_torture_current); |
@@ -669,6 +732,8 @@ rcu_torture_reader(void *arg) | |||
669 | rcu_stutter_wait(); | 732 | rcu_stutter_wait(); |
670 | } while (!kthread_should_stop() && !fullstop); | 733 | } while (!kthread_should_stop() && !fullstop); |
671 | 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); | ||
672 | while (!kthread_should_stop()) | 737 | while (!kthread_should_stop()) |
673 | schedule_timeout_uninterruptible(1); | 738 | schedule_timeout_uninterruptible(1); |
674 | return 0; | 739 | return 0; |
@@ -699,14 +764,15 @@ rcu_torture_printk(char *page) | |||
699 | cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); | 764 | cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); |
700 | cnt += sprintf(&page[cnt], | 765 | cnt += sprintf(&page[cnt], |
701 | "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 " |
702 | "rtmbe: %d", | 767 | "rtmbe: %d nt: %ld", |
703 | rcu_torture_current, | 768 | rcu_torture_current, |
704 | rcu_torture_current_version, | 769 | rcu_torture_current_version, |
705 | list_empty(&rcu_torture_freelist), | 770 | list_empty(&rcu_torture_freelist), |
706 | atomic_read(&n_rcu_torture_alloc), | 771 | atomic_read(&n_rcu_torture_alloc), |
707 | atomic_read(&n_rcu_torture_alloc_fail), | 772 | atomic_read(&n_rcu_torture_alloc_fail), |
708 | atomic_read(&n_rcu_torture_free), | 773 | atomic_read(&n_rcu_torture_free), |
709 | atomic_read(&n_rcu_torture_mberror)); | 774 | atomic_read(&n_rcu_torture_mberror), |
775 | n_rcu_torture_timers); | ||
710 | if (atomic_read(&n_rcu_torture_mberror) != 0) | 776 | if (atomic_read(&n_rcu_torture_mberror) != 0) |
711 | cnt += sprintf(&page[cnt], " !!!"); | 777 | cnt += sprintf(&page[cnt], " !!!"); |
712 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); | 778 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); |
@@ -862,10 +928,10 @@ rcu_torture_print_module_parms(char *tag) | |||
862 | printk(KERN_ALERT "%s" TORTURE_FLAG | 928 | printk(KERN_ALERT "%s" TORTURE_FLAG |
863 | "--- %s: nreaders=%d nfakewriters=%d " | 929 | "--- %s: nreaders=%d nfakewriters=%d " |
864 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " | 930 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " |
865 | "shuffle_interval=%d stutter=%d\n", | 931 | "shuffle_interval=%d stutter=%d irqreader=%d\n", |
866 | torture_type, tag, nrealreaders, nfakewriters, | 932 | torture_type, tag, nrealreaders, nfakewriters, |
867 | stat_interval, verbose, test_no_idle_hz, shuffle_interval, | 933 | stat_interval, verbose, test_no_idle_hz, shuffle_interval, |
868 | stutter); | 934 | stutter, irqreader); |
869 | } | 935 | } |
870 | 936 | ||
871 | static void | 937 | static void |