diff options
author | kib <kib@FreeBSD.org> | 2008-12-05 20:50:24 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2008-12-05 20:50:24 +0000 |
commit | ccad2ebfb201232304a87b7e7df40462dbede7b5 (patch) | |
tree | e6cf97b3b6771409805827fd23c9d98d223476d3 | |
parent | ca85df46cbe8e0ca1a618e41a0fb6ef93b7b940e (diff) | |
download | FreeBSD-src-ccad2ebfb201232304a87b7e7df40462dbede7b5.zip FreeBSD-src-ccad2ebfb201232304a87b7e7df40462dbede7b5.tar.gz |
Several threads in a process may do vfork() simultaneously. Then, all
parent threads sleep on the parent' struct proc until corresponding
child releases the vmspace. Each sleep is interlocked with proc mutex of
the child, that triggers assertion in the sleepq_add(). The assertion
requires that at any time, all simultaneous sleepers for the channel use
the same interlock.
Silent the assertion by using conditional variable allocated in the
child. Broadcast the variable event on exec() and exit().
Since struct proc * sleep wait channel is overloaded for several
unrelated events, I was unable to remove wakeups from the places where
cv_broadcast() is added, except exec().
Reported and tested by: ganbold
Suggested and reviewed by: jhb
MFC after: 2 week
-rw-r--r-- | sys/kern/kern_exec.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_exit.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_proc.c | 1 | ||||
-rw-r--r-- | sys/sys/proc.h | 2 |
5 files changed, 7 insertions, 2 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 02f9f2e..c89c9a6 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -609,7 +609,7 @@ interpret: p->p_flag |= P_EXEC; if (p->p_pptr && (p->p_flag & P_PPWAIT)) { p->p_flag &= ~P_PPWAIT; - wakeup(p->p_pptr); + cv_broadcast(&p->p_pwait); } /* diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 3b45233..4b4a417 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -543,6 +543,7 @@ exit1(struct thread *td, int rv) * proc lock. */ wakeup(p->p_pptr); + cv_broadcast(&p->p_pwait); sched_exit(p->p_pptr, td); PROC_SLOCK(p); p->p_state = PRS_ZOMBIE; @@ -774,6 +775,7 @@ loop: PROC_UNLOCK(p); tdsignal(t, NULL, SIGCHLD, p->p_ksi); wakeup(t); + cv_broadcast(&p->p_pwait); PROC_UNLOCK(t); sx_xunlock(&proctree_lock); return (0); diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 96dd9c8..e7e98b1 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -754,7 +754,7 @@ again: */ PROC_LOCK(p2); while (p2->p_flag & P_PPWAIT) - msleep(p1, &p2->p_mtx, PWAIT, "ppwait", 0); + cv_wait(&p2->p_pwait, &p2->p_mtx); PROC_UNLOCK(p2); /* diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 4e5ceea..0b6b6d5 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -231,6 +231,7 @@ proc_init(void *mem, int size, int flags) bzero(&p->p_mtx, sizeof(struct mtx)); mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK); mtx_init(&p->p_slock, "process slock", NULL, MTX_SPIN | MTX_RECURSE); + cv_init(&p->p_pwait, "ppwait"); TAILQ_INIT(&p->p_threads); /* all threads in proc */ EVENTHANDLER_INVOKE(process_init, p); p->p_stats = pstats_alloc(); diff --git a/sys/sys/proc.h b/sys/sys/proc.h index fd490b0..c592aaf 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -40,6 +40,7 @@ #include <sys/callout.h> /* For struct callout. */ #include <sys/event.h> /* For struct klist. */ +#include <sys/condvar.h> #ifndef _KERNEL #include <sys/filedesc.h> #endif @@ -540,6 +541,7 @@ struct proc { STAILQ_HEAD(, ktr_request) p_ktr; /* (o) KTR event queue. */ LIST_HEAD(, mqueue_notifier) p_mqnotifier; /* (c) mqueue notifiers.*/ struct kdtrace_proc *p_dtrace; /* (*) DTrace-specific data. */ + struct cv p_pwait; /* (*) wait cv for exit/exec */ }; #define p_session p_pgrp->pg_session |