kern_condvar.c revision 167786
1/*-
2 * Copyright (c) 2000 Jake Burkholder <jake@freebsd.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/kern/kern_condvar.c 167786 2007-03-21 20:46:26Z jhb $");
29
30#include "opt_ktrace.h"
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/lock.h>
35#include <sys/mutex.h>
36#include <sys/proc.h>
37#include <sys/kernel.h>
38#include <sys/ktr.h>
39#include <sys/condvar.h>
40#include <sys/sched.h>
41#include <sys/signalvar.h>
42#include <sys/sleepqueue.h>
43#include <sys/resourcevar.h>
44#ifdef KTRACE
45#include <sys/uio.h>
46#include <sys/ktrace.h>
47#endif
48
49/*
50 * Common sanity checks for cv_wait* functions.
51 */
52#define	CV_ASSERT(cvp, mp, td) do {					\
53	KASSERT((td) != NULL, ("%s: curthread NULL", __func__));	\
54	KASSERT(TD_IS_RUNNING(td), ("%s: not TDS_RUNNING", __func__));	\
55	KASSERT((cvp) != NULL, ("%s: cvp NULL", __func__));		\
56	KASSERT((mp) != NULL, ("%s: mp NULL", __func__));		\
57	mtx_assert((mp), MA_OWNED | MA_NOTRECURSED);			\
58} while (0)
59
60/*
61 * Initialize a condition variable.  Must be called before use.
62 */
63void
64cv_init(struct cv *cvp, const char *desc)
65{
66
67	cvp->cv_description = desc;
68	cvp->cv_waiters = 0;
69}
70
71/*
72 * Destroy a condition variable.  The condition variable must be re-initialized
73 * in order to be re-used.
74 */
75void
76cv_destroy(struct cv *cvp)
77{
78#ifdef INVARIANTS
79	struct sleepqueue *sq;
80
81	sleepq_lock(cvp);
82	sq = sleepq_lookup(cvp);
83	sleepq_release(cvp);
84	KASSERT(sq == NULL, ("%s: associated sleep queue non-empty", __func__));
85#endif
86}
87
88/*
89 * Wait on a condition variable.  The current thread is placed on the condition
90 * variable's wait queue and suspended.  A cv_signal or cv_broadcast on the same
91 * condition variable will resume the thread.  The mutex is released before
92 * sleeping and will be held on return.  It is recommended that the mutex be
93 * held when cv_signal or cv_broadcast are called.
94 */
95void
96cv_wait(struct cv *cvp, struct mtx *mp)
97{
98	WITNESS_SAVE_DECL(mp);
99	struct thread *td;
100
101	td = curthread;
102#ifdef KTRACE
103	if (KTRPOINT(td, KTR_CSW))
104		ktrcsw(1, 0);
105#endif
106	CV_ASSERT(cvp, mp, td);
107	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
108	    "Waiting on \"%s\"", cvp->cv_description);
109	WITNESS_SAVE(&mp->mtx_object, mp);
110
111	if (cold || panicstr) {
112		/*
113		 * During autoconfiguration, just give interrupts
114		 * a chance, then just return.  Don't run any other
115		 * thread or panic below, in case this is the idle
116		 * process and already asleep.
117		 */
118		return;
119	}
120
121	sleepq_lock(cvp);
122
123	cvp->cv_waiters++;
124	DROP_GIANT();
125	mtx_unlock(mp);
126
127	sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR,
128		   0);
129	sleepq_wait(cvp);
130
131#ifdef KTRACE
132	if (KTRPOINT(td, KTR_CSW))
133		ktrcsw(0, 0);
134#endif
135	PICKUP_GIANT();
136	mtx_lock(mp);
137	WITNESS_RESTORE(&mp->mtx_object, mp);
138}
139
140/*
141 * Wait on a condition variable.  This function differs from cv_wait by
142 * not aquiring the mutex after condition variable was signaled.
143 */
144void
145cv_wait_unlock(struct cv *cvp, struct mtx *mp)
146{
147	struct thread *td;
148
149	td = curthread;
150#ifdef KTRACE
151	if (KTRPOINT(td, KTR_CSW))
152		ktrcsw(1, 0);
153#endif
154	CV_ASSERT(cvp, mp, td);
155	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
156	    "Waiting on \"%s\"", cvp->cv_description);
157
158	if (cold || panicstr) {
159		/*
160		 * During autoconfiguration, just give interrupts
161		 * a chance, then just return.  Don't run any other
162		 * thread or panic below, in case this is the idle
163		 * process and already asleep.
164		 */
165		mtx_unlock(mp);
166		return;
167	}
168
169	sleepq_lock(cvp);
170
171	cvp->cv_waiters++;
172	DROP_GIANT();
173	mtx_unlock(mp);
174
175	sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR,
176		   0);
177	sleepq_wait(cvp);
178
179#ifdef KTRACE
180	if (KTRPOINT(td, KTR_CSW))
181		ktrcsw(0, 0);
182#endif
183	PICKUP_GIANT();
184}
185
186/*
187 * Wait on a condition variable, allowing interruption by signals.  Return 0 if
188 * the thread was resumed with cv_signal or cv_broadcast, EINTR or ERESTART if
189 * a signal was caught.  If ERESTART is returned the system call should be
190 * restarted if possible.
191 */
192int
193cv_wait_sig(struct cv *cvp, struct mtx *mp)
194{
195	struct thread *td;
196	struct proc *p;
197	int rval;
198	WITNESS_SAVE_DECL(mp);
199
200	td = curthread;
201	p = td->td_proc;
202#ifdef KTRACE
203	if (KTRPOINT(td, KTR_CSW))
204		ktrcsw(1, 0);
205#endif
206	CV_ASSERT(cvp, mp, td);
207	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
208	    "Waiting on \"%s\"", cvp->cv_description);
209	WITNESS_SAVE(&mp->mtx_object, mp);
210
211	if (cold || panicstr) {
212		/*
213		 * After a panic, or during autoconfiguration, just give
214		 * interrupts a chance, then just return; don't run any other
215		 * procs or panic below, in case this is the idle process and
216		 * already asleep.
217		 */
218		return (0);
219	}
220
221	sleepq_lock(cvp);
222
223	cvp->cv_waiters++;
224	DROP_GIANT();
225	mtx_unlock(mp);
226
227	sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR |
228	    SLEEPQ_INTERRUPTIBLE, 0);
229	rval = sleepq_wait_sig(cvp);
230
231#ifdef KTRACE
232	if (KTRPOINT(td, KTR_CSW))
233		ktrcsw(0, 0);
234#endif
235	PICKUP_GIANT();
236	mtx_lock(mp);
237	WITNESS_RESTORE(&mp->mtx_object, mp);
238
239	return (rval);
240}
241
242/*
243 * Wait on a condition variable for at most timo/hz seconds.  Returns 0 if the
244 * process was resumed by cv_signal or cv_broadcast, EWOULDBLOCK if the timeout
245 * expires.
246 */
247int
248cv_timedwait(struct cv *cvp, struct mtx *mp, int timo)
249{
250	struct thread *td;
251	int rval;
252	WITNESS_SAVE_DECL(mp);
253
254	td = curthread;
255	rval = 0;
256#ifdef KTRACE
257	if (KTRPOINT(td, KTR_CSW))
258		ktrcsw(1, 0);
259#endif
260	CV_ASSERT(cvp, mp, td);
261	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
262	    "Waiting on \"%s\"", cvp->cv_description);
263	WITNESS_SAVE(&mp->mtx_object, mp);
264
265	if (cold || panicstr) {
266		/*
267		 * After a panic, or during autoconfiguration, just give
268		 * interrupts a chance, then just return; don't run any other
269		 * thread or panic below, in case this is the idle process and
270		 * already asleep.
271		 */
272		return 0;
273	}
274
275	sleepq_lock(cvp);
276
277	cvp->cv_waiters++;
278	DROP_GIANT();
279	mtx_unlock(mp);
280
281	sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR,
282		   0);
283	sleepq_set_timeout(cvp, timo);
284	rval = sleepq_timedwait(cvp);
285
286#ifdef KTRACE
287	if (KTRPOINT(td, KTR_CSW))
288		ktrcsw(0, 0);
289#endif
290	PICKUP_GIANT();
291	mtx_lock(mp);
292	WITNESS_RESTORE(&mp->mtx_object, mp);
293
294	return (rval);
295}
296
297/*
298 * Wait on a condition variable for at most timo/hz seconds, allowing
299 * interruption by signals.  Returns 0 if the thread was resumed by cv_signal
300 * or cv_broadcast, EWOULDBLOCK if the timeout expires, and EINTR or ERESTART if
301 * a signal was caught.
302 */
303int
304cv_timedwait_sig(struct cv *cvp, struct mtx *mp, int timo)
305{
306	struct thread *td;
307	struct proc *p;
308	int rval;
309	WITNESS_SAVE_DECL(mp);
310
311	td = curthread;
312	p = td->td_proc;
313	rval = 0;
314#ifdef KTRACE
315	if (KTRPOINT(td, KTR_CSW))
316		ktrcsw(1, 0);
317#endif
318	CV_ASSERT(cvp, mp, td);
319	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
320	    "Waiting on \"%s\"", cvp->cv_description);
321	WITNESS_SAVE(&mp->mtx_object, mp);
322
323	if (cold || panicstr) {
324		/*
325		 * After a panic, or during autoconfiguration, just give
326		 * interrupts a chance, then just return; don't run any other
327		 * thread or panic below, in case this is the idle process and
328		 * already asleep.
329		 */
330		return 0;
331	}
332
333	sleepq_lock(cvp);
334
335	cvp->cv_waiters++;
336	DROP_GIANT();
337	mtx_unlock(mp);
338
339	sleepq_add(cvp, &mp->mtx_object, cvp->cv_description, SLEEPQ_CONDVAR |
340	    SLEEPQ_INTERRUPTIBLE, 0);
341	sleepq_set_timeout(cvp, timo);
342	rval = sleepq_timedwait_sig(cvp);
343
344#ifdef KTRACE
345	if (KTRPOINT(td, KTR_CSW))
346		ktrcsw(0, 0);
347#endif
348	PICKUP_GIANT();
349	mtx_lock(mp);
350	WITNESS_RESTORE(&mp->mtx_object, mp);
351
352	return (rval);
353}
354
355/*
356 * Signal a condition variable, wakes up one waiting thread.  Will also wakeup
357 * the swapper if the process is not in memory, so that it can bring the
358 * sleeping process in.  Note that this may also result in additional threads
359 * being made runnable.  Should be called with the same mutex as was passed to
360 * cv_wait held.
361 */
362void
363cv_signal(struct cv *cvp)
364{
365
366	sleepq_lock(cvp);
367	if (cvp->cv_waiters > 0) {
368		cvp->cv_waiters--;
369		sleepq_signal(cvp, SLEEPQ_CONDVAR, -1, 0);
370	} else
371		sleepq_release(cvp);
372}
373
374/*
375 * Broadcast a signal to a condition variable.  Wakes up all waiting threads.
376 * Should be called with the same mutex as was passed to cv_wait held.
377 */
378void
379cv_broadcastpri(struct cv *cvp, int pri)
380{
381
382	sleepq_lock(cvp);
383	if (cvp->cv_waiters > 0) {
384		cvp->cv_waiters = 0;
385		sleepq_broadcast(cvp, SLEEPQ_CONDVAR, pri, 0);
386	} else
387		sleepq_release(cvp);
388}
389