Make svr4_sys_waitsys() a lot less ugly and mark it MPSAFE.

- If the WNOWAIT flag isn't specified and either of WEXITED or WTRAPPED is
  set, then just call kern_wait() and let it do all the work.  This means
  that this function no longer has to duplicate the work to teardown
  zombies that is done in kern_wait().  Instead, if the above conditions
  aren't true, then it uses a simpler loop to implement WNOWAIT and/or
  tracing for only stopped or continued processes.  This function still
  has to duplicate code from kern_wait() for the latter two cases, but
  those are much simpler.
- Sync the code to handle the WCONTINUED and WSTOPPED cases with the
  equivalent code in kern_wait().
- Fix several places that would return with the proctree lock still held.
- Lock the current process to prevent lost wakeup races when blocking.
This commit is contained in:
John Baldwin 2006-07-19 19:01:10 +00:00
parent 9079458ad2
commit a3616b117c
2 changed files with 152 additions and 158 deletions

View File

@ -104,7 +104,7 @@ static int svr4_mknod(struct thread *, register_t *, char *,
svr4_mode_t, svr4_dev_t);
static __inline clock_t timeval_to_clock_t(struct timeval *);
static int svr4_setinfo (struct proc *, int, svr4_siginfo_t *);
static int svr4_setinfo (pid_t , struct rusage *, int, svr4_siginfo_t *);
struct svr4_hrtcntl_args;
static int svr4_hrtcntl (struct thread *, struct svr4_hrtcntl_args *,
@ -1083,12 +1083,12 @@ svr4_sys_hrtsys(td, uap)
static int
svr4_setinfo(p, st, s)
struct proc *p;
svr4_setinfo(pid, ru, st, s)
pid_t pid;
struct rusage *ru;
int st;
svr4_siginfo_t *s;
{
struct timeval utime, stime;
svr4_siginfo_t i;
int sig;
@ -1097,13 +1097,10 @@ svr4_setinfo(p, st, s)
i.svr4_si_signo = SVR4_SIGCHLD;
i.svr4_si_errno = 0; /* XXX? */
if (p) {
i.svr4_si_pid = p->p_pid;
PROC_LOCK(p);
calcru(p, &utime, &stime);
PROC_UNLOCK(p);
i.svr4_si_stime = stime.tv_sec;
i.svr4_si_utime = utime.tv_sec;
i.svr4_si_pid = pid;
if (ru) {
i.svr4_si_stime = ru->ru_stime.tv_sec;
i.svr4_si_utime = ru->ru_utime.tv_sec;
}
if (WIFEXITED(st)) {
@ -1142,188 +1139,185 @@ svr4_sys_waitsys(td, uap)
struct thread *td;
struct svr4_sys_waitsys_args *uap;
{
int nfound;
struct rusage ru;
pid_t pid;
int nfound, status;
int error, *retval = td->td_retval;
struct proc *p, *q, *t;
struct proc *p, *q;
p = td->td_proc;
DPRINTF(("waitsys(%d, %d, %p, %x)\n",
uap->grp, uap->id,
uap->info, uap->options));
q = td->td_proc;
switch (uap->grp) {
case SVR4_P_PID:
case SVR4_P_PID:
pid = uap->id;
break;
case SVR4_P_PGID:
PROC_LOCK(p);
uap->id = -p->p_pgid;
PROC_UNLOCK(p);
PROC_LOCK(q);
pid = -q->p_pgid;
PROC_UNLOCK(q);
break;
case SVR4_P_ALL:
uap->id = WAIT_ANY;
pid = WAIT_ANY;
break;
default:
return EINVAL;
}
DPRINTF(("waitsys(%d, %d, %p, %x)\n",
uap->grp, uap->id,
uap->info, uap->options));
/* Hand off the easy cases to kern_wait(). */
if (!(uap->options & (SVR4_WNOWAIT)) &&
(uap->options & (SVR4_WEXITED | SVR4_WTRAPPED))) {
int options;
options = 0;
if (uap->options & SVR4_WSTOPPED)
options |= WUNTRACED;
if (uap->options & SVR4_WCONTINUED)
options |= WCONTINUED;
if (uap->options & SVR4_WNOHANG)
options |= WNOHANG;
error = kern_wait(td, pid, &status, options, &ru);
if (error)
return (error);
if (uap->options & SVR4_WNOHANG && *retval == 0)
error = svr4_setinfo(*retval, NULL, 0, uap->info);
else
error = svr4_setinfo(*retval, &ru, status, uap->info);
*retval = 0;
return (error);
}
/*
* Ok, handle the weird cases. Either WNOWAIT is set (meaning we
* just want to see if there is a process to harvest, we dont'
* want to actually harvest it), or WEXIT and WTRAPPED are clear
* meaning we want to ignore zombies. Either way, we don't have
* to handle harvesting zombies here. We do have to duplicate the
* other portions of kern_wait() though, especially for the
* WCONTINUED and WSTOPPED.
*/
loop:
nfound = 0;
sx_slock(&proctree_lock);
LIST_FOREACH(q, &p->p_children, p_sibling) {
PROC_LOCK(q);
if (uap->id != WAIT_ANY &&
q->p_pid != uap->id &&
q->p_pgid != -uap->id) {
PROC_UNLOCK(q);
DPRINTF(("pid %d pgid %d != %d\n", q->p_pid,
q->p_pgid, uap->id));
LIST_FOREACH(p, &q->p_children, p_sibling) {
PROC_LOCK(p);
if (pid != WAIT_ANY &&
p->p_pid != pid && p->p_pgid != -pid) {
PROC_UNLOCK(p);
DPRINTF(("pid %d pgid %d != %d\n", p->p_pid,
p->p_pgid, pid));
continue;
}
if (p_canwait(td, p)) {
PROC_UNLOCK(p);
continue;
}
nfound++;
if ((q->p_state == PRS_ZOMBIE) &&
/*
* See if we have a zombie. If so, WNOWAIT should be set,
* as otherwise we should have called kern_wait() up above.
*/
if ((p->p_state == PRS_ZOMBIE) &&
((uap->options & (SVR4_WEXITED|SVR4_WTRAPPED)))) {
PROC_UNLOCK(q);
KASSERT(uap->options & SVR4_WNOWAIT,
("WNOWAIT is clear"));
/* Found a zombie, so cache info in local variables. */
pid = p->p_pid;
status = p->p_xstat;
ru = *p->p_ru;
calcru(p, &ru.ru_utime, &ru.ru_stime);
PROC_UNLOCK(p);
sx_sunlock(&proctree_lock);
/* Copy the info out to userland. */
*retval = 0;
DPRINTF(("found %d\n", q->p_pid));
error = svr4_setinfo(q, q->p_xstat, uap->info);
if (error != 0)
return error;
if ((uap->options & SVR4_WNOWAIT)) {
DPRINTF(("Don't wait\n"));
return 0;
}
/*
* If we got the child via ptrace(2) or procfs, and
* the parent is different (meaning the process was
* attached, rather than run as a child), then we need
* to give it back to the old parent, and send the
* parent a SIGCHLD. The rest of the cleanup will be
* done when the old parent waits on the child.
*/
sx_xlock(&proctree_lock);
PROC_LOCK(q);
if (q->p_flag & P_TRACED) {
if (q->p_oppid != q->p_pptr->p_pid) {
PROC_UNLOCK(q);
t = pfind(q->p_oppid);
if (t == NULL) {
t = initproc;
PROC_LOCK(initproc);
}
PROC_LOCK(q);
proc_reparent(q, t);
q->p_oppid = 0;
q->p_flag &= ~(P_TRACED | P_WAITED);
PROC_UNLOCK(q);
psignal(t, SIGCHLD);
wakeup(t);
PROC_UNLOCK(t);
sx_xunlock(&proctree_lock);
return 0;
}
}
PROC_UNLOCK(q);
sx_xunlock(&proctree_lock);
q->p_xstat = 0;
ruadd(&p->p_stats->p_cru, &p->p_crux, q->p_ru,
&q->p_rux);
FREE(q->p_ru, M_ZOMBIE);
q->p_ru = NULL;
/*
* Decrement the count of procs running with this uid.
*/
(void)chgproccnt(q->p_ucred->cr_ruidinfo, -1, 0);
/*
* Release reference to text vnode.
*/
if (q->p_textvp)
vrele(q->p_textvp);
/*
* Free up credentials.
*/
crfree(q->p_ucred);
q->p_ucred = NULL;
/*
* Remove unused arguments
*/
pargs_drop(q->p_args);
PROC_UNLOCK(q);
/*
* Finally finished with old proc entry.
* Unlink it from its process group and free it.
*/
sx_xlock(&proctree_lock);
leavepgrp(q);
sx_xlock(&allproc_lock);
LIST_REMOVE(q, p_list); /* off zombproc */
sx_xunlock(&allproc_lock);
LIST_REMOVE(q, p_sibling);
sx_xunlock(&proctree_lock);
PROC_LOCK(q);
sigacts_free(q->p_sigacts);
q->p_sigacts = NULL;
PROC_UNLOCK(q);
/*
* Give machine-dependent layer a chance
* to free anything that cpu_exit couldn't
* release while still running in process context.
*/
vm_waitproc(q);
#if defined(__NetBSD__)
pool_put(&proc_pool, q);
#endif
#ifdef __FreeBSD__
mtx_destroy(&q->p_mtx);
#ifdef MAC
mac_destroy_proc(q);
#endif
uma_zfree(proc_zone, q);
#endif
nprocs--;
return 0;
DPRINTF(("found %d\n", pid));
return (svr4_setinfo(pid, &ru, status, uap->info));
}
/* XXXKSE this needs clarification */
if (P_SHOULDSTOP(q) && ((q->p_flag & P_WAITED) == 0) &&
(q->p_flag & P_TRACED ||
(uap->options & (SVR4_WSTOPPED|SVR4_WCONTINUED)))) {
DPRINTF(("jobcontrol %d\n", q->p_pid));
/*
* See if we have a stopped or continued process.
* XXX: This duplicates the same code in kern_wait().
*/
mtx_lock_spin(&sched_lock);
if ((p->p_flag & P_STOPPED_SIG) &&
(p->p_suspcount == p->p_numthreads) &&
(p->p_flag & P_WAITED) == 0 &&
(p->p_flag & P_TRACED || uap->options & SVR4_WSTOPPED)) {
mtx_unlock_spin(&sched_lock);
if (((uap->options & SVR4_WNOWAIT)) == 0)
q->p_flag |= P_WAITED;
PROC_UNLOCK(q);
p->p_flag |= P_WAITED;
sx_sunlock(&proctree_lock);
pid = p->p_pid;
status = W_STOPCODE(p->p_xstat);
ru = *p->p_ru;
calcru(p, &ru.ru_utime, &ru.ru_stime);
PROC_UNLOCK(p);
if (((uap->options & SVR4_WNOWAIT)) == 0) {
PROC_LOCK(q);
sigqueue_take(p->p_ksi);
PROC_UNLOCK(q);
}
*retval = 0;
return svr4_setinfo(q, W_STOPCODE(q->p_xstat),
uap->info);
DPRINTF(("jobcontrol %d\n", pid));
return (svr4_setinfo(pid, &ru, status, uap->info));
}
PROC_UNLOCK(q);
mtx_unlock_spin(&sched_lock);
if (uap->options & SVR4_WCONTINUED &&
(p->p_flag & P_CONTINUED)) {
sx_sunlock(&proctree_lock);
if (((uap->options & SVR4_WNOWAIT)) == 0)
p->p_flag &= ~P_CONTINUED;
pid = p->p_pid;
ru = *p->p_ru;
status = SIGCONT;
calcru(p, &ru.ru_utime, &ru.ru_stime);
PROC_UNLOCK(p);
if (((uap->options & SVR4_WNOWAIT)) == 0) {
PROC_LOCK(q);
sigqueue_take(p->p_ksi);
PROC_UNLOCK(q);
}
*retval = 0;
DPRINTF(("jobcontrol %d\n", pid));
return (svr4_setinfo(pid, &ru, status, uap->info));
}
PROC_UNLOCK(p);
}
if (nfound == 0)
return ECHILD;
if (nfound == 0) {
sx_sunlock(&proctree_lock);
return (ECHILD);
}
if (uap->options & SVR4_WNOHANG) {
sx_sunlock(&proctree_lock);
*retval = 0;
if ((error = svr4_setinfo(NULL, 0, uap->info)) != 0)
return error;
return 0;
return (svr4_setinfo(0, NULL, 0, uap->info));
}
if ((error = tsleep(p, PWAIT | PCATCH, "svr4_wait", 0)) != 0)
PROC_LOCK(q);
sx_sunlock(&proctree_lock);
if (q->p_flag & P_STATCHILD) {
q->p_flag &= ~P_STATCHILD;
error = 0;
} else
error = msleep(q, &q->p_mtx, PWAIT | PCATCH, "svr4_wait", 0);
PROC_UNLOCK(q);
if (error)
return error;
goto loop;
}

View File

@ -183,7 +183,7 @@
struct svr4_statvfs *fs); }
105 AUE_NULL UNIMPL whoknows
106 AUE_NULL UNIMPL nfssvc
107 AUE_NULL STD { int svr4_sys_waitsys(int grp, int id, \
107 AUE_NULL MSTD { int svr4_sys_waitsys(int grp, int id, \
union svr4_siginfo *info, int options); }
108 AUE_NULL UNIMPL sigsendsys
109 AUE_NULL MSTD { int svr4_sys_hrtsys(int cmd, int fun, \