kern_condvar.c revision 136445
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 136445 2004-10-12 18:36:20Z 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	struct thread *td;
99	WITNESS_SAVE_DECL(mp);
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, cvp->cv_description, SLEEPQ_CONDVAR);
128	sleepq_wait(cvp);
129
130#ifdef KTRACE
131	if (KTRPOINT(td, KTR_CSW))
132		ktrcsw(0, 0);
133#endif
134	PICKUP_GIANT();
135	mtx_lock(mp);
136	WITNESS_RESTORE(&mp->mtx_object, mp);
137}
138
139/*
140 * Wait on a condition variable, allowing interruption by signals.  Return 0 if
141 * the thread was resumed with cv_signal or cv_broadcast, EINTR or ERESTART if
142 * a signal was caught.  If ERESTART is returned the system call should be
143 * restarted if possible.
144 */
145int
146cv_wait_sig(struct cv *cvp, struct mtx *mp)
147{
148	struct thread *td;
149	struct proc *p;
150	int rval, sig;
151	WITNESS_SAVE_DECL(mp);
152
153	td = curthread;
154	p = td->td_proc;
155#ifdef KTRACE
156	if (KTRPOINT(td, KTR_CSW))
157		ktrcsw(1, 0);
158#endif
159	CV_ASSERT(cvp, mp, td);
160	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
161	    "Waiting on \"%s\"", cvp->cv_description);
162	WITNESS_SAVE(&mp->mtx_object, mp);
163
164	if (cold || panicstr) {
165		/*
166		 * After a panic, or during autoconfiguration, just give
167		 * interrupts a chance, then just return; don't run any other
168		 * procs or panic below, in case this is the idle process and
169		 * already asleep.
170		 */
171		return (0);
172	}
173
174	sleepq_lock(cvp);
175
176	/*
177	 * Don't bother sleeping if we are exiting and not the exiting
178	 * thread or if our thread is marked as interrupted.
179	 */
180	mtx_lock_spin(&sched_lock);
181	rval = thread_sleep_check(td);
182	mtx_unlock_spin(&sched_lock);
183	if (rval != 0) {
184		sleepq_release(cvp);
185		return (rval);
186	}
187
188	cvp->cv_waiters++;
189	DROP_GIANT();
190	mtx_unlock(mp);
191
192	sleepq_add(cvp, mp, cvp->cv_description, SLEEPQ_CONDVAR |
193	    SLEEPQ_INTERRUPTIBLE);
194	sig = sleepq_catch_signals(cvp);
195	rval = sleepq_wait_sig(cvp);
196	if (rval == 0)
197		rval = sleepq_calc_signal_retval(sig);
198
199#ifdef KTRACE
200	if (KTRPOINT(td, KTR_CSW))
201		ktrcsw(0, 0);
202#endif
203	PICKUP_GIANT();
204	mtx_lock(mp);
205	WITNESS_RESTORE(&mp->mtx_object, mp);
206
207	return (rval);
208}
209
210/*
211 * Wait on a condition variable for at most timo/hz seconds.  Returns 0 if the
212 * process was resumed by cv_signal or cv_broadcast, EWOULDBLOCK if the timeout
213 * expires.
214 */
215int
216cv_timedwait(struct cv *cvp, struct mtx *mp, int timo)
217{
218	struct thread *td;
219	int rval;
220	WITNESS_SAVE_DECL(mp);
221
222	td = curthread;
223	rval = 0;
224#ifdef KTRACE
225	if (KTRPOINT(td, KTR_CSW))
226		ktrcsw(1, 0);
227#endif
228	CV_ASSERT(cvp, mp, td);
229	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
230	    "Waiting on \"%s\"", cvp->cv_description);
231	WITNESS_SAVE(&mp->mtx_object, mp);
232
233	if (cold || panicstr) {
234		/*
235		 * After a panic, or during autoconfiguration, just give
236		 * interrupts a chance, then just return; don't run any other
237		 * thread or panic below, in case this is the idle process and
238		 * already asleep.
239		 */
240		return 0;
241	}
242
243	sleepq_lock(cvp);
244
245	cvp->cv_waiters++;
246	DROP_GIANT();
247	mtx_unlock(mp);
248
249	sleepq_add(cvp, mp, cvp->cv_description, SLEEPQ_CONDVAR);
250	sleepq_set_timeout(cvp, timo);
251	rval = sleepq_timedwait(cvp);
252
253#ifdef KTRACE
254	if (KTRPOINT(td, KTR_CSW))
255		ktrcsw(0, 0);
256#endif
257	PICKUP_GIANT();
258	mtx_lock(mp);
259	WITNESS_RESTORE(&mp->mtx_object, mp);
260
261	return (rval);
262}
263
264/*
265 * Wait on a condition variable for at most timo/hz seconds, allowing
266 * interruption by signals.  Returns 0 if the thread was resumed by cv_signal
267 * or cv_broadcast, EWOULDBLOCK if the timeout expires, and EINTR or ERESTART if
268 * a signal was caught.
269 */
270int
271cv_timedwait_sig(struct cv *cvp, struct mtx *mp, int timo)
272{
273	struct thread *td;
274	struct proc *p;
275	int rval;
276	int sig;
277	WITNESS_SAVE_DECL(mp);
278
279	td = curthread;
280	p = td->td_proc;
281	rval = 0;
282#ifdef KTRACE
283	if (KTRPOINT(td, KTR_CSW))
284		ktrcsw(1, 0);
285#endif
286	CV_ASSERT(cvp, mp, td);
287	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object,
288	    "Waiting on \"%s\"", cvp->cv_description);
289	WITNESS_SAVE(&mp->mtx_object, mp);
290
291	if (cold || panicstr) {
292		/*
293		 * After a panic, or during autoconfiguration, just give
294		 * interrupts a chance, then just return; don't run any other
295		 * thread or panic below, in case this is the idle process and
296		 * already asleep.
297		 */
298		return 0;
299	}
300
301	sleepq_lock(cvp);
302
303	/*
304	 * Don't bother sleeping if we are exiting and not the exiting
305	 * thread or if our thread is marked as interrupted.
306	 */
307	mtx_lock_spin(&sched_lock);
308	rval = thread_sleep_check(td);
309	mtx_unlock_spin(&sched_lock);
310	if (rval != 0) {
311		sleepq_release(cvp);
312		return (rval);
313	}
314
315	cvp->cv_waiters++;
316	DROP_GIANT();
317	mtx_unlock(mp);
318
319	sleepq_add(cvp, mp, cvp->cv_description, SLEEPQ_CONDVAR |
320	    SLEEPQ_INTERRUPTIBLE);
321	sleepq_set_timeout(cvp, timo);
322	sig = sleepq_catch_signals(cvp);
323	rval = sleepq_timedwait_sig(cvp, sig != 0);
324	if (rval == 0)
325		rval = sleepq_calc_signal_retval(sig);
326
327#ifdef KTRACE
328	if (KTRPOINT(td, KTR_CSW))
329		ktrcsw(0, 0);
330#endif
331	PICKUP_GIANT();
332	mtx_lock(mp);
333	WITNESS_RESTORE(&mp->mtx_object, mp);
334
335	return (rval);
336}
337
338/*
339 * Signal a condition variable, wakes up one waiting thread.  Will also wakeup
340 * the swapper if the process is not in memory, so that it can bring the
341 * sleeping process in.  Note that this may also result in additional threads
342 * being made runnable.  Should be called with the same mutex as was passed to
343 * cv_wait held.
344 */
345void
346cv_signal(struct cv *cvp)
347{
348
349	sleepq_lock(cvp);
350	if (cvp->cv_waiters > 0) {
351		cvp->cv_waiters--;
352		sleepq_signal(cvp, SLEEPQ_CONDVAR, -1);
353	} else
354		sleepq_release(cvp);
355}
356
357/*
358 * Broadcast a signal to a condition variable.  Wakes up all waiting threads.
359 * Should be called with the same mutex as was passed to cv_wait held.
360 */
361void
362cv_broadcastpri(struct cv *cvp, int pri)
363{
364
365	sleepq_lock(cvp);
366	if (cvp->cv_waiters > 0) {
367		cvp->cv_waiters = 0;
368		sleepq_broadcast(cvp, SLEEPQ_CONDVAR, pri);
369	} else
370		sleepq_release(cvp);
371}
372