It's rare, but there *are* cases where a thread wants to sleep and
doesn't expect any wakeup() calls at all; e.g., nanosleep() and
sigsuspend().  In these cases, there's no point in requiring a valid
wait channel identifier or adding the thread to the sleep queue.

Diff below explicitly allows tsleep() with ident==NULL, but skips
adding the thread to a sleep queue in that case.

As an implementation detail, we currently treat "p_wchan == NULL" as
an indication that the thread has been woken up.  To avoid breaking
that assumption this diff uses "p_wchan == NOSLPQUE" to identify
threads that are still sleeping, but aren't associated with a sleep
queue.

This also lets us tighten the the kernel lock KASSERT in tsleep():
rather than weakly only checking non-timed sleeps, we can precisely
check all sleeps that use a wait channel.

ok?


Index: share/man/man9/tsleep.9
===================================================================
RCS file: /home/matthew/cvs-mirror/cvs/src/share/man/man9/tsleep.9,v
retrieving revision 1.9
diff -u -p -r1.9 tsleep.9
--- share/man/man9/tsleep.9     22 Jan 2014 07:32:47 -0000      1.9
+++ share/man/man9/tsleep.9     8 Jul 2014 16:25:53 -0000
@@ -96,8 +96,9 @@ The same identifier must be used in a ca
 .Fn wakeup
 to get the process going again.
 .Fa ident
-should not be
-.Dv NULL .
+may be
+.Dv NULL
+if the process is not waiting on a wait channel.
 .It Fa priority
 The process priority to be used when the process is awakened and put on
 the queue of runnable processes.
Index: sys/kern/kern_synch.c
===================================================================
RCS file: /home/matthew/cvs-mirror/cvs/src/sys/kern/kern_synch.c,v
retrieving revision 1.115
diff -u -p -r1.115 kern_synch.c
--- sys/kern/kern_synch.c       22 Mar 2014 06:05:45 -0000      1.115
+++ sys/kern/kern_synch.c       8 Jul 2014 16:32:50 -0000
@@ -78,6 +78,12 @@ sleep_queue_init(void)
                TAILQ_INIT(&slpque[i]);
 }
 
+/*
+ * Dummy wait channel identifier for threads that don't expect a wakeup().
+ * Threads sleeping on this wait channel aren't queued on slpque.
+ */
+#define        NOSLPQUE        ((volatile void *)(&noslpque))
+static char noslpque;
 
 /*
  * During autoconfiguration or after a panic, a sleep will simply
@@ -109,8 +115,17 @@ tsleep(const volatile void *ident, int p
 
        KASSERT((priority & ~(PRIMASK | PCATCH)) == 0);
 
+       /* Make sure caller gave us at least one reason to stop sleeping. */
+       KASSERT(ident != NULL || (priority & PCATCH) != 0 || timo != 0);
+
 #ifdef MULTIPROCESSOR
-       KASSERT(timo || __mp_lock_held(&kernel_lock));
+       if (ident != NULL) {
+               /*
+                * Waiting on a condition variable requires a lock, otherwise
+                * we might miss our wakeup notification.
+                */
+               KASSERT(__mp_lock_held(&kernel_lock));
+       }
 #endif
 
        if (cold || panicstr) {
@@ -191,12 +206,11 @@ sleep_setup(struct sleep_state *sls, con
 {
        struct proc *p = curproc;
 
-#ifdef DIAGNOSTIC
+       KASSERT(ident != NOSLPQUE);
+       KASSERT(p->p_stat == SONPROC);
+
        if (ident == NULL)
-               panic("tsleep: no ident");
-       if (p->p_stat != SONPROC)
-               panic("tsleep: not SONPROC");
-#endif
+               ident = NOSLPQUE;
 
 #ifdef KTRACE
        if (KTRPOINT(p, KTR_CSW))
@@ -213,7 +227,8 @@ sleep_setup(struct sleep_state *sls, con
        p->p_wmesg = wmesg;
        p->p_slptime = 0;
        p->p_priority = prio & PRIMASK;
-       TAILQ_INSERT_TAIL(&slpque[LOOKUP(ident)], p, p_runq);
+       if (ident != NOSLPQUE)
+               TAILQ_INSERT_TAIL(&slpque[LOOKUP(ident)], p, p_runq);
 }
 
 void
@@ -350,7 +365,8 @@ void
 unsleep(struct proc *p)
 {
        if (p->p_wchan) {
-               TAILQ_REMOVE(&slpque[LOOKUP(p->p_wchan)], p, p_runq);
+               if (p->p_wchan != NOSLPQUE)
+                       TAILQ_REMOVE(&slpque[LOOKUP(p->p_wchan)], p, p_runq);
                p->p_wchan = NULL;
        }
 }
@@ -365,6 +381,9 @@ wakeup_n(const volatile void *ident, int
        struct proc *p;
        struct proc *pnext;
        int s;
+
+       KASSERT(ident != NULL);
+       KASSERT(ident != NOSLPQUE);
 
        SCHED_LOCK(s);
        qp = &slpque[LOOKUP(ident)];

Reply via email to