@@ -827,11 +827,21 @@ static void rcu_tasks_pertask(struct task_struct *t, struct list_head *hop)
827
827
static void rcu_tasks_postscan (struct list_head * hop )
828
828
{
829
829
/*
830
- * Wait for tasks that are in the process of exiting. This
831
- * does only part of the job, ensuring that all tasks that were
832
- * previously exiting reach the point where they have disabled
833
- * preemption, allowing the later synchronize_rcu() to finish
834
- * the job.
830
+ * Exiting tasks may escape the tasklist scan. Those are vulnerable
831
+ * until their final schedule() with TASK_DEAD state. To cope with
832
+ * this, divide the fragile exit path part in two intersecting
833
+ * read side critical sections:
834
+ *
835
+ * 1) An _SRCU_ read side starting before calling exit_notify(),
836
+ * which may remove the task from the tasklist, and ending after
837
+ * the final preempt_disable() call in do_exit().
838
+ *
839
+ * 2) An _RCU_ read side starting with the final preempt_disable()
840
+ * call in do_exit() and ending with the final call to schedule()
841
+ * with TASK_DEAD state.
842
+ *
843
+ * This handles the part 1). And postgp will handle part 2) with a
844
+ * call to synchronize_rcu().
835
845
*/
836
846
synchronize_srcu (& tasks_rcu_exit_srcu );
837
847
}
@@ -898,7 +908,10 @@ static void rcu_tasks_postgp(struct rcu_tasks *rtp)
898
908
*
899
909
* In addition, this synchronize_rcu() waits for exiting tasks
900
910
* to complete their final preempt_disable() region of execution,
901
- * cleaning up after the synchronize_srcu() above.
911
+ * cleaning up after synchronize_srcu(&tasks_rcu_exit_srcu),
912
+ * enforcing the whole region before tasklist removal until
913
+ * the final schedule() with TASK_DEAD state to be an RCU TASKS
914
+ * read side critical section.
902
915
*/
903
916
synchronize_rcu ();
904
917
}
@@ -988,15 +1001,23 @@ void show_rcu_tasks_classic_gp_kthread(void)
988
1001
EXPORT_SYMBOL_GPL (show_rcu_tasks_classic_gp_kthread );
989
1002
#endif // !defined(CONFIG_TINY_RCU)
990
1003
991
- /* Do the srcu_read_lock() for the above synchronize_srcu(). */
1004
+ /*
1005
+ * Contribute to protect against tasklist scan blind spot while the
1006
+ * task is exiting and may be removed from the tasklist. See
1007
+ * corresponding synchronize_srcu() for further details.
1008
+ */
992
1009
void exit_tasks_rcu_start (void ) __acquires (& tasks_rcu_exit_srcu )
993
1010
{
994
1011
preempt_disable ();
995
1012
current -> rcu_tasks_idx = __srcu_read_lock (& tasks_rcu_exit_srcu );
996
1013
preempt_enable ();
997
1014
}
998
1015
999
- /* Do the srcu_read_unlock() for the above synchronize_srcu(). */
1016
+ /*
1017
+ * Contribute to protect against tasklist scan blind spot while the
1018
+ * task is exiting and may be removed from the tasklist. See
1019
+ * corresponding synchronize_srcu() for further details.
1020
+ */
1000
1021
void exit_tasks_rcu_finish (void ) __releases (& tasks_rcu_exit_srcu )
1001
1022
{
1002
1023
struct task_struct * t = current ;
0 commit comments