Download raw body.
fix stop signal delivery
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.
Cheers
--
:wq Claudio
Index: kern/kern_exec.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_exec.c,v
diff -u -p -r1.260 kern_exec.c
--- kern/kern_exec.c 25 Jan 2025 19:21:40 -0000 1.260
+++ kern/kern_exec.c 4 Feb 2025 13:59:51 -0000
@@ -556,6 +556,7 @@ sys_execve(struct proc *p, void *v, regi
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);
}
Index: kern/kern_exit.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_exit.c,v
diff -u -p -r1.240 kern_exit.c
--- kern/kern_exit.c 17 Dec 2024 14:45:00 -0000 1.240
+++ kern/kern_exit.c 4 Feb 2025 13:59:51 -0000
@@ -148,6 +148,8 @@ exit1(struct proc *p, int xexit, int xsi
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 xsi
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 xsi
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,
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, st
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);
Index: kern/kern_fork.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_fork.c,v
diff -u -p -r1.268 kern_fork.c
--- kern/kern_fork.c 10 Nov 2024 06:51:59 -0000 1.268
+++ kern/kern_fork.c 4 Feb 2025 13:59:51 -0000
@@ -593,8 +593,8 @@ thread_fork(struct proc *curp, void *sta
* 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);
Index: kern/kern_proc.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_proc.c,v
diff -u -p -r1.101 kern_proc.c
--- kern/kern_proc.c 22 Oct 2024 11:54:04 -0000 1.101
+++ kern/kern_proc.c 4 Feb 2025 13:59:51 -0000
@@ -501,7 +501,7 @@ proc_printit(struct proc *p, const char
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",
Index: kern/kern_sig.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sig.c,v
diff -u -p -r1.359 kern_sig.c
--- kern/kern_sig.c 25 Jan 2025 19:21:40 -0000 1.359
+++ kern/kern_sig.c 4 Feb 2025 13:59:51 -0000
@@ -121,9 +121,7 @@ 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);
@@ -133,6 +131,7 @@ void postsig(struct proc *, int, struct
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 */
@@ -202,11 +201,6 @@ cansignal(struct proc *p, struct process
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);
}
@@ -916,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)
@@ -1008,8 +1001,7 @@ ptsignal_locked(struct proc *p, int sign
}
}
- if (type != SPROPAGATED)
- knote_locked(&pr->ps_klist, NOTE_SIGNAL | signum);
+ knote_locked(&pr->ps_klist, NOTE_SIGNAL | signum);
prop = sigprop[signum];
@@ -1057,20 +1049,11 @@ ptsignal_locked(struct proc *p, int sign
}
/*
* 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();
@@ -1221,7 +1204,9 @@ ptsignal_locked(struct proc *p, int sign
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;
}
/*
@@ -1268,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() */
@@ -1443,10 +1435,16 @@ cursig(struct proc *p, struct sigctx *sc
* 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) {
/*
@@ -1496,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;
}
@@ -1551,7 +1570,8 @@ process_continue(struct process *pr, int
* 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
@@ -1562,55 +1582,128 @@ process_continue(struct process *pr, int
}
/*
+ * Signal all but p threads of a process pr to stop because of `flag'.
+ * Depending on `mode' stopped and sleeping threads may be woken up.
+ */
+void
+process_stop(struct process *pr, int flag, int mode)
+{
+ struct proc *q, *p = NULL;
+
+ MUTEX_ASSERT_LOCKED(&pr->ps_mtx);
+
+ /* 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_SUSPSIG | P_SUSPSINGLE) == 0);
+ }
+
+ pr->ps_suspendcnt = pr->ps_threadcnt;
+ TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
+ if (q == p)
+ continue;
+ atomic_setbits_int(&q->p_flag, flag);
+
+ /*
+ * XXX in ptsignal the SCHED_LOCK is already held so we can't
+ * grab it here until that is fixed.
+ */
+ /* XXX SCHED_LOCK(); */
+ SCHED_ASSERT_LOCKED();
+
+ switch (q->p_stat) {
+ case SSTOP:
+ if (mode == SINGLE_EXIT) {
+ unsleep(q);
+ setrunnable(q);
+ } else
+ --pr->ps_suspendcnt;
+ break;
+ case SSLEEP:
+ /* if it's not interruptible, then just have to wait */
+ if (q->p_flag & P_SINTR) {
+ /* merely need to suspend? just stop it */
+ if (mode == SINGLE_SUSPEND) {
+ q->p_stat = SSTOP;
+ --pr->ps_suspendcnt;
+ break;
+ } else {
+ /* need to unwind or exit, so wake it */
+ unsleep(q);
+ setrunnable(q);
+ }
+ }
+ break;
+ case SONPROC:
+ signotify(q);
+ break;
+ case SRUN:
+ case SIDL:
+ case SDEAD:
+ break;
+ }
+ /* XXX SCHED_UNLOCK(); */
+ }
+}
+
+/*
* Put the argument process into the stopped state and notify the parent
* via wakeup. Signals are handled elsewhere. The process must not be
* 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);
}
}
@@ -2080,8 +2173,8 @@ userret(struct proc *p)
struct sigctx ctx;
int signum;
- if (p->p_flag & P_SUSPSINGLE)
- single_thread_check(p, 0);
+ if (p->p_flag & (P_SUSPSINGLE | P_SUSPSIG))
+ proc_suspend_check(p, 0);
/* send SIGPROF or SIGVTALRM if their timers interrupted this thread */
if (p->p_flag & P_PROFPEND) {
@@ -2118,13 +2211,14 @@ userret(struct proc *p)
}
int
-single_thread_check_locked(struct proc *p, int deep)
+proc_suspend_check_locked(struct proc *p, int deep)
{
struct process *pr = p->p_p;
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 */
@@ -2149,29 +2243,22 @@ single_thread_check_locked(struct proc *
/* 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);
}
int
-single_thread_check(struct proc *p, int deep)
+proc_suspend_check(struct proc *p, int deep)
{
int error;
mtx_enter(&p->p_p->ps_mtx);
- error = single_thread_check_locked(p, deep);
+ error = proc_suspend_check_locked(p, deep);
mtx_leave(&p->p_p->ps_mtx);
return error;
@@ -2190,13 +2277,12 @@ int
single_thread_set(struct proc *p, int flags)
{
struct process *pr = p->p_p;
- struct proc *q;
int error, mode = flags & SINGLE_MASK;
KASSERT(curproc == p);
mtx_enter(&pr->ps_mtx);
- error = single_thread_check_locked(p, flags & SINGLE_DEEP);
+ error = proc_suspend_check_locked(p, flags & SINGLE_DEEP);
if (error) {
mtx_leave(&pr->ps_mtx);
return error;
@@ -2217,55 +2303,17 @@ single_thread_set(struct proc *p, int fl
panic("single_thread_mode = %d", mode);
#endif
}
- KASSERT((p->p_flag & P_SUSPSINGLE) == 0);
pr->ps_single = p;
- pr->ps_singlecnt = pr->ps_threadcnt;
- TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
- if (q == p)
- continue;
- SCHED_LOCK();
- atomic_setbits_int(&q->p_flag, P_SUSPSINGLE);
- switch (q->p_stat) {
- case SSTOP:
- if (mode == SINGLE_EXIT) {
- unsleep(q);
- setrunnable(q);
- } else
- --pr->ps_singlecnt;
- break;
- case SSLEEP:
- /* if it's not interruptible, then just have to wait */
- if (q->p_flag & P_SINTR) {
- /* merely need to suspend? just stop it */
- if (mode == SINGLE_SUSPEND) {
- q->p_stat = SSTOP;
- --pr->ps_singlecnt;
- break;
- }
- /* need to unwind or exit, so wake it */
- unsleep(q);
- setrunnable(q);
- }
- break;
- case SONPROC:
- signotify(q);
- break;
- case SRUN:
- case SIDL:
- case SDEAD:
- break;
- }
- SCHED_UNLOCK();
- }
+ SCHED_LOCK();
+ process_stop(pr, P_SUSPSINGLE, mode);
+ 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;
}
@@ -2281,8 +2329,8 @@ single_thread_wait(struct process *pr, i
/* 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;
Index: kern/kern_synch.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_synch.c,v
diff -u -p -r1.218 kern_synch.c
--- kern/kern_synch.c 22 Jan 2025 16:14:22 -0000 1.218
+++ kern/kern_synch.c 4 Feb 2025 13:59:51 -0000
@@ -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,33 +456,48 @@ 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;
- if ((err = single_thread_check(p, 1)) != 0) {
+ if ((err = proc_suspend_check(p, 1)) != 0) {
if (err != EWOULDBLOCK)
return err;
/* 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;
Index: kern/sys_process.c
===================================================================
RCS file: /cvs/src/sys/kern/sys_process.c,v
diff -u -p -r1.105 sys_process.c
--- kern/sys_process.c 15 Dec 2024 18:25:12 -0000 1.105
+++ kern/sys_process.c 4 Feb 2025 13:59:51 -0000
@@ -330,12 +330,11 @@ ptrace_ctrl(struct proc *p, int req, pid
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
* 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
* 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
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, p
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, p
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;
}
Index: sys/proc.h
===================================================================
RCS file: /cvs/src/sys/sys/proc.h,v
diff -u -p -r1.378 proc.h
--- sys/proc.h 25 Jan 2025 19:21:40 -0000 1.378
+++ sys/proc.h 4 Feb 2025 13:59:51 -0000
@@ -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,12 +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 single_thread_check(struct proc *, int);
+
+int proc_suspend_check(struct proc *, int);
+void process_suspend_signal(struct process *);
+void process_stop(struct process *, int, int);
void child_return(void *);
Index: sys/signalvar.h
===================================================================
RCS file: /cvs/src/sys/sys/signalvar.h,v
diff -u -p -r1.57 signalvar.h
--- sys/signalvar.h 4 Nov 2024 22:41:50 -0000 1.57
+++ sys/signalvar.h 4 Feb 2025 13:59:51 -0000
@@ -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