summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2003-09-21 01:37:10 -0700
committerLinus Torvalds <torvalds@home.osdl.org>2003-09-21 01:37:10 -0700
commitfeaecce47220d2d8bf6f0d76e09dc623dbf991e0 (patch)
treebc7955b15df8dcdad59480fadc41c024c46e0231 /kernel
parent55b50278ec233024c2e5be04855d66ebdcebc35e (diff)
[PATCH] Fix setpgid and threads
From: Jeremy Fitzhardinge <jeremy@goop.org> I'm resending my patch to fix this problem. To recap: every task_struct has its own copy of the thread group's pgrp. Only the thread group leader is allowed to change the tgrp's pgrp, but it only updates its own copy of pgrp, while all the other threads in the tgrp use the old value they inherited on creation. This patch simply updates all the other thread's pgrp when the tgrp leader changes pgrp. Ulrich has already expressed reservations about this patch since it is (1) incomplete (it doesn't cover the case of other ids which have similar problems), (2) racy (it doesn't synchronize with other threads looking at the task pgrp, so they could see an inconsistent view) and (3) slow (it takes linear time with respect to the number of threads in the tgrp). My reaction is that (1) it fixes the actual bug I'm encountering in a real program. (2) doesn't really matter for pgrp, since it is mostly an issue with respect to the terminal job-control code (which is even more broken without this patch. Regarding (3), I think there are very few programs which have a large number of threads which change process group id on a regular basis (a heavily multi-threaded job-control shell?). Ulrich also said he has a (proposed?) much better fix, which I've been looking forward to. I'm submitting this patch as a stop-gap fix for a real bug, and perhaps to prompt the improved patch. An alternative fix, at least for pgrp, is to change all references to ->pgrp to group_leader->pgrp. This may be sufficient on its own, but it would be a reasonably intrusive patch (I count 95 instances in 32 files in the 2.6.0-test3-mm3 tree).
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c24
-rw-r--r--kernel/fork.c2
-rw-r--r--kernel/pid.c4
-rw-r--r--kernel/signal.c4
-rw-r--r--kernel/sys.c17
5 files changed, 26 insertions, 25 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index b6174f82adf9..c565fd69d559 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -152,7 +152,7 @@ static int will_become_orphaned_pgrp(int pgrp, task_t *ignored_task)
|| p->state >= TASK_ZOMBIE
|| p->real_parent->pid == 1)
continue;
- if (p->real_parent->pgrp != pgrp
+ if (process_group(p->real_parent) != pgrp
&& p->real_parent->session == p->session) {
ret = 0;
break;
@@ -247,9 +247,9 @@ void __set_special_pids(pid_t session, pid_t pgrp)
curr->session = session;
attach_pid(curr, PIDTYPE_SID, session);
}
- if (curr->pgrp != pgrp) {
+ if (process_group(curr) != pgrp) {
detach_pid(curr, PIDTYPE_PGID);
- curr->pgrp = pgrp;
+ curr->group_leader->__pgrp = pgrp;
attach_pid(curr, PIDTYPE_PGID, pgrp);
}
}
@@ -508,9 +508,9 @@ static inline void reparent_thread(task_t *p, task_t *father, int traced)
* than we are, and it was the only connection
* outside, so the child pgrp is now orphaned.
*/
- if ((p->pgrp != father->pgrp) &&
+ if ((process_group(p) != process_group(father)) &&
(p->session == father->session)) {
- int pgrp = p->pgrp;
+ int pgrp = process_group(p);
if (will_become_orphaned_pgrp(pgrp, NULL) && has_stopped_jobs(pgrp)) {
__kill_pg_info(SIGHUP, (void *)1, pgrp);
@@ -618,12 +618,12 @@ static void exit_notify(struct task_struct *tsk)
t = tsk->real_parent;
- if ((t->pgrp != tsk->pgrp) &&
+ if ((process_group(t) != process_group(tsk)) &&
(t->session == tsk->session) &&
- will_become_orphaned_pgrp(tsk->pgrp, tsk) &&
- has_stopped_jobs(tsk->pgrp)) {
- __kill_pg_info(SIGHUP, (void *)1, tsk->pgrp);
- __kill_pg_info(SIGCONT, (void *)1, tsk->pgrp);
+ will_become_orphaned_pgrp(process_group(tsk), tsk) &&
+ has_stopped_jobs(process_group(tsk))) {
+ __kill_pg_info(SIGHUP, (void *)1, process_group(tsk));
+ __kill_pg_info(SIGCONT, (void *)1, process_group(tsk));
}
/* Let father know we died
@@ -813,10 +813,10 @@ static int eligible_child(pid_t pid, int options, task_t *p)
if (p->pid != pid)
return 0;
} else if (!pid) {
- if (p->pgrp != current->pgrp)
+ if (process_group(p) != process_group(current))
return 0;
} else if (pid != -1) {
- if (p->pgrp != -pid)
+ if (process_group(p) != -pid)
return 0;
}
diff --git a/kernel/fork.c b/kernel/fork.c
index 37d79b4e16e6..50535a16c71e 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1004,7 +1004,7 @@ struct task_struct *copy_process(unsigned long clone_flags,
attach_pid(p, PIDTYPE_PID, p->pid);
if (thread_group_leader(p)) {
attach_pid(p, PIDTYPE_TGID, p->tgid);
- attach_pid(p, PIDTYPE_PGID, p->pgrp);
+ attach_pid(p, PIDTYPE_PGID, process_group(p));
attach_pid(p, PIDTYPE_SID, p->session);
if (p->pid)
__get_cpu_var(process_counts)++;
diff --git a/kernel/pid.c b/kernel/pid.c
index 00413e3967b9..713f54eaeda9 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -250,13 +250,13 @@ void switch_exec_pids(task_t *leader, task_t *thread)
attach_pid(thread, PIDTYPE_PID, thread->pid);
attach_pid(thread, PIDTYPE_TGID, thread->tgid);
- attach_pid(thread, PIDTYPE_PGID, thread->pgrp);
+ attach_pid(thread, PIDTYPE_PGID, leader->__pgrp);
attach_pid(thread, PIDTYPE_SID, thread->session);
list_add_tail(&thread->tasks, &init_task.tasks);
attach_pid(leader, PIDTYPE_PID, leader->pid);
attach_pid(leader, PIDTYPE_TGID, leader->tgid);
- attach_pid(leader, PIDTYPE_PGID, leader->pgrp);
+ attach_pid(leader, PIDTYPE_PGID, leader->__pgrp);
attach_pid(leader, PIDTYPE_SID, leader->session);
}
diff --git a/kernel/signal.c b/kernel/signal.c
index 72333be1fd42..852da1a009da 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1139,7 +1139,7 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid)
static int kill_something_info(int sig, struct siginfo *info, int pid)
{
if (!pid) {
- return kill_pg_info(sig, info, current->pgrp);
+ return kill_pg_info(sig, info, process_group(current));
} else if (pid == -1) {
int retval = 0, count = 0;
struct task_struct * p;
@@ -1798,7 +1798,7 @@ relock:
/* signals can be posted during this window */
- if (is_orphaned_pgrp(current->pgrp))
+ if (is_orphaned_pgrp(process_group(current)))
goto relock;
spin_lock_irq(&current->sighand->siglock);
diff --git a/kernel/sys.c b/kernel/sys.c
index 02b5a12dfd59..9c6da1d16d9a 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -290,7 +290,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
break;
case PRIO_PGRP:
if (!who)
- who = current->pgrp;
+ who = process_group(current);
for_each_task_pid(who, PIDTYPE_PGID, p, l, pid)
error = set_one_prio(p, niceval, error);
break;
@@ -346,7 +346,7 @@ asmlinkage long sys_getpriority(int which, int who)
break;
case PRIO_PGRP:
if (!who)
- who = current->pgrp;
+ who = process_group(current);
for_each_task_pid(who, PIDTYPE_PGID, p, l, pid) {
niceval = 20 - task_nice(p);
if (niceval > retval)
@@ -979,11 +979,12 @@ ok_pgid:
if (err)
goto out;
- if (p->pgrp != pgid) {
+ if (process_group(p) != pgid) {
detach_pid(p, PIDTYPE_PGID);
- p->pgrp = pgid;
+ p->group_leader->__pgrp = pgid;
attach_pid(p, PIDTYPE_PGID, pgid);
}
+
err = 0;
out:
/* All paths lead to here, thus we are safe. -DaveM */
@@ -994,7 +995,7 @@ out:
asmlinkage long sys_getpgid(pid_t pid)
{
if (!pid) {
- return current->pgrp;
+ return process_group(current);
} else {
int retval;
struct task_struct *p;
@@ -1006,7 +1007,7 @@ asmlinkage long sys_getpgid(pid_t pid)
if (p) {
retval = security_task_getpgid(p);
if (!retval)
- retval = p->pgrp;
+ retval = process_group(p);
}
read_unlock(&tasklist_lock);
return retval;
@@ -1016,7 +1017,7 @@ asmlinkage long sys_getpgid(pid_t pid)
asmlinkage long sys_getpgrp(void)
{
/* SMP - assuming writes are word atomic this is fine */
- return current->pgrp;
+ return process_group(current);
}
asmlinkage long sys_getsid(pid_t pid)
@@ -1059,7 +1060,7 @@ asmlinkage long sys_setsid(void)
__set_special_pids(current->pid, current->pid);
current->tty = NULL;
current->tty_old_pgrp = 0;
- err = current->pgrp;
+ err = process_group(current);
out:
write_unlock_irq(&tasklist_lock);
return err;