#include "opt_ktrace.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysmsg.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/vnode.h>
#include <sys/acct.h>
#include <sys/ktrace.h>
#include <sys/unistd.h>
#include <sys/jail.h>
#include <sys/lwp.h>
#include <vm/vm.h>
#include <sys/lock.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_extern.h>
#include <sys/vmmeter.h>
#include <sys/refcount.h>
#include <sys/thread2.h>
#include <sys/signal2.h>
#include <sys/spinlock2.h>
#include <sys/dsched.h>
static MALLOC_DEFINE(M_ATFORK, "atfork", "atfork callback");
static MALLOC_DEFINE(M_REAPER, "reaper", "process reapers");
struct forklist {
forklist_fn function;
TAILQ_ENTRY(forklist) next;
};
TAILQ_HEAD(forklist_head, forklist);
static struct forklist_head fork_list = TAILQ_HEAD_INITIALIZER(fork_list);
static struct lwp *lwp_fork1(struct lwp *, struct proc *, int flags,
const cpumask_t *mask);
static void lwp_fork2(struct lwp *lp1, struct proc *destproc,
struct lwp *lp2, int flags);
static int lwp_create1(struct lwp_params *params,
const cpumask_t *mask);
static struct lock reaper_lock = LOCK_INITIALIZER("reapgl", 0, 0);
static volatile unsigned int reap_tid = 1;
int forksleep;
static int
rb_lwp_compare(struct lwp *lp1, struct lwp *lp2)
{
if (lp1->lwp_tid < lp2->lwp_tid)
return(-1);
if (lp1->lwp_tid > lp2->lwp_tid)
return(1);
return(0);
}
RB_GENERATE2(lwp_rb_tree, lwp, u.lwp_rbnode, rb_lwp_compare, lwpid_t, lwp_tid);
static void
wake_umtx_threads(struct proc *p1)
{
struct lwp *lp;
struct thread *td;
RB_FOREACH(lp, lwp_rb_tree, &p1->p_lwp_tree) {
td = lp->lwp_thread;
if (td && (td->td_flags & TDF_TSLEEPQ) &&
(td->td_wdomain & PDOMAIN_MASK) == PDOMAIN_UMTX) {
wakeup_domain(td->td_wchan, PDOMAIN_UMTX);
}
}
}
int
sys_fork(struct sysmsg *sysmsg, const struct fork_args *uap)
{
struct lwp *lp = curthread->td_lwp;
struct proc *p2;
int error;
error = fork1(lp, RFFDG | RFPROC | RFPGLOCK, &p2);
if (error == 0) {
PHOLD(p2);
start_forked_proc(lp, p2);
sysmsg->sysmsg_fds[0] = p2->p_pid;
sysmsg->sysmsg_fds[1] = 0;
PRELE(p2);
}
return error;
}
int
sys_vfork(struct sysmsg *sysmsg, const struct vfork_args *uap)
{
struct lwp *lp = curthread->td_lwp;
struct proc *p2;
int error;
error = fork1(lp, RFFDG | RFPROC | RFPPWAIT | RFMEM | RFPGLOCK, &p2);
if (error == 0) {
PHOLD(p2);
start_forked_proc(lp, p2);
sysmsg->sysmsg_fds[0] = p2->p_pid;
sysmsg->sysmsg_fds[1] = 0;
PRELE(p2);
}
return error;
}
int
sys_rfork(struct sysmsg *sysmsg, const struct rfork_args *uap)
{
struct lwp *lp = curthread->td_lwp;
struct proc *p2;
int error;
if ((uap->flags & RFKERNELONLY) != 0)
return (EINVAL);
error = fork1(lp, uap->flags | RFPGLOCK, &p2);
if (error == 0) {
if (p2) {
PHOLD(p2);
start_forked_proc(lp, p2);
sysmsg->sysmsg_fds[0] = p2->p_pid;
sysmsg->sysmsg_fds[1] = 0;
PRELE(p2);
} else {
sysmsg->sysmsg_fds[0] = 0;
sysmsg->sysmsg_fds[1] = 0;
}
}
return error;
}
static int
lwp_create1(struct lwp_params *uprm, const cpumask_t *umask)
{
struct proc *p = curproc;
struct lwp *lp;
struct lwp_params params;
cpumask_t *mask = NULL, mask0;
int error;
error = copyin(uprm, ¶ms, sizeof(params));
if (error)
goto fail2;
if (umask != NULL) {
error = copyin(umask, &mask0, sizeof(mask0));
if (error)
goto fail2;
CPUMASK_ANDMASK(mask0, smp_active_mask);
if (CPUMASK_TESTNZERO(mask0))
mask = &mask0;
}
lwkt_gettoken(&p->p_token);
plimit_lwp_fork(p);
lp = lwp_fork1(curthread->td_lwp, p, RFPROC | RFMEM, mask);
lwp_fork2(curthread->td_lwp, p, lp, RFPROC | RFMEM);
error = cpu_prepare_lwp(lp, ¶ms);
if (error)
goto fail;
if (params.lwp_tid1 != NULL &&
(error = copyout(&lp->lwp_tid, params.lwp_tid1, sizeof(lp->lwp_tid))))
goto fail;
if (params.lwp_tid2 != NULL &&
(error = copyout(&lp->lwp_tid, params.lwp_tid2, sizeof(lp->lwp_tid))))
goto fail;
p->p_usched->resetpriority(lp);
crit_enter();
lp->lwp_stat = LSRUN;
p->p_usched->setrunqueue(lp);
crit_exit();
lwkt_reltoken(&p->p_token);
return (0);
fail:
while (lp->lwp_lock > 0)
tsleep(lp, 0, "lwpfail", 1);
lwp_rb_tree_RB_REMOVE(&p->p_lwp_tree, lp);
--p->p_nthreads;
atomic_set_int(&lp->lwp_mpflags, LWP_MP_WEXIT);
lp->lwp_thread->td_flags |= TDF_EXITING;
lwkt_remove_tdallq(lp->lwp_thread);
PHOLD(p);
biosched_done(lp->lwp_thread);
dsched_exit_thread(lp->lwp_thread);
lwp_dispose(lp);
lwkt_reltoken(&p->p_token);
fail2:
return (error);
}
int
sys_lwp_create(struct sysmsg *sysmsg, const struct lwp_create_args *uap)
{
return (lwp_create1(uap->params, NULL));
}
int
sys_lwp_create2(struct sysmsg *sysmsg, const struct lwp_create2_args *uap)
{
return (lwp_create1(uap->params, uap->mask));
}
int nprocs = 1;
int
fork1(struct lwp *lp1, int flags, struct proc **procp)
{
struct proc *p1 = lp1->lwp_proc;
struct proc *p2;
struct proc *pptr;
struct pgrp *p1grp;
struct pgrp *plkgrp;
struct lwp *lp2;
struct sysreaper *reap;
uid_t uid;
int ok, error;
static int curfail = 0;
static struct timeval lastfail;
struct forklist *ep;
struct filedesc_to_leader *fdtol;
if ((flags & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG))
return (EINVAL);
lwkt_gettoken(&p1->p_token);
plkgrp = NULL;
p2 = NULL;
if ((flags & RFPROC) == 0) {
if (p1->p_nthreads != 1) {
error = EINVAL;
goto done;
}
vm_fork(p1, NULL, NULL, flags);
if ((flags & RFMEM) == 0)
wake_umtx_threads(p1);
if (flags & RFCFDG) {
struct filedesc *fdtmp;
fdtmp = fdinit(p1);
fdfree(p1, fdtmp);
}
if (flags & RFFDG) {
if (p1->p_fd->fd_refcnt > 1) {
struct filedesc *newfd;
error = fdcopy(p1, &newfd);
if (error != 0) {
error = ENOMEM;
goto done;
}
fdfree(p1, newfd);
}
}
*procp = NULL;
error = 0;
goto done;
}
p1grp = p1->p_pgrp;
if ((flags & RFPGLOCK) && (plkgrp = p1->p_pgrp) != NULL) {
pgref(plkgrp);
lockmgr(&plkgrp->pg_lock, LK_SHARED);
if (CURSIG_NOBLOCK(lp1)) {
error = ERESTART;
goto done;
}
}
uid = lp1->lwp_thread->td_ucred->cr_ruid;
if ((nprocs >= maxproc - 10 && uid != 0) || nprocs >= maxproc) {
if (ppsratecheck(&lastfail, &curfail, 1))
kprintf("maxproc limit exceeded by uid %d, please "
"see tuning(7) and login.conf(5).\n", uid);
tsleep(&forksleep, 0, "fork", hz / 2);
error = EAGAIN;
goto done;
}
atomic_add_int(&nprocs, 1);
ok = chgproccnt(lp1->lwp_thread->td_ucred->cr_ruidinfo, 1,
plimit_getadjvalue(RLIMIT_NPROC));
if (!ok) {
atomic_add_int(&nprocs, -1);
if (ppsratecheck(&lastfail, &curfail, 1)) {
kprintf("maxproc limit of %jd "
"exceeded by \"%s\" uid %d, "
"please see tuning(7) and login.conf(5).\n",
plimit_getadjvalue(RLIMIT_NPROC),
p1->p_comm,
uid);
}
tsleep(&forksleep, 0, "fork", hz / 2);
error = EAGAIN;
goto done;
}
p2 = kmalloc(sizeof(struct proc), M_PROC, M_WAITOK|M_ZERO);
mycpu->gd_forkid += ncpus;
p2->p_forkid = mycpu->gd_forkid + mycpu->gd_cpuid;
p2->p_lasttid = 0;
p2->p_stat = SIDL;
if ((reap = p1->p_reaper) != NULL) {
reaper_hold(reap);
p2->p_reaper = reap;
} else {
p2->p_reaper = NULL;
}
RB_INIT(&p2->p_lwp_tree);
spin_init(&p2->p_spin, "procfork1");
lwkt_token_init(&p2->p_token, "proc");
lwkt_gettoken(&p2->p_token);
p2->p_uidpcpu = kmalloc(sizeof(*p2->p_uidpcpu) * ncpus,
M_SUBPROC, M_WAITOK | M_ZERO);
if (flags & RFTHREAD) {
p2->p_peers = p1->p_peers;
p1->p_peers = p2;
p2->p_leader = p1->p_leader;
} else {
p2->p_leader = p2;
}
proc_add_allproc(p2);
bcopy(&p1->p_startcopy, &p2->p_startcopy,
((caddr_t)&p2->p_endcopy - (caddr_t)&p2->p_startcopy));
if (p1->p_flags & P_PROFIL)
startprofclock(p2);
p2->p_ucred = crhold(lp1->lwp_thread->td_ucred);
if (jailed(p2->p_ucred))
p2->p_flags |= P_JAILED;
if (p2->p_args)
refcount_acquire(&p2->p_args->ar_ref);
p2->p_usched = p1->p_usched;
dsched_enter_proc(p2);
if (flags & RFSIGSHARE) {
p2->p_sigacts = p1->p_sigacts;
refcount_acquire(&p2->p_sigacts->ps_refcnt);
} else {
p2->p_sigacts = kmalloc(sizeof(*p2->p_sigacts),
M_SUBPROC, M_WAITOK);
bcopy(p1->p_sigacts, p2->p_sigacts, sizeof(*p2->p_sigacts));
refcount_init(&p2->p_sigacts->ps_refcnt, 1);
}
if (flags & RFLINUXTHPN)
p2->p_sigparent = SIGUSR1;
else
p2->p_sigparent = SIGCHLD;
p2->p_textvp = p1->p_textvp;
if (p2->p_textvp)
vref(p2->p_textvp);
if (p1->p_textnch.mount)
cache_copy(&p1->p_textnch, &p2->p_textnch);
if (flags & RFCFDG) {
p2->p_fd = fdinit(p1);
fdtol = NULL;
} else if (flags & RFFDG) {
error = fdcopy(p1, &p2->p_fd);
if (error != 0) {
error = ENOMEM;
goto done;
}
fdtol = NULL;
} else {
p2->p_fd = fdshare(p1);
if (p1->p_fdtol == NULL) {
p1->p_fdtol = filedesc_to_leader_alloc(NULL,
p1->p_leader);
}
if ((flags & RFTHREAD) != 0) {
fdtol = p1->p_fdtol;
fdtol->fdl_refcount++;
} else {
fdtol = filedesc_to_leader_alloc(p1->p_fdtol, p2);
}
}
p2->p_fdtol = fdtol;
p2->p_limit = plimit_fork(p1);
if ((p2->p_depth & 31) != 31)
++p2->p_depth;
p2->p_flags |= p1->p_flags & P_SUGID;
if (p1->p_session->s_ttyvp != NULL && (p1->p_flags & P_CONTROLT))
p2->p_flags |= P_CONTROLT;
if (flags & RFPPWAIT) {
p2->p_flags |= P_PPWAIT;
if (p1->p_upmap)
atomic_add_int(&p1->p_upmap->invfork, 1);
}
if (p1->p_vkernel)
vkernel_inherit(p1, p2);
pgref(p1grp);
lwkt_gettoken(&p1grp->pg_token);
LIST_INSERT_AFTER(p1, p2, p_pglist);
lwkt_reltoken(&p1grp->pg_token);
if (flags & RFNOWAIT) {
pptr = reaper_get(reap);
if (pptr == NULL) {
pptr = initproc;
PHOLD(pptr);
}
} else {
pptr = p1;
}
p2->p_pptr = pptr;
p2->p_ppid = pptr->p_pid;
LIST_INIT(&p2->p_children);
lwkt_gettoken(&pptr->p_token);
LIST_INSERT_HEAD(&pptr->p_children, p2, p_sibling);
lwkt_reltoken(&pptr->p_token);
if (flags & RFNOWAIT)
PRELE(pptr);
varsymset_init(&p2->p_varsymset, &p1->p_varsymset);
callout_init_mp(&p2->p_ithandle);
#ifdef KTRACE
if ((p1->p_traceflag & KTRFAC_INHERIT) && p2->p_tracenode == NULL) {
p2->p_traceflag = p1->p_traceflag;
p2->p_tracenode = ktrinherit(p1->p_tracenode);
}
#endif
PHOLD(p1);
lp2 = lwp_fork1(lp1, p2, flags, NULL);
vm_fork(p1, p2, lp2, flags);
if ((flags & RFMEM) == 0)
wake_umtx_threads(p1);
lwp_fork2(lp1, p2, lp2, flags);
if (flags == (RFFDG | RFPROC | RFPGLOCK)) {
mycpu->gd_cnt.v_forks++;
mycpu->gd_cnt.v_forkpages += btoc(p2->p_vmspace->vm_dsize) +
btoc(p2->p_vmspace->vm_ssize);
} else if (flags == (RFFDG | RFPROC | RFPPWAIT | RFMEM | RFPGLOCK)) {
mycpu->gd_cnt.v_vforks++;
mycpu->gd_cnt.v_vforkpages += btoc(p2->p_vmspace->vm_dsize) +
btoc(p2->p_vmspace->vm_ssize);
} else if (p1 == &proc0) {
mycpu->gd_cnt.v_kthreads++;
mycpu->gd_cnt.v_kthreadpages += btoc(p2->p_vmspace->vm_dsize) +
btoc(p2->p_vmspace->vm_ssize);
} else {
mycpu->gd_cnt.v_rforks++;
mycpu->gd_cnt.v_rforkpages += btoc(p2->p_vmspace->vm_dsize) +
btoc(p2->p_vmspace->vm_ssize);
}
TAILQ_FOREACH(ep, &fork_list, next) {
(*ep->function)(p1, p2, flags);
}
microtime(&p2->p_start);
p2->p_acflag = AFORK;
KNOTE(&p1->p_klist, NOTE_FORK | p2->p_pid);
*procp = p2;
error = 0;
done:
if (p2)
lwkt_reltoken(&p2->p_token);
lwkt_reltoken(&p1->p_token);
if (plkgrp) {
lockmgr(&plkgrp->pg_lock, LK_RELEASE);
pgrel(plkgrp);
}
return (error);
}
static struct lwp *
lwp_fork1(struct lwp *lp1, struct proc *destproc, int flags,
const cpumask_t *mask)
{
struct lwp *lp2;
lp2 = kmalloc(sizeof(struct lwp), M_LWP, M_WAITOK|M_ZERO);
lp2->lwp_proc = destproc;
lp2->lwp_stat = LSRUN;
bcopy(&lp1->lwp_startcopy, &lp2->lwp_startcopy,
(unsigned) ((caddr_t)&lp2->lwp_endcopy -
(caddr_t)&lp2->lwp_startcopy));
if (mask != NULL)
lp2->lwp_cpumask = *mask;
lwkt_token_init(&lp2->lwp_token, "lwp_token");
TAILQ_INIT(&lp2->lwp_lpmap_backing_list);
spin_init(&lp2->lwp_spin, "lwptoken");
lp2->lwp_tid = lp1->lwp_tid;
return lp2;
}
static void
lwp_fork2(struct lwp *lp1, struct proc *destproc, struct lwp *lp2, int flags)
{
globaldata_t gd = mycpu;
struct thread *td2;
lp2->lwp_vmspace = destproc->p_vmspace;
if (flags & RFMEM) {
lp2->lwp_sigstk.ss_flags = SS_DISABLE;
lp2->lwp_sigstk.ss_size = 0;
lp2->lwp_sigstk.ss_sp = NULL;
lp2->lwp_flags &= ~LWP_ALTSTACK;
} else {
lp2->lwp_flags |= lp1->lwp_flags & LWP_ALTSTACK;
}
crit_enter();
lp2->lwp_cpbase = gd->gd_schedclock.time - gd->gd_schedclock.periodic;
destproc->p_usched->heuristic_forking(lp1, lp2);
crit_exit();
CPUMASK_ANDMASK(lp2->lwp_cpumask, usched_mastermask);
td2 = lwkt_alloc_thread(NULL, LWKT_THREAD_STACK, gd->gd_cpuid, 0);
lp2->lwp_thread = td2;
td2->td_wakefromcpu = gd->gd_cpuid;
td2->td_ucred = crhold(destproc->p_ucred);
td2->td_proc = destproc;
td2->td_lwp = lp2;
td2->td_switch = cpu_heavy_switch;
#ifdef NO_LWKT_SPLIT_USERPRI
lwkt_setpri(td2, TDPRI_USER_NORM);
#else
lwkt_setpri(td2, TDPRI_KERN_USER);
#endif
lwkt_set_comm(td2, "%s", destproc->p_comm);
cpu_fork(lp1, lp2, flags);
kqueue_init(&lp2->lwp_kqueue, destproc->p_fd);
while (lwp_rb_tree_RB_INSERT(&destproc->p_lwp_tree, lp2) != NULL) {
++lp2->lwp_tid;
if (lp2->lwp_tid == 0 || lp2->lwp_tid == 0x3FFFFFFF)
lp2->lwp_tid = 1;
}
destproc->p_lasttid = lp2->lwp_tid;
destproc->p_nthreads++;
pmap_maybethreaded(&destproc->p_vmspace->vm_pmap);
destproc->p_flags |= P_MAYBETHREADED;
if (lp1->lwp_lpmap &&
(lp1->lwp_lpmap->blockallsigs & 0x7FFFFFFF)) {
lwp_usermap(lp2, 0);
if (lp2->lwp_lpmap) {
lp2->lwp_lpmap->blockallsigs =
lp1->lwp_lpmap->blockallsigs;
}
}
}
int
at_fork(forklist_fn function)
{
struct forklist *ep;
#ifdef INVARIANTS
if (rm_at_fork(function)) {
kprintf("WARNING: fork callout entry (%p) already present\n",
function);
}
#endif
ep = kmalloc(sizeof(*ep), M_ATFORK, M_WAITOK|M_ZERO);
ep->function = function;
TAILQ_INSERT_TAIL(&fork_list, ep, next);
return (0);
}
int
rm_at_fork(forklist_fn function)
{
struct forklist *ep;
TAILQ_FOREACH(ep, &fork_list, next) {
if (ep->function == function) {
TAILQ_REMOVE(&fork_list, ep, next);
kfree(ep, M_ATFORK);
return(1);
}
}
return (0);
}
void
start_forked_proc(struct lwp *lp1, struct proc *p2)
{
struct lwp *lp2 = ONLY_LWP_IN_PROC(p2);
int pflags;
KASSERT(p2->p_stat == SIDL,
("cannot start forked process, bad status: %p", p2));
p2->p_usched->resetpriority(lp2);
crit_enter();
p2->p_stat = SACTIVE;
lp2->lwp_stat = LSRUN;
p2->p_usched->setrunqueue(lp2);
crit_exit();
PRELE(lp1->lwp_proc);
while ((pflags = p2->p_flags) & P_PPWAIT) {
cpu_ccfence();
tsleep_interlock(lp1->lwp_proc, 0);
if (atomic_cmpset_int(&p2->p_flags, pflags, pflags))
tsleep(lp1->lwp_proc, PINTERLOCKED, "ppwait", 0);
}
}
int
sys_procctl(struct sysmsg *sysmsg, const struct procctl_args *uap)
{
struct proc *p = curproc;
struct proc *p2;
struct sysreaper *reap;
union reaper_info udata;
int error;
if (uap->idtype != P_PID)
return EINVAL;
if (uap->id != 0 && uap->id != (id_t)p->p_pid)
return EINVAL;
switch(uap->cmd) {
case PROC_REAP_ACQUIRE:
lwkt_gettoken(&p->p_token);
reap = kmalloc(sizeof(*reap), M_REAPER, M_WAITOK|M_ZERO);
if (p->p_reaper == NULL || p->p_reaper->p != p) {
reaper_init(p, reap);
error = 0;
} else {
kfree(reap, M_REAPER);
error = EALREADY;
}
lwkt_reltoken(&p->p_token);
break;
case PROC_REAP_RELEASE:
lwkt_gettoken(&p->p_token);
release_again:
reap = p->p_reaper;
KKASSERT(reap != NULL);
if (reap->p == p) {
reaper_hold(reap);
lockmgr(&reap->lock, LK_EXCLUSIVE);
if (reap->p != p) {
lockmgr(&reap->lock, LK_RELEASE);
reaper_drop(reap);
goto release_again;
}
reap->p = NULL;
p->p_reaper = reap->parent;
if (p->p_reaper)
reaper_hold(p->p_reaper);
lockmgr(&reap->lock, LK_RELEASE);
reaper_drop(reap);
reaper_drop(reap);
error = 0;
} else {
error = ENOTCONN;
}
lwkt_reltoken(&p->p_token);
break;
case PROC_REAP_STATUS:
bzero(&udata, sizeof(udata));
lwkt_gettoken_shared(&p->p_token);
if ((reap = p->p_reaper) != NULL && reap->p == p) {
udata.status.flags = reap->flags;
udata.status.refs = reap->refs - 1;
}
p2 = LIST_FIRST(&p->p_children);
udata.status.pid_head = p2 ? p2->p_pid : -1;
lwkt_reltoken(&p->p_token);
if (uap->data) {
error = copyout(&udata, uap->data,
sizeof(udata.status));
} else {
error = 0;
}
break;
case PROC_REAP_KILL:
if (uap->data)
error = copyin(uap->data, &udata, sizeof(udata.kill));
else
error = EINVAL;
if (error != 0)
break;
lwkt_gettoken(&p->p_token);
reap = p->p_reaper;
if (reap->p == p)
error = reaper_kill(reap, &udata.kill);
else
error = ENOTCONN;
lwkt_reltoken(&p->p_token);
if (error == 0) {
error = copyout(&udata, uap->data,
sizeof(udata.kill));
}
break;
case PROC_PDEATHSIG_CTL:
error = EINVAL;
if (uap->data) {
int dsig = 0;
error = copyin(uap->data, &dsig, sizeof(dsig));
if (error == 0 && dsig >= 0 && dsig <= _SIG_MAXSIG)
p->p_deathsig = dsig;
}
break;
case PROC_PDEATHSIG_STATUS:
error = EINVAL;
if (uap->data) {
error = copyout(&p->p_deathsig, uap->data,
sizeof(p->p_deathsig));
}
break;
default:
error = EINVAL;
break;
}
return error;
}
void
reaper_hold(struct sysreaper *reap)
{
KKASSERT(reap->refs > 0);
refcount_acquire(&reap->refs);
}
void
reaper_drop(struct sysreaper *next)
{
struct sysreaper *reap;
while ((reap = next) != NULL) {
if (refcount_release(&reap->refs)) {
next = reap->parent;
KKASSERT(reap->p == NULL);
lockmgr(&reaper_lock, LK_EXCLUSIVE);
reap->parent = NULL;
kfree(reap, M_REAPER);
lockmgr(&reaper_lock, LK_RELEASE);
} else {
next = NULL;
}
}
}
void
reaper_init(struct proc *p, struct sysreaper *reap)
{
reap->parent = p->p_reaper;
reap->p = p;
if (p == initproc) {
reap->flags = REAPER_STAT_OWNED | REAPER_STAT_REALINIT;
reap->refs = 2;
} else {
reap->flags = REAPER_STAT_OWNED;
reap->refs = 1;
}
lockinit(&reap->lock, "subrp", 0, 0);
cpu_sfence();
p->p_reaper = reap;
}
struct sysreaper *
reaper_exit(struct proc *p)
{
struct sysreaper *reap;
if ((reap = p->p_reaper) != NULL && reap->p == p) {
lockmgr(&reap->lock, LK_EXCLUSIVE);
p->p_reaper = reap->parent;
if (p->p_reaper)
reaper_hold(p->p_reaper);
reap->p = NULL;
lockmgr(&reap->lock, LK_RELEASE);
reaper_drop(reap);
}
if ((reap = p->p_reaper) != NULL) {
p->p_reaper = NULL;
}
return reap;
}
struct proc *
reaper_get(struct sysreaper *reap)
{
struct sysreaper *next;
struct proc *reproc;
if (reap == NULL)
return NULL;
reaper_hold(reap);
while (reap) {
lockmgr(&reap->lock, LK_SHARED);
if (reap->p) {
if (reap->p) {
reproc = reap->p;
PHOLD(reproc);
lockmgr(&reap->lock, LK_RELEASE);
reaper_drop(reap);
return reproc;
}
lockmgr(&reap->lock, LK_RELEASE);
continue;
}
next = reap->parent;
while (next) {
reaper_hold(next);
if (next->refs == 2 && next->p == NULL) {
lockmgr(&reap->lock, LK_RELEASE);
lockmgr(&reap->lock, LK_EXCLUSIVE);
if (next->refs == 2 &&
reap->parent == next &&
next->p == NULL) {
reap->parent = next->parent;
next->parent = NULL;
reaper_drop(next);
reaper_drop(next);
next = reap->parent;
continue;
}
}
break;
}
lockmgr(&reap->lock, LK_RELEASE);
reaper_drop(reap);
reap = next;
}
return NULL;
}
int
reaper_sigtest(struct proc *sender, struct proc *target, int reaper_ok)
{
struct sysreaper *sreap;
struct sysreaper *reap;
int r;
sreap = sender->p_reaper;
if (sreap == NULL)
return 1;
if (sreap == target->p_reaper) {
if (sreap->p == target && sreap->p != sender && reaper_ok == 0)
return 0;
return 1;
}
lockmgr(&reaper_lock, LK_SHARED);
r = 0;
for (reap = target->p_reaper; reap; reap = reap->parent) {
if (sreap == reap) {
if (sreap->p != target || reaper_ok)
r = 1;
break;
}
}
lockmgr(&reaper_lock, LK_RELEASE);
return r;
}
int
reaper_kill(struct sysreaper *reap, struct reaper_kill *rk)
{
struct proc *starting, *parent, *cur, *next;
unsigned int tid;
bool any_signaled, visited;
if (!_SIG_VALID(rk->signal))
return EINVAL;
rk->killed = 0;
rk->pid_failed = 0;
tid = atomic_fetchadd_int(&reap_tid, 1);
if (tid == 0)
tid = atomic_fetchadd_int(&reap_tid, 1);
reaper_hold(reap);
any_signaled = true;
while (any_signaled) {
any_signaled = false;
lockmgr(&reap->lock, LK_EXCLUSIVE);
starting = reap->p;
parent = starting;
PHOLD(parent);
lwkt_gettoken(&parent->p_token);
cur = LIST_FIRST(&parent->p_children);
if (cur == NULL)
goto done;
PHOLD(cur);
lwkt_gettoken(&cur->p_token);
visited = false;
while (true) {
if (cur->p_reaptid != tid) {
cur->p_reaptid = tid;
any_signaled = true;
if (CANSIGNAL(starting, rk->signal, 0)) {
ksignal(cur, rk->signal);
rk->killed++;
} else if (rk->pid_failed == 0) {
rk->pid_failed = cur->p_pid;
}
}
if (cur->p_reaptid != tid) {
any_signaled = true;
lwkt_reltoken(&cur->p_token);
PRELE(cur);
break;
}
KKASSERT(cur->p_pptr == parent);
if (!visited && !(rk->flags & REAPER_KILL_CHILDREN) &&
cur->p_reaper == reap &&
!LIST_EMPTY(&cur->p_children)) {
next = LIST_FIRST(&cur->p_children);
PHOLD(next);
lwkt_token_swap();
lwkt_reltoken(&parent->p_token);
PRELE(parent);
lwkt_gettoken(&next->p_token);
parent = cur;
} else if (LIST_NEXT(cur, p_sibling) != NULL) {
next = LIST_NEXT(cur, p_sibling);
PHOLD(next);
lwkt_reltoken(&cur->p_token);
PRELE(cur);
lwkt_gettoken(&next->p_token);
visited = false;
} else {
lwkt_reltoken(&cur->p_token);
PRELE(cur);
if (parent == starting)
break;
next = parent;
parent = next->p_pptr;
KASSERT(parent != NULL,
("%s: went out of reaper", __func__));
PHOLD(parent);
lwkt_gettoken(&parent->p_token);
lwkt_token_swap();
visited = true;
}
cur = next;
}
done:
lwkt_reltoken(&parent->p_token);
PRELE(parent);
lockmgr(&reap->lock, LK_RELEASE);
if (any_signaled)
tsleep(&tid, 0, "reapkill", 1);
}
reaper_drop(reap);
return 0;
}