diff options
author | Oleg Nesterov <oleg@redhat.com> | 2016-11-14 13:46:12 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-11-22 06:33:43 -0500 |
commit | 8e5bfa8c1f8471aa4a2d30be631ef2b50e10abaf (patch) | |
tree | ad3d1c2dc3f1770ac865c8f1df91a53c1e276728 | |
parent | 18f649ef344127ef6de23a5a4272dbe2fdb73dde (diff) |
sched/autogroup: Do not use autogroup->tg in zombie threads
Exactly because for_each_thread() in autogroup_move_group() can't see it
and update its ->sched_task_group before _put() and possibly free().
So the exiting task needs another sched_move_task() before exit_notify()
and we need to re-introduce the PF_EXITING (or similar) check removed by
the previous change for another reason.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: hartsjc@redhat.com
Cc: vbendel@redhat.com
Cc: vlovejoy@redhat.com
Link: http://lkml.kernel.org/r/20161114184612.GA15968@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | kernel/exit.c | 1 | ||||
-rw-r--r-- | kernel/sched/auto_group.c | 19 |
3 files changed, 22 insertions, 0 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index 348f51b0ec92..e9c009dc3a4a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -2567,6 +2567,7 @@ extern void sched_autogroup_create_attach(struct task_struct *p); | |||
2567 | extern void sched_autogroup_detach(struct task_struct *p); | 2567 | extern void sched_autogroup_detach(struct task_struct *p); |
2568 | extern void sched_autogroup_fork(struct signal_struct *sig); | 2568 | extern void sched_autogroup_fork(struct signal_struct *sig); |
2569 | extern void sched_autogroup_exit(struct signal_struct *sig); | 2569 | extern void sched_autogroup_exit(struct signal_struct *sig); |
2570 | extern void sched_autogroup_exit_task(struct task_struct *p); | ||
2570 | #ifdef CONFIG_PROC_FS | 2571 | #ifdef CONFIG_PROC_FS |
2571 | extern void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m); | 2572 | extern void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m); |
2572 | extern int proc_sched_autogroup_set_nice(struct task_struct *p, int nice); | 2573 | extern int proc_sched_autogroup_set_nice(struct task_struct *p, int nice); |
@@ -2576,6 +2577,7 @@ static inline void sched_autogroup_create_attach(struct task_struct *p) { } | |||
2576 | static inline void sched_autogroup_detach(struct task_struct *p) { } | 2577 | static inline void sched_autogroup_detach(struct task_struct *p) { } |
2577 | static inline void sched_autogroup_fork(struct signal_struct *sig) { } | 2578 | static inline void sched_autogroup_fork(struct signal_struct *sig) { } |
2578 | static inline void sched_autogroup_exit(struct signal_struct *sig) { } | 2579 | static inline void sched_autogroup_exit(struct signal_struct *sig) { } |
2580 | static inline void sched_autogroup_exit_task(struct task_struct *p) { } | ||
2579 | #endif | 2581 | #endif |
2580 | 2582 | ||
2581 | extern int yield_to(struct task_struct *p, bool preempt); | 2583 | extern int yield_to(struct task_struct *p, bool preempt); |
diff --git a/kernel/exit.c b/kernel/exit.c index 9d68c45ebbe3..3076f3089919 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -836,6 +836,7 @@ void __noreturn do_exit(long code) | |||
836 | */ | 836 | */ |
837 | perf_event_exit_task(tsk); | 837 | perf_event_exit_task(tsk); |
838 | 838 | ||
839 | sched_autogroup_exit_task(tsk); | ||
839 | cgroup_exit(tsk); | 840 | cgroup_exit(tsk); |
840 | 841 | ||
841 | /* | 842 | /* |
diff --git a/kernel/sched/auto_group.c b/kernel/sched/auto_group.c index ad2b19ad6ca0..f1c8fd566246 100644 --- a/kernel/sched/auto_group.c +++ b/kernel/sched/auto_group.c | |||
@@ -115,10 +115,26 @@ bool task_wants_autogroup(struct task_struct *p, struct task_group *tg) | |||
115 | * If we race with autogroup_move_group() the caller can use the old | 115 | * If we race with autogroup_move_group() the caller can use the old |
116 | * value of signal->autogroup but in this case sched_move_task() will | 116 | * value of signal->autogroup but in this case sched_move_task() will |
117 | * be called again before autogroup_kref_put(). | 117 | * be called again before autogroup_kref_put(). |
118 | * | ||
119 | * However, there is no way sched_autogroup_exit_task() could tell us | ||
120 | * to avoid autogroup->tg, so we abuse PF_EXITING flag for this case. | ||
118 | */ | 121 | */ |
122 | if (p->flags & PF_EXITING) | ||
123 | return false; | ||
124 | |||
119 | return true; | 125 | return true; |
120 | } | 126 | } |
121 | 127 | ||
128 | void sched_autogroup_exit_task(struct task_struct *p) | ||
129 | { | ||
130 | /* | ||
131 | * We are going to call exit_notify() and autogroup_move_group() can't | ||
132 | * see this thread after that: we can no longer use signal->autogroup. | ||
133 | * See the PF_EXITING check in task_wants_autogroup(). | ||
134 | */ | ||
135 | sched_move_task(p); | ||
136 | } | ||
137 | |||
122 | static void | 138 | static void |
123 | autogroup_move_group(struct task_struct *p, struct autogroup *ag) | 139 | autogroup_move_group(struct task_struct *p, struct autogroup *ag) |
124 | { | 140 | { |
@@ -142,6 +158,9 @@ autogroup_move_group(struct task_struct *p, struct autogroup *ag) | |||
142 | * In the latter case for_each_thread() can not miss a migrating thread, | 158 | * In the latter case for_each_thread() can not miss a migrating thread, |
143 | * cpu_cgroup_attach() must not be possible after cgroup_exit() and it | 159 | * cpu_cgroup_attach() must not be possible after cgroup_exit() and it |
144 | * can't be removed from thread list, we hold ->siglock. | 160 | * can't be removed from thread list, we hold ->siglock. |
161 | * | ||
162 | * If an exiting thread was already removed from thread list we rely on | ||
163 | * sched_autogroup_exit_task(). | ||
145 | */ | 164 | */ |
146 | for_each_thread(p, t) | 165 | for_each_thread(p, t) |
147 | sched_move_task(t); | 166 | sched_move_task(t); |