diff options
Diffstat (limited to 'kernel/rcutorture.c')
-rw-r--r-- | kernel/rcutorture.c | 93 |
1 files changed, 71 insertions, 22 deletions
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 85cb90588a55..3245b40952c6 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/moduleparam.h> | 39 | #include <linux/moduleparam.h> |
40 | #include <linux/percpu.h> | 40 | #include <linux/percpu.h> |
41 | #include <linux/notifier.h> | 41 | #include <linux/notifier.h> |
42 | #include <linux/reboot.h> | ||
42 | #include <linux/freezer.h> | 43 | #include <linux/freezer.h> |
43 | #include <linux/cpu.h> | 44 | #include <linux/cpu.h> |
44 | #include <linux/delay.h> | 45 | #include <linux/delay.h> |
@@ -108,7 +109,6 @@ struct rcu_torture { | |||
108 | int rtort_mbtest; | 109 | int rtort_mbtest; |
109 | }; | 110 | }; |
110 | 111 | ||
111 | static int fullstop = 0; /* stop generating callbacks at test end. */ | ||
112 | static LIST_HEAD(rcu_torture_freelist); | 112 | static LIST_HEAD(rcu_torture_freelist); |
113 | static struct rcu_torture *rcu_torture_current = NULL; | 113 | static struct rcu_torture *rcu_torture_current = NULL; |
114 | static long rcu_torture_current_version = 0; | 114 | static long rcu_torture_current_version = 0; |
@@ -136,6 +136,30 @@ static int stutter_pause_test = 0; | |||
136 | #endif | 136 | #endif |
137 | int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; | 137 | int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; |
138 | 138 | ||
139 | #define FULLSTOP_SIGNALED 1 /* Bail due to signal. */ | ||
140 | #define FULLSTOP_CLEANUP 2 /* Orderly shutdown. */ | ||
141 | static int fullstop; /* stop generating callbacks at test end. */ | ||
142 | DEFINE_MUTEX(fullstop_mutex); /* protect fullstop transitions and */ | ||
143 | /* spawning of kthreads. */ | ||
144 | |||
145 | /* | ||
146 | * Detect and respond to a signal-based shutdown. | ||
147 | */ | ||
148 | static int | ||
149 | rcutorture_shutdown_notify(struct notifier_block *unused1, | ||
150 | unsigned long unused2, void *unused3) | ||
151 | { | ||
152 | if (fullstop) | ||
153 | return NOTIFY_DONE; | ||
154 | if (signal_pending(current)) { | ||
155 | mutex_lock(&fullstop_mutex); | ||
156 | if (!ACCESS_ONCE(fullstop)) | ||
157 | fullstop = FULLSTOP_SIGNALED; | ||
158 | mutex_unlock(&fullstop_mutex); | ||
159 | } | ||
160 | return NOTIFY_DONE; | ||
161 | } | ||
162 | |||
139 | /* | 163 | /* |
140 | * Allocate an element from the rcu_tortures pool. | 164 | * Allocate an element from the rcu_tortures pool. |
141 | */ | 165 | */ |
@@ -199,11 +223,12 @@ rcu_random(struct rcu_random_state *rrsp) | |||
199 | static void | 223 | static void |
200 | rcu_stutter_wait(void) | 224 | rcu_stutter_wait(void) |
201 | { | 225 | { |
202 | while (stutter_pause_test || !rcutorture_runnable) | 226 | while ((stutter_pause_test || !rcutorture_runnable) && !fullstop) { |
203 | if (rcutorture_runnable) | 227 | if (rcutorture_runnable) |
204 | schedule_timeout_interruptible(1); | 228 | schedule_timeout_interruptible(1); |
205 | else | 229 | else |
206 | schedule_timeout_interruptible(round_jiffies_relative(HZ)); | 230 | schedule_timeout_interruptible(round_jiffies_relative(HZ)); |
231 | } | ||
207 | } | 232 | } |
208 | 233 | ||
209 | /* | 234 | /* |
@@ -599,7 +624,7 @@ rcu_torture_writer(void *arg) | |||
599 | rcu_stutter_wait(); | 624 | rcu_stutter_wait(); |
600 | } while (!kthread_should_stop() && !fullstop); | 625 | } while (!kthread_should_stop() && !fullstop); |
601 | VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); | 626 | VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); |
602 | while (!kthread_should_stop()) | 627 | while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED) |
603 | schedule_timeout_uninterruptible(1); | 628 | schedule_timeout_uninterruptible(1); |
604 | return 0; | 629 | return 0; |
605 | } | 630 | } |
@@ -624,7 +649,7 @@ rcu_torture_fakewriter(void *arg) | |||
624 | } while (!kthread_should_stop() && !fullstop); | 649 | } while (!kthread_should_stop() && !fullstop); |
625 | 650 | ||
626 | VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); | 651 | VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); |
627 | while (!kthread_should_stop()) | 652 | while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED) |
628 | schedule_timeout_uninterruptible(1); | 653 | schedule_timeout_uninterruptible(1); |
629 | return 0; | 654 | return 0; |
630 | } | 655 | } |
@@ -734,7 +759,7 @@ rcu_torture_reader(void *arg) | |||
734 | VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); | 759 | VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); |
735 | if (irqreader && cur_ops->irqcapable) | 760 | if (irqreader && cur_ops->irqcapable) |
736 | del_timer_sync(&t); | 761 | del_timer_sync(&t); |
737 | while (!kthread_should_stop()) | 762 | while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED) |
738 | schedule_timeout_uninterruptible(1); | 763 | schedule_timeout_uninterruptible(1); |
739 | return 0; | 764 | return 0; |
740 | } | 765 | } |
@@ -831,7 +856,7 @@ rcu_torture_stats(void *arg) | |||
831 | do { | 856 | do { |
832 | schedule_timeout_interruptible(stat_interval * HZ); | 857 | schedule_timeout_interruptible(stat_interval * HZ); |
833 | rcu_torture_stats_print(); | 858 | rcu_torture_stats_print(); |
834 | } while (!kthread_should_stop()); | 859 | } while (!kthread_should_stop() && !fullstop); |
835 | VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping"); | 860 | VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping"); |
836 | return 0; | 861 | return 0; |
837 | } | 862 | } |
@@ -843,49 +868,52 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */ | |||
843 | */ | 868 | */ |
844 | static void rcu_torture_shuffle_tasks(void) | 869 | static void rcu_torture_shuffle_tasks(void) |
845 | { | 870 | { |
846 | cpumask_t tmp_mask; | 871 | cpumask_var_t tmp_mask; |
847 | int i; | 872 | int i; |
848 | 873 | ||
849 | cpus_setall(tmp_mask); | 874 | if (!alloc_cpumask_var(&tmp_mask, GFP_KERNEL)) |
875 | BUG(); | ||
876 | |||
877 | cpumask_setall(tmp_mask); | ||
850 | get_online_cpus(); | 878 | get_online_cpus(); |
851 | 879 | ||
852 | /* No point in shuffling if there is only one online CPU (ex: UP) */ | 880 | /* No point in shuffling if there is only one online CPU (ex: UP) */ |
853 | if (num_online_cpus() == 1) { | 881 | if (num_online_cpus() == 1) |
854 | put_online_cpus(); | 882 | goto out; |
855 | return; | ||
856 | } | ||
857 | 883 | ||
858 | if (rcu_idle_cpu != -1) | 884 | if (rcu_idle_cpu != -1) |
859 | cpu_clear(rcu_idle_cpu, tmp_mask); | 885 | cpumask_clear_cpu(rcu_idle_cpu, tmp_mask); |
860 | 886 | ||
861 | set_cpus_allowed_ptr(current, &tmp_mask); | 887 | set_cpus_allowed_ptr(current, tmp_mask); |
862 | 888 | ||
863 | if (reader_tasks) { | 889 | if (reader_tasks) { |
864 | for (i = 0; i < nrealreaders; i++) | 890 | for (i = 0; i < nrealreaders; i++) |
865 | if (reader_tasks[i]) | 891 | if (reader_tasks[i]) |
866 | set_cpus_allowed_ptr(reader_tasks[i], | 892 | set_cpus_allowed_ptr(reader_tasks[i], |
867 | &tmp_mask); | 893 | tmp_mask); |
868 | } | 894 | } |
869 | 895 | ||
870 | if (fakewriter_tasks) { | 896 | if (fakewriter_tasks) { |
871 | for (i = 0; i < nfakewriters; i++) | 897 | for (i = 0; i < nfakewriters; i++) |
872 | if (fakewriter_tasks[i]) | 898 | if (fakewriter_tasks[i]) |
873 | set_cpus_allowed_ptr(fakewriter_tasks[i], | 899 | set_cpus_allowed_ptr(fakewriter_tasks[i], |
874 | &tmp_mask); | 900 | tmp_mask); |
875 | } | 901 | } |
876 | 902 | ||
877 | if (writer_task) | 903 | if (writer_task) |
878 | set_cpus_allowed_ptr(writer_task, &tmp_mask); | 904 | set_cpus_allowed_ptr(writer_task, tmp_mask); |
879 | 905 | ||
880 | if (stats_task) | 906 | if (stats_task) |
881 | set_cpus_allowed_ptr(stats_task, &tmp_mask); | 907 | set_cpus_allowed_ptr(stats_task, tmp_mask); |
882 | 908 | ||
883 | if (rcu_idle_cpu == -1) | 909 | if (rcu_idle_cpu == -1) |
884 | rcu_idle_cpu = num_online_cpus() - 1; | 910 | rcu_idle_cpu = num_online_cpus() - 1; |
885 | else | 911 | else |
886 | rcu_idle_cpu--; | 912 | rcu_idle_cpu--; |
887 | 913 | ||
914 | out: | ||
888 | put_online_cpus(); | 915 | put_online_cpus(); |
916 | free_cpumask_var(tmp_mask); | ||
889 | } | 917 | } |
890 | 918 | ||
891 | /* Shuffle tasks across CPUs, with the intent of allowing each CPU in the | 919 | /* Shuffle tasks across CPUs, with the intent of allowing each CPU in the |
@@ -899,7 +927,7 @@ rcu_torture_shuffle(void *arg) | |||
899 | do { | 927 | do { |
900 | schedule_timeout_interruptible(shuffle_interval * HZ); | 928 | schedule_timeout_interruptible(shuffle_interval * HZ); |
901 | rcu_torture_shuffle_tasks(); | 929 | rcu_torture_shuffle_tasks(); |
902 | } while (!kthread_should_stop()); | 930 | } while (!kthread_should_stop() && !fullstop); |
903 | VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping"); | 931 | VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping"); |
904 | return 0; | 932 | return 0; |
905 | } | 933 | } |
@@ -914,10 +942,10 @@ rcu_torture_stutter(void *arg) | |||
914 | do { | 942 | do { |
915 | schedule_timeout_interruptible(stutter * HZ); | 943 | schedule_timeout_interruptible(stutter * HZ); |
916 | stutter_pause_test = 1; | 944 | stutter_pause_test = 1; |
917 | if (!kthread_should_stop()) | 945 | if (!kthread_should_stop() && !fullstop) |
918 | schedule_timeout_interruptible(stutter * HZ); | 946 | schedule_timeout_interruptible(stutter * HZ); |
919 | stutter_pause_test = 0; | 947 | stutter_pause_test = 0; |
920 | } while (!kthread_should_stop()); | 948 | } while (!kthread_should_stop() && !fullstop); |
921 | VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping"); | 949 | VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping"); |
922 | return 0; | 950 | return 0; |
923 | } | 951 | } |
@@ -934,12 +962,27 @@ rcu_torture_print_module_parms(char *tag) | |||
934 | stutter, irqreader); | 962 | stutter, irqreader); |
935 | } | 963 | } |
936 | 964 | ||
965 | static struct notifier_block rcutorture_nb = { | ||
966 | .notifier_call = rcutorture_shutdown_notify, | ||
967 | }; | ||
968 | |||
937 | static void | 969 | static void |
938 | rcu_torture_cleanup(void) | 970 | rcu_torture_cleanup(void) |
939 | { | 971 | { |
940 | int i; | 972 | int i; |
941 | 973 | ||
942 | fullstop = 1; | 974 | mutex_lock(&fullstop_mutex); |
975 | if (!fullstop) { | ||
976 | /* If being signaled, let it happen, then exit. */ | ||
977 | mutex_unlock(&fullstop_mutex); | ||
978 | schedule_timeout_interruptible(10 * HZ); | ||
979 | if (cur_ops->cb_barrier != NULL) | ||
980 | cur_ops->cb_barrier(); | ||
981 | return; | ||
982 | } | ||
983 | fullstop = FULLSTOP_CLEANUP; | ||
984 | mutex_unlock(&fullstop_mutex); | ||
985 | unregister_reboot_notifier(&rcutorture_nb); | ||
943 | if (stutter_task) { | 986 | if (stutter_task) { |
944 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); | 987 | VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); |
945 | kthread_stop(stutter_task); | 988 | kthread_stop(stutter_task); |
@@ -1015,6 +1058,8 @@ rcu_torture_init(void) | |||
1015 | { &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops, | 1058 | { &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops, |
1016 | &srcu_ops, &sched_ops, &sched_ops_sync, }; | 1059 | &srcu_ops, &sched_ops, &sched_ops_sync, }; |
1017 | 1060 | ||
1061 | mutex_lock(&fullstop_mutex); | ||
1062 | |||
1018 | /* Process args and tell the world that the torturer is on the job. */ | 1063 | /* Process args and tell the world that the torturer is on the job. */ |
1019 | for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { | 1064 | for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { |
1020 | cur_ops = torture_ops[i]; | 1065 | cur_ops = torture_ops[i]; |
@@ -1024,6 +1069,7 @@ rcu_torture_init(void) | |||
1024 | if (i == ARRAY_SIZE(torture_ops)) { | 1069 | if (i == ARRAY_SIZE(torture_ops)) { |
1025 | printk(KERN_ALERT "rcutorture: invalid torture type: \"%s\"\n", | 1070 | printk(KERN_ALERT "rcutorture: invalid torture type: \"%s\"\n", |
1026 | torture_type); | 1071 | torture_type); |
1072 | mutex_unlock(&fullstop_mutex); | ||
1027 | return (-EINVAL); | 1073 | return (-EINVAL); |
1028 | } | 1074 | } |
1029 | if (cur_ops->init) | 1075 | if (cur_ops->init) |
@@ -1146,9 +1192,12 @@ rcu_torture_init(void) | |||
1146 | goto unwind; | 1192 | goto unwind; |
1147 | } | 1193 | } |
1148 | } | 1194 | } |
1195 | register_reboot_notifier(&rcutorture_nb); | ||
1196 | mutex_unlock(&fullstop_mutex); | ||
1149 | return 0; | 1197 | return 0; |
1150 | 1198 | ||
1151 | unwind: | 1199 | unwind: |
1200 | mutex_unlock(&fullstop_mutex); | ||
1152 | rcu_torture_cleanup(); | 1201 | rcu_torture_cleanup(); |
1153 | return firsterr; | 1202 | return firsterr; |
1154 | } | 1203 | } |