#include "opt_ktrace.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysmsg.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/ktrace.h>
#include <sys/pioctl.h>
#include <sys/tty.h>
#include <sys/wait.h>
#include <sys/vnode.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/taskqueue.h>
#include <sys/ptrace.h>
#include <sys/acct.h>
#include <sys/filedesc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/jail.h>
#include <sys/kern_syscall.h>
#include <sys/unistd.h>
#include <sys/eventhandler.h>
#include <sys/dsched.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_extern.h>
#include <sys/refcount.h>
#include <sys/spinlock2.h>
static void reaplwps(void *context, int dummy);
static void reaplwp(struct lwp *lp);
static void killlwps(struct lwp *lp);
static MALLOC_DEFINE(M_ATEXIT, "atexit", "atexit callback");
struct exitlist {
exitlist_fn function;
TAILQ_ENTRY(exitlist) next;
};
TAILQ_HEAD(exit_list_head, exitlist);
static struct exit_list_head exit_list = TAILQ_HEAD_INITIALIZER(exit_list);
static struct task *deadlwp_task[MAXCPU];
static struct lwplist deadlwp_list[MAXCPU];
static struct lwkt_token deadlwp_token[MAXCPU];
void (*linux_task_drop_callback)(thread_t td);
void (*linux_proc_drop_callback)(struct proc *p);
int
sys_exit(struct sysmsg *sysmsg, const struct exit_args *uap)
{
exit1(W_EXITCODE(uap->rval, 0));
}
int
sys_extexit(struct sysmsg *sysmsg, const struct extexit_args *uap)
{
struct proc *p = curproc;
int action, who;
int error;
action = EXTEXIT_ACTION(uap->how);
who = EXTEXIT_WHO(uap->how);
switch (who) {
case EXTEXIT_PROC:
case EXTEXIT_LWP:
break;
default:
return (EINVAL);
}
switch (action) {
case EXTEXIT_SIMPLE:
break;
case EXTEXIT_SETINT:
error = copyout(&uap->status, uap->addr, sizeof(uap->status));
if (error)
return (error);
break;
default:
return (EINVAL);
}
lwkt_gettoken(&p->p_token);
switch (who) {
case EXTEXIT_LWP:
if (p->p_nthreads > 1) {
lwp_exit(0, NULL);
}
default:
case EXTEXIT_PROC:
lwkt_reltoken(&p->p_token);
exit1(W_EXITCODE(uap->status, 0));
}
lwkt_reltoken(&p->p_token);
}
int
killalllwps(int forexec)
{
struct lwp *lp = curthread->td_lwp;
struct proc *p = lp->lwp_proc;
int fakestop;
lwkt_gettoken(&p->p_token);
if (p->p_flags & P_WEXIT) {
lwkt_reltoken(&p->p_token);
return (EALREADY);
}
p->p_flags |= P_WEXIT;
lwkt_gettoken(&lp->lwp_token);
if (lp->lwp_mpflags & LWP_MP_WSTOP) {
fakestop = 0;
} else {
atomic_set_int(&lp->lwp_mpflags, LWP_MP_WSTOP);
++p->p_nstopped;
fakestop = 1;
wakeup(&p->p_nstopped);
}
atomic_set_int(&lp->lwp_mpflags, LWP_MP_WEXIT);
if (p->p_nthreads > 1)
killlwps(lp);
if (fakestop && (lp->lwp_mpflags & LWP_MP_WSTOP)) {
atomic_clear_int(&lp->lwp_mpflags, LWP_MP_WSTOP);
--p->p_nstopped;
}
if (forexec) {
atomic_clear_int(&lp->lwp_mpflags, LWP_MP_WEXIT);
p->p_flags &= ~P_WEXIT;
}
lwkt_reltoken(&lp->lwp_token);
lwkt_reltoken(&p->p_token);
return(0);
}
static void
killlwps(struct lwp *lp)
{
struct proc *p = lp->lwp_proc;
struct lwp *tlp;
FOREACH_LWP_IN_PROC(tlp, p) {
LWPHOLD(tlp);
lwkt_gettoken(&tlp->lwp_token);
if ((tlp->lwp_mpflags & LWP_MP_WEXIT) == 0) {
atomic_set_int(&tlp->lwp_mpflags, LWP_MP_WEXIT);
lwpsignal(p, tlp, SIGKILL);
}
lwkt_reltoken(&tlp->lwp_token);
LWPRELE(tlp);
}
wakeup(p);
while (p->p_nthreads > 1)
tsleep(&p->p_nthreads, 0, "killlwps", 0);
}
void
exit1(int rv)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct lwp *lp = td->td_lwp;
struct proc *q;
struct proc *pp;
struct proc *reproc;
struct sysreaper *reap;
struct vmspace *vm;
struct vnode *vtmp;
struct exitlist *ep;
int error;
lwkt_gettoken(&p->p_token);
if (p->p_pid == 1) {
kprintf("init died (signal %d, exit %d)\n",
WTERMSIG(rv), WEXITSTATUS(rv));
panic("Going nowhere without my init!");
}
varsymset_clean(&p->p_varsymset);
lockuninit(&p->p_varsymset.vx_lock);
error = killalllwps(0);
if (error) {
lwp_exit(0, NULL);
}
if (p == p->p_leader) {
struct sysmsg sysmsg;
sysmsg.extargs.kill.signum = SIGKILL;
q = p->p_peers;
while (q) {
sysmsg.extargs.kill.pid = q->p_pid;
sys_kill(&sysmsg, &sysmsg.extargs.kill);
q = q->p_peers;
}
while (p->p_peers)
tsleep((caddr_t)p, 0, "exit1", 0);
}
#ifdef PGINPROF
vmsizmon();
#endif
STOPEVENT(p, S_EXIT, rv);
p->p_flags |= P_POSTEXIT;
p->p_xstat = rv;
TAILQ_FOREACH(ep, &exit_list, next)
(*ep->function)(td);
if (p->p_flags & P_PROFIL)
stopprofclock(p);
SIGEMPTYSET(p->p_siglist);
SIGEMPTYSET(lp->lwp_siglist);
if (timevalisset(&p->p_realtimer.it_value))
callout_terminate(&p->p_ithandle);
funsetownlst(&p->p_sigiolst);
fdfree(p, NULL);
if (p->p_leader->p_peers) {
q = p->p_leader;
while(q->p_peers != p)
q = q->p_peers;
q->p_peers = p->p_peers;
wakeup((caddr_t)p->p_leader);
}
semexit(p);
vm = p->p_vmspace;
if (p->p_vkernel) {
vkernel_lwp_exit(lp);
vkernel_exit(p);
}
lwkt_reltoken(&p->p_token);
vmspace_relexit(vm);
lwkt_gettoken(&p->p_token);
if (SESS_LEADER(p)) {
struct session *sp = p->p_session;
if (sp->s_ttyvp) {
if (sp->s_ttyp && (sp->s_ttyp->t_session == sp)) {
if (sp->s_ttyp->t_pgrp)
pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
sp->s_ttyp->t_timeout = hz;
ttywait(sp->s_ttyp);
ttyclosesession(sp, 1);
}
ttyclosesession(sp, 0);
}
sp->s_leader = NULL;
}
fixjobc(p, p->p_pgrp, 0);
(void)acct_process(p);
#ifdef KTRACE
if (p->p_tracenode)
ktrdestroy(&p->p_tracenode);
p->p_traceflag = 0;
#endif
if ((vtmp = p->p_textvp) != NULL) {
p->p_textvp = NULL;
vrele(vtmp);
}
if (p->p_textnch.ncp)
cache_drop(&p->p_textnch);
if (p->p_flags & P_PPWAIT) {
if (p->p_pptr && p->p_pptr->p_upmap)
atomic_add_int(&p->p_pptr->p_upmap->invfork, -1);
atomic_clear_int(&p->p_flags, P_PPWAIT);
wakeup(p->p_pptr);
}
proc_move_allproc_zombie(p);
pp = p->p_pptr;
atomic_add_long(&pp->p_waitgen, 1);
pp = NULL;
reap = reaper_exit(p);
reproc = NULL;
q = LIST_FIRST(&p->p_children);
if (q) {
reproc = reaper_get(reap);
lwkt_gettoken(&reproc->p_token);
while ((q = LIST_FIRST(&p->p_children)) != NULL) {
PHOLD(q);
lwkt_gettoken(&q->p_token);
if (q != LIST_FIRST(&p->p_children)) {
lwkt_reltoken(&q->p_token);
PRELE(q);
continue;
}
LIST_REMOVE(q, p_sibling);
LIST_INSERT_HEAD(&reproc->p_children, q, p_sibling);
q->p_pptr = reproc;
q->p_ppid = reproc->p_pid;
q->p_reaptid = 0;
q->p_sigparent = SIGCHLD;
if (q->p_flags & P_TRACED) {
q->p_flags &= ~P_TRACED;
ksignal(q, SIGKILL);
}
if (q->p_deathsig)
ksignal(q, q->p_deathsig);
lwkt_reltoken(&q->p_token);
PRELE(q);
}
lwkt_reltoken(&reproc->p_token);
wakeup(reproc);
}
calcru_proc(p, &p->p_ru);
KNOTE(&p->p_klist, NOTE_EXIT);
if (p->p_pptr->p_sigacts->ps_flag & (PS_NOCLDWAIT | PS_CLDSIGIGN)) {
if (reproc == NULL)
reproc = reaper_get(reap);
proc_reparent(p, reproc);
}
if (reproc)
PRELE(reproc);
if (reap)
reaper_drop(reap);
pp = p->p_pptr;
PHOLD(pp);
if (p->p_sigparent && pp != initproc) {
int sig = p->p_sigparent;
if (sig != SIGUSR1 && sig != SIGCHLD)
sig = SIGCHLD;
ksignal(pp, sig);
} else {
ksignal(pp, SIGCHLD);
}
p->p_flags &= ~P_TRACED;
PRELE(pp);
if (p->p_limit) {
struct plimit *rlimit;
rlimit = p->p_limit;
p->p_limit = NULL;
plimit_free(rlimit);
}
lwp_exit(1, pp);
}
void
lwp_exit(int masterexit, void *waddr)
{
struct thread *td = curthread;
struct lwp *lp = td->td_lwp;
struct proc *p = lp->lwp_proc;
int dowake = 0;
p->p_usched->release_curproc(lp);
lwp_userunmap(lp);
ASSERT_LWKT_TOKEN_HELD(&p->p_token);
atomic_set_int(&lp->lwp_mpflags, LWP_MP_WEXIT);
if (lp->lwp_vkernel)
vkernel_lwp_exit(lp);
kqueue_terminate(&lp->lwp_kqueue);
if (td->td_linux_task)
linux_task_drop_callback(td);
if (masterexit && p->p_linux_mm)
linux_proc_drop_callback(p);
if (td->td_ucred) {
crfree(td->td_ucred);
td->td_ucred = NULL;
}
if (td->td_limit) {
struct plimit *rlimit;
rlimit = td->td_limit;
td->td_limit = NULL;
plimit_free(rlimit);
}
if (p->p_fd)
fexitcache(td);
while (lp->lwp_lock > 0)
tsleep(lp, 0, "lwpexit", 1);
ruadd(&p->p_ru, &lp->lwp_ru);
PHOLD(p);
dsched_exit_thread(td);
biosched_done(curthread);
if (masterexit == 0) {
int cpu = mycpuid;
lwp_rb_tree_RB_REMOVE(&p->p_lwp_tree, lp);
--p->p_nthreads;
if ((p->p_flags & P_MAYBETHREADED) && p->p_nthreads <= 1)
dowake = 1;
lwkt_gettoken(&deadlwp_token[cpu]);
LIST_INSERT_HEAD(&deadlwp_list[cpu], lp, u.lwp_reap_entry);
taskqueue_enqueue(taskqueue_thread[cpu], deadlwp_task[cpu]);
lwkt_reltoken(&deadlwp_token[cpu]);
} else {
--p->p_nthreads;
if ((p->p_flags & P_MAYBETHREADED) && p->p_nthreads <= 1)
dowake = 1;
}
lwkt_reltoken(&p->p_token);
p->p_usched->heuristic_exiting(lp, p);
if (dowake)
wakeup(&p->p_nthreads);
if (waddr)
wakeup(waddr);
cpu_lwp_exit();
}
static int
lwp_wait(struct lwp *lp)
{
struct thread *td = lp->lwp_thread;
u_int mpflags;
KKASSERT(lwkt_preempted_proc() != lp);
for (;;) {
mpflags = td->td_mpflags;
cpu_ccfence();
if (mpflags & TDF_MP_EXITSIG)
break;
tsleep_interlock(td, 0);
if (atomic_cmpset_int(&td->td_mpflags, mpflags,
mpflags | TDF_MP_EXITWAIT)) {
tsleep(td, PINTERLOCKED, "lwpxt", 0);
}
}
if (lp->lwp_lock > 0) {
tsleep(lp, 0, "lwpwait1", 1);
return(0);
}
if (td->td_refs) {
tsleep(td, 0, "lwpwait2", 1);
return(0);
}
while ((td->td_flags & (TDF_RUNNING |
TDF_RUNQ |
TDF_PREEMPT_LOCK |
TDF_EXITING)) != TDF_EXITING) {
tsleep(lp, 0, "lwpwait3", 1);
return (0);
}
KASSERT((td->td_flags & (TDF_RUNQ|TDF_TSLEEPQ)) == 0,
("lwp_wait: td %p (%s) still on run or sleep queue",
td, td->td_comm));
return (1);
}
void
lwp_dispose(struct lwp *lp)
{
struct thread *td = lp->lwp_thread;
KKASSERT(lwkt_preempted_proc() != lp);
KKASSERT(lp->lwp_lock == 0);
KKASSERT(td->td_refs == 0);
KKASSERT((td->td_flags & (TDF_RUNNING |
TDF_RUNQ |
TDF_PREEMPT_LOCK |
TDF_EXITING)) == TDF_EXITING);
PRELE(lp->lwp_proc);
lp->lwp_proc = NULL;
if (td != NULL) {
td->td_proc = NULL;
td->td_lwp = NULL;
lp->lwp_thread = NULL;
lwkt_free_thread(td);
}
kfree(lp, M_LWP);
}
int
sys_wait4(struct sysmsg *sysmsg, const struct wait_args *uap)
{
struct __wrusage wrusage;
int error;
int status;
int options;
id_t id;
idtype_t idtype;
options = uap->options | WEXITED | WTRAPPED;
id = uap->pid;
if (id == WAIT_ANY) {
idtype = P_ALL;
} else if (id == WAIT_MYPGRP) {
idtype = P_PGID;
id = curproc->p_pgid;
} else if (id < 0) {
idtype = P_PGID;
id = -id;
} else {
idtype = P_PID;
}
error = kern_wait(idtype, id, &status, options, &wrusage,
NULL, &sysmsg->sysmsg_result);
if (error == 0 && uap->status)
error = copyout(&status, uap->status, sizeof(*uap->status));
if (error == 0 && uap->rusage) {
ruadd(&wrusage.wru_self, &wrusage.wru_children);
error = copyout(&wrusage.wru_self, uap->rusage, sizeof(*uap->rusage));
}
return (error);
}
int
sys_wait6(struct sysmsg *sysmsg, const struct wait6_args *uap)
{
struct __wrusage wrusage;
siginfo_t info;
siginfo_t *infop;
int error;
int status;
int options;
id_t id;
idtype_t idtype;
options = uap->options;
idtype = uap->idtype;
id = uap->id;
infop = uap->info ? &info : NULL;
switch(idtype) {
case P_PID:
case P_PGID:
if (id == WAIT_MYPGRP) {
idtype = P_PGID;
id = curproc->p_pgid;
}
break;
default:
break;
}
error = kern_wait(idtype, id, &status, options,
&wrusage, infop, &sysmsg->sysmsg_result);
if (error == 0 && uap->status)
error = copyout(&status, uap->status, sizeof(*uap->status));
if (error == 0 && uap->wrusage)
error = copyout(&wrusage, uap->wrusage, sizeof(*uap->wrusage));
if (error == 0 && uap->info)
error = copyout(&info, uap->info, sizeof(*uap->info));
return (error);
}
int
kern_wait(idtype_t idtype, id_t id, int *status, int options,
struct __wrusage *wrusage, siginfo_t *info, int *res)
{
struct thread *td = curthread;
struct lwp *lp;
struct proc *q = td->td_proc;
struct proc *p, *t;
struct ucred *cr;
struct pargs *pa;
struct sigacts *ps;
int nfound, error;
long waitgen;
if (options &~ (WUNTRACED|WNOHANG|WCONTINUED|WLINUXCLONE|WSTOPPED|
WEXITED|WTRAPPED|WNOWAIT)) {
return (EINVAL);
}
if ((options & (WEXITED | WUNTRACED | WCONTINUED | WTRAPPED)) == 0) {
return (EINVAL);
}
lwkt_gettoken(&q->p_token);
loop:
if (STOPLWP(q, td->td_lwp))
tstop();
nfound = 0;
waitgen = atomic_fetchadd_long(&q->p_waitgen, 0x80000000);
LIST_FOREACH(p, &q->p_children, p_sibling) {
if (PWAITRES_PENDING(p))
continue;
switch(idtype) {
case P_ALL:
PHOLD(p);
break;
case P_PID:
if (p->p_pid != (pid_t)id)
continue;
PHOLD(p);
break;
case P_PGID:
if (p->p_pgid != (pid_t)id)
continue;
PHOLD(p);
break;
case P_SID:
PHOLD(p);
if (p->p_session && p->p_session->s_sid != (pid_t)id) {
PRELE(p);
continue;
}
break;
case P_UID:
PHOLD(p);
if (p->p_ucred->cr_uid != (uid_t)id) {
PRELE(p);
continue;
}
break;
case P_GID:
PHOLD(p);
if (p->p_ucred->cr_gid != (gid_t)id) {
PRELE(p);
continue;
}
break;
case P_JAILID:
PHOLD(p);
if (p->p_ucred->cr_prison &&
p->p_ucred->cr_prison->pr_id != (int)id) {
PRELE(p);
continue;
}
break;
default:
continue;
}
if ((p->p_sigparent != SIGCHLD) ^
((options & WLINUXCLONE) != 0)) {
PRELE(p);
continue;
}
nfound++;
if (p->p_stat == SZOMB && (options & WEXITED)) {
if (PHOLDZOMB(p)) {
PRELE(p);
goto loop;
}
lwkt_gettoken(&p->p_token);
if (p->p_pptr != q) {
lwkt_reltoken(&p->p_token);
PRELE(p);
PRELEZOMB(p);
goto loop;
}
PWAITRES_SET(p);
while (p->p_nthreads > 0) {
tsleep(&p->p_nthreads, 0, "lwpzomb", hz);
}
while ((lp = RB_ROOT(&p->p_lwp_tree)) != NULL) {
while (lp->lwp_lock > 0)
tsleep(lp, 0, "zomblwp", 1);
lwp_rb_tree_RB_REMOVE(&p->p_lwp_tree, lp);
reaplwp(lp);
}
KKASSERT(p->p_nthreads == 0);
PRELE(p);
PSTALL(p, "reap3", 1);
*res = p->p_pid;
*status = p->p_xstat;
wrusage->wru_self = p->p_ru;
wrusage->wru_children = p->p_cru;
if (info) {
bzero(info, sizeof(*info));
info->si_errno = 0;
info->si_signo = SIGCHLD;
if (WIFEXITED(p->p_xstat)) {
info->si_code = CLD_EXITED;
info->si_status =
WEXITSTATUS(p->p_xstat);
} else {
info->si_code = CLD_KILLED;
info->si_status = WTERMSIG(p->p_xstat);
}
info->si_pid = p->p_pid;
info->si_uid = p->p_ucred->cr_uid;
}
if (options & WNOWAIT) {
lwkt_reltoken(&p->p_token);
PRELEZOMB(p);
error = 0;
goto done;
}
if (p->p_oppid && (t = pfind(p->p_oppid)) != NULL) {
p->p_oppid = 0;
proc_reparent(p, t);
ksignal(t, SIGCHLD);
wakeup((caddr_t)t);
PRELE(t);
lwkt_reltoken(&p->p_token);
PRELEZOMB(p);
error = 0;
goto done;
}
proc_remove_zombie(p);
proc_userunmap(p);
lwkt_reltoken(&p->p_token);
leavepgrp(p);
p->p_xstat = 0;
ruadd(&q->p_cru, &p->p_ru);
ruadd(&q->p_cru, &p->p_cru);
chgproccnt(p->p_ucred->cr_ruidinfo, -1, 0);
spin_lock(&p->p_spin);
cr = p->p_ucred;
p->p_ucred = NULL;
spin_unlock(&p->p_spin);
crfree(cr);
pa = p->p_args;
p->p_args = NULL;
if (pa && refcount_release(&pa->ar_ref)) {
kfree(pa, M_PARGS);
pa = NULL;
}
ps = p->p_sigacts;
p->p_sigacts = NULL;
if (ps && refcount_release(&ps->ps_refcnt)) {
kfree(ps, M_SUBPROC);
ps = NULL;
}
PSTALL(p, "reap4", 1);
lwkt_reltoken(&q->p_token);
vmspace_exitfree(p);
lwkt_gettoken(&q->p_token);
PSTALL(p, "reap5", 1);
PRELEZOMB(p);
kfree(p->p_uidpcpu, M_SUBPROC);
kfree(p, M_PROC);
atomic_add_int(&nprocs, -1);
error = 0;
goto done;
}
if ((p->p_stat == SSTOP || p->p_stat == SCORE) &&
(p->p_flags & P_WAITED) == 0 &&
(((p->p_flags & P_TRACED) && (options & WTRAPPED)) ||
(options & WSTOPPED))) {
lwkt_gettoken(&p->p_token);
if (p->p_pptr != q) {
lwkt_reltoken(&p->p_token);
PRELE(p);
goto loop;
}
if ((p->p_stat != SSTOP && p->p_stat != SCORE) ||
(p->p_flags & P_WAITED) != 0 ||
((p->p_flags & P_TRACED) == 0 &&
(options & WUNTRACED) == 0)) {
lwkt_reltoken(&p->p_token);
PRELE(p);
goto loop;
}
if ((options & WNOWAIT) == 0)
p->p_flags |= P_WAITED;
*res = p->p_pid;
*status = W_STOPCODE(p->p_xstat);
bzero(wrusage, sizeof(*wrusage));
error = 0;
if (info) {
bzero(info, sizeof(*info));
if (p->p_flags & P_TRACED)
info->si_code = CLD_TRAPPED;
else
info->si_code = CLD_STOPPED;
info->si_status = WSTOPSIG(p->p_xstat);
}
lwkt_reltoken(&p->p_token);
PRELE(p);
goto done;
}
if ((options & WCONTINUED) && (p->p_flags & P_CONTINUED)) {
lwkt_gettoken(&p->p_token);
if (p->p_pptr != q) {
lwkt_reltoken(&p->p_token);
PRELE(p);
goto loop;
}
if ((p->p_flags & P_CONTINUED) == 0) {
lwkt_reltoken(&p->p_token);
PRELE(p);
goto loop;
}
*res = p->p_pid;
if ((options & WNOWAIT) == 0)
p->p_flags &= ~P_CONTINUED;
*status = SIGCONT;
error = 0;
if (info) {
bzero(info, sizeof(*info));
info->si_code = CLD_CONTINUED;
info->si_status = WSTOPSIG(p->p_xstat);
}
lwkt_reltoken(&p->p_token);
PRELE(p);
goto done;
}
PRELE(p);
}
if (nfound == 0) {
error = ECHILD;
goto done;
}
if (options & WNOHANG) {
*res = 0;
error = 0;
goto done;
}
error = 0;
while ((waitgen & 0x7FFFFFFF) == (q->p_waitgen & 0x7FFFFFFF)) {
tsleep_interlock(q, PCATCH);
waitgen = atomic_fetchadd_long(&q->p_waitgen, 0x80000000);
if ((waitgen & 0x7FFFFFFF) == (q->p_waitgen & 0x7FFFFFFF)) {
error = tsleep(q, PCATCH | PINTERLOCKED, "wait", 0);
break;
}
}
if (error) {
done:
lwkt_reltoken(&q->p_token);
return (error);
}
goto loop;
}
void
proc_reparent(struct proc *child, struct proc *parent)
{
struct proc *opp;
PHOLD(parent);
while ((opp = child->p_pptr) != parent) {
PHOLD(opp);
lwkt_gettoken(&opp->p_token);
lwkt_gettoken(&child->p_token);
lwkt_gettoken(&parent->p_token);
if (child->p_pptr != opp) {
lwkt_reltoken(&parent->p_token);
lwkt_reltoken(&child->p_token);
lwkt_reltoken(&opp->p_token);
PRELE(opp);
continue;
}
LIST_REMOVE(child, p_sibling);
LIST_INSERT_HEAD(&parent->p_children, child, p_sibling);
child->p_pptr = parent;
child->p_ppid = parent->p_pid;
child->p_reaptid = 0;
lwkt_reltoken(&parent->p_token);
lwkt_reltoken(&child->p_token);
lwkt_reltoken(&opp->p_token);
if (LIST_EMPTY(&opp->p_children))
wakeup(opp);
PRELE(opp);
break;
}
PRELE(parent);
}
int
at_exit(exitlist_fn function)
{
struct exitlist *ep;
#ifdef INVARIANTS
if (rm_at_exit(function))
kprintf("WARNING: exit callout entry (%p) already present\n",
function);
#endif
ep = kmalloc(sizeof(*ep), M_ATEXIT, M_NOWAIT);
if (ep == NULL)
return (ENOMEM);
ep->function = function;
TAILQ_INSERT_TAIL(&exit_list, ep, next);
return (0);
}
int
rm_at_exit(exitlist_fn function)
{
struct exitlist *ep;
TAILQ_FOREACH(ep, &exit_list, next) {
if (ep->function == function) {
TAILQ_REMOVE(&exit_list, ep, next);
kfree(ep, M_ATEXIT);
return(1);
}
}
return (0);
}
static void
reaplwps(void *context, int dummy)
{
struct lwplist *lwplist = context;
struct lwp *lp;
int cpu = mycpuid;
lwkt_gettoken(&deadlwp_token[cpu]);
while ((lp = LIST_FIRST(lwplist))) {
LIST_REMOVE(lp, u.lwp_reap_entry);
reaplwp(lp);
}
lwkt_reltoken(&deadlwp_token[cpu]);
}
static void
reaplwp(struct lwp *lp)
{
while (lwp_wait(lp) == 0)
;
lwp_dispose(lp);
}
static void
deadlwp_init(void)
{
int cpu;
for (cpu = 0; cpu < ncpus; cpu++) {
lwkt_token_init(&deadlwp_token[cpu], "deadlwpl");
LIST_INIT(&deadlwp_list[cpu]);
deadlwp_task[cpu] = kmalloc(sizeof(*deadlwp_task[cpu]),
M_DEVBUF, M_WAITOK);
TASK_INIT(deadlwp_task[cpu], 0, reaplwps, &deadlwp_list[cpu]);
}
}
SYSINIT(deadlwpinit, SI_SUB_CONFIGURE, SI_ORDER_ANY, deadlwp_init, NULL);