diff options
Diffstat (limited to 'kernel/rcutorture.c')
-rw-r--r-- | kernel/rcutorture.c | 288 |
1 files changed, 270 insertions, 18 deletions
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 2e2726d790b9..2e138db03382 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
@@ -64,6 +64,9 @@ static int irqreader = 1; /* RCU readers from irq (timers). */ | |||
64 | static int fqs_duration = 0; /* Duration of bursts (us), 0 to disable. */ | 64 | static int fqs_duration = 0; /* Duration of bursts (us), 0 to disable. */ |
65 | static int fqs_holdoff = 0; /* Hold time within burst (us). */ | 65 | static int fqs_holdoff = 0; /* Hold time within burst (us). */ |
66 | static int fqs_stutter = 3; /* Wait time between bursts (s). */ | 66 | static int fqs_stutter = 3; /* Wait time between bursts (s). */ |
67 | static int test_boost = 1; /* Test RCU prio boost: 0=no, 1=maybe, 2=yes. */ | ||
68 | static int test_boost_interval = 7; /* Interval between boost tests, seconds. */ | ||
69 | static int test_boost_duration = 4; /* Duration of each boost test, seconds. */ | ||
67 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ | 70 | static char *torture_type = "rcu"; /* What RCU implementation to torture. */ |
68 | 71 | ||
69 | module_param(nreaders, int, 0444); | 72 | module_param(nreaders, int, 0444); |
@@ -88,6 +91,12 @@ module_param(fqs_holdoff, int, 0444); | |||
88 | MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)"); | 91 | MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)"); |
89 | module_param(fqs_stutter, int, 0444); | 92 | module_param(fqs_stutter, int, 0444); |
90 | MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)"); | 93 | MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)"); |
94 | module_param(test_boost, int, 0444); | ||
95 | MODULE_PARM_DESC(test_boost, "Test RCU prio boost: 0=no, 1=maybe, 2=yes."); | ||
96 | module_param(test_boost_interval, int, 0444); | ||
97 | MODULE_PARM_DESC(test_boost_interval, "Interval between boost tests, seconds."); | ||
98 | module_param(test_boost_duration, int, 0444); | ||
99 | MODULE_PARM_DESC(test_boost_duration, "Duration of each boost test, seconds."); | ||
91 | module_param(torture_type, charp, 0444); | 100 | module_param(torture_type, charp, 0444); |
92 | MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); | 101 | MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); |
93 | 102 | ||
@@ -109,6 +118,7 @@ static struct task_struct *stats_task; | |||
109 | static struct task_struct *shuffler_task; | 118 | static struct task_struct *shuffler_task; |
110 | static struct task_struct *stutter_task; | 119 | static struct task_struct *stutter_task; |
111 | static struct task_struct *fqs_task; | 120 | static struct task_struct *fqs_task; |
121 | static struct task_struct *boost_tasks[NR_CPUS]; | ||
112 | 122 | ||
113 | #define RCU_TORTURE_PIPE_LEN 10 | 123 | #define RCU_TORTURE_PIPE_LEN 10 |
114 | 124 | ||
@@ -120,8 +130,8 @@ struct rcu_torture { | |||
120 | }; | 130 | }; |
121 | 131 | ||
122 | static LIST_HEAD(rcu_torture_freelist); | 132 | static LIST_HEAD(rcu_torture_freelist); |
123 | static struct rcu_torture *rcu_torture_current; | 133 | static struct rcu_torture __rcu *rcu_torture_current; |
124 | static long rcu_torture_current_version; | 134 | static unsigned long rcu_torture_current_version; |
125 | static struct rcu_torture rcu_tortures[10 * RCU_TORTURE_PIPE_LEN]; | 135 | static struct rcu_torture rcu_tortures[10 * RCU_TORTURE_PIPE_LEN]; |
126 | static DEFINE_SPINLOCK(rcu_torture_lock); | 136 | static DEFINE_SPINLOCK(rcu_torture_lock); |
127 | static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) = | 137 | static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) = |
@@ -134,6 +144,10 @@ static atomic_t n_rcu_torture_alloc_fail; | |||
134 | static atomic_t n_rcu_torture_free; | 144 | static atomic_t n_rcu_torture_free; |
135 | static atomic_t n_rcu_torture_mberror; | 145 | static atomic_t n_rcu_torture_mberror; |
136 | static atomic_t n_rcu_torture_error; | 146 | static atomic_t n_rcu_torture_error; |
147 | static long n_rcu_torture_boost_ktrerror; | ||
148 | static long n_rcu_torture_boost_rterror; | ||
149 | static long n_rcu_torture_boost_failure; | ||
150 | static long n_rcu_torture_boosts; | ||
137 | static long n_rcu_torture_timers; | 151 | static long n_rcu_torture_timers; |
138 | static struct list_head rcu_torture_removed; | 152 | static struct list_head rcu_torture_removed; |
139 | static cpumask_var_t shuffle_tmp_mask; | 153 | static cpumask_var_t shuffle_tmp_mask; |
@@ -147,14 +161,26 @@ static int stutter_pause_test; | |||
147 | #endif | 161 | #endif |
148 | int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; | 162 | int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; |
149 | 163 | ||
164 | #if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) | ||
165 | #define rcu_can_boost() 1 | ||
166 | #else /* #if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) */ | ||
167 | #define rcu_can_boost() 0 | ||
168 | #endif /* #else #if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU) */ | ||
169 | |||
170 | static unsigned long boost_starttime; /* jiffies of next boost test start. */ | ||
171 | DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */ | ||
172 | /* and boost task create/destroy. */ | ||
173 | |||
150 | /* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */ | 174 | /* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */ |
151 | 175 | ||
152 | #define FULLSTOP_DONTSTOP 0 /* Normal operation. */ | 176 | #define FULLSTOP_DONTSTOP 0 /* Normal operation. */ |
153 | #define FULLSTOP_SHUTDOWN 1 /* System shutdown with rcutorture running. */ | 177 | #define FULLSTOP_SHUTDOWN 1 /* System shutdown with rcutorture running. */ |
154 | #define FULLSTOP_RMMOD 2 /* Normal rmmod of rcutorture. */ | 178 | #define FULLSTOP_RMMOD 2 /* Normal rmmod of rcutorture. */ |
155 | static int fullstop = FULLSTOP_RMMOD; | 179 | static int fullstop = FULLSTOP_RMMOD; |
156 | DEFINE_MUTEX(fullstop_mutex); /* Protect fullstop transitions and spawning */ | 180 | /* |
157 | /* of kthreads. */ | 181 | * Protect fullstop transitions and spawning of kthreads. |
182 | */ | ||
183 | static DEFINE_MUTEX(fullstop_mutex); | ||
158 | 184 | ||
159 | /* | 185 | /* |
160 | * Detect and respond to a system shutdown. | 186 | * Detect and respond to a system shutdown. |
@@ -275,6 +301,7 @@ struct rcu_torture_ops { | |||
275 | void (*fqs)(void); | 301 | void (*fqs)(void); |
276 | int (*stats)(char *page); | 302 | int (*stats)(char *page); |
277 | int irq_capable; | 303 | int irq_capable; |
304 | int can_boost; | ||
278 | char *name; | 305 | char *name; |
279 | }; | 306 | }; |
280 | 307 | ||
@@ -303,6 +330,10 @@ static void rcu_read_delay(struct rcu_random_state *rrsp) | |||
303 | mdelay(longdelay_ms); | 330 | mdelay(longdelay_ms); |
304 | if (!(rcu_random(rrsp) % (nrealreaders * 2 * shortdelay_us))) | 331 | if (!(rcu_random(rrsp) % (nrealreaders * 2 * shortdelay_us))) |
305 | udelay(shortdelay_us); | 332 | udelay(shortdelay_us); |
333 | #ifdef CONFIG_PREEMPT | ||
334 | if (!preempt_count() && !(rcu_random(rrsp) % (nrealreaders * 20000))) | ||
335 | preempt_schedule(); /* No QS if preempt_disable() in effect */ | ||
336 | #endif | ||
306 | } | 337 | } |
307 | 338 | ||
308 | static void rcu_torture_read_unlock(int idx) __releases(RCU) | 339 | static void rcu_torture_read_unlock(int idx) __releases(RCU) |
@@ -360,6 +391,7 @@ static struct rcu_torture_ops rcu_ops = { | |||
360 | .fqs = rcu_force_quiescent_state, | 391 | .fqs = rcu_force_quiescent_state, |
361 | .stats = NULL, | 392 | .stats = NULL, |
362 | .irq_capable = 1, | 393 | .irq_capable = 1, |
394 | .can_boost = rcu_can_boost(), | ||
363 | .name = "rcu" | 395 | .name = "rcu" |
364 | }; | 396 | }; |
365 | 397 | ||
@@ -402,6 +434,7 @@ static struct rcu_torture_ops rcu_sync_ops = { | |||
402 | .fqs = rcu_force_quiescent_state, | 434 | .fqs = rcu_force_quiescent_state, |
403 | .stats = NULL, | 435 | .stats = NULL, |
404 | .irq_capable = 1, | 436 | .irq_capable = 1, |
437 | .can_boost = rcu_can_boost(), | ||
405 | .name = "rcu_sync" | 438 | .name = "rcu_sync" |
406 | }; | 439 | }; |
407 | 440 | ||
@@ -418,6 +451,7 @@ static struct rcu_torture_ops rcu_expedited_ops = { | |||
418 | .fqs = rcu_force_quiescent_state, | 451 | .fqs = rcu_force_quiescent_state, |
419 | .stats = NULL, | 452 | .stats = NULL, |
420 | .irq_capable = 1, | 453 | .irq_capable = 1, |
454 | .can_boost = rcu_can_boost(), | ||
421 | .name = "rcu_expedited" | 455 | .name = "rcu_expedited" |
422 | }; | 456 | }; |
423 | 457 | ||
@@ -536,6 +570,8 @@ static void srcu_read_delay(struct rcu_random_state *rrsp) | |||
536 | delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick); | 570 | delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick); |
537 | if (!delay) | 571 | if (!delay) |
538 | schedule_timeout_interruptible(longdelay); | 572 | schedule_timeout_interruptible(longdelay); |
573 | else | ||
574 | rcu_read_delay(rrsp); | ||
539 | } | 575 | } |
540 | 576 | ||
541 | static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl) | 577 | static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl) |
@@ -676,6 +712,112 @@ static struct rcu_torture_ops sched_expedited_ops = { | |||
676 | }; | 712 | }; |
677 | 713 | ||
678 | /* | 714 | /* |
715 | * RCU torture priority-boost testing. Runs one real-time thread per | ||
716 | * CPU for moderate bursts, repeatedly registering RCU callbacks and | ||
717 | * spinning waiting for them to be invoked. If a given callback takes | ||
718 | * too long to be invoked, we assume that priority inversion has occurred. | ||
719 | */ | ||
720 | |||
721 | struct rcu_boost_inflight { | ||
722 | struct rcu_head rcu; | ||
723 | int inflight; | ||
724 | }; | ||
725 | |||
726 | static void rcu_torture_boost_cb(struct rcu_head *head) | ||
727 | { | ||
728 | struct rcu_boost_inflight *rbip = | ||
729 | container_of(head, struct rcu_boost_inflight, rcu); | ||
730 | |||
731 | smp_mb(); /* Ensure RCU-core accesses precede clearing ->inflight */ | ||
732 | rbip->inflight = 0; | ||
733 | } | ||
734 | |||
735 | static int rcu_torture_boost(void *arg) | ||
736 | { | ||
737 | unsigned long call_rcu_time; | ||
738 | unsigned long endtime; | ||
739 | unsigned long oldstarttime; | ||
740 | struct rcu_boost_inflight rbi = { .inflight = 0 }; | ||
741 | struct sched_param sp; | ||
742 | |||
743 | VERBOSE_PRINTK_STRING("rcu_torture_boost started"); | ||
744 | |||
745 | /* Set real-time priority. */ | ||
746 | sp.sched_priority = 1; | ||
747 | if (sched_setscheduler(current, SCHED_FIFO, &sp) < 0) { | ||
748 | VERBOSE_PRINTK_STRING("rcu_torture_boost RT prio failed!"); | ||
749 | n_rcu_torture_boost_rterror++; | ||
750 | } | ||
751 | |||
752 | init_rcu_head_on_stack(&rbi.rcu); | ||
753 | /* Each pass through the following loop does one boost-test cycle. */ | ||
754 | do { | ||
755 | /* Wait for the next test interval. */ | ||
756 | oldstarttime = boost_starttime; | ||
757 | while (jiffies - oldstarttime > ULONG_MAX / 2) { | ||
758 | schedule_timeout_uninterruptible(1); | ||
759 | rcu_stutter_wait("rcu_torture_boost"); | ||
760 | if (kthread_should_stop() || | ||
761 | fullstop != FULLSTOP_DONTSTOP) | ||
762 | goto checkwait; | ||
763 | } | ||
764 | |||
765 | /* Do one boost-test interval. */ | ||
766 | endtime = oldstarttime + test_boost_duration * HZ; | ||
767 | call_rcu_time = jiffies; | ||
768 | while (jiffies - endtime > ULONG_MAX / 2) { | ||
769 | /* If we don't have a callback in flight, post one. */ | ||
770 | if (!rbi.inflight) { | ||
771 | smp_mb(); /* RCU core before ->inflight = 1. */ | ||
772 | rbi.inflight = 1; | ||
773 | call_rcu(&rbi.rcu, rcu_torture_boost_cb); | ||
774 | if (jiffies - call_rcu_time > | ||
775 | test_boost_duration * HZ - HZ / 2) { | ||
776 | VERBOSE_PRINTK_STRING("rcu_torture_boost boosting failed"); | ||
777 | n_rcu_torture_boost_failure++; | ||
778 | } | ||
779 | call_rcu_time = jiffies; | ||
780 | } | ||
781 | cond_resched(); | ||
782 | rcu_stutter_wait("rcu_torture_boost"); | ||
783 | if (kthread_should_stop() || | ||
784 | fullstop != FULLSTOP_DONTSTOP) | ||
785 | goto checkwait; | ||
786 | } | ||
787 | |||
788 | /* | ||
789 | * Set the start time of the next test interval. | ||
790 | * Yes, this is vulnerable to long delays, but such | ||
791 | * delays simply cause a false negative for the next | ||
792 | * interval. Besides, we are running at RT priority, | ||
793 | * so delays should be relatively rare. | ||
794 | */ | ||
795 | while (oldstarttime == boost_starttime) { | ||
796 | if (mutex_trylock(&boost_mutex)) { | ||
797 | boost_starttime = jiffies + | ||
798 | test_boost_interval * HZ; | ||
799 | n_rcu_torture_boosts++; | ||
800 | mutex_unlock(&boost_mutex); | ||
801 | break; | ||
802 | } | ||
803 | schedule_timeout_uninterruptible(1); | ||
804 | } | ||
805 | |||
806 | /* Go do the stutter. */ | ||
807 | checkwait: rcu_stutter_wait("rcu_torture_boost"); | ||
808 | } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); | ||
809 | |||
810 | /* Clean up and exit. */ | ||
811 | VERBOSE_PRINTK_STRING("rcu_torture_boost task stopping"); | ||
812 | destroy_rcu_head_on_stack(&rbi.rcu); | ||
813 | rcutorture_shutdown_absorb("rcu_torture_boost"); | ||
814 | while (!kthread_should_stop() || rbi.inflight) | ||
815 | schedule_timeout_uninterruptible(1); | ||
816 | smp_mb(); /* order accesses to ->inflight before stack-frame death. */ | ||
817 | return 0; | ||
818 | } | ||
819 | |||
820 | /* | ||
679 | * RCU torture force-quiescent-state kthread. Repeatedly induces | 821 | * RCU torture force-quiescent-state kthread. Repeatedly induces |
680 | * bursts of calls to force_quiescent_state(), increasing the probability | 822 | * bursts of calls to force_quiescent_state(), increasing the probability |
681 | * of occurrence of some important types of race conditions. | 823 | * of occurrence of some important types of race conditions. |
@@ -731,7 +873,8 @@ rcu_torture_writer(void *arg) | |||
731 | continue; | 873 | continue; |
732 | rp->rtort_pipe_count = 0; | 874 | rp->rtort_pipe_count = 0; |
733 | udelay(rcu_random(&rand) & 0x3ff); | 875 | udelay(rcu_random(&rand) & 0x3ff); |
734 | old_rp = rcu_torture_current; | 876 | old_rp = rcu_dereference_check(rcu_torture_current, |
877 | current == writer_task); | ||
735 | rp->rtort_mbtest = 1; | 878 | rp->rtort_mbtest = 1; |
736 | rcu_assign_pointer(rcu_torture_current, rp); | 879 | rcu_assign_pointer(rcu_torture_current, rp); |
737 | smp_wmb(); /* Mods to old_rp must follow rcu_assign_pointer() */ | 880 | smp_wmb(); /* Mods to old_rp must follow rcu_assign_pointer() */ |
@@ -743,7 +886,7 @@ rcu_torture_writer(void *arg) | |||
743 | old_rp->rtort_pipe_count++; | 886 | old_rp->rtort_pipe_count++; |
744 | cur_ops->deferred_free(old_rp); | 887 | cur_ops->deferred_free(old_rp); |
745 | } | 888 | } |
746 | rcu_torture_current_version++; | 889 | rcutorture_record_progress(++rcu_torture_current_version); |
747 | oldbatch = cur_ops->completed(); | 890 | oldbatch = cur_ops->completed(); |
748 | rcu_stutter_wait("rcu_torture_writer"); | 891 | rcu_stutter_wait("rcu_torture_writer"); |
749 | } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); | 892 | } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP); |
@@ -923,8 +1066,9 @@ rcu_torture_printk(char *page) | |||
923 | } | 1066 | } |
924 | cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); | 1067 | cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); |
925 | cnt += sprintf(&page[cnt], | 1068 | cnt += sprintf(&page[cnt], |
926 | "rtc: %p ver: %ld tfle: %d rta: %d rtaf: %d rtf: %d " | 1069 | "rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d " |
927 | "rtmbe: %d nt: %ld", | 1070 | "rtmbe: %d rtbke: %ld rtbre: %ld " |
1071 | "rtbf: %ld rtb: %ld nt: %ld", | ||
928 | rcu_torture_current, | 1072 | rcu_torture_current, |
929 | rcu_torture_current_version, | 1073 | rcu_torture_current_version, |
930 | list_empty(&rcu_torture_freelist), | 1074 | list_empty(&rcu_torture_freelist), |
@@ -932,8 +1076,15 @@ rcu_torture_printk(char *page) | |||
932 | atomic_read(&n_rcu_torture_alloc_fail), | 1076 | atomic_read(&n_rcu_torture_alloc_fail), |
933 | atomic_read(&n_rcu_torture_free), | 1077 | atomic_read(&n_rcu_torture_free), |
934 | atomic_read(&n_rcu_torture_mberror), | 1078 | atomic_read(&n_rcu_torture_mberror), |
1079 | n_rcu_torture_boost_ktrerror, | ||
1080 | n_rcu_torture_boost_rterror, | ||
1081 | n_rcu_torture_boost_failure, | ||
1082 | n_rcu_torture_boosts, | ||
935 | n_rcu_torture_timers); | 1083 | n_rcu_torture_timers); |
936 | if (atomic_read(&n_rcu_torture_mberror) != 0) | 1084 | if (atomic_read(&n_rcu_torture_mberror) != 0 || |
1085 | n_rcu_torture_boost_ktrerror != 0 || | ||
1086 | n_rcu_torture_boost_rterror != 0 || | ||
1087 | n_rcu_torture_boost_failure != 0) | ||
937 | cnt += sprintf(&page[cnt], " !!!"); | 1088 | cnt += sprintf(&page[cnt], " !!!"); |
938 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); | 1089 | cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); |
939 | if (i > 1) { | 1090 | if (i > 1) { |
@@ -1085,28 +1236,98 @@ rcu_torture_stutter(void *arg) | |||
1085 | } | 1236 | } |
1086 | 1237 | ||
1087 | static inline void | 1238 | static inline void |
1088 | rcu_torture_print_module_parms(char *tag) | 1239 | rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, char *tag) |
1089 | { | 1240 | { |
1090 | printk(KERN_ALERT "%s" TORTURE_FLAG | 1241 | printk(KERN_ALERT "%s" TORTURE_FLAG |
1091 | "--- %s: nreaders=%d nfakewriters=%d " | 1242 | "--- %s: nreaders=%d nfakewriters=%d " |
1092 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " | 1243 | "stat_interval=%d verbose=%d test_no_idle_hz=%d " |
1093 | "shuffle_interval=%d stutter=%d irqreader=%d " | 1244 | "shuffle_interval=%d stutter=%d irqreader=%d " |
1094 | "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d\n", | 1245 | "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d " |
1246 | "test_boost=%d/%d test_boost_interval=%d " | ||
1247 | "test_boost_duration=%d\n", | ||
1095 | torture_type, tag, nrealreaders, nfakewriters, | 1248 | torture_type, tag, nrealreaders, nfakewriters, |
1096 | stat_interval, verbose, test_no_idle_hz, shuffle_interval, | 1249 | stat_interval, verbose, test_no_idle_hz, shuffle_interval, |
1097 | stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter); | 1250 | stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter, |
1251 | test_boost, cur_ops->can_boost, | ||
1252 | test_boost_interval, test_boost_duration); | ||
1098 | } | 1253 | } |
1099 | 1254 | ||
1100 | static struct notifier_block rcutorture_nb = { | 1255 | static struct notifier_block rcutorture_shutdown_nb = { |
1101 | .notifier_call = rcutorture_shutdown_notify, | 1256 | .notifier_call = rcutorture_shutdown_notify, |
1102 | }; | 1257 | }; |
1103 | 1258 | ||
1259 | static void rcutorture_booster_cleanup(int cpu) | ||
1260 | { | ||
1261 | struct task_struct *t; | ||
1262 | |||
1263 | if (boost_tasks[cpu] == NULL) | ||
1264 | return; | ||
1265 | mutex_lock(&boost_mutex); | ||
1266 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_boost task"); | ||
1267 | t = boost_tasks[cpu]; | ||
1268 | boost_tasks[cpu] = NULL; | ||
1269 | mutex_unlock(&boost_mutex); | ||
1270 | |||
1271 | /* This must be outside of the mutex, otherwise deadlock! */ | ||
1272 | kthread_stop(t); | ||
1273 | } | ||
1274 | |||
1275 | static int rcutorture_booster_init(int cpu) | ||
1276 | { | ||
1277 | int retval; | ||
1278 | |||
1279 | if (boost_tasks[cpu] != NULL) | ||
1280 | return 0; /* Already created, nothing more to do. */ | ||
1281 | |||
1282 | /* Don't allow time recalculation while creating a new task. */ | ||
1283 | mutex_lock(&boost_mutex); | ||
1284 | VERBOSE_PRINTK_STRING("Creating rcu_torture_boost task"); | ||
1285 | boost_tasks[cpu] = kthread_create(rcu_torture_boost, NULL, | ||
1286 | "rcu_torture_boost"); | ||
1287 | if (IS_ERR(boost_tasks[cpu])) { | ||
1288 | retval = PTR_ERR(boost_tasks[cpu]); | ||
1289 | VERBOSE_PRINTK_STRING("rcu_torture_boost task create failed"); | ||
1290 | n_rcu_torture_boost_ktrerror++; | ||
1291 | boost_tasks[cpu] = NULL; | ||
1292 | mutex_unlock(&boost_mutex); | ||
1293 | return retval; | ||
1294 | } | ||
1295 | kthread_bind(boost_tasks[cpu], cpu); | ||
1296 | wake_up_process(boost_tasks[cpu]); | ||
1297 | mutex_unlock(&boost_mutex); | ||
1298 | return 0; | ||
1299 | } | ||
1300 | |||
1301 | static int rcutorture_cpu_notify(struct notifier_block *self, | ||
1302 | unsigned long action, void *hcpu) | ||
1303 | { | ||
1304 | long cpu = (long)hcpu; | ||
1305 | |||
1306 | switch (action) { | ||
1307 | case CPU_ONLINE: | ||
1308 | case CPU_DOWN_FAILED: | ||
1309 | (void)rcutorture_booster_init(cpu); | ||
1310 | break; | ||
1311 | case CPU_DOWN_PREPARE: | ||
1312 | rcutorture_booster_cleanup(cpu); | ||
1313 | break; | ||
1314 | default: | ||
1315 | break; | ||
1316 | } | ||
1317 | return NOTIFY_OK; | ||
1318 | } | ||
1319 | |||
1320 | static struct notifier_block rcutorture_cpu_nb = { | ||
1321 | .notifier_call = rcutorture_cpu_notify, | ||
1322 | }; | ||
1323 | |||
1104 | static void | 1324 | static void |
1105 | rcu_torture_cleanup(void) | 1325 | rcu_torture_cleanup(void) |
1106 | { | 1326 | { |
1107 | int i; | 1327 | int i; |
1108 | 1328 | ||
1109 | mutex_lock(&fullstop_mutex); | 1329 | mutex_lock(&fullstop_mutex); |
1330 | rcutorture_record_test_transition(); | ||
1110 | if (fullstop == FULLSTOP_SHUTDOWN) { | 1331 | if (fullstop == FULLSTOP_SHUTDOWN) { |
1111 | printk(KERN_WARNING /* but going down anyway, so... */ | 1332 | printk(KERN_WARNING /* but going down anyway, so... */ |
1112 | "Concurrent 'rmmod rcutorture' and shutdown illegal!\n"); | 1333 | "Concurrent 'rmmod rcutorture' and shutdown illegal!\n"); |
@@ -1118,7 +1339,7 @@ rcu_torture_cleanup(void) | |||
1118 | } | 1339 | } |
1119 | fullstop = FULLSTOP_RMMOD; | 1340 | fullstop = FULLSTOP_RMMOD; |
1120 | mutex_unlock(&fullstop_mutex); | 1341 | mutex_unlock(&fullstop_mutex); |
1121 | unregister_reboot_notifier(&rcutorture_nb); | 1342 | unregister_reboot_notifier(&rcutorture_shutdown_nb); |
1122 | if (stutter_task) { | 1343 | if (stutter_task) { |
1123 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); | 1344 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); |
1124 | kthread_stop(stutter_task); | 1345 | kthread_stop(stutter_task); |
@@ -1175,6 +1396,12 @@ rcu_torture_cleanup(void) | |||
1175 | kthread_stop(fqs_task); | 1396 | kthread_stop(fqs_task); |
1176 | } | 1397 | } |
1177 | fqs_task = NULL; | 1398 | fqs_task = NULL; |
1399 | if ((test_boost == 1 && cur_ops->can_boost) || | ||
1400 | test_boost == 2) { | ||
1401 | unregister_cpu_notifier(&rcutorture_cpu_nb); | ||
1402 | for_each_possible_cpu(i) | ||
1403 | rcutorture_booster_cleanup(i); | ||
1404 | } | ||
1178 | 1405 | ||
1179 | /* Wait for all RCU callbacks to fire. */ | 1406 | /* Wait for all RCU callbacks to fire. */ |
1180 | 1407 | ||
@@ -1186,9 +1413,9 @@ rcu_torture_cleanup(void) | |||
1186 | if (cur_ops->cleanup) | 1413 | if (cur_ops->cleanup) |
1187 | cur_ops->cleanup(); | 1414 | cur_ops->cleanup(); |
1188 | if (atomic_read(&n_rcu_torture_error)) | 1415 | if (atomic_read(&n_rcu_torture_error)) |
1189 | rcu_torture_print_module_parms("End of test: FAILURE"); | 1416 | rcu_torture_print_module_parms(cur_ops, "End of test: FAILURE"); |
1190 | else | 1417 | else |
1191 | rcu_torture_print_module_parms("End of test: SUCCESS"); | 1418 | rcu_torture_print_module_parms(cur_ops, "End of test: SUCCESS"); |
1192 | } | 1419 | } |
1193 | 1420 | ||
1194 | static int __init | 1421 | static int __init |
@@ -1233,7 +1460,7 @@ rcu_torture_init(void) | |||
1233 | nrealreaders = nreaders; | 1460 | nrealreaders = nreaders; |
1234 | else | 1461 | else |
1235 | nrealreaders = 2 * num_online_cpus(); | 1462 | nrealreaders = 2 * num_online_cpus(); |
1236 | rcu_torture_print_module_parms("Start of test"); | 1463 | rcu_torture_print_module_parms(cur_ops, "Start of test"); |
1237 | fullstop = FULLSTOP_DONTSTOP; | 1464 | fullstop = FULLSTOP_DONTSTOP; |
1238 | 1465 | ||
1239 | /* Set up the freelist. */ | 1466 | /* Set up the freelist. */ |
@@ -1254,6 +1481,10 @@ rcu_torture_init(void) | |||
1254 | atomic_set(&n_rcu_torture_free, 0); | 1481 | atomic_set(&n_rcu_torture_free, 0); |
1255 | atomic_set(&n_rcu_torture_mberror, 0); | 1482 | atomic_set(&n_rcu_torture_mberror, 0); |
1256 | atomic_set(&n_rcu_torture_error, 0); | 1483 | atomic_set(&n_rcu_torture_error, 0); |
1484 | n_rcu_torture_boost_ktrerror = 0; | ||
1485 | n_rcu_torture_boost_rterror = 0; | ||
1486 | n_rcu_torture_boost_failure = 0; | ||
1487 | n_rcu_torture_boosts = 0; | ||
1257 | for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) | 1488 | for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) |
1258 | atomic_set(&rcu_torture_wcount[i], 0); | 1489 | atomic_set(&rcu_torture_wcount[i], 0); |
1259 | for_each_possible_cpu(cpu) { | 1490 | for_each_possible_cpu(cpu) { |
@@ -1367,7 +1598,28 @@ rcu_torture_init(void) | |||
1367 | goto unwind; | 1598 | goto unwind; |
1368 | } | 1599 | } |
1369 | } | 1600 | } |
1370 | register_reboot_notifier(&rcutorture_nb); | 1601 | if (test_boost_interval < 1) |
1602 | test_boost_interval = 1; | ||
1603 | if (test_boost_duration < 2) | ||
1604 | test_boost_duration = 2; | ||
1605 | if ((test_boost == 1 && cur_ops->can_boost) || | ||
1606 | test_boost == 2) { | ||
1607 | int retval; | ||
1608 | |||
1609 | boost_starttime = jiffies + test_boost_interval * HZ; | ||
1610 | register_cpu_notifier(&rcutorture_cpu_nb); | ||
1611 | for_each_possible_cpu(i) { | ||
1612 | if (cpu_is_offline(i)) | ||
1613 | continue; /* Heuristic: CPU can go offline. */ | ||
1614 | retval = rcutorture_booster_init(i); | ||
1615 | if (retval < 0) { | ||
1616 | firsterr = retval; | ||
1617 | goto unwind; | ||
1618 | } | ||
1619 | } | ||
1620 | } | ||
1621 | register_reboot_notifier(&rcutorture_shutdown_nb); | ||
1622 | rcutorture_record_test_transition(); | ||
1371 | mutex_unlock(&fullstop_mutex); | 1623 | mutex_unlock(&fullstop_mutex); |
1372 | return 0; | 1624 | return 0; |
1373 | 1625 | ||