[PATCH] fix handling of SIGCHILD from reaped child

From: KAMEZAWA Hiroyuki
Date: Tue Feb 20 2007 - 04:34:11 EST


SUSv3 says
==
if SIGCHLD is blocked, if wait() or waitpid() return because the status of a
child process is available, any pending SIGCHLD signal shall be cleared unless
the status of another child process is available.
==

This patch tries to implement above functionality (clear SIGCHLD from reaped
process.)
please review...

works well on 2.6.20/ia64/NUMA environment and passed my easy test.

Signed-Off-By: KAMEZAWA Hiroyuki <kamezawa.hiroyu@xxxxxxxxxxxxxx>

Index: linux-2.6.20-devel/kernel/signal.c
===================================================================
--- linux-2.6.20-devel.orig/kernel/signal.c
+++ linux-2.6.20-devel/kernel/signal.c
@@ -382,7 +382,7 @@ unblock_all_signals(void)
spin_unlock_irqrestore(&current->sighand->siglock, flags);
}

-static int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
+static int collect_signal(int sig, struct sigpending *list, siginfo_t *info, pid_t checkpid)
{
struct sigqueue *q, *first = NULL;
int still_pending = 0;
@@ -394,13 +394,24 @@ static int collect_signal(int sig, struc
* Collect the siginfo appropriate to this signal. Check if
* there is another siginfo for the same signal.
*/
- list_for_each_entry(q, &list->list, list) {
- if (q->info.si_signo == sig) {
- if (first) {
- still_pending = 1;
- break;
+ if (unlikely(checkpid)) {
+ list_for_each_entry(q, &list->list, list) {
+ if (q->info.si_signo == sig) {
+ if (q->info.si_pid == checkpid)
+ first = q;
+ else
+ still_pending = 1;
+ }
+ }
+ } else {
+ list_for_each_entry(q, &list->list, list) {
+ if (q->info.si_signo == sig) {
+ if (first) {
+ still_pending = 1;
+ break;
+ }
+ first = q;
}
- first = q;
}
}
if (first) {
@@ -415,7 +426,8 @@ static int collect_signal(int sig, struc
a fast-pathed signal or we must have been
out of queue space. So zero out the info.
*/
- sigdelset(&list->signal, sig);
+ if (!still_pending)
+ sigdelset(&list->signal, sig);
info->si_signo = sig;
info->si_errno = 0;
info->si_code = 0;
@@ -440,7 +452,7 @@ static int __dequeue_signal(struct sigpe
}
}

- if (!collect_signal(sig, pending, info))
+ if (!collect_signal(sig, pending, info, 0))
sig = 0;
}

@@ -493,6 +505,24 @@ int dequeue_signal(struct task_struct *t
}

/*
+ * only called by do_wait() when it reaps its child.
+ * This function clears pending SIGCHLD from the reaped child.
+ * rerely does real job...
+ * Becasue we only check SIGCHLD, just checks shared_pending.
+ */
+void clear_stale_sigchild(struct task_struct *tsk, pid_t child_id)
+{
+ siginfo_t info;
+ unsigned long flags;
+ spin_lock_irqsave(&tsk->sighand->siglock, flags);
+ collect_signal(SIGCHLD, &tsk->signal->shared_pending, &info, child_id);
+ spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+ return;
+}
+
+
+
+/*
* Tell a process that it has a new active signal..
*
* NOTE! we rely on the previous spin_lock to
Index: linux-2.6.20-devel/kernel/exit.c
===================================================================
--- linux-2.6.20-devel.orig/kernel/exit.c
+++ linux-2.6.20-devel/kernel/exit.c
@@ -1252,8 +1252,12 @@ static int wait_task_zombie(struct task_
}
write_unlock_irq(&tasklist_lock);
}
- if (p != NULL)
+ if (p != NULL) {
release_task(p);
+ /* if we received sigchild from "p" and p is released,
+ we remove sigchild from it. */
+ clear_stale_sigchild(current, retval);
+ }
BUG_ON(!retval);
return retval;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/