aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2014-01-21 18:49:56 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-21 19:19:46 -0500
commit0c740d0afc3bff0a097ad03a1c8df92757516f5c (patch)
tree0408d1f977bfe4f93418675b201feaf53f69d9e3 /include
parent9853a407b97d8d066b5a865173a4859a3e69fd8a (diff)
introduce for_each_thread() to replace the buggy while_each_thread()
while_each_thread() and next_thread() should die, almost every lockless usage is wrong. 1. Unless g == current, the lockless while_each_thread() is not safe. while_each_thread(g, t) can loop forever if g exits, next_thread() can't reach the unhashed thread in this case. Note that this can happen even if g is the group leader, it can exec. 2. Even if while_each_thread() itself was correct, people often use it wrongly. It was never safe to just take rcu_read_lock() and loop unless you verify that pid_alive(g) == T, even the first next_thread() can point to the already freed/reused memory. This patch adds signal_struct->thread_head and task->thread_node to create the normal rcu-safe list with the stable head. The new for_each_thread(g, t) helper is always safe under rcu_read_lock() as long as this task_struct can't go away. Note: of course it is ugly to have both task_struct->thread_node and the old task_struct->thread_group, we will kill it later, after we change the users of while_each_thread() to use for_each_thread(). Perhaps we can kill it even before we convert all users, we can reimplement next_thread(t) using the new thread_head/thread_node. But we can't do this right now because this will lead to subtle behavioural changes. For example, do/while_each_thread() always sees at least one task, while for_each_thread() can do nothing if the whole thread group has died. Or thread_group_empty(), currently its semantics is not clear unless thread_group_leader(p) and we need to audit the callers before we can change it. So this patch adds the new interface which has to coexist with the old one for some time, hopefully the next changes will be more or less straightforward and the old one will go away soon. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Reviewed-by: Sergey Dyasly <dserrg@gmail.com> Tested-by: Sergey Dyasly <dserrg@gmail.com> Reviewed-by: Sameer Nanda <snanda@chromium.org> Acked-by: David Rientjes <rientjes@google.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mandeep Singh Baines <msb@chromium.org> Cc: "Ma, Xindong" <xindong.ma@intel.com> Cc: Michal Hocko <mhocko@suse.cz> Cc: "Tu, Xiaobing" <xiaobing.tu@intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include')
-rw-r--r--include/linux/init_task.h2
-rw-r--r--include/linux/sched.h12
2 files changed, 14 insertions, 0 deletions
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index f0e52383a001..1516a8ff8f92 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -41,6 +41,7 @@ extern struct fs_struct init_fs;
41 41
42#define INIT_SIGNALS(sig) { \ 42#define INIT_SIGNALS(sig) { \
43 .nr_threads = 1, \ 43 .nr_threads = 1, \
44 .thread_head = LIST_HEAD_INIT(init_task.thread_node), \
44 .wait_chldexit = __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\ 45 .wait_chldexit = __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\
45 .shared_pending = { \ 46 .shared_pending = { \
46 .list = LIST_HEAD_INIT(sig.shared_pending.list), \ 47 .list = LIST_HEAD_INIT(sig.shared_pending.list), \
@@ -222,6 +223,7 @@ extern struct task_group root_task_group;
222 [PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \ 223 [PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \
223 }, \ 224 }, \
224 .thread_group = LIST_HEAD_INIT(tsk.thread_group), \ 225 .thread_group = LIST_HEAD_INIT(tsk.thread_group), \
226 .thread_node = LIST_HEAD_INIT(init_signals.thread_head), \
225 INIT_IDS \ 227 INIT_IDS \
226 INIT_PERF_EVENTS(tsk) \ 228 INIT_PERF_EVENTS(tsk) \
227 INIT_TRACE_IRQFLAGS \ 229 INIT_TRACE_IRQFLAGS \
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ffccdad050b5..485234d2fd42 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -549,6 +549,7 @@ struct signal_struct {
549 atomic_t sigcnt; 549 atomic_t sigcnt;
550 atomic_t live; 550 atomic_t live;
551 int nr_threads; 551 int nr_threads;
552 struct list_head thread_head;
552 553
553 wait_queue_head_t wait_chldexit; /* for wait4() */ 554 wait_queue_head_t wait_chldexit; /* for wait4() */
554 555
@@ -1271,6 +1272,7 @@ struct task_struct {
1271 /* PID/PID hash table linkage. */ 1272 /* PID/PID hash table linkage. */
1272 struct pid_link pids[PIDTYPE_MAX]; 1273 struct pid_link pids[PIDTYPE_MAX];
1273 struct list_head thread_group; 1274 struct list_head thread_group;
1275 struct list_head thread_node;
1274 1276
1275 struct completion *vfork_done; /* for vfork() */ 1277 struct completion *vfork_done; /* for vfork() */
1276 int __user *set_child_tid; /* CLONE_CHILD_SETTID */ 1278 int __user *set_child_tid; /* CLONE_CHILD_SETTID */
@@ -2341,6 +2343,16 @@ extern bool current_is_single_threaded(void);
2341#define while_each_thread(g, t) \ 2343#define while_each_thread(g, t) \
2342 while ((t = next_thread(t)) != g) 2344 while ((t = next_thread(t)) != g)
2343 2345
2346#define __for_each_thread(signal, t) \
2347 list_for_each_entry_rcu(t, &(signal)->thread_head, thread_node)
2348
2349#define for_each_thread(p, t) \
2350 __for_each_thread((p)->signal, t)
2351
2352/* Careful: this is a double loop, 'break' won't work as expected. */
2353#define for_each_process_thread(p, t) \
2354 for_each_process(p) for_each_thread(p, t)
2355
2344static inline int get_nr_threads(struct task_struct *tsk) 2356static inline int get_nr_threads(struct task_struct *tsk)
2345{ 2357{
2346 return tsk->signal->nr_threads; 2358 return tsk->signal->nr_threads;