diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-11-22 15:51:35 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-11-22 15:51:35 -0500 |
commit | 000b8949e903fc8bf78b99ac8568347251986ebf (patch) | |
tree | d18c763adf4ca196c63ec4e5eb5a38b8d98a1051 | |
parent | 7cfc4317ea56615aaa006f37fc89ed248fcc0fc0 (diff) | |
parent | 8e5bfa8c1f8471aa4a2d30be631ef2b50e10abaf (diff) |
Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull scheduler fixes from Ingo Molnar:
"Two fixes for autogroup scheduling, for races when turning the feature
on/off via /proc/sys/kernel/sched_autogroup_enabled"
* 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
sched/autogroup: Do not use autogroup->tg in zombie threads
sched/autogroup: Fix autogroup_move_group() to never skip sched_move_task()
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | kernel/exit.c | 1 | ||||
-rw-r--r-- | kernel/sched/auto_group.c | 36 |
3 files changed, 31 insertions, 8 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 a5d966cb8891..f1c8fd566246 100644 --- a/kernel/sched/auto_group.c +++ b/kernel/sched/auto_group.c | |||
@@ -111,10 +111,13 @@ bool task_wants_autogroup(struct task_struct *p, struct task_group *tg) | |||
111 | { | 111 | { |
112 | if (tg != &root_task_group) | 112 | if (tg != &root_task_group) |
113 | return false; | 113 | return false; |
114 | |||
115 | /* | 114 | /* |
116 | * We can only assume the task group can't go away on us if | 115 | * If we race with autogroup_move_group() the caller can use the old |
117 | * autogroup_move_group() can see us on ->thread_group list. | 116 | * value of signal->autogroup but in this case sched_move_task() will |
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 | */ |
119 | if (p->flags & PF_EXITING) | 122 | if (p->flags & PF_EXITING) |
120 | return false; | 123 | return false; |
@@ -122,6 +125,16 @@ bool task_wants_autogroup(struct task_struct *p, struct task_group *tg) | |||
122 | return true; | 125 | return true; |
123 | } | 126 | } |
124 | 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 | |||
125 | static void | 138 | static void |
126 | autogroup_move_group(struct task_struct *p, struct autogroup *ag) | 139 | autogroup_move_group(struct task_struct *p, struct autogroup *ag) |
127 | { | 140 | { |
@@ -138,13 +151,20 @@ autogroup_move_group(struct task_struct *p, struct autogroup *ag) | |||
138 | } | 151 | } |
139 | 152 | ||
140 | p->signal->autogroup = autogroup_kref_get(ag); | 153 | p->signal->autogroup = autogroup_kref_get(ag); |
141 | 154 | /* | |
142 | if (!READ_ONCE(sysctl_sched_autogroup_enabled)) | 155 | * We can't avoid sched_move_task() after we changed signal->autogroup, |
143 | goto out; | 156 | * this process can already run with task_group() == prev->tg or we can |
144 | 157 | * race with cgroup code which can read autogroup = prev under rq->lock. | |
158 | * In the latter case for_each_thread() can not miss a migrating thread, | ||
159 | * cpu_cgroup_attach() must not be possible after cgroup_exit() and it | ||
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(). | ||
164 | */ | ||
145 | for_each_thread(p, t) | 165 | for_each_thread(p, t) |
146 | sched_move_task(t); | 166 | sched_move_task(t); |
147 | out: | 167 | |
148 | unlock_task_sighand(p, &flags); | 168 | unlock_task_sighand(p, &flags); |
149 | autogroup_kref_put(prev); | 169 | autogroup_kref_put(prev); |
150 | } | 170 | } |