diff options
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 |