* Add proc->p_token and use it to interlock signal-related operations.
* Remove the use of proc_token in various signal paths. Note that proc_token
is still used in conjuction with pfind().
* Remove the use of proc_token in CURSIG*()/issignal() sequences, which
also removes its use in the tsleep path and the syscall path. p->p_token
is use instead.
* Move the automatic interlock in the tsleep code to before the CURSIG code,
fixing a rare race where a SIGCHLD could race against a parent process
in sigsuspend(). Also acquire p->p_token here to interlock LWP_SINTR
handling.
lwkt_set_comm(&thread0, "thread0");
RB_INIT(&proc0.p_lwp_tree);
spin_init(&proc0.p_spin);
+ lwkt_token_init(&proc0.p_token, "iproc");
proc0.p_lasttid = 0; /* +1 = next TID */
lwp_rb_tree_RB_INSERT(&proc0.p_lwp_tree, &lwp0);
lwp0.lwp_thread = &thread0;
rlim_t limit;
int error;
- lwkt_gettoken(&proc_token); /* needed for proc_*() calls */
+ lwkt_gettoken(&p->p_token); /* needed for proc_*() calls */
PRINTF(("calling generic_elf_coredump\n"));
limit = p->p_rlimit[RLIMIT_CORE].rlim_cur;
} else {
error = ERANGE;
}
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return error;
}
p->p_pid != pid && p->p_pgid != -pid)
continue;
- /* This special case handles a kthread spawned by linux_clone
+ /*
+ * This special case handles a kthread spawned by linux_clone
* (see linux_misc.c). The linux_wait4 and linux_waitpid
* functions need to be able to distinguish between waiting
* on a process and waiting on a thread. It is a thread if
RB_INIT(&p2->p_lwp_tree);
spin_init(&p2->p_spin);
+ lwkt_token_init(&p2->p_token, "iproc");
p2->p_lasttid = -1; /* first tid will be 0 */
/*
if (sig <= 0 || sig > _SIG_MAXSIG)
return (EINVAL);
- lwkt_gettoken(&proc_token);
+ lwkt_gettoken(&p->p_token);
if (oact) {
oact->sa_handler = ps->ps_sigact[_SIG_IDX(sig)];
*/
if (sig == SIGKILL || sig == SIGSTOP) {
if (act->sa_handler != SIG_DFL) {
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return (EINVAL);
}
#if 0
/* (not needed, SIG_DFL forces action to occur) */
- if (act->sa_flags & SA_MAILBOX)
+ if (act->sa_flags & SA_MAILBOX) {
+ lwkt_reltoken(&p->p_token);
return (EINVAL);
+ }
#endif
}
crit_exit();
}
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return (0);
}
{
struct thread *td = curthread;
struct lwp *lp = td->td_lwp;
+ struct proc *p = td->td_proc;
int error;
- lwkt_gettoken(&proc_token);
+ lwkt_gettoken(&p->p_token);
if (oset != NULL)
*oset = lp->lwp_sigmask;
}
}
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return (error);
}
lwkt_reltoken(&proc_token);
return (ESRCH);
}
+ PHOLD(p);
+ lwkt_gettoken(&p->p_token);
if (!CANSIGNAL(p, sig)) {
+ lwkt_reltoken(&p->p_token);
+ PRELE(p);
lwkt_reltoken(&proc_token);
return (EPERM);
}
* during exit, which is allowed.
*/
if (p->p_flag & P_WEXIT) {
+ lwkt_reltoken(&p->p_token);
+ PRELE(p);
lwkt_reltoken(&proc_token);
return (0);
}
if (tid != -1) {
lp = lwp_rb_tree_RB_LOOKUP(&p->p_lwp_tree, tid);
if (lp == NULL) {
+ lwkt_reltoken(&p->p_token);
+ PRELE(p);
lwkt_reltoken(&proc_token);
return (ESRCH);
}
}
if (sig)
lwpsignal(p, lp, sig);
+ lwkt_reltoken(&p->p_token);
+ PRELE(p);
lwkt_reltoken(&proc_token);
return (0);
}
+
/*
* If we come here, pid is a special broadcast pid.
* This doesn't mix with a tid.
KKASSERT(lp == NULL || lp->lwp_proc == p);
- lwkt_gettoken(&proc_token);
+ lwkt_gettoken(&p->p_token);
prop = sigprop(sig);
* in the process flags.
*/
if (lp && (lp->lwp_flag & LWP_WEXIT)) {
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return;
}
* and if it is set to SIG_IGN, action will be SIG_DFL here.
*/
if (SIGISMEMBER(p->p_sigignore, sig)) {
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return;
}
if (SIGISMEMBER(p->p_sigcatch, sig))
*/
if (prop & SA_TTYSTOP && p->p_pgrp->pg_jobc == 0 &&
action == SIG_DFL) {
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return;
}
SIG_CONTSIGMASK(p->p_siglist);
lwp_signotify(lp);
out:
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
crit_exit();
}
/*
- * proc_token must be held
+ * p->p_token must be held
*/
static void
lwp_signotify(struct lwp *lp)
{
- ASSERT_LWKT_TOKEN_HELD(&proc_token);
+ ASSERT_LWKT_TOKEN_HELD(&lp->lwp_proc->p_token);
crit_enter();
if (lp->lwp_stat == LSSLEEP || lp->lwp_stat == LSSTOP) {
#endif
/*
- * Caller must hold proc_token
+ * Caller must hold p->p_token
*/
void
proc_stop(struct proc *p)
{
struct lwp *lp;
- ASSERT_LWKT_TOKEN_HELD(&proc_token);
+ ASSERT_LWKT_TOKEN_HELD(&p->p_token);
crit_enter();
/* If somebody raced us, be happy with it */
{
struct lwp *lp;
- ASSERT_LWKT_TOKEN_HELD(&proc_token);
+ ASSERT_LWKT_TOKEN_HELD(&p->p_token);
crit_enter();
if (p->p_stat != SSTOP) {
struct timespec rts, ets, ts;
struct timeval tv;
- lwkt_gettoken(&proc_token);
-
error = 0;
sig = 0;
ets.tv_sec = 0; /* silence compiler warning */
timespecsub(&ts, &rts);
TIMESPEC_TO_TIMEVAL(&tv, &ts);
hz = tvtohz_high(&tv);
- } else
+ } else {
hz = 0;
+ }
lp->lwp_sigmask = savedmask;
SIGSETNAND(lp->lwp_sigmask, waitset);
lwp_delsig(lp, sig); /* take the signal! */
if (sig == SIGKILL) {
- lwkt_reltoken(&proc_token);
sigexit(lp, sig);
/* NOT REACHED */
}
}
- lwkt_reltoken(&proc_token);
-
return (error);
}
* Stop signals with default action are processed immediately, then cleared;
* they aren't returned. This is checked after each entry to the system for
* a syscall or trap (though this can usually be done without calling issignal
- * by checking the pending signal masks in the CURSIG macro.) The normal call
- * sequence is
+ * by checking the pending signal masks in the CURSIG macro).
*
- * This routine is called via CURSIG/__cursig and the MP lock might not be
- * held. Obtain the MP lock for the duration of the operation.
+ * This routine is called via CURSIG/__cursig. We will acquire and release
+ * p->p_token but if the caller needs to interlock the test the caller must
+ * also hold p->p_token.
*
* while (sig = CURSIG(curproc))
* postsig(sig);
+ *
+ * MPSAFE
*/
int
issignal(struct lwp *lp, int maytrace)
sigset_t mask;
int sig, prop;
- lwkt_gettoken(&proc_token);
+ lwkt_gettoken(&p->p_token);
for (;;) {
int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG);
if (p->p_flag & P_PPWAIT)
SIG_STOPSIGMASK(mask);
if (SIGISEMPTY(mask)) { /* no signal to send */
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return (0);
}
sig = sig_ffs(&mask);
*/
break; /* == ignore */
} else {
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return (sig);
}
* This signal has an action, let
* postsig() process it.
*/
- lwkt_reltoken(&proc_token);
+ lwkt_reltoken(&p->p_token);
return (sig);
}
lwp_delsig(lp, sig); /* take the signal! */
ident, wmesg, lp->lwp_stat));
/*
+ * We interlock the sleep queue if the caller has not already done
+ * it for us. This must be done before we potentially acquire any
+ * tokens or we can loose the wakeup.
+ */
+ if ((flags & PINTERLOCKED) == 0) {
+ id = LOOKUP(ident);
+ _tsleep_interlock(gd, ident, flags);
+ }
+
+ /*
* Setup for the current process (if this is a process).
+ *
+ * We hold the process token if lp && catch. The resume
+ * code will release it.
*/
if (lp) {
if (catch) {
* Early termination only occurs when tsleep() is
* entered while in a normal LSRUN state.
*/
+ lwkt_gettoken(&p->p_token);
if ((sig = CURSIG(lp)) != 0)
goto resume;
goto resume;
/*
- * Causes ksignal to wake us up when.
+ * Causes ksignal to wake us up if a signal is
+ * received (interlocked with p->p_token).
*/
lp->lwp_flag |= LWP_SINTR;
}
+ } else {
+ KKASSERT(p == NULL);
}
/*
- * We interlock the sleep queue if the caller has not already done
- * it for us.
- */
- if ((flags & PINTERLOCKED) == 0) {
- id = LOOKUP(ident);
- _tsleep_interlock(gd, ident, flags);
- }
-
- /*
- *
- * If no interlock was set we do an integrated interlock here.
* Make sure the current process has been untangled from
* the userland scheduler and initialize slptime to start
- * counting. We must interlock the sleep queue before doing
- * this to avoid wakeup/process-ipi races which can occur under
- * heavy loads.
+ * counting.
*/
if (lp) {
p->p_usched->release_curproc(lp);
* interlock, the user must poll it prior to any system call
* that it wishes to interlock a mailbox signal against since
* the flag is cleared on *any* system call that sleeps.
+ *
+ * p->p_token is held in the p && catch case.
*/
resume:
if (p) {
error = ERESTART;
}
}
+ if (catch)
+ lwkt_reltoken(&p->p_token);
lp->lwp_flag &= ~(LWP_BREAKTSLEEP | LWP_SINTR);
p->p_flag &= ~P_MAILBOX;
}
struct lwp *lp;
crit_enter();
- lwkt_gettoken(&proc_token);
+ lp = td->td_lwp;
+
+ if (lp)
+ lwkt_gettoken(&lp->lwp_proc->p_token);
/*
* cpu interlock. Thread flags are only manipulated on
* the cpu owning the thread. proc flags are only manipulated
- * by the older of the MP lock. We have both.
+ * by the holder of p->p_token. We have both.
*/
if (td->td_flags & TDF_TSLEEP_DESCHEDULED) {
td->td_flags |= TDF_TIMEOUT;
- if ((lp = td->td_lwp) != NULL) {
+ if (lp) {
lp->lwp_flag |= LWP_BREAKTSLEEP;
if (lp->lwp_proc->p_stat != SSTOP)
setrunnable(lp);
_tsleep_wakeup(td);
}
}
- lwkt_reltoken(&proc_token);
+ if (lp)
+ lwkt_reltoken(&lp->lwp_proc->p_token);
crit_exit();
}
/*
* setrunnable()
*
- * Make a process runnable. The proc_token must be held on call. This only
- * has an effect if we are in SSLEEP. We only break out of the
+ * Make a process runnable. lp->lwp_proc->p_token must be held on call.
+ * This only has an effect if we are in SSLEEP. We only break out of the
* tsleep if LWP_BREAKTSLEEP is set, otherwise we just fix-up the state.
*
- * NOTE: With proc_token held we can only safely manipulate the process
+ * NOTE: With p_token held we can only safely manipulate the process
* structure and the lp's lwp_stat.
*/
void
setrunnable(struct lwp *lp)
{
- ASSERT_LWKT_TOKEN_HELD(&proc_token);
+ ASSERT_LWKT_TOKEN_HELD(&lp->lwp_proc->p_token);
crit_enter();
if (lp->lwp_stat == LSSTOP)
lp->lwp_stat = LSSLEEP;
}
int
-kern_ptrace(struct proc *curp, int req, pid_t pid, void *addr, int data, int *res)
+kern_ptrace(struct proc *curp, int req, pid_t pid, void *addr,
+ int data, int *res)
{
struct proc *p, *pp;
struct lwp *lp;
return (ESRCH);
}
+ lwkt_gettoken(&p->p_token);
/* Can't trace a process that's currently exec'ing. */
if ((p->p_flag & P_INEXEC) != 0) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EAGAIN;
}
case PT_ATTACH:
/* Self */
if (p->p_pid == curp->p_pid) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EINVAL;
}
/* Already traced */
if (p->p_flag & P_TRACED) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EBUSY;
}
if (curp->p_flag & P_TRACED)
for (pp = curp->p_pptr; pp != NULL; pp = pp->p_pptr)
if (pp == p) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return (EINVAL);
}
if ((p->p_ucred->cr_ruid != curp->p_ucred->cr_ruid) ||
(p->p_flag & P_SUGID)) {
if ((error = priv_check_cred(curp->p_ucred, PRIV_ROOT, 0)) != 0) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return error;
}
/* can't trace init when securelevel > 0 */
if (securelevel > 0 && p->p_pid == 1) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EPERM;
}
#endif
/* not being traced... */
if ((p->p_flag & P_TRACED) == 0) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EPERM;
}
/* not being traced by YOU */
if (p->p_pptr != curp) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EBUSY;
}
/* not currently stopped */
if (p->p_stat != SSTOP ||
(p->p_flag & P_WAITED) == 0) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EBUSY;
}
break;
default:
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EINVAL;
}
/* set my trace flag and "owner" so it can read/write me */
p->p_flag |= P_TRACED;
p->p_oppid = p->p_pptr->p_pid;
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return 0;
case PT_DETACH:
/* Zero means do not send any signal */
if (data < 0 || data > _SIG_MAXSIG) {
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EINVAL;
}
if (req == PT_STEP) {
if ((error = ptrace_single_step (lp))) {
LWPRELE(lp);
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return error;
}
if ((error = ptrace_set_pc (lp,
(u_long)(uintfptr_t)addr))) {
LWPRELE(lp);
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return error;
}
ksignal(p, data);
}
crit_exit();
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return 0;
}
if (!write)
*res = tmp;
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return (error);
uio.uio_rw = UIO_WRITE;
break;
default:
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return (EINVAL);
}
error = procfs_domem(curp, lp, NULL, &uio);
piod->piod_len -= uio.uio_resid;
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return (error);
#endif /* PT_SETREGS */
#if defined(PT_SETREGS) || defined(PT_GETREGS)
if (!procfs_validregs(lp)) { /* no P_SYSTEM procs please */
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EINVAL;
} else {
uio.uio_rw = write ? UIO_WRITE : UIO_READ;
uio.uio_td = curthread;
t = procfs_doregs(curp, lp, NULL, &uio);
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return t;
}
#endif /* PT_SETFPREGS */
#if defined(PT_SETFPREGS) || defined(PT_GETFPREGS)
if (!procfs_validfpregs(lp)) { /* no P_SYSTEM procs please */
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EINVAL;
} else {
uio.uio_rw = write ? UIO_WRITE : UIO_READ;
uio.uio_td = curthread;
t = procfs_dofpregs(curp, lp, NULL, &uio);
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return t;
}
#endif /* PT_SETDBREGS */
#if defined(PT_SETDBREGS) || defined(PT_GETDBREGS)
if (!procfs_validdbregs(lp)) { /* no P_SYSTEM procs please */
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return EINVAL;
} else {
uio.uio_rw = write ? UIO_WRITE : UIO_READ;
uio.uio_td = curthread;
t = procfs_dodbregs(curp, lp, NULL, &uio);
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return t;
}
break;
}
+ lwkt_reltoken(&p->p_token);
lwkt_reltoken(&proc_token);
return 0;
}
void (*p_userret)(void);/* p: return-to-user hook */
struct spinlock p_spin; /* Spinlock for LWP access to proc */
+ struct lwkt_token p_token; /* Token for LWP access to proc */
};
#define lwp_wchan lwp_thread->td_wchan
* process, 0 if none. If there is a pending stop signal with default
* action, the process stops in issignal().
*
- * MP SAFE
+ * This function does not interlock pending signals. If the caller needs
+ * to interlock the caller must acquire the per-proc token.
+ *
+ * MPSAFE
*/
static __inline
int
struct proc *p = lp->lwp_proc;
int error;
+ ASSERT_LWKT_TOKEN_HELD(&p->p_token);
ASSERT_LWKT_TOKEN_HELD(&proc_token);
/* Can't trace a process that's currently exec'ing. */
char msg[PROCFS_CTLLEN+1];
vfs_namemap_t *nm;
+ ASSERT_LWKT_TOKEN_HELD(&p->p_token);
ASSERT_LWKT_TOKEN_HELD(&proc_token);
if (uio->uio_rw != UIO_WRITE)
}
pfs->pfs_lockowner = curproc->p_pid;
+ lwkt_gettoken(&p->p_token);
+
switch (pfs->pfs_type) {
case Pnote:
case Pnotepg:
rtval = EOPNOTSUPP;
break;
}
+ lwkt_reltoken(&p->p_token);
LWPRELE(lp);
+
pfs->pfs_lockowner = 0;
lwkt_reltoken(&proc_token);
wakeup(&pfs->pfs_lockowner);