summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2020-03-20 08:14:08 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2020-03-20 08:14:08 +0000
commitba2cb96f1c85c7a9907b97861e3dafa28aa51921 (patch)
treedda9fe00d6b10a71c004f8fc14dc6d27ce5dfe1b
parent7b5b63f0825840f39895aa8977d896ce423e064c (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.c4
-rw-r--r--sys/kern/kern_sig.c47
-rw-r--r--sys/sys/proc.h4
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. */