diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2020-03-20 08:14:08 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2020-03-20 08:14:08 +0000 |
commit | ba2cb96f1c85c7a9907b97861e3dafa28aa51921 (patch) | |
tree | dda9fe00d6b10a71c004f8fc14dc6d27ce5dfe1b | |
parent | 7b5b63f0825840f39895aa8977d896ce423e064c (diff) |
Use atomic operations to update ps_singlecount. This makes
single_thread_check() safe to be called without KERNEL_LOCK().
single_thread_wait() needs to use sleep_setup() and sleep_finish()
instead of tsleep() to make sure no wakeup() is lost.
Input kettenis@, with and OK visa@
-rw-r--r-- | sys/kern/kern_fork.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 47 | ||||
-rw-r--r-- | sys/sys/proc.h | 4 |
3 files changed, 33 insertions, 22 deletions
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 71129dd4079..6cfb39b6252 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_fork.c,v 1.224 2020/03/16 11:58:46 mpi Exp $ */ +/* $OpenBSD: kern_fork.c,v 1.225 2020/03/20 08:14:07 claudio Exp $ */ /* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */ /* @@ -563,7 +563,7 @@ thread_fork(struct proc *curp, void *stack, void *tcb, pid_t *tidptr, * count ourselves in. */ if (pr->ps_single) { - pr->ps_singlecount++; + atomic_inc_int(&pr->ps_singlecount); atomic_setbits_int(&p->p_flag, P_SUSPSINGLE); } diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 9caba3240d4..97592e86395 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sig.c,v 1.254 2020/03/18 15:48:21 visa Exp $ */ +/* $OpenBSD: kern_sig.c,v 1.255 2020/03/20 08:14:07 claudio Exp $ */ /* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */ /* @@ -1885,11 +1885,8 @@ userret(struct proc *p) KERNEL_UNLOCK(); } - if (p->p_flag & P_SUSPSINGLE) { - KERNEL_LOCK(); + if (p->p_flag & P_SUSPSINGLE) single_thread_check(p, 0); - KERNEL_UNLOCK(); - } WITNESS_WARN(WARN_PANIC, NULL, "userret: returning"); @@ -1913,10 +1910,13 @@ single_thread_check(struct proc *p, int deep) return (EINTR); } - if (--pr->ps_singlecount == 0) + if (atomic_dec_int_nv(&pr->ps_singlecount) == 0) wakeup(&pr->ps_singlecount); - if (pr->ps_flags & PS_SINGLEEXIT) + if (pr->ps_flags & PS_SINGLEEXIT) { + KERNEL_LOCK(); exit1(p, 0, 0, EXIT_THREAD_NOCHECK); + KERNEL_UNLOCK(); + } /* not exiting and don't need to unwind, so suspend */ SCHED_LOCK(s); @@ -1948,6 +1948,7 @@ single_thread_set(struct proc *p, enum single_thread_mode mode, int deep) int error; KERNEL_ASSERT_LOCKED(); + KASSERT(curproc == p); if ((error = single_thread_check(p, deep))) return error; @@ -1968,8 +1969,9 @@ single_thread_set(struct proc *p, enum single_thread_mode mode, int deep) panic("single_thread_mode = %d", mode); #endif } - pr->ps_single = p; pr->ps_singlecount = 0; + membar_producer(); + pr->ps_single = p; TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) { int s; @@ -1980,7 +1982,7 @@ single_thread_set(struct proc *p, enum single_thread_mode mode, int deep) SCHED_LOCK(s); if (q->p_stat == SSTOP) { setrunnable(q); - pr->ps_singlecount++; + atomic_inc_int(&pr->ps_singlecount); } SCHED_UNLOCK(s); } @@ -1991,7 +1993,7 @@ single_thread_set(struct proc *p, enum single_thread_mode mode, int deep) switch (q->p_stat) { case SIDL: case SRUN: - pr->ps_singlecount++; + atomic_inc_int(&pr->ps_singlecount); break; case SSLEEP: /* if it's not interruptible, then just have to wait */ @@ -2005,18 +2007,18 @@ single_thread_set(struct proc *p, enum single_thread_mode mode, int deep) /* need to unwind or exit, so wake it */ setrunnable(q); } - pr->ps_singlecount++; + atomic_inc_int(&pr->ps_singlecount); break; case SSTOP: if (mode == SINGLE_EXIT) { setrunnable(q); - pr->ps_singlecount++; + atomic_inc_int(&pr->ps_singlecount); } break; case SDEAD: break; case SONPROC: - pr->ps_singlecount++; + atomic_inc_int(&pr->ps_singlecount); signotify(q); break; } @@ -2029,20 +2031,28 @@ single_thread_set(struct proc *p, enum single_thread_mode mode, int deep) 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 waited = 0; + struct sleep_state sls; + int wait; /* wait until they're all suspended */ - while (pr->ps_singlecount > 0) { - tsleep_nsec(&pr->ps_singlecount, PWAIT, "suspend", INFSLP); - waited = 1; + wait = pr->ps_singlecount > 0; + while (wait) { + sleep_setup(&sls, &pr->ps_singlecount, PWAIT, "suspend"); + wait = pr->ps_singlecount > 0; + sleep_finish(&sls, wait); if (!recheck) break; } - return waited; + return wait; } void @@ -2052,6 +2062,7 @@ single_thread_clear(struct proc *p, int flag) struct proc *q; KASSERT(pr->ps_single == p); + KASSERT(curproc == p); KERNEL_ASSERT_LOCKED(); pr->ps_single = NULL; diff --git a/sys/sys/proc.h b/sys/sys/proc.h index d6f3b225de3..a7ac9db5229 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.292 2020/03/19 13:55:20 anton Exp $ */ +/* $OpenBSD: proc.h,v 1.293 2020/03/20 08:14:07 claudio Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -200,7 +200,7 @@ struct process { int ps_siglist; /* Signals pending for the process. */ struct proc *ps_single; /* Single threading to this thread. */ - int ps_singlecount; /* Not yet suspended threads. */ + u_int ps_singlecount; /* [a] Not yet suspended threads. */ int ps_traceflag; /* Kernel trace points. */ struct vnode *ps_tracevp; /* Trace to vnode. */ |