Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c +++ sys/kern/kern_fork.c @@ -1044,9 +1044,11 @@ * cpu_switch()'ed to, but when children start up they arrive here * instead, so we must do much the same things as mi_switch() would. */ - if ((dtd = PCPU_GET(deadthread))) { + dtd = PCPU_GET(deadthread); + if (__predict_false(dtd != NULL)) { + atomic_thread_fence_rel(); PCPU_SET(deadthread, NULL); - thread_stash(dtd); + dtd->td_state = TDS_UNUSED; } thread_unlock(td); Index: sys/kern/kern_synch.c =================================================================== --- sys/kern/kern_synch.c +++ sys/kern/kern_synch.c @@ -530,12 +530,14 @@ CTR4(KTR_PROC, "mi_switch: new thread %ld (td_sched %p, pid %ld, %s)", td->td_tid, td_get_sched(td), td->td_proc->p_pid, td->td_name); - /* + /* * If the last thread was exiting, finish cleaning it up. */ - if ((td = PCPU_GET(deadthread))) { + td = PCPU_GET(deadthread); + if (__predict_false(td != NULL)) { + atomic_thread_fence_rel(); PCPU_SET(deadthread, NULL); - thread_stash(td); + td->td_state = TDS_UNUSED; } } Index: sys/kern/kern_thread.c =================================================================== --- sys/kern/kern_thread.c +++ sys/kern/kern_thread.c @@ -130,7 +130,6 @@ static struct mtx zombie_lock; MTX_SYSINIT(zombie_lock, &zombie_lock, "zombie lock", MTX_SPIN); -static void thread_zombie(struct thread *); static int thread_unsuspend_one(struct thread *td, struct proc *p, bool boundary); @@ -240,6 +239,7 @@ panic("bad state for thread unlinking"); /* NOTREACHED */ case TDS_INACTIVE: + case TDS_UNUSED: break; default: panic("bad thread state"); @@ -347,26 +347,31 @@ rw_init(&tidhash_lock, "tidhash"); } -/* - * Place an unused thread on the zombie list. - * Use the slpq as that must be unused by now. - */ -void -thread_zombie(struct thread *td) +static void +thread_wait_unused(struct thread *td) { - mtx_lock_spin(&zombie_lock); - TAILQ_INSERT_HEAD(&zombie_threads, td, td_slpq); - mtx_unlock_spin(&zombie_lock); + + while (atomic_load_int(&td->td_state) != TDS_UNUSED) + cpu_spinwait(); } /* - * Release a thread that has exited after cpu_throw(). + * Place an exiting thread on the zombie list. + * The thread cannot be reaped until it transitions to TDS_UNUSED. + * + * Use the slpq as that must be unused by now. */ void thread_stash(struct thread *td) { + atomic_subtract_rel_int(&td->td_proc->p_exitthreads, 1); - thread_zombie(td); + + MPASS(PCPU_GET(deadthread) == NULL); + PCPU_SET(deadthread, td); + mtx_lock_spin(&zombie_lock); + TAILQ_INSERT_TAIL(&zombie_threads, td, td_slpq); + mtx_unlock_spin(&zombie_lock); } /* @@ -389,6 +394,7 @@ mtx_unlock_spin(&zombie_lock); while (td_first) { td_next = TAILQ_NEXT(td_first, td_slpq); + thread_wait_unused(td_first); thread_cow_free(td_first); thread_free(td_first); td_first = td_next; @@ -571,7 +577,7 @@ } } - PCPU_SET(deadthread, td); + thread_stash(td); } else { /* * The last thread is exiting.. but not through exit() Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -331,7 +331,8 @@ */ struct pcb *td_pcb; /* (k) Kernel VA of pcb and kstack. */ enum td_states { - TDS_INACTIVE = 0x0, + TDS_UNUSED = 0x0, + TDS_INACTIVE, TDS_INHIBITED, TDS_CAN_RUN, TDS_RUNQ,