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)];