summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_synch.c
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2001-08-10 21:08:56 +0000
committerjhb <jhb@FreeBSD.org>2001-08-10 21:08:56 +0000
commit54a0ded8d40ac4cc05cb95d789925432a192f167 (patch)
tree03a87caafd2a5e30157dfc8286d53fecf5a7d96a /sys/kern/kern_synch.c
parent22915435df9de745286e8fe0078066cb617c9350 (diff)
downloadFreeBSD-src-54a0ded8d40ac4cc05cb95d789925432a192f167.zip
FreeBSD-src-54a0ded8d40ac4cc05cb95d789925432a192f167.tar.gz
Work around a race between msleep() and endtsleep() where it was possible
for endtsleep() to be executing when msleep() resumed, for endtsleep() to spin on sched_lock long enough for the other process to loop on msleep() and sleep again resulting in endtsleep() waking up the "wrong" msleep. Obtained from: BSD/OS
Diffstat (limited to 'sys/kern/kern_synch.c')
-rw-r--r--sys/kern/kern_synch.c26
1 files changed, 23 insertions, 3 deletions
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index e6dfb01..140ebd7 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -451,8 +451,20 @@ msleep(ident, mtx, priority, wmesg, timo)
p->p_sflag &= ~PS_TIMEOUT;
if (sig == 0)
rval = EWOULDBLOCK;
- } else if (timo)
- callout_stop(&p->p_slpcallout);
+ } else if (timo && callout_stop(&p->p_slpcallout) == 0) {
+ /*
+ * This isn't supposed to be pretty. If we are here, then
+ * the endtsleep() callout is currently executing on another
+ * CPU and is either spinning on the sched_lock or will be
+ * soon. If we don't synchronize here, there is a chance
+ * that this process may msleep() again before the callout
+ * has a chance to run and the callout may end up waking up
+ * the wrong msleep(). Yuck.
+ */
+ p->p_sflag |= PS_TIMEOUT;
+ p->p_stats->p_ru.ru_nivcsw++;
+ mi_switch();
+ }
mtx_unlock_spin(&sched_lock);
if (rval == 0 && catch) {
@@ -498,7 +510,15 @@ endtsleep(arg)
CTR3(KTR_PROC, "endtsleep: proc %p (pid %d, %s)", p, p->p_pid,
p->p_comm);
mtx_lock_spin(&sched_lock);
- if (p->p_wchan != NULL) {
+ /*
+ * This is the other half of the synchronization with msleep()
+ * described above. If the PS_TIMEOUT flag is set, we lost the
+ * race and just need to put the process back on the runqueue.
+ */
+ if ((p->p_sflag & PS_TIMEOUT) != 0) {
+ p->p_sflag &= ~PS_TIMEOUT;
+ setrunqueue(p);
+ } else if (p->p_wchan != NULL) {
if (p->p_stat == SSLEEP)
setrunnable(p);
else
OpenPOWER on IntegriCloud