Download raw body.
fix stop signal delivery
On Tue, Feb 04, 2025 at 03:02:06PM +0100, Claudio Jeker wrote:
> Long story short, stop signals (SIGSTOP and SIGTTIN, SIGTTOU and SIGTSTP)
> are busted for multithreaded processes (e.g. mpv needs workarounds because
> of that).
>
> This diff fixes this by totally reworking the way processes are stopped.
> Now this diff is too big to go in like this but it would be great if
> people could try this out. Especially if you have problems with stop
> signals or also when you debug multithreaded processes with egdb.
Updated diff now that the first bits of this have been committed.
--
:wq Claudio
diff --git kern/kern_exec.c kern/kern_exec.c
index de95acf6b19..fec6bec2a75 100644
--- kern/kern_exec.c
+++ kern/kern_exec.c
@@ -556,6 +556,7 @@ sys_execve(struct proc *p, void *v, register_t *retval)
if (pr->ps_flags & PS_PPWAIT) {
atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT);
atomic_clearbits_int(&pr->ps_pptr->ps_flags, PS_ISPWAIT);
+ atomic_setbits_int(&pr->ps_pptr->ps_flags, PS_WAITEVENT);
wakeup(pr->ps_pptr);
}
diff --git kern/kern_exit.c kern/kern_exit.c
index aec356ade88..3e85863a183 100644
--- kern/kern_exit.c
+++ kern/kern_exit.c
@@ -148,6 +148,8 @@ exit1(struct proc *p, int xexit, int xsig, int flags)
atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT);
atomic_clearbits_int(&pr->ps_pptr->ps_flags,
PS_ISPWAIT);
+ atomic_setbits_int(&pr->ps_pptr->ps_flags,
+ PS_WAITEVENT);
wakeup(pr->ps_pptr);
}
@@ -162,13 +164,11 @@ exit1(struct proc *p, int xexit, int xsig, int flags)
pr->ps_exitcnt++;
/*
- * if somebody else wants to take us to single threaded mode,
- * count ourselves out.
+ * if somebody else wants to take us to single threaded mode
+ * or stop us, count ourselves out.
*/
- if (pr->ps_single) {
- if (--pr->ps_singlecnt == 0)
- wakeup(&pr->ps_singlecnt);
- }
+ if (pr->ps_single || ISSET(pr->ps_flags, PS_STOPPING))
+ process_suspend_signal(pr);
/* proc is off ps_threads list so update accounting of process now */
tuagg_add_runtime();
@@ -358,6 +358,7 @@ exit1(struct proc *p, int xexit, int xsig, int flags)
if (pr->ps_flags & PS_NOZOMBIE) {
struct process *ppr = pr->ps_pptr;
process_reparent(pr, initprocess);
+ atomic_setbits_int(&ppr->ps_flags, PS_WAITEVENT);
wakeup(ppr);
}
mtx_leave(&pr->ps_mtx);
@@ -486,6 +487,8 @@ reaper(void *arg)
if (pr->ps_flags & PS_ZOMBIE) {
/* Post SIGCHLD and wake up parent. */
prsignal(pr->ps_pptr, SIGCHLD);
+ atomic_setbits_int(&pr->ps_pptr->ps_flags,
+ PS_WAITEVENT);
wakeup(pr->ps_pptr);
} else {
/* No one will wait for us, just zap it. */
@@ -508,15 +511,19 @@ dowait6(struct proc *q, idtype_t idtype, id_t id, int *statusp, int options,
memset(info, 0, sizeof(*info));
loop:
+ atomic_clearbits_int(&q->p_p->ps_flags, PS_WAITEVENT);
nfound = 0;
LIST_FOREACH(pr, &q->p_p->ps_children, ps_sibling) {
+ mtx_enter(&pr->ps_mtx);
if ((pr->ps_flags & PS_NOZOMBIE) ||
(idtype == P_PID && id != pr->ps_pid) ||
- (idtype == P_PGID && id != pr->ps_pgid))
+ (idtype == P_PGID && id != pr->ps_pgid)) {
+ mtx_leave(&pr->ps_mtx);
continue;
-
+ }
nfound++;
if ((options & WEXITED) && (pr->ps_flags & PS_ZOMBIE)) {
+ mtx_leave(&pr->ps_mtx);
*retval = pr->ps_pid;
if (info != NULL) {
info->si_pid = pr->ps_pid;
@@ -545,10 +552,9 @@ loop:
}
if ((options & WTRAPPED) && (pr->ps_flags & PS_TRACED) &&
(pr->ps_flags & PS_WAITED) == 0 &&
+ (pr->ps_flags & PS_STOPPED) &&
(pr->ps_flags & PS_TRAPPED)) {
- if (single_thread_wait(pr, 0))
- goto loop;
-
+ mtx_leave(&pr->ps_mtx);
if ((options & WNOWAIT) == 0)
atomic_setbits_int(&pr->ps_flags, PS_WAITED);
@@ -571,6 +577,7 @@ loop:
(pr->ps_flags & PS_WAITED) == 0 &&
(pr->ps_flags & PS_STOPPED) &&
(pr->ps_flags & PS_TRAPPED) == 0) {
+ mtx_leave(&pr->ps_mtx);
if ((options & WNOWAIT) == 0)
atomic_setbits_int(&pr->ps_flags, PS_WAITED);
@@ -590,6 +597,7 @@ loop:
return (0);
}
if ((options & WCONTINUED) && (pr->ps_flags & PS_CONTINUED)) {
+ mtx_leave(&pr->ps_mtx);
if ((options & WNOWAIT) == 0)
atomic_clearbits_int(&pr->ps_flags,
PS_CONTINUED);
@@ -609,6 +617,7 @@ loop:
memset(rusage, 0, sizeof(*rusage));
return (0);
}
+ mtx_leave(&pr->ps_mtx);
}
/*
* Look in the orphans list too, to allow the parent to
@@ -638,7 +647,9 @@ loop:
*retval = 0;
return (0);
}
- if ((error = tsleep_nsec(q->p_p, PWAIT | PCATCH, "wait", INFSLP)) != 0)
+ sleep_setup(q->p_p, PWAIT | PCATCH, "wait");
+ if ((error = sleep_finish(0,
+ !ISSET(READ_ONCE(q->p_p->ps_flags), PS_WAITEVENT))) != 0)
return (error);
goto loop;
}
@@ -746,6 +757,7 @@ proc_finish_wait(struct proc *waiter, struct process *pr)
process_reparent(pr, tr);
mtx_leave(&pr->ps_mtx);
prsignal(tr, SIGCHLD);
+ atomic_setbits_int(&tr->ps_flags, PS_WAITEVENT);
wakeup(tr);
} else {
mtx_leave(&pr->ps_mtx);
diff --git kern/kern_fork.c kern/kern_fork.c
index 33a85a45fe3..61bc434d8b4 100644
--- kern/kern_fork.c
+++ kern/kern_fork.c
@@ -593,8 +593,8 @@ thread_fork(struct proc *curp, void *stack, void *tcb, pid_t *tidptr,
* if somebody else wants to take us to single threaded mode,
* count ourselves in.
*/
- if (pr->ps_single) {
- pr->ps_singlecnt++;
+ if (pr->ps_single || ISSET(pr->ps_flags, PS_STOPPING)) {
+ pr->ps_suspendcnt++;
atomic_setbits_int(&p->p_flag, P_SUSPSINGLE);
}
mtx_leave(&pr->ps_mtx);
diff --git kern/kern_proc.c kern/kern_proc.c
index 7abbab5fd48..2ce1f577199 100644
--- kern/kern_proc.c
+++ kern/kern_proc.c
@@ -501,7 +501,7 @@ proc_printit(struct proc *p, const char *modif,
p->p_runpri, p->p_usrpri, p->p_slppri, p->p_p->ps_nice);
(*pr)(" wchan=%p, wmesg=%s, ps_single=%p scnt=%d ecnt=%d\n",
p->p_wchan, (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "",
- p->p_p->ps_single, p->p_p->ps_singlecnt, p->p_p->ps_exitcnt);
+ p->p_p->ps_single, p->p_p->ps_suspendcnt, p->p_p->ps_exitcnt);
(*pr)(" forw=%p, list=%p,%p\n",
TAILQ_NEXT(p, p_runq), p->p_list.le_next, p->p_list.le_prev);
(*pr)(" process=%p user=%p, vmspace=%p\n",
diff --git kern/kern_sig.c kern/kern_sig.c
index b3ebcee6686..e5649febda1 100644
--- kern/kern_sig.c
+++ kern/kern_sig.c
@@ -121,12 +121,9 @@ const int sigprop[NSIG] = {
void setsigvec(struct proc *, int, struct sigaction *);
int proc_trap(struct proc *, int);
-void proc_stop(struct proc *p, int);
-void proc_stop_sweep(void *);
-void *proc_stop_si;
+void proc_stop(struct proc *p);
void process_continue(struct process *, int);
-void process_stop(struct process *, int, int);
void setsigctx(struct proc *, int, struct sigctx *);
void postsig_done(struct proc *, int, sigset_t, int);
@@ -134,6 +131,7 @@ void postsig(struct proc *, int, struct sigctx *);
int cansignal(struct proc *, struct process *, int);
void ptsignal_locked(struct proc *, int, enum signal_type);
+int proc_suspend_check_locked(struct proc *, int);
struct pool sigacts_pool; /* memory pool for sigacts structures */
@@ -203,11 +201,6 @@ cansignal(struct proc *p, struct process *qr, int signum)
void
signal_init(void)
{
- proc_stop_si = softintr_establish(IPL_SOFTCLOCK, proc_stop_sweep,
- NULL);
- if (proc_stop_si == NULL)
- panic("signal_init failed to register softintr");
-
pool_init(&sigacts_pool, sizeof(struct sigacts), 0, IPL_NONE,
PR_WAITOK, "sigapl", NULL);
}
@@ -917,7 +910,6 @@ prsignal(struct process *pr, int signum)
/*
* type = SPROCESS process signal, can be diverted (sigwait())
* type = STHREAD thread signal, but should be propagated if unhandled
- * type = SPROPAGATED propagated to this thread, so don't propagate again
*/
void
ptsignal(struct proc *p, int signum, enum signal_type type)
@@ -1009,8 +1001,7 @@ ptsignal_locked(struct proc *p, int signum, enum signal_type type)
}
}
- if (type != SPROPAGATED)
- knote_locked(&pr->ps_klist, NOTE_SIGNAL | signum);
+ knote_locked(&pr->ps_klist, NOTE_SIGNAL | signum);
prop = sigprop[signum];
@@ -1058,20 +1049,11 @@ ptsignal_locked(struct proc *p, int signum, enum signal_type type)
}
/*
* If delivered to process, mark as pending there. Continue and stop
- * signals will be propagated to all threads. So they are always
- * marked at thread level.
+ * signals are always marked at process level.
*/
siglist = (type == SPROCESS) ? &pr->ps_siglist : &p->p_siglist;
if (prop & (SA_CONT | SA_STOP))
- siglist = &p->p_siglist;
-
- /*
- * XXX delay processing of SA_STOP signals unless action == SIG_DFL?
- */
- if (prop & SA_STOP && type != SPROPAGATED)
- TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link)
- if (q != p)
- ptsignal_locked(q, signum, SPROPAGATED);
+ siglist = &pr->ps_siglist;
SCHED_LOCK();
@@ -1222,7 +1204,9 @@ ptsignal_locked(struct proc *p, int signum, enum signal_type type)
goto out;
mask = 0;
pr->ps_xsig = signum;
- proc_stop(p, 0);
+ atomic_setbits_int(&pr->ps_flags, PS_STOPPING);
+ process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND);
+ wakeparent = 1;
goto out;
}
/*
@@ -1269,8 +1253,15 @@ out:
}
SCHED_UNLOCK();
- if (wakeparent)
- wakeup(pr->ps_pptr);
+ if (wakeparent) {
+ if (prop & SA_STOP)
+ process_suspend_signal(pr);
+ else {
+ atomic_setbits_int(&pr->ps_pptr->ps_flags,
+ PS_WAITEVENT);
+ wakeup(pr->ps_pptr);
+ }
+ }
}
/* fill the signal context which should be used by postsig() and issignal() */
@@ -1444,10 +1435,16 @@ cursig(struct proc *p, struct sigctx *sctx, int deep)
* then clear the signal.
*/
if (sctx->sig_stop) {
+ mtx_enter(&pr->ps_mtx);
pr->ps_xsig = signum;
+ atomic_setbits_int(&pr->ps_flags, PS_STOPPING);
SCHED_LOCK();
- proc_stop(p, 1);
+ process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND);
SCHED_UNLOCK();
+ atomic_setbits_int(&p->p_flag, P_SUSPSIG);
+ process_suspend_signal(pr);
+ proc_stop(p);
+ mtx_leave(&pr->ps_mtx);
break;
} else if (prop & SA_IGNORE) {
/*
@@ -1497,21 +1494,42 @@ proc_trap(struct proc *p, int signum)
{
struct process *pr = p->p_p;
- single_thread_set(p, SINGLE_SUSPEND | SINGLE_NOWAIT);
- pr->ps_xsig = signum;
+ mtx_enter(&pr->ps_mtx);
+ /*
+ * Wait until any other suspend condition cleared,
+ * including other traps.
+ */
+ proc_suspend_check_locked(p, 0);
+ atomic_setbits_int(&pr->ps_flags, PS_STOPPING | PS_TRAPPED);
SCHED_LOCK();
- atomic_setbits_int(&pr->ps_flags, PS_TRAPPED);
- proc_stop(p, 1);
+ process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND);
+ SCHED_UNLOCK();
+ atomic_setbits_int(&p->p_flag, P_SUSPSIG);
+ pr->ps_xsig = signum;
+ pr->ps_trapped = p;
+
+ process_suspend_signal(pr);
+ proc_stop(p);
+ /*
+ * Clear all flags for proc and process by hand here since ptrace
+ * just calls setrunnable on the thread without clearing anything.
+ */
+ atomic_clearbits_int(&p->p_flag, P_SUSPSIG);
atomic_clearbits_int(&pr->ps_flags,
PS_WAITED | PS_STOPPED | PS_TRAPPED);
- SCHED_UNLOCK();
signum = pr->ps_xsig;
pr->ps_xsig = 0;
- if ((p->p_flag & P_TRACESINGLE) == 0)
- single_thread_clear(p);
+ pr->ps_trapped = NULL;
+
+ if ((p->p_flag & P_TRACESINGLE) == 0) {
+ SCHED_LOCK();
+ process_continue(pr, P_SUSPSIG);
+ SCHED_UNLOCK();
+ }
atomic_clearbits_int(&p->p_flag, P_TRACESINGLE);
+ mtx_leave(&pr->ps_mtx);
return signum;
}
@@ -1552,7 +1570,8 @@ process_continue(struct process *pr, int flag)
* Clearing either makes the thread runnable or puts
* it back into some sleep queue.
*/
- if (q->p_stat == SSTOP) {
+ if (q->p_stat == SSTOP &&
+ ISSET(q->p_flag, P_SUSPSIG | P_SUSPSINGLE) == 0) {
if (q->p_wchan == NULL)
setrunnable(q);
else
@@ -1576,10 +1595,10 @@ process_stop(struct process *pr, int flag, int mode)
/* skip curproc if it is part of pr, caller takes care of that */
if (curproc->p_p == pr) {
p = curproc;
- KASSERT(ISSET(p->p_flag, P_SUSPSINGLE) == 0);
+ KASSERT(ISSET(p->p_flag, P_SUSPSIG | P_SUSPSINGLE) == 0);
}
- pr->ps_singlecnt = pr->ps_threadcnt;
+ pr->ps_suspendcnt = pr->ps_threadcnt;
TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
if (q == p)
continue;
@@ -1598,7 +1617,7 @@ process_stop(struct process *pr, int flag, int mode)
unsleep(q);
setrunnable(q);
} else
- --pr->ps_singlecnt;
+ --pr->ps_suspendcnt;
break;
case SSLEEP:
/* if it's not interruptible, then just have to wait */
@@ -1606,7 +1625,7 @@ process_stop(struct process *pr, int flag, int mode)
/* merely need to suspend? just stop it */
if (mode == SINGLE_SUSPEND) {
q->p_stat = SSTOP;
- --pr->ps_singlecnt;
+ --pr->ps_suspendcnt;
} else {
/* need to unwind or exit, so wake it */
unsleep(q);
@@ -1632,50 +1651,58 @@ process_stop(struct process *pr, int flag, int mode)
* on the run queue.
*/
void
-proc_stop(struct proc *p, int sw)
+proc_stop(struct proc *p)
{
struct process *pr = p->p_p;
-#ifdef MULTIPROCESSOR
- SCHED_ASSERT_LOCKED();
-#endif
- /* do not stop exiting procs */
- if (ISSET(p->p_flag, P_WEXIT))
- return;
+ MUTEX_ASSERT_LOCKED(&pr->ps_mtx);
- p->p_stat = SSTOP;
- atomic_clearbits_int(&pr->ps_flags, PS_WAITED);
- atomic_setbits_int(&pr->ps_flags, PS_STOPPING);
- atomic_setbits_int(&p->p_flag, P_SUSPSIG);
/*
- * We need this soft interrupt to be handled fast.
- * Extra calls to softclock don't hurt.
+ * XXX to mi_switch we need to release the ps_mtx but then
+ * another ptsignal can race against this thread and see
+ * it in the wrong state. So instead set p_stat while still
+ * holding the ps_mtx.
+ * We still have an issue when dropping the ps_mtx and grabbing
+ * the SCHED_LOCK but that can't be fixed.
*/
- softintr_schedule(proc_stop_si);
- if (sw)
- mi_switch();
+ SCHED_LOCK();
+ p->p_stat = SSTOP;
+ SCHED_UNLOCK();
+ mtx_leave(&pr->ps_mtx);
+ SCHED_LOCK();
+ mi_switch();
+ SCHED_UNLOCK();
+ mtx_enter(&pr->ps_mtx);
}
/*
- * Called from a soft interrupt to send signals to the parents of stopped
- * processes.
- * We can't do this in proc_stop because it's called with nasty locks held
- * and we would need recursive scheduler lock to deal with that.
+ * Signal either the parent process or the ps_single thread depending on
+ * the mode. Only do this if the suspendcnt dropped to 0. If curproc part
+ * of the process count it out first.
*/
void
-proc_stop_sweep(void *v)
+process_suspend_signal(struct process *pr)
{
- struct process *pr;
+ MUTEX_ASSERT_LOCKED(&pr->ps_mtx);
- LIST_FOREACH(pr, &allprocess, ps_list) {
- if ((pr->ps_flags & PS_STOPPING) == 0)
- continue;
+ /* if part of the process, count us out */
+ if (curproc->p_p == pr)
+ --pr->ps_suspendcnt;
+
+ if (pr->ps_suspendcnt != 0)
+ return;
+
+ if (pr->ps_single == NULL) {
+ atomic_clearbits_int(&pr->ps_flags,
+ PS_STOPPING | PS_WAITED | PS_CONTINUED);
atomic_setbits_int(&pr->ps_flags, PS_STOPPED);
- atomic_clearbits_int(&pr->ps_flags, PS_STOPPING);
if ((pr->ps_pptr->ps_sigacts->ps_sigflags & SAS_NOCLDSTOP) == 0)
prsignal(pr->ps_pptr, SIGCHLD);
+ atomic_setbits_int(&pr->ps_pptr->ps_flags, PS_WAITEVENT);
wakeup(pr->ps_pptr);
+ } else {
+ wakeup(&pr->ps_suspendcnt);
}
}
@@ -2145,7 +2172,7 @@ userret(struct proc *p)
struct sigctx ctx;
int signum;
- if (p->p_flag & P_SUSPSINGLE)
+ if (p->p_flag & (P_SUSPSINGLE | P_SUSPSIG))
proc_suspend_check(p, 0);
/* send SIGPROF or SIGVTALRM if their timers interrupted this thread */
@@ -2189,7 +2216,8 @@ proc_suspend_check_locked(struct proc *p, int deep)
MUTEX_ASSERT_LOCKED(&pr->ps_mtx);
- if (pr->ps_single == NULL || pr->ps_single == p)
+ if ((pr->ps_single == NULL || pr->ps_single == p) &&
+ !ISSET(pr->ps_flags, PS_STOPPING))
return (0);
/* if we're in deep, we need to unwind to the edge */
@@ -2214,18 +2242,11 @@ proc_suspend_check_locked(struct proc *p, int deep)
/* NOTREACHED */
}
- if (--pr->ps_singlecnt == 0)
- wakeup(&pr->ps_singlecnt);
+ process_suspend_signal(pr);
/* not exiting and don't need to unwind, so suspend */
- mtx_leave(&pr->ps_mtx);
-
- SCHED_LOCK();
- p->p_stat = SSTOP;
- mi_switch();
- SCHED_UNLOCK();
- mtx_enter(&pr->ps_mtx);
- } while (pr->ps_single != NULL);
+ proc_stop(p);
+ } while (pr->ps_single != NULL || ISSET(pr->ps_flags, PS_STOPPING));
return (0);
}
@@ -2288,12 +2309,10 @@ single_thread_set(struct proc *p, int flags)
SCHED_UNLOCK();
/* count ourself out */
- --pr->ps_singlecnt;
+ --pr->ps_suspendcnt;
mtx_leave(&pr->ps_mtx);
- if ((flags & SINGLE_NOWAIT) == 0)
- single_thread_wait(pr, 1);
-
+ single_thread_wait(pr, 1);
return 0;
}
@@ -2309,8 +2328,8 @@ single_thread_wait(struct process *pr, int recheck)
/* wait until they're all suspended */
mtx_enter(&pr->ps_mtx);
- while ((wait = pr->ps_singlecnt > 0)) {
- msleep_nsec(&pr->ps_singlecnt, &pr->ps_mtx, PWAIT, "suspend",
+ while ((wait = pr->ps_suspendcnt > 0)) {
+ msleep_nsec(&pr->ps_suspendcnt, &pr->ps_mtx, PWAIT, "suspend",
INFSLP);
if (!recheck)
break;
diff --git kern/kern_synch.c kern/kern_synch.c
index 8548aea1c87..db97fde0c29 100644
--- kern/kern_synch.c
+++ kern/kern_synch.c
@@ -64,8 +64,6 @@
int sleep_signal_check(struct proc *, int);
-extern void proc_stop(struct proc *p, int);
-
/*
* We're only looking at 7 bits of the address; everything is
* aligned to 4, lots of things are aligned to greater powers
@@ -458,6 +456,7 @@ sleep_finish(int timo, int do_sleep)
int
sleep_signal_check(struct proc *p, int after_sleep)
{
+ struct process *pr = p->p_p;
struct sigctx ctx;
int err, sig;
@@ -467,24 +466,38 @@ sleep_signal_check(struct proc *p, int after_sleep)
/* requested to stop */
if (!after_sleep) {
- mtx_enter(&p->p_p->ps_mtx);
- if (--p->p_p->ps_singlecnt == 0)
- wakeup(&p->p_p->ps_singlecnt);
- mtx_leave(&p->p_p->ps_mtx);
+ mtx_enter(&pr->ps_mtx);
+ process_suspend_signal(pr);
SCHED_LOCK();
p->p_stat = SSTOP;
SCHED_UNLOCK();
+ mtx_leave(&pr->ps_mtx);
}
}
if ((sig = cursig(p, &ctx, 1)) != 0) {
if (ctx.sig_stop) {
if (!after_sleep) {
- p->p_p->ps_xsig = sig;
+ mtx_enter(&pr->ps_mtx);
+ pr->ps_xsig = sig;
+ /*
+ * This is for stop signals delivered before
+ * sleep_setup() was called. We need to do the
+ * full dance here before going to sleep.
+ */
+ atomic_clearbits_int(&p->p_siglist,
+ sigmask(sig));
+ atomic_setbits_int(&pr->ps_flags, PS_STOPPING);
+ SCHED_LOCK();
+ process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND);
+ SCHED_UNLOCK();
+ atomic_setbits_int(&p->p_flag, P_SUSPSIG);
+ process_suspend_signal(pr);
SCHED_LOCK();
- proc_stop(p, 0);
+ p->p_stat = SSTOP;
SCHED_UNLOCK();
+ mtx_leave(&pr->ps_mtx);
}
} else if (ctx.sig_intr && !ctx.sig_ignore)
return EINTR;
diff --git kern/sys_process.c kern/sys_process.c
index d06b8f9e19a..365e44258c7 100644
--- kern/sys_process.c
+++ kern/sys_process.c
@@ -330,12 +330,11 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data)
case PT_ATTACH:
case PT_DETACH:
/* Find the process we're supposed to be operating on. */
- if ((tr = prfind(pid)) == NULL) {
+ if (pid > THREAD_PID_OFFSET) {
error = ESRCH;
goto fail;
}
- t = TAILQ_FIRST(&tr->ps_threads);
- break;
+ /* FALLTHROUGH */
/* calls that accept a PID or a thread ID */
case PT_CONTINUE:
@@ -466,16 +465,6 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data)
* from where it stopped."
*/
- if (pid < THREAD_PID_OFFSET && tr->ps_single)
- t = tr->ps_single;
- else if (t == tr->ps_single)
- atomic_setbits_int(&t->p_flag, P_TRACESINGLE);
- else {
- error = EINVAL;
- goto fail;
- }
-
-
/* If the address parameter is not (int *)1, set the pc. */
if ((int *)addr != (int *)1)
if ((error = process_set_pc(t, addr)) != 0)
@@ -504,9 +493,6 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data)
* from where it stopped."
*/
- if (pid < THREAD_PID_OFFSET && tr->ps_single)
- t = tr->ps_single;
-
#ifdef PT_STEP
/*
* Stop single stepping.
@@ -525,25 +511,29 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data)
memset(tr->ps_ptstat, 0, sizeof(*tr->ps_ptstat));
/* Finally, deliver the requested signal (or none). */
- if (t->p_stat == SSTOP) {
- tr->ps_xsig = data;
+ if (tr->ps_trapped == t) {
SCHED_LOCK();
+ if (pid >= THREAD_PID_OFFSET)
+ atomic_setbits_int(&t->p_flag,
+ P_TRACESINGLE);
+ tr->ps_xsig = data;
unsleep(t);
setrunnable(t);
SCHED_UNLOCK();
- } else {
+ } else if (pid < THREAD_PID_OFFSET) {
if (data != 0)
- psignal(t, data);
+ ptsignal(t, data, SPROCESS);
+ } else {
+ /* can not signal a single thread */
+ error = EINVAL;
+ goto fail;
}
break;
case PT_KILL:
- if (pid < THREAD_PID_OFFSET && tr->ps_single)
- t = tr->ps_single;
-
/* just send the process a KILL signal. */
data = SIGKILL;
- goto sendsig; /* in PT_CONTINUE, above. */
+ goto sendsig; /* in PT_DETACH, above. */
case PT_ATTACH:
/*
@@ -625,9 +615,13 @@ ptrace_kstate(struct proc *p, int req, pid_t pid, void *addr)
tr->ps_ptmask = pe->pe_set_event;
break;
case PT_GET_PROCESS_STATE:
- if (tr->ps_single)
- tr->ps_ptstat->pe_tid =
- tr->ps_single->p_tid + THREAD_PID_OFFSET;
+ mtx_enter(&tr->ps_mtx);
+ if (tr->ps_trapped != NULL)
+ tr->ps_ptstat->pe_tid = tr->ps_trapped->p_tid +
+ THREAD_PID_OFFSET;
+ else
+ tr->ps_ptstat->pe_tid = 0;
+ mtx_leave(&tr->ps_mtx);
memcpy(addr, tr->ps_ptstat, sizeof *tr->ps_ptstat);
break;
default:
@@ -812,21 +806,26 @@ ptrace_ustate(struct proc *p, int req, pid_t pid, void *addr, int data,
static inline struct process *
process_tprfind(pid_t tpid, struct proc **tp)
{
- if (tpid > THREAD_PID_OFFSET) {
- struct proc *t = tfind(tpid - THREAD_PID_OFFSET);
+ struct process *tr;
+ struct proc *t;
+ if (tpid > THREAD_PID_OFFSET) {
+ t = tfind(tpid - THREAD_PID_OFFSET);
if (t == NULL)
return NULL;
- *tp = t;
- return t->p_p;
+ tr = t->p_p;
} else {
- struct process *tr = prfind(tpid);
-
+ tr = prfind(tpid);
if (tr == NULL)
return NULL;
- *tp = TAILQ_FIRST(&tr->ps_threads);
- return tr;
+ if (tr->ps_trapped != NULL)
+ t = tr->ps_trapped;
+ else
+ t = TAILQ_FIRST(&tr->ps_threads);
}
+
+ *tp = t;
+ return tr;
}
diff --git sys/proc.h sys/proc.h
index dd4b8686cf9..a68e925e044 100644
--- sys/proc.h
+++ sys/proc.h
@@ -190,7 +190,8 @@ struct process {
int ps_siglist; /* Signals pending for the process. */
struct proc *ps_single; /* [m] Thread for single-threading. */
- u_int ps_singlecnt; /* [m] Number of threads to suspend. */
+ struct proc *ps_trapped; /* [m] Thread trapped for ptrace. */
+ u_int ps_suspendcnt; /* [m] Number of threads to suspend. */
u_int ps_exitcnt; /* [m] Number of threads in exit1. */
int ps_traceflag; /* Kernel trace points. */
@@ -303,6 +304,7 @@ struct process {
#define PS_CHROOT 0x01000000 /* Process is chrooted */
#define PS_NOBTCFI 0x02000000 /* No Branch Target CFI */
#define PS_ITIMER 0x04000000 /* Virtual interval timers running */
+#define PS_WAITEVENT 0x10000000 /* wait(2) event pending */
#define PS_CONTINUED 0x20000000 /* Continued proc not yet waited for */
#define PS_STOPPED 0x40000000 /* Stopped process */
#define PS_TRAPPED 0x80000000 /* Stopped due to tracing event */
@@ -313,8 +315,8 @@ struct process {
"\013WAITED" "\014COREDUMP" "\015SINGLEEXIT" "\016SINGLEUNWIND" \
"\017NOZOMBIE" "\020STOPPING" "\021SYSTEM" "\022EMBRYO" "\023ZOMBIE" \
"\024NOBROADCASTKILL" "\025PLEDGE" "\026WXNEEDED" "\027EXECPLEDGE" \
- "\030ORPHAN" "\031CHROOT" "\032NOBTCFI" "\033ITIMER" "\036CONTINUED" \
- "\037STOPPED" "\040TRAPPED")
+ "\030ORPHAN" "\031CHROOT" "\032NOBTCFI" "\033ITIMER" "\035WAITEVENT" \
+ "\036CONTINUED" "\037STOPPED" "\040TRAPPED")
struct kcov_dev;
struct lock_list_entry;
@@ -596,13 +598,14 @@ refreshcreds(struct proc *p)
#define SINGLE_MASK 0x0f
/* extra flags for single_thread_set */
#define SINGLE_DEEP 0x10 /* call is in deep */
-#define SINGLE_NOWAIT 0x20 /* do not wait for other threads to stop */
int single_thread_set(struct proc *, int);
int single_thread_wait(struct process *, int);
void single_thread_clear(struct proc *);
int proc_suspend_check(struct proc *, int);
+void process_suspend_signal(struct process *);
+void process_stop(struct process *, int, int);
void child_return(void *);
diff --git sys/signalvar.h sys/signalvar.h
index b41ca3adf3c..dffc5e01567 100644
--- sys/signalvar.h
+++ sys/signalvar.h
@@ -89,7 +89,7 @@ struct sigacts {
#define sigcantmask (sigmask(SIGKILL) | sigmask(SIGSTOP))
#ifdef _KERNEL
-enum signal_type { SPROCESS, STHREAD, SPROPAGATED };
+enum signal_type { SPROCESS, STHREAD };
struct sigio_ref;
fix stop signal delivery