Commit 0c740d0a authored by Oleg Nesterov's avatar Oleg Nesterov Committed by Linus Torvalds

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: default avatarOleg Nesterov <oleg@redhat.com>
Reviewed-by: default avatarSergey Dyasly <dserrg@gmail.com>
Tested-by: default avatarSergey Dyasly <dserrg@gmail.com>
Reviewed-by: default avatarSameer Nanda <snanda@chromium.org>
Acked-by: default avatarDavid 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9853a407
...@@ -41,6 +41,7 @@ extern struct fs_struct init_fs; ...@@ -41,6 +41,7 @@ extern struct fs_struct init_fs;
#define INIT_SIGNALS(sig) { \ #define INIT_SIGNALS(sig) { \
.nr_threads = 1, \ .nr_threads = 1, \
.thread_head = LIST_HEAD_INIT(init_task.thread_node), \
.wait_chldexit = __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\ .wait_chldexit = __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\
.shared_pending = { \ .shared_pending = { \
.list = LIST_HEAD_INIT(sig.shared_pending.list), \ .list = LIST_HEAD_INIT(sig.shared_pending.list), \
...@@ -222,6 +223,7 @@ extern struct task_group root_task_group; ...@@ -222,6 +223,7 @@ extern struct task_group root_task_group;
[PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \ [PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \
}, \ }, \
.thread_group = LIST_HEAD_INIT(tsk.thread_group), \ .thread_group = LIST_HEAD_INIT(tsk.thread_group), \
.thread_node = LIST_HEAD_INIT(init_signals.thread_head), \
INIT_IDS \ INIT_IDS \
INIT_PERF_EVENTS(tsk) \ INIT_PERF_EVENTS(tsk) \
INIT_TRACE_IRQFLAGS \ INIT_TRACE_IRQFLAGS \
......
...@@ -549,6 +549,7 @@ struct signal_struct { ...@@ -549,6 +549,7 @@ struct signal_struct {
atomic_t sigcnt; atomic_t sigcnt;
atomic_t live; atomic_t live;
int nr_threads; int nr_threads;
struct list_head thread_head;
wait_queue_head_t wait_chldexit; /* for wait4() */ wait_queue_head_t wait_chldexit; /* for wait4() */
...@@ -1271,6 +1272,7 @@ struct task_struct { ...@@ -1271,6 +1272,7 @@ struct task_struct {
/* PID/PID hash table linkage. */ /* PID/PID hash table linkage. */
struct pid_link pids[PIDTYPE_MAX]; struct pid_link pids[PIDTYPE_MAX];
struct list_head thread_group; struct list_head thread_group;
struct list_head thread_node;
struct completion *vfork_done; /* for vfork() */ struct completion *vfork_done; /* for vfork() */
int __user *set_child_tid; /* CLONE_CHILD_SETTID */ int __user *set_child_tid; /* CLONE_CHILD_SETTID */
...@@ -2341,6 +2343,16 @@ extern bool current_is_single_threaded(void); ...@@ -2341,6 +2343,16 @@ extern bool current_is_single_threaded(void);
#define while_each_thread(g, t) \ #define while_each_thread(g, t) \
while ((t = next_thread(t)) != g) while ((t = next_thread(t)) != g)
#define __for_each_thread(signal, t) \
list_for_each_entry_rcu(t, &(signal)->thread_head, thread_node)
#define for_each_thread(p, t) \
__for_each_thread((p)->signal, t)
/* Careful: this is a double loop, 'break' won't work as expected. */
#define for_each_process_thread(p, t) \
for_each_process(p) for_each_thread(p, t)
static inline int get_nr_threads(struct task_struct *tsk) static inline int get_nr_threads(struct task_struct *tsk)
{ {
return tsk->signal->nr_threads; return tsk->signal->nr_threads;
......
...@@ -74,6 +74,7 @@ static void __unhash_process(struct task_struct *p, bool group_dead) ...@@ -74,6 +74,7 @@ static void __unhash_process(struct task_struct *p, bool group_dead)
__this_cpu_dec(process_counts); __this_cpu_dec(process_counts);
} }
list_del_rcu(&p->thread_group); list_del_rcu(&p->thread_group);
list_del_rcu(&p->thread_node);
} }
/* /*
......
...@@ -1035,6 +1035,11 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) ...@@ -1035,6 +1035,11 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
sig->nr_threads = 1; sig->nr_threads = 1;
atomic_set(&sig->live, 1); atomic_set(&sig->live, 1);
atomic_set(&sig->sigcnt, 1); atomic_set(&sig->sigcnt, 1);
/* list_add(thread_node, thread_head) without INIT_LIST_HEAD() */
sig->thread_head = (struct list_head)LIST_HEAD_INIT(tsk->thread_node);
tsk->thread_node = (struct list_head)LIST_HEAD_INIT(sig->thread_head);
init_waitqueue_head(&sig->wait_chldexit); init_waitqueue_head(&sig->wait_chldexit);
sig->curr_target = tsk; sig->curr_target = tsk;
init_sigpending(&sig->shared_pending); init_sigpending(&sig->shared_pending);
...@@ -1474,6 +1479,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1474,6 +1479,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
atomic_inc(&current->signal->sigcnt); atomic_inc(&current->signal->sigcnt);
list_add_tail_rcu(&p->thread_group, list_add_tail_rcu(&p->thread_group,
&p->group_leader->thread_group); &p->group_leader->thread_group);
list_add_tail_rcu(&p->thread_node,
&p->signal->thread_head);
} }
attach_pid(p, PIDTYPE_PID); attach_pid(p, PIDTYPE_PID);
nr_threads++; nr_threads++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment