diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -307,8 +307,17 @@ p = td->td_proc; if ((p->p_flag & P_HADTHREADS) != 0) { PROC_LOCK(p); + while (p->p_singlethr > 0) { + error = msleep(&p->p_singlethr, &p->p_mtx, + PWAIT | PCATCH, "exec1t", 0); + if (error != 0) { + error = ERESTART; + goto unlock; + } + } if (thread_single(p, SINGLE_BOUNDARY) != 0) error = ERESTART; +unlock: PROC_UNLOCK(p); } KASSERT(error != 0 || (td->td_pflags & TDP_EXECVMSPC) == 0, diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -213,6 +213,15 @@ __unreachable(); } +void +proc_set_p2_wexit(struct proc *p) +{ + PROC_LOCK_ASSERT(p, MA_OWNED); + p->p_flag2 |= P2_WEXIT; + while (p->p_singlethr > 0) + msleep(&p->p_singlethr, &p->p_mtx, PWAIT | PCATCH, "exit1t", 0); +} + /* * Exit: deallocate address space and other resources, change proc state to * zombie, and unlink proc from allproc and parent's lists. Save exit status @@ -251,6 +260,8 @@ * MUST abort all other threads before proceeding past here. */ PROC_LOCK(p); + proc_set_p2_wexit(p); + /* * First check if some other thread or external request got * here before us. If so, act appropriately: exit or suspend. diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -3399,10 +3399,10 @@ static struct sx stop_all_proc_blocker; SX_SYSINIT(stop_all_proc_blocker, &stop_all_proc_blocker, "sapblk"); -void +bool stop_all_proc_block(void) { - sx_xlock(&stop_all_proc_blocker); + return (sx_xlock_sig(&stop_all_proc_blocker) == 0); } void @@ -3426,7 +3426,8 @@ int r, gen; bool restart, seen_stopped, seen_exiting, stopped_some; - stop_all_proc_block(); + if (!stop_all_proc_block()) + return; cp = curproc; allproc_loop: @@ -3446,7 +3447,7 @@ PROC_UNLOCK(p); continue; } - if ((p->p_flag & P_WEXIT) != 0) { + if ((p->p_flag2 & P2_WEXIT) != 0) { seen_exiting = true; PROC_UNLOCK(p); continue; diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c --- a/sys/kern/kern_procctl.c +++ b/sys/kern/kern_procctl.c @@ -254,7 +254,7 @@ PROC_LOCK(p); } -static bool +static void reap_kill_proc_locked(struct thread *td, struct proc *p2, ksiginfo_t *ksi, struct procctl_reaper_kill *rk, int *error) { @@ -270,7 +270,7 @@ rk->rk_fpid = p2->p_pid; *error = error1; } - return (true); + return; } /* @@ -291,47 +291,38 @@ * race. */ need_stop = p2 != td->td_proc && - (p2->p_flag & (P_KPROC | P_SYSTEM)) == 0 && + (td->td_proc->p_flag2 & P2_WEXIT) == 0 && + (p2->p_flag & (P_KPROC | P_SYSTEM | P_STOPPED)) == 0 && (rk->rk_flags & REAPER_KILL_CHILDREN) == 0; if (need_stop) { - if (P_SHOULDSTOP(p2) == P_STOPPED_SINGLE) - return (false); /* retry later */ xlocked = sx_xlocked(&proctree_lock); sx_unlock(&proctree_lock); r = thread_single(p2, SINGLE_ALLPROC); - if (r != 0) { - reap_kill_proc_relock(p2, xlocked); - return (false); - } + reap_kill_proc_relock(p2, xlocked); + if (r != 0) + need_stop = false; } pksignal(p2, rk->rk_sig, ksi); rk->rk_killed++; *error = error1; - if (need_stop) { - reap_kill_proc_relock(p2, xlocked); + if (need_stop) thread_single_end(p2, SINGLE_ALLPROC); - } - return (true); } -static bool +static void reap_kill_proc(struct thread *td, struct proc *p2, ksiginfo_t *ksi, struct procctl_reaper_kill *rk, int *error) { - bool res; - - res = true; PROC_LOCK(p2); - if ((p2->p_flag & P_WEXIT) == 0) { + if ((p2->p_flag2 & P2_WEXIT) == 0) { _PHOLD_LITE(p2); - res = reap_kill_proc_locked(td, p2, ksi, rk, error); + reap_kill_proc_locked(td, p2, ksi, rk, error); _PRELE(p2); } PROC_UNLOCK(p2); - return (res); } struct reap_kill_tracker { @@ -346,11 +337,25 @@ { struct reap_kill_tracker *t; + PROC_LOCK(p2); + if ((p2->p_flag2 & P2_WEXIT) != 0) { + PROC_UNLOCK(p2); + return; + } + _PHOLD_LITE(p2); + PROC_UNLOCK(p2); t = malloc(sizeof(struct reap_kill_tracker), M_TEMP, M_WAITOK); t->parent = p2; TAILQ_INSERT_TAIL(tracker, t, link); } +static void +reap_kill_sched_free(struct reap_kill_tracker *t) +{ + PRELE(t->parent); + free(t, M_TEMP); +} + static void reap_kill_children(struct thread *td, struct proc *reaper, struct procctl_reaper_kill *rk, ksiginfo_t *ksi, int *error) @@ -380,8 +385,20 @@ TAILQ_INIT(&tracker); reap_kill_sched(&tracker, reaper); while ((t = TAILQ_FIRST(&tracker)) != NULL) { - MPASS((t->parent->p_treeflag & P_TREE_REAPER) != 0); TAILQ_REMOVE(&tracker, t, link); + + /* + * Since reap_kill_proc() drops proctree_lock sx, it + * is possible that the tracked reaper is no longer. + * In this case the subtree is reparented to the new + * reaper, which should handle it. + */ + if ((t->parent->p_treeflag & P_TREE_REAPER) == 0) { + reap_kill_sched_free(t); + res = true; + continue; + } + LIST_FOREACH(p2, &t->parent->p_reaplist, p_reapsibling) { if (t->parent == reaper && (rk->rk_flags & REAPER_KILL_SUBTREE) != 0 && @@ -391,11 +408,10 @@ reap_kill_sched(&tracker, p2); if (alloc_unr_specific(pids, p2->p_pid) != p2->p_pid) continue; - if (!reap_kill_proc(td, p2, ksi, rk, error)) - free_unr(pids, p2->p_pid); + reap_kill_proc(td, p2, ksi, rk, error); res = true; } - free(t, M_TEMP); + reap_kill_sched_free(t); } return (res); } @@ -412,8 +428,21 @@ * repeated. */ init_unrhdr(&pids, 1, PID_MAX, UNR_NO_MTX); + PROC_LOCK(td->td_proc); + if ((td->td_proc->p_flag2 & P2_WEXIT) != 0) { + PROC_UNLOCK(td->td_proc); + goto out; + } + td->td_proc->p_singlethr++; + PROC_UNLOCK(td->td_proc); while (reap_kill_subtree_once(td, p, reaper, rk, ksi, error, &pids)) ; + PROC_LOCK(td->td_proc); + td->td_proc->p_singlethr--; + if (td->td_proc->p_singlethr == 0) + wakeup(&p->p_singlethr); + PROC_UNLOCK(td->td_proc); +out: clean_unrhdr(&pids); clear_unrhdr(&pids); } @@ -1050,8 +1079,8 @@ sapblk = false; if (cmd_info->sapblk != NULL) { sapblk = cmd_info->sapblk(td, data); - if (sapblk) - stop_all_proc_block(); + if (sapblk && !stop_all_proc_block()) + return (ERESTART); } switch (cmd_info->lock_tree) { diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -2928,7 +2928,7 @@ /* * We should allow pending but ignored signals below - * only if there is sigwait() active, or P_TRACED was + * if there is sigwait() active, or P_TRACED was * on when they were posted. */ if (SIGISMEMBER(ps->ps_sigignore, sig) && @@ -2937,6 +2937,16 @@ return (SIGSTATUS_IGNORE); } + /* + * If the process is going to single-thread mode to prepare + * for exit, there is no sense in delivering any signal + * to usermode. Another important consequence is that + * msleep(..., PCATCH, ...) now is only interruptible by a + * suspend request. + */ + if ((p->p_flag2 & P2_WEXIT) != 0) + return (SIGSTATUS_IGNORE); + if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) { /* * If traced, always stop. @@ -3406,6 +3416,8 @@ struct proc *p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); + proc_set_p2_wexit(p); + p->p_acflag |= AXSIG; /* * We must be single-threading to generate a core dump. This diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -99,7 +99,7 @@ "struct proc KBI p_pid"); _Static_assert(offsetof(struct proc, p_filemon) == 0x3c8, "struct proc KBI p_filemon"); -_Static_assert(offsetof(struct proc, p_comm) == 0x3e0, +_Static_assert(offsetof(struct proc, p_comm) == 0x3e4, "struct proc KBI p_comm"); _Static_assert(offsetof(struct proc, p_emuldata) == 0x4c8, "struct proc KBI p_emuldata"); @@ -1144,24 +1144,20 @@ * ALLPROC suspend tries to avoid spurious EINTR for * threads sleeping interruptable, by suspending the * thread directly, similarly to sig_suspend_threads(). - * Since such sleep is not performed at the user - * boundary, TDF_BOUNDARY flag is not set, and TDF_ALLPROCSUSP - * is used to avoid immediate un-suspend. + * Since such sleep is not neccessary performed at the user + * boundary, TDF_ALLPROCSUSP is used to avoid immediate + * un-suspend. */ - if (TD_IS_SUSPENDED(td2) && (td2->td_flags & (TDF_BOUNDARY | - TDF_ALLPROCSUSP)) == 0) { + if (TD_IS_SUSPENDED(td2) && (td2->td_flags & + TDF_ALLPROCSUSP) == 0) { wakeup_swapper |= thread_unsuspend_one(td2, p, false); thread_lock(td2); goto restart; } if (TD_CAN_ABORT(td2)) { - if ((td2->td_flags & TDF_SBDRY) == 0) { - thread_suspend_one(td2); - td2->td_flags |= TDF_ALLPROCSUSP; - } else { - wakeup_swapper |= sleepq_abort(td2, ERESTART); - return (wakeup_swapper); - } + td2->td_flags |= TDF_ALLPROCSUSP; + wakeup_swapper |= sleepq_abort(td2, ERESTART); + return (wakeup_swapper); } break; default: @@ -1207,10 +1203,18 @@ mtx_assert(&Giant, MA_NOTOWNED); PROC_LOCK_ASSERT(p, MA_OWNED); - if ((p->p_flag & P_HADTHREADS) == 0 && mode != SINGLE_ALLPROC) + /* + * Is someone already single threading? + * Or may be singlethreading is not needed at all. + */ + if (mode == SINGLE_ALLPROC) { + while ((p->p_flag & P_STOPPED_SINGLE) != 0) { + if ((p->p_flag2 & P2_WEXIT) != 0) + return (1); + msleep(&p->p_flag, &p->p_mtx, PCATCH, "thrsgl", 0); + } + } else if ((p->p_flag & P_HADTHREADS) == 0) return (0); - - /* Is someone already single threading? */ if (p->p_singlethread != NULL && p->p_singlethread != td) return (1); @@ -1224,8 +1228,12 @@ else p->p_flag &= ~P_SINGLE_BOUNDARY; } - if (mode == SINGLE_ALLPROC) + if (mode == SINGLE_ALLPROC) { p->p_flag |= P_TOTAL_STOP; + thread_lock(td); + td->td_flags |= TDF_DOING_SA; + thread_unlock(td); + } p->p_flag |= P_STOPPED_SINGLE; PROC_SLOCK(p); p->p_singlethread = td; @@ -1242,7 +1250,7 @@ if (TD_IS_INHIBITED(td2)) { wakeup_swapper |= weed_inhib(mode, td2, p); #ifdef SMP - } else if (TD_IS_RUNNING(td2) && td != td2) { + } else if (TD_IS_RUNNING(td2)) { forward_signal(td2); thread_unlock(td2); #endif @@ -1312,6 +1320,11 @@ } } PROC_SUNLOCK(p); + if (mode == SINGLE_ALLPROC) { + thread_lock(td); + td->td_flags &= ~TDF_DOING_SA; + thread_unlock(td); + } return (0); } @@ -1598,7 +1611,8 @@ if (!P_SHOULDSTOP(p)) { FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); - if (TD_IS_SUSPENDED(td)) { + if (TD_IS_SUSPENDED(td) && (td->td_flags & + TDF_DOING_SA) == 0) { wakeup_swapper |= thread_unsuspend_one(td, p, true); } else @@ -1669,6 +1683,7 @@ PROC_SUNLOCK(p); if (wakeup_swapper) kick_proc0(); + wakeup(&p->p_flag); } /* diff --git a/sys/sys/proc.h b/sys/sys/proc.h --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -468,7 +468,7 @@ #define TDF_THRWAKEUP 0x00100000 /* Libthr thread must not suspend itself. */ #define TDF_SEINTR 0x00200000 /* EINTR on stop attempts. */ #define TDF_SWAPINREQ 0x00400000 /* Swapin request due to wakeup. */ -#define TDF_UNUSED23 0x00800000 /* --available-- */ +#define TDF_DOING_SA 0x00800000 /* Doing SINGLE_ALLPROC, do not unsuspend me */ #define TDF_SCHED0 0x01000000 /* Reserved for scheduler private use */ #define TDF_SCHED1 0x02000000 /* Reserved for scheduler private use */ #define TDF_SCHED2 0x04000000 /* Reserved for scheduler private use */ @@ -691,6 +691,8 @@ int p_pendingexits; /* (c) Count of pending thread exits. */ struct filemon *p_filemon; /* (c) filemon-specific data. */ int p_pdeathsig; /* (c) Signal from parent on exit. */ + int p_singlethr; /* (c) Count of threads doing + external thread_single() */ /* End area that is zeroed on creation. */ #define p_endzero p_magic @@ -847,6 +849,9 @@ #define P2_NO_NEW_PRIVS 0x00008000 /* Ignore setuid */ #define P2_WXORX_DISABLE 0x00010000 /* WX mappings enabled */ #define P2_WXORX_ENABLE_EXEC 0x00020000 /* WXORX enabled after exec */ +#define P2_WEXIT 0x00040000 /* exit just started, no + external thread_single() is + permitted */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ @@ -1155,6 +1160,7 @@ struct proc *proc_realparent(struct proc *child); void proc_reap(struct thread *td, struct proc *p, int *status, int options); void proc_reparent(struct proc *child, struct proc *newparent, bool set_oppid); +void proc_set_p2_wexit(struct proc *p); void proc_set_traced(struct proc *p, bool stop); void proc_wkilled(struct proc *p); struct pstats *pstats_alloc(void); @@ -1232,7 +1238,7 @@ void thread_unsuspend(struct proc *p); void thread_wait(struct proc *p); -void stop_all_proc_block(void); +bool stop_all_proc_block(void); void stop_all_proc_unblock(void); void stop_all_proc(void); void resume_all_proc(void);