diff options
author | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2014-01-31 20:37:28 -0500 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2014-02-23 12:03:21 -0500 |
commit | 7fafaac5b9ce22cc57777865390520476ad2262d (patch) | |
tree | c857b24b21d277b45803878af0715c817fb4716a | |
parent | 14562d1cf12b434da2c69b5603a4149ac43f3b48 (diff) |
rcutorture: Fix rcutorture shutdown races
Not all of the rcutorture kthreads waited for kthread_should_stop()
before returning from their top-level functions, and none of them
used torture_shutdown_absorb() properly. These problems can result in
segfaults and hangs at shutdown time, and some recent changes perturbed
timing sufficiently to make them much more probable. This commit
therefore creates a torture_kthread_stopping() function that does the
proper kthread shutdown dance in one centralized location.
Accommodate this grouping by making VERBOSE_TOROUT_STRING() capable of
taking a non-const string as its argument, which allows the new
torture_kthread_stopping() to pass its "title" argument directly to
the updated version of VERBOSE_TOROUT_STRING().
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-rw-r--r-- | include/linux/torture.h | 3 | ||||
-rw-r--r-- | kernel/rcu/rcutorture.c | 39 | ||||
-rw-r--r-- | kernel/torture.c | 26 |
3 files changed, 35 insertions, 33 deletions
diff --git a/include/linux/torture.h b/include/linux/torture.h index c79c41d543ef..2ea11094daf6 100644 --- a/include/linux/torture.h +++ b/include/linux/torture.h | |||
@@ -45,7 +45,7 @@ | |||
45 | #define TOROUT_STRING(s) \ | 45 | #define TOROUT_STRING(s) \ |
46 | pr_alert("%s" TORTURE_FLAG s "\n", torture_type) | 46 | pr_alert("%s" TORTURE_FLAG s "\n", torture_type) |
47 | #define VERBOSE_TOROUT_STRING(s) \ | 47 | #define VERBOSE_TOROUT_STRING(s) \ |
48 | do { if (verbose) pr_alert("%s" TORTURE_FLAG s "\n", torture_type); } while (0) | 48 | do { if (verbose) pr_alert("%s" TORTURE_FLAG " %s\n", torture_type, s); } while (0) |
49 | #define VERBOSE_TOROUT_ERRSTRING(s) \ | 49 | #define VERBOSE_TOROUT_ERRSTRING(s) \ |
50 | do { if (verbose) pr_alert("%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0) | 50 | do { if (verbose) pr_alert("%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0) |
51 | 51 | ||
@@ -88,5 +88,6 @@ void torture_init_end(void); | |||
88 | bool torture_cleanup(void); | 88 | bool torture_cleanup(void); |
89 | bool torture_must_stop(void); | 89 | bool torture_must_stop(void); |
90 | bool torture_must_stop_irq(void); | 90 | bool torture_must_stop_irq(void); |
91 | void torture_kthread_stopping(char *title); | ||
91 | 92 | ||
92 | #endif /* __LINUX_TORTURE_H */ | 93 | #endif /* __LINUX_TORTURE_H */ |
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 6e9ba51b23b9..bcaafd6cf633 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c | |||
@@ -602,12 +602,13 @@ checkwait: stutter_wait("rcu_torture_boost"); | |||
602 | } while (!torture_must_stop()); | 602 | } while (!torture_must_stop()); |
603 | 603 | ||
604 | /* Clean up and exit. */ | 604 | /* Clean up and exit. */ |
605 | VERBOSE_TOROUT_STRING("rcu_torture_boost task stopping"); | 605 | while (!kthread_should_stop() || rbi.inflight) { |
606 | torture_shutdown_absorb("rcu_torture_boost"); | 606 | torture_shutdown_absorb("rcu_torture_boost"); |
607 | while (!kthread_should_stop() || rbi.inflight) | ||
608 | schedule_timeout_uninterruptible(1); | 607 | schedule_timeout_uninterruptible(1); |
608 | } | ||
609 | smp_mb(); /* order accesses to ->inflight before stack-frame death. */ | 609 | smp_mb(); /* order accesses to ->inflight before stack-frame death. */ |
610 | destroy_rcu_head_on_stack(&rbi.rcu); | 610 | destroy_rcu_head_on_stack(&rbi.rcu); |
611 | torture_kthread_stopping("rcu_torture_boost"); | ||
611 | return 0; | 612 | return 0; |
612 | } | 613 | } |
613 | 614 | ||
@@ -638,10 +639,7 @@ rcu_torture_fqs(void *arg) | |||
638 | } | 639 | } |
639 | stutter_wait("rcu_torture_fqs"); | 640 | stutter_wait("rcu_torture_fqs"); |
640 | } while (!torture_must_stop()); | 641 | } while (!torture_must_stop()); |
641 | VERBOSE_TOROUT_STRING("rcu_torture_fqs task stopping"); | 642 | torture_kthread_stopping("rcu_torture_fqs"); |
642 | torture_shutdown_absorb("rcu_torture_fqs"); | ||
643 | while (!kthread_should_stop()) | ||
644 | schedule_timeout_uninterruptible(1); | ||
645 | return 0; | 643 | return 0; |
646 | } | 644 | } |
647 | 645 | ||
@@ -710,10 +708,7 @@ rcu_torture_writer(void *arg) | |||
710 | rcutorture_record_progress(++rcu_torture_current_version); | 708 | rcutorture_record_progress(++rcu_torture_current_version); |
711 | stutter_wait("rcu_torture_writer"); | 709 | stutter_wait("rcu_torture_writer"); |
712 | } while (!torture_must_stop()); | 710 | } while (!torture_must_stop()); |
713 | VERBOSE_TOROUT_STRING("rcu_torture_writer task stopping"); | 711 | torture_kthread_stopping("rcu_torture_writer"); |
714 | torture_shutdown_absorb("rcu_torture_writer"); | ||
715 | while (!kthread_should_stop()) | ||
716 | schedule_timeout_uninterruptible(1); | ||
717 | return 0; | 712 | return 0; |
718 | } | 713 | } |
719 | 714 | ||
@@ -748,10 +743,7 @@ rcu_torture_fakewriter(void *arg) | |||
748 | stutter_wait("rcu_torture_fakewriter"); | 743 | stutter_wait("rcu_torture_fakewriter"); |
749 | } while (!torture_must_stop()); | 744 | } while (!torture_must_stop()); |
750 | 745 | ||
751 | VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task stopping"); | 746 | torture_kthread_stopping("rcu_torture_fakewriter"); |
752 | torture_shutdown_absorb("rcu_torture_fakewriter"); | ||
753 | while (!kthread_should_stop()) | ||
754 | schedule_timeout_uninterruptible(1); | ||
755 | return 0; | 747 | return 0; |
756 | } | 748 | } |
757 | 749 | ||
@@ -892,12 +884,9 @@ rcu_torture_reader(void *arg) | |||
892 | schedule(); | 884 | schedule(); |
893 | stutter_wait("rcu_torture_reader"); | 885 | stutter_wait("rcu_torture_reader"); |
894 | } while (!torture_must_stop()); | 886 | } while (!torture_must_stop()); |
895 | VERBOSE_TOROUT_STRING("rcu_torture_reader task stopping"); | ||
896 | torture_shutdown_absorb("rcu_torture_reader"); | ||
897 | if (irqreader && cur_ops->irq_capable) | 887 | if (irqreader && cur_ops->irq_capable) |
898 | del_timer_sync(&t); | 888 | del_timer_sync(&t); |
899 | while (!kthread_should_stop()) | 889 | torture_kthread_stopping("rcu_torture_reader"); |
900 | schedule_timeout_uninterruptible(1); | ||
901 | return 0; | 890 | return 0; |
902 | } | 891 | } |
903 | 892 | ||
@@ -1010,7 +999,7 @@ rcu_torture_stats(void *arg) | |||
1010 | rcu_torture_stats_print(); | 999 | rcu_torture_stats_print(); |
1011 | torture_shutdown_absorb("rcu_torture_stats"); | 1000 | torture_shutdown_absorb("rcu_torture_stats"); |
1012 | } while (!torture_must_stop()); | 1001 | } while (!torture_must_stop()); |
1013 | VERBOSE_TOROUT_STRING("rcu_torture_stats task stopping"); | 1002 | torture_kthread_stopping("rcu_torture_stats"); |
1014 | return 0; | 1003 | return 0; |
1015 | } | 1004 | } |
1016 | 1005 | ||
@@ -1171,12 +1160,9 @@ static int rcu_torture_barrier_cbs(void *arg) | |||
1171 | if (atomic_dec_and_test(&barrier_cbs_count)) | 1160 | if (atomic_dec_and_test(&barrier_cbs_count)) |
1172 | wake_up(&barrier_wq); | 1161 | wake_up(&barrier_wq); |
1173 | } while (!torture_must_stop()); | 1162 | } while (!torture_must_stop()); |
1174 | VERBOSE_TOROUT_STRING("rcu_torture_barrier_cbs task stopping"); | ||
1175 | torture_shutdown_absorb("rcu_torture_barrier_cbs"); | ||
1176 | while (!kthread_should_stop()) | ||
1177 | schedule_timeout_interruptible(1); | ||
1178 | cur_ops->cb_barrier(); | 1163 | cur_ops->cb_barrier(); |
1179 | destroy_rcu_head_on_stack(&rcu); | 1164 | destroy_rcu_head_on_stack(&rcu); |
1165 | torture_kthread_stopping("rcu_torture_barrier_cbs"); | ||
1180 | return 0; | 1166 | return 0; |
1181 | } | 1167 | } |
1182 | 1168 | ||
@@ -1207,10 +1193,7 @@ static int rcu_torture_barrier(void *arg) | |||
1207 | n_barrier_successes++; | 1193 | n_barrier_successes++; |
1208 | schedule_timeout_interruptible(HZ / 10); | 1194 | schedule_timeout_interruptible(HZ / 10); |
1209 | } while (!torture_must_stop()); | 1195 | } while (!torture_must_stop()); |
1210 | VERBOSE_TOROUT_STRING("rcu_torture_barrier task stopping"); | 1196 | torture_kthread_stopping("rcu_torture_barrier"); |
1211 | torture_shutdown_absorb("rcu_torture_barrier"); | ||
1212 | while (!kthread_should_stop()) | ||
1213 | schedule_timeout_interruptible(1); | ||
1214 | return 0; | 1197 | return 0; |
1215 | } | 1198 | } |
1216 | 1199 | ||
diff --git a/kernel/torture.c b/kernel/torture.c index 5e2838f902f9..330576660cf4 100644 --- a/kernel/torture.c +++ b/kernel/torture.c | |||
@@ -169,7 +169,7 @@ torture_onoff(void *arg) | |||
169 | } | 169 | } |
170 | schedule_timeout_interruptible(onoff_interval); | 170 | schedule_timeout_interruptible(onoff_interval); |
171 | } | 171 | } |
172 | VERBOSE_TOROUT_STRING("torture_onoff task stopping"); | 172 | torture_kthread_stopping("torture_onoff"); |
173 | return 0; | 173 | return 0; |
174 | } | 174 | } |
175 | 175 | ||
@@ -370,7 +370,7 @@ static int torture_shuffle(void *arg) | |||
370 | torture_shuffle_tasks(); | 370 | torture_shuffle_tasks(); |
371 | torture_shutdown_absorb("torture_shuffle"); | 371 | torture_shutdown_absorb("torture_shuffle"); |
372 | } while (!torture_must_stop()); | 372 | } while (!torture_must_stop()); |
373 | VERBOSE_TOROUT_STRING("torture_shuffle task stopping"); | 373 | torture_kthread_stopping("torture_shuffle"); |
374 | return 0; | 374 | return 0; |
375 | } | 375 | } |
376 | 376 | ||
@@ -465,7 +465,7 @@ static int torture_shutdown(void *arg) | |||
465 | jiffies_snap = jiffies; | 465 | jiffies_snap = jiffies; |
466 | } | 466 | } |
467 | if (torture_must_stop()) { | 467 | if (torture_must_stop()) { |
468 | VERBOSE_TOROUT_STRING("torture_shutdown task stopping"); | 468 | torture_kthread_stopping("torture_shutdown"); |
469 | return 0; | 469 | return 0; |
470 | } | 470 | } |
471 | 471 | ||
@@ -583,7 +583,7 @@ static int torture_stutter(void *arg) | |||
583 | ACCESS_ONCE(stutter_pause_test) = 0; | 583 | ACCESS_ONCE(stutter_pause_test) = 0; |
584 | torture_shutdown_absorb("torture_stutter"); | 584 | torture_shutdown_absorb("torture_stutter"); |
585 | } while (!torture_must_stop()); | 585 | } while (!torture_must_stop()); |
586 | VERBOSE_TOROUT_STRING("torture_stutter task stopping"); | 586 | torture_kthread_stopping("torture_stutter"); |
587 | return 0; | 587 | return 0; |
588 | } | 588 | } |
589 | 589 | ||
@@ -696,3 +696,21 @@ bool torture_must_stop_irq(void) | |||
696 | return ACCESS_ONCE(fullstop) != FULLSTOP_DONTSTOP; | 696 | return ACCESS_ONCE(fullstop) != FULLSTOP_DONTSTOP; |
697 | } | 697 | } |
698 | EXPORT_SYMBOL_GPL(torture_must_stop_irq); | 698 | EXPORT_SYMBOL_GPL(torture_must_stop_irq); |
699 | |||
700 | /* | ||
701 | * Each kthread must wait for kthread_should_stop() before returning from | ||
702 | * its top-level function, otherwise segfaults ensue. This function | ||
703 | * prints a "stopping" message and waits for kthread_should_stop(), and | ||
704 | * should be called from all torture kthreads immediately prior to | ||
705 | * returning. | ||
706 | */ | ||
707 | void torture_kthread_stopping(char *title) | ||
708 | { | ||
709 | if (verbose) | ||
710 | VERBOSE_TOROUT_STRING(title); | ||
711 | while (!kthread_should_stop()) { | ||
712 | torture_shutdown_absorb(title); | ||
713 | schedule_timeout_uninterruptible(1); | ||
714 | } | ||
715 | } | ||
716 | EXPORT_SYMBOL_GPL(torture_kthread_stopping); | ||