copy_process/attach_pid minor fix + cleanups
Subsystems (4)
v2 cover copy_process/attach_pid minor fix + cleanups
Hello.
1/4 and 2/4 look like the obvious cleanups.
3/4 tries to fix the minor problem. I have to admit, initially I thought that this problem is more serious. But it seems that nothing really bad can happen if the new task has the wrong pid or group_leader. Still I think this makes sense.
4/4 is offtopic minor cleanup.
This is also another preparation for while_each_thread() fixes.
Oleg.
PATCH v2 1/4 de_thread: use change_pid() rather than detach_pid/attach_pid
de_thread() can use change_pid() instead of detach + attach. This looks better and this ensures that, say, next_thread() can never see a task with ->pid == NULL.
--- fs/exec.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)
fs/exec.c +1 -2
@@ -945,9 +945,8 @@ static int de_thread(struct task_struct *tsk)* Note: The old leader also uses this pid until release_task * is called. Odd but simple and correct. */ -detach_pid (tsk,PIDTYPE_PID ); tsk->pid = leader->pid; -attach_pid (tsk,PIDTYPE_PID ,task_pid (leader)); +change_pid (tsk,PIDTYPE_PID ,task_pid (leader));transfer_pid (leader, tsk,PIDTYPE_PGID );transfer_pid (leader, tsk,PIDTYPE_SID );
Signature · 1 line
1.5.5.1
Re: [PATCH 1/4] de_thread: use change_pid() rather than detach_pid/attach_pid
Quoted 3 lines · Oleg Nesterov
de_thread()can usechange_pid()instead of detach + attach. This looks better and this ensures that, say,next_thread()can never see a task with ->pid == NULL.
I want to say that there I though there was a good reason something to do with the exit logic.
However I just read through attach_pid, detach_pid, and change_pid. And the sequence detach_pid(...); attach_pid(...) is equiavalent to change_pid(...);
Quoted 19 lines
Signed-off-by Oleg Nesterov <[email protected]>--- fs/exec.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)fs/exec.c +1
-2@@ -945,9 +945,8 @@ static int de_thread(struct task_struct *tsk)* Note: The old leader also uses this pid until release_task * is called. Odd but simple and correct. */ -detach_pid (tsk,PIDTYPE_PID ); tsk->pid = leader->pid; -attach_pid (tsk,PIDTYPE_PID ,task_pid (leader)); +change_pid (tsk,PIDTYPE_PID ,task_pid (leader));transfer_pid (leader, tsk,PIDTYPE_PGID );transfer_pid (leader, tsk,PIDTYPE_SID );
PATCH v2 2/4 copy_process: unify CLONE_THREAD-or-thread_group_leader code
In reply to Oleg NesterovCleanup and preparation for the next changes.
Move the "if (clone_flags & CLONE_THREAD)" code down under "if (likely(p->pid))" and turn it into into the "else" branch. This makes the process/thread initialization more symmetrical and removes one check.
--- kernel/fork.c | 15 +++++++-------- 1 files changed, 7 insertions(+), 8 deletions(-)
kernel/fork.c +7 -8
@@ -1446,14 +1446,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto bad_fork_free_pid; } -if (clone_flags &CLONE_THREAD ) { - current->signal->nr_threads++; -atomic_inc (¤t->signal->live); -atomic_inc (¤t->signal->sigcnt); - p->group_leader = current->group_leader; -list_add_tail_rcu (&p->thread_group, &p->group_leader->thread_group); - } -if (likely (p->pid)) {ptrace_init_task (p, (clone_flags &CLONE_PTRACE ) || trace);
@@ -1470,6 +1462,13 @@ static struct task_struct *copy_process(unsigned long clone_flags,list_add_tail (&p->sibling, &p->real_parent->children);list_add_tail_rcu (&p->tasks, &init_task.tasks);__this_cpu_inc (process_counts); + }else { + current->signal->nr_threads++; +atomic_inc (¤t->signal->live); +atomic_inc (¤t->signal->sigcnt); + p->group_leader = current->group_leader; +list_add_tail_rcu (&p->thread_group, + &p->group_leader->thread_group); }attach_pid (p,PIDTYPE_PID , pid); nr_threads++;
Signature · 1 line
1.5.5.1
PATCH v2 3/4 copy_process: don't add the uninitialized child to thread/task/pid lists
In reply to Oleg Nesterovcopy_process() adds the new child to thread_group/init_task.tasks list and then does attach_pid(child, PIDTYPE_PID). This means that the lockless next_thread() or next_task() can see this thread with the wrong pid. Say, "ls /proc/pid/task" can list the same inode twice.
We could move attach_pid(child, PIDTYPE_PID) up, but in this case find_task_by_vpid() can find the new thread before it was fully initialized. And this is already true for PIDTYPE_PGID/PIDTYPE_SID,
With this patch copy_process() initializes child->pids[*].pid first, then calls attach_pid() to insert the task into the pid->tasks list.
attach_pid() no longer need the "struct pid*" argument, it is always called after pid_link->pid was already set.
--- include/linux/pid.h | 6 ++---- kernel/fork.c | 16 +++++++++++++--- kernel/pid.c | 12 ++++-------- 3 files changed, 19 insertions(+), 15 deletions(-)
include/linux/pid.h +2 -4
@@ -86,11 +86,9 @@ extern struct task_struct *get_pid_task(struct pid *pid, enum pid_type);extern struct pid *get_task_pid (struct task_struct *task,enum pid_type type);/* -* attach_pid() and detach_pid() must be called with the tasklist_lock -* write-held. +* these helpers must be called with the tasklist_lock write-held. */ -extern void attach_pid (struct task_struct *task,enum pid_type type, -struct pid *pid); +extern void attach_pid (struct task_struct *task,enum pid_type );extern void detach_pid (struct task_struct *task,enum pid_type );extern void change_pid (struct task_struct *task,enum pid_type ,struct pid *pid);
kernel/fork.c +13 -3
@@ -1121,6 +1121,12 @@ static void posix_cpu_timers_init(struct task_struct *tsk)INIT_LIST_HEAD (&tsk->cpu_timers[2 ]); } +static inline void +init_task_pid (struct task_struct *task,enum pid_type type,struct pid *pid) +{ + task->pids[type].pid = pid; +} +/* * This creates a new process as a copy of the old one, * but does not actually start it yet.
@@ -1449,7 +1455,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,if (likely (p->pid)) {ptrace_init_task (p, (clone_flags &CLONE_PTRACE ) || trace); +init_task_pid (p,PIDTYPE_PID , pid);if (thread_group_leader (p)) { +init_task_pid (p,PIDTYPE_PGID ,task_pgrp (current)); +init_task_pid (p,PIDTYPE_SID ,task_session (current)); +if (is_child_reaper (pid)) {ns_of_pid (pid)->child_reaper = p; p->signal->flags |=SIGNAL_UNKILLABLE ;
@@ -1457,10 +1467,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->signal->leader_pid = pid; p->signal->tty =tty_kref_get (current->signal->tty); -attach_pid (p,PIDTYPE_PGID ,task_pgrp (current)); -attach_pid (p,PIDTYPE_SID ,task_session (current));list_add_tail (&p->sibling, &p->real_parent->children);list_add_tail_rcu (&p->tasks, &init_task.tasks); +attach_pid (p,PIDTYPE_PGID ); +attach_pid (p,PIDTYPE_SID );__this_cpu_inc (process_counts); }else { current->signal->nr_threads++;
@@ -1470,7 +1480,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,list_add_tail_rcu (&p->thread_group, &p->group_leader->thread_group); } -attach_pid (p,PIDTYPE_PID , pid); +attach_pid (p,PIDTYPE_PID ); nr_threads++; }
kernel/pid.c +4 -8
@@ -373,14 +373,10 @@ EXPORT_SYMBOL_GPL(find_vpid);/* * attach_pid() must be called with the tasklist_lock write-held. */ -void attach_pid (struct task_struct *task,enum pid_type type, -struct pid *pid) +void attach_pid (struct task_struct *task,enum pid_type type) { -struct pid_link *link; - - link = &task->pids[type]; - link->pid = pid; -hlist_add_head_rcu (&link->node, &pid->tasks[type]); +struct pid_link *link = &task->pids[type]; +hlist_add_head_rcu (&link->node, &link->pid->tasks[type]); } static void __change_pid(struct task_struct *task, enum pid_type type,
@@ -412,7 +408,7 @@ void change_pid(struct task_struct *task, enum pid_type type, struct pid *pid) {__change_pid (task, type, pid); -attach_pid (task, type, pid); +attach_pid (task, type); }/* transfer_pid is an optimization of attach_pid(new), detach_pid(old) */
Signature · 1 line
1.5.5.1
PATCH v2 4/4 copy_process: consolidate the lockless CLONE_THREAD checks
In reply to Oleg Nesterovcopy_process() does a lot of "chaotic" initializations and checks CLONE_THREAD twice before it takes tasklist. In particular it sets "p->group_leader = p" and then changes it again under tasklist if !thread_group_leader(p).
This looks a bit confusing, lets create a single "if (CLONE_THREAD)" block which initializes ->exit_signal, ->group_leader, and ->tgid.
--- kernel/fork.c | 33 ++++++++++++++++----------------- 1 files changed, 16 insertions(+), 17 deletions(-)
kernel/fork.c +16 -17
@@ -1360,11 +1360,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto bad_fork_cleanup_io; } - p->pid =pid_nr (pid); - p->tgid = p->pid; -if (clone_flags &CLONE_THREAD ) - p->tgid = current->tgid; - p->set_child_tid = (clone_flags &CLONE_CHILD_SETTID ) ? child_tidptr :NULL ;/* * Clear TID on mm_release()?
@@ -1400,12 +1395,19 @@ static struct task_struct *copy_process(unsigned long clone_flags,clear_all_latency_tracing (p);/* ok, now we should be set up.. */ -if (clone_flags &CLONE_THREAD ) + p->pid =pid_nr (pid); +if (clone_flags &CLONE_THREAD ) { p->exit_signal =-1 ; -else if (clone_flags &CLONE_PARENT ) - p->exit_signal = current->group_leader->exit_signal; -else - p->exit_signal = (clone_flags &CSIGNAL ); + p->group_leader = current->group_leader; + p->tgid = current->tgid; + }else { +if (clone_flags &CLONE_PARENT ) + p->exit_signal = current->group_leader->exit_signal; +else + p->exit_signal = (clone_flags &CSIGNAL ); + p->group_leader = p; + p->tgid = p->pid; + } p->pdeath_signal =0 ; p->exit_state =0 ;
@@ -1414,15 +1416,13 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->nr_dirtied_pause =128 >> (PAGE_SHIFT -10 ); p->dirty_paused_when =0 ; -/* -* Ok, make it visible to the rest of the system. -* We dont wake it up yet. -*/ - p->group_leader = p;INIT_LIST_HEAD (&p->thread_group); p->task_works =NULL ; -/* Need tasklist lock for parent etc handling! */ +/* +* Make it visible to the rest of the system, but dont wake it up yet. +* Need tasklist lock for parent etc handling! +*/ write_lock_irq (&tasklist_lock);/* CLONE_PARENT re-uses the old parent */
@@ -1476,7 +1476,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, current->signal->nr_threads++;atomic_inc (¤t->signal->live);atomic_inc (¤t->signal->sigcnt); - p->group_leader = current->group_leader;list_add_tail_rcu (&p->thread_group, &p->group_leader->thread_group); }
Signature · 1 line
1.5.5.1