From: Claudio Jeker Subject: Re: fix stop signal delivery To: tech@openbsd.org Date: Thu, 20 Feb 2025 14:05:38 +0100 On Tue, Feb 18, 2025 at 03:52:49PM +0100, Claudio Jeker wrote: > On Mon, Feb 10, 2025 at 07:49:39AM +0100, Claudio Jeker wrote: > > 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. > > This is the latest version of this diff. New version that fixes a race in proc_stop(). This has been stable for me even when hammering the system with signal-stress over and over again. -- :wq Claudio Index: dev/dt/dt_prov_static.c =================================================================== RCS file: /cvs/src/sys/dev/dt/dt_prov_static.c,v diff -u -p -r1.24 dt_prov_static.c --- dev/dt/dt_prov_static.c 9 Jan 2025 17:43:33 -0000 1.24 +++ dev/dt/dt_prov_static.c 20 Feb 2025 12:33:50 -0000 @@ -44,9 +44,11 @@ DT_STATIC_PROBE2(sched, off__cpu, "pid_t DT_STATIC_PROBE0(sched, on__cpu); DT_STATIC_PROBE0(sched, remain__cpu); DT_STATIC_PROBE0(sched, sleep); +DT_STATIC_PROBE0(sched, stop); DT_STATIC_PROBE3(sched, steal, "pid_t", "pid_t", "int"); DT_STATIC_PROBE2(sched, unsleep, "pid_t", "pid_t"); DT_STATIC_PROBE3(sched, wakeup, "pid_t", "pid_t", "int"); +DT_STATIC_PROBE3(sched, unstop, "pid_t", "pid_t", "int"); /* * Raw syscalls @@ -116,9 +118,11 @@ struct dt_probe *const dtps_static[] = { &_DT_STATIC_P(sched, on__cpu), &_DT_STATIC_P(sched, remain__cpu), &_DT_STATIC_P(sched, sleep), + &_DT_STATIC_P(sched, stop), &_DT_STATIC_P(sched, steal), &_DT_STATIC_P(sched, unsleep), &_DT_STATIC_P(sched, wakeup), + &_DT_STATIC_P(sched, unstop), /* Raw syscalls */ &_DT_STATIC_P(raw_syscalls, sys_enter), &_DT_STATIC_P(raw_syscalls, sys_exit), Index: dev/pci/drm/drm_linux.c =================================================================== RCS file: /cvs/src/sys/dev/pci/drm/drm_linux.c,v diff -u -p -r1.120 drm_linux.c --- dev/pci/drm/drm_linux.c 7 Feb 2025 03:03:08 -0000 1.120 +++ dev/pci/drm/drm_linux.c 20 Feb 2025 12:33:50 -0000 @@ -122,7 +122,7 @@ __set_current_state(int state) SCHED_LOCK(); unsleep(p); p->p_stat = SONPROC; - atomic_clearbits_int(&p->p_flag, P_WSLEEP); + atomic_clearbits_int(&p->p_flag, P_INSCHED); SCHED_UNLOCK(); } Index: kern/kern_exit.c =================================================================== RCS file: /cvs/src/sys/kern/kern_exit.c,v diff -u -p -r1.242 kern_exit.c --- kern/kern_exit.c 17 Feb 2025 10:16:05 -0000 1.242 +++ kern/kern_exit.c 20 Feb 2025 12:33:52 -0000 @@ -164,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 != NULL || 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(); @@ -525,7 +523,6 @@ loop: } 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; @@ -548,17 +545,15 @@ loop: pr->ps_xsig); if (rusage != NULL) memcpy(rusage, pr->ps_ru, sizeof(*rusage)); + mtx_leave(&pr->ps_mtx); if ((options & WNOWAIT) == 0) proc_finish_wait(q, pr); return (0); } if ((options & WTRAPPED) && (pr->ps_flags & PS_TRACED) && (pr->ps_flags & PS_WAITED) == 0 && + (pr->ps_flags & PS_STOPPED) && (pr->ps_flags & PS_TRAPPED)) { - mtx_leave(&pr->ps_mtx); - if (single_thread_wait(pr, 0)) - goto loop; - if ((options & WNOWAIT) == 0) atomic_setbits_int(&pr->ps_flags, PS_WAITED); @@ -573,6 +568,7 @@ loop: if (statusp != NULL) *statusp = W_STOPCODE(pr->ps_xsig); + mtx_leave(&pr->ps_mtx); if (rusage != NULL) memset(rusage, 0, sizeof(*rusage)); return (0); @@ -581,7 +577,6 @@ 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); @@ -596,12 +591,12 @@ loop: if (statusp != NULL) *statusp = W_STOPCODE(pr->ps_xsig); + mtx_leave(&pr->ps_mtx); if (rusage != NULL) memset(rusage, 0, sizeof(*rusage)); 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); @@ -615,6 +610,7 @@ loop: info->si_status = SIGCONT; } + mtx_leave(&pr->ps_mtx); if (statusp != NULL) *statusp = _WCONTINUED; if (rusage != NULL) 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 20 Feb 2025 12:33:52 -0000 @@ -590,12 +590,13 @@ thread_fork(struct proc *curp, void *sta pr->ps_threadcnt++; /* - * if somebody else wants to take us to single threaded mode, - * count ourselves in. + * if somebody else wants to take us to single threaded mode + * or suspend the process, count ourselves in. */ - if (pr->ps_single) { - pr->ps_singlecnt++; - atomic_setbits_int(&p->p_flag, P_SUSPSINGLE); + if (pr->ps_single != NULL || ISSET(pr->ps_flags, PS_STOPPING)) { + pr->ps_suspendcnt++; + atomic_setbits_int(&p->p_flag, + curp->p_flag & (P_SUSPSINGLE | P_SUSPSIG)); } 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 20 Feb 2025 12:33:52 -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_sched.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sched.c,v diff -u -p -r1.103 kern_sched.c --- kern/kern_sched.c 24 Nov 2024 13:05:14 -0000 1.103 +++ kern/kern_sched.c 20 Feb 2025 12:33:52 -0000 @@ -277,7 +277,7 @@ setrunqueue(struct cpu_info *ci, struct KASSERT(ci != NULL); SCHED_ASSERT_LOCKED(); KASSERT(p->p_wchan == NULL); - KASSERT(!ISSET(p->p_flag, P_WSLEEP)); + KASSERT(!ISSET(p->p_flag, P_INSCHED)); p->p_cpu = ci; p->p_stat = SRUN; @@ -368,7 +368,7 @@ again: } KASSERT(p->p_wchan == NULL); - KASSERT(!ISSET(p->p_flag, P_WSLEEP)); + KASSERT(!ISSET(p->p_flag, P_INSCHED)); return (p); } Index: kern/kern_sig.c =================================================================== RCS file: /cvs/src/sys/kern/kern_sig.c,v diff -u -p -r1.363 kern_sig.c --- kern/kern_sig.c 17 Feb 2025 15:45:55 -0000 1.363 +++ kern/kern_sig.c 20 Feb 2025 12:33:52 -0000 @@ -61,6 +61,7 @@ #include #include #include +#include #include #include @@ -121,12 +122,10 @@ 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_setup(struct proc *p); +void proc_stop_finish(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 +133,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 */ @@ -203,11 +203,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); } @@ -917,7 +912,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 +1003,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]; @@ -1058,20 +1051,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(); @@ -1222,7 +1206,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; } /* @@ -1270,8 +1256,13 @@ out: SCHED_UNLOCK(); if (wakeparent) { - atomic_setbits_int(&pr->ps_pptr->ps_flags, PS_WAITEVENT); - wakeup(pr->ps_pptr); + if (prop & SA_STOP) + process_suspend_signal(pr); + else { + atomic_setbits_int(&pr->ps_pptr->ps_flags, + PS_WAITEVENT); + wakeup(pr->ps_pptr); + } } } @@ -1446,10 +1437,17 @@ 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); + atomic_setbits_int(&p->p_flag, P_SUSPSIG); + proc_stop_setup(p); SCHED_UNLOCK(); + process_suspend_signal(pr); + proc_stop_finish(p); + mtx_leave(&pr->ps_mtx); break; } else if (prop & SA_IGNORE) { /* @@ -1499,29 +1497,43 @@ proc_trap(struct proc *p, int signum) { struct process *pr = p->p_p; - single_thread_set(p, SINGLE_SUSPEND | SINGLE_NOWAIT); - 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(); + process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND); + atomic_setbits_int(&p->p_flag, P_SUSPSIG); + proc_stop_setup(p); + SCHED_UNLOCK(); pr->ps_xsig = signum; pr->ps_trapped = p; - mtx_leave(&pr->ps_mtx); - SCHED_LOCK(); - atomic_setbits_int(&pr->ps_flags, PS_TRAPPED); - proc_stop(p, 1); + process_suspend_signal(pr); + proc_stop_finish(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(); - mtx_enter(&pr->ps_mtx); signum = pr->ps_xsig; pr->ps_xsig = 0; pr->ps_trapped = NULL; - mtx_leave(&pr->ps_mtx); - if ((p->p_flag & P_TRACESINGLE) == 0) - single_thread_clear(p); + 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; } @@ -1562,7 +1574,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 @@ -1586,10 +1599,10 @@ process_stop(struct process *pr, int fla /* 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; @@ -1608,7 +1621,7 @@ process_stop(struct process *pr, int fla unsleep(q); setrunnable(q); } else - --pr->ps_singlecnt; + --pr->ps_suspendcnt; break; case SSLEEP: /* if it's not interruptible, then just have to wait */ @@ -1616,7 +1629,7 @@ process_stop(struct process *pr, int fla /* 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); @@ -1637,56 +1650,77 @@ process_stop(struct process *pr, int fla } /* - * 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. + * Prepare a proc to be stopped. */ void -proc_stop(struct proc *p, int sw) +proc_stop_setup(struct proc *p) { - struct process *pr = p->p_p; - -#ifdef MULTIPROCESSOR + MUTEX_ASSERT_LOCKED(&p->p_p->ps_mtx); + /* + * 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(); -#endif - /* do not stop exiting procs */ - if (ISSET(p->p_flag, P_WEXIT)) - return; + TRACEPOINT(sched, stop, NULL); + + atomic_setbits_int(&p->p_flag, P_INSCHED); 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. - */ - softintr_schedule(proc_stop_si); - if (sw) + /* XXX SCHED_UNLOCK(); */ +} + +/* + * Finish stopping a process if the condition still holds. + */ +void +proc_stop_finish(struct proc *p) +{ + struct process *pr = p->p_p; + + MUTEX_ASSERT_LOCKED(&pr->ps_mtx); + mtx_leave(&pr->ps_mtx); + SCHED_LOCK(); + + atomic_clearbits_int(&p->p_flag, P_INSCHED); + if (p->p_stat == SSTOP) { + p->p_ru.ru_nvcsw++; mi_switch(); + } + KASSERT(p->p_stat == SONPROC); + + 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); } } @@ -2156,7 +2190,7 @@ userret(struct proc *p) struct sigctx ctx; int signum; - if (p->p_flag & P_SUSPSINGLE) + if (atomic_load_int(&p->p_flag) & (P_SUSPSINGLE | P_SUSPSIG)) proc_suspend_check(p, 0); /* send SIGPROF or SIGVTALRM if their timers interrupted this thread */ @@ -2200,7 +2234,8 @@ proc_suspend_check_locked(struct proc *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 */ @@ -2225,18 +2260,14 @@ proc_suspend_check_locked(struct proc *p /* NOTREACHED */ } - if (--pr->ps_singlecnt == 0) - wakeup(&pr->ps_singlecnt); - - /* not exiting and don't need to unwind, so suspend */ - mtx_leave(&pr->ps_mtx); - SCHED_LOCK(); - p->p_stat = SSTOP; - mi_switch(); + proc_stop_setup(p); SCHED_UNLOCK(); - mtx_enter(&pr->ps_mtx); - } while (pr->ps_single != NULL); + process_suspend_signal(pr); + + /* not exiting and don't need to unwind, so suspend */ + proc_stop_finish(p); + } while (pr->ps_single != NULL || ISSET(pr->ps_flags, PS_STOPPING)); return (0); } @@ -2299,37 +2330,15 @@ single_thread_set(struct proc *p, int fl SCHED_UNLOCK(); /* count ourself out */ - --pr->ps_singlecnt; - mtx_leave(&pr->ps_mtx); - - if ((flags & SINGLE_NOWAIT) == 0) - single_thread_wait(pr, 1); + --pr->ps_suspendcnt; - return 0; -} - -/* - * Wait for other threads to stop. If recheck is false then the function - * returns non-zero if the caller needs to restart the check else 0 is - * returned. If recheck is true the return value is always 0. - */ -int -single_thread_wait(struct process *pr, int recheck) -{ - int wait; - - /* 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", + /* wait until all other threads suspended */ + while (pr->ps_suspendcnt > 0) + msleep_nsec(&pr->ps_suspendcnt, &pr->ps_mtx, PWAIT, "suspend", INFSLP); - if (!recheck) - break; - } - KASSERT((pr->ps_single->p_flag & P_SUSPSINGLE) == 0); mtx_leave(&pr->ps_mtx); - - return wait; + KASSERT((pr->ps_single->p_flag & P_SUSPSINGLE) == 0); + return 0; } void Index: kern/kern_synch.c =================================================================== RCS file: /cvs/src/sys/kern/kern_synch.c,v diff -u -p -r1.219 kern_synch.c --- kern/kern_synch.c 5 Feb 2025 12:21:27 -0000 1.219 +++ kern/kern_synch.c 20 Feb 2025 12:33:52 -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 @@ -353,7 +351,7 @@ sleep_setup(const volatile void *ident, p->p_wmesg = wmesg; p->p_slptime = 0; p->p_slppri = prio & PRIMASK; - atomic_setbits_int(&p->p_flag, P_WSLEEP); + atomic_setbits_int(&p->p_flag, P_INSCHED); TAILQ_INSERT_TAIL(&slpque[LOOKUP(ident)], p, p_runq); if (prio & PCATCH) atomic_setbits_int(&p->p_flag, P_SINTR); @@ -399,7 +397,7 @@ sleep_finish(int timo, int do_sleep) unsleep(p); if (p->p_stat == SSTOP) do_sleep = 1; - atomic_clearbits_int(&p->p_flag, P_WSLEEP); + atomic_clearbits_int(&p->p_flag, P_INSCHED); if (do_sleep) { KASSERT(p->p_stat == SSLEEP || p->p_stat == SSTOP); @@ -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 a /* 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/sched_bsd.c =================================================================== RCS file: /cvs/src/sys/kern/sched_bsd.c,v diff -u -p -r1.98 sched_bsd.c --- kern/sched_bsd.c 24 Nov 2024 13:02:37 -0000 1.98 +++ kern/sched_bsd.c 20 Feb 2025 12:33:52 -0000 @@ -465,9 +465,15 @@ setrunnable(struct proc *p) panic("setrunnable"); case SSTOP: prio = p->p_usrpri; - /* if not yet asleep, unstop but don't add to runqueue */ - if (ISSET(p->p_flag, P_WSLEEP)) { - p->p_stat = SSLEEP; + TRACEPOINT(sched, unstop, p->p_tid + THREAD_PID_OFFSET, + p->p_p->ps_pid, CPU_INFO_UNIT(p->p_cpu)); + + /* If not yet stopped or asleep, unstop but don't add to runq */ + if (ISSET(p->p_flag, P_INSCHED)) { + if (p->p_wchan != NULL) + p->p_stat = SSLEEP; + else + p->p_stat = SONPROC; return; } setrunqueue(NULL, p, prio); @@ -475,12 +481,12 @@ setrunnable(struct proc *p) case SSLEEP: prio = p->p_slppri; + TRACEPOINT(sched, wakeup, p->p_tid + THREAD_PID_OFFSET, + p->p_p->ps_pid, CPU_INFO_UNIT(p->p_cpu)); /* if not yet asleep, don't add to runqueue */ - if (ISSET(p->p_flag, P_WSLEEP)) + if (ISSET(p->p_flag, P_INSCHED)) return; setrunqueue(NULL, p, prio); - TRACEPOINT(sched, wakeup, p->p_tid + THREAD_PID_OFFSET, - p->p_p->ps_pid, CPU_INFO_UNIT(p->p_cpu)); break; } if (p->p_slptime > 1) { Index: sys/proc.h =================================================================== RCS file: /cvs/src/sys/sys/proc.h,v diff -u -p -r1.382 proc.h --- sys/proc.h 17 Feb 2025 15:45:55 -0000 1.382 +++ sys/proc.h 20 Feb 2025 12:33:52 -0000 @@ -191,7 +191,7 @@ struct process { struct proc *ps_single; /* [m] Thread for single-threading. */ struct proc *ps_trapped; /* [m] Thread trapped for ptrace. */ - u_int ps_singlecnt; /* [m] Number of threads to suspend. */ + 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. */ @@ -437,7 +437,7 @@ struct proc { #define P_ALRMPEND 0x00000004 /* SIGVTALRM needs to be posted */ #define P_SIGSUSPEND 0x00000008 /* Need to restore before-suspend mask*/ #define P_CANTSLEEP 0x00000010 /* insomniac thread */ -#define P_WSLEEP 0x00000020 /* Working on going to sleep. */ +#define P_INSCHED 0x00000020 /* Switching scheduler state. */ #define P_SINTR 0x00000080 /* Sleep is interruptible. */ #define P_SYSTEM 0x00000200 /* No sigs, stats or swapping. */ #define P_TIMEOUT 0x00000400 /* Timing out during sleep. */ @@ -451,7 +451,7 @@ struct proc { #define P_BITS \ ("\20" "\01INKTR" "\02PROFPEND" "\03ALRMPEND" "\04SIGSUSPEND" \ - "\05CANTSLEEP" "\06WSLEEP" "\010SINTR" "\012SYSTEM" "\013TIMEOUT" \ + "\05CANTSLEEP" "\06INSCHED" "\010SINTR" "\012SYSTEM" "\013TIMEOUT" \ "\015TRACESINGLE" "\016WEXIT" "\020OWEUPC" "\024SUSPSINGLE" \ "\033THREAD" "\034SUSPSIG" "\037CPUPEG") @@ -599,13 +599,13 @@ 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 *); 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 20 Feb 2025 12:33:52 -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;