1238510fcSJason Evans /*- 2238510fcSJason Evans * Copyright (c) 2000 Jake Burkholder <jake@freebsd.org>. 3238510fcSJason Evans * All rights reserved. 4238510fcSJason Evans * 5238510fcSJason Evans * Redistribution and use in source and binary forms, with or without 6238510fcSJason Evans * modification, are permitted provided that the following conditions 7238510fcSJason Evans * are met: 8238510fcSJason Evans * 1. Redistributions of source code must retain the above copyright 9238510fcSJason Evans * notice, this list of conditions and the following disclaimer. 10238510fcSJason Evans * 2. Redistributions in binary form must reproduce the above copyright 11238510fcSJason Evans * notice, this list of conditions and the following disclaimer in the 12238510fcSJason Evans * documentation and/or other materials provided with the distribution. 13238510fcSJason Evans * 14238510fcSJason Evans * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15238510fcSJason Evans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16238510fcSJason Evans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17238510fcSJason Evans * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18238510fcSJason Evans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19238510fcSJason Evans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20238510fcSJason Evans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21238510fcSJason Evans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22238510fcSJason Evans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23238510fcSJason Evans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24238510fcSJason Evans * SUCH DAMAGE. 25238510fcSJason Evans */ 26238510fcSJason Evans 27677b542eSDavid E. O'Brien #include <sys/cdefs.h> 28677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 29677b542eSDavid E. O'Brien 30238510fcSJason Evans #include "opt_ktrace.h" 31238510fcSJason Evans 32238510fcSJason Evans #include <sys/param.h> 33238510fcSJason Evans #include <sys/systm.h> 34fb919e4dSMark Murray #include <sys/lock.h> 35fb919e4dSMark Murray #include <sys/mutex.h> 36238510fcSJason Evans #include <sys/proc.h> 37238510fcSJason Evans #include <sys/kernel.h> 38238510fcSJason Evans #include <sys/ktr.h> 39238510fcSJason Evans #include <sys/condvar.h> 404e997f4bSJeff Roberson #include <sys/sched.h> 41238510fcSJason Evans #include <sys/signalvar.h> 42238510fcSJason Evans #include <sys/resourcevar.h> 43238510fcSJason Evans #ifdef KTRACE 44238510fcSJason Evans #include <sys/uio.h> 45238510fcSJason Evans #include <sys/ktrace.h> 46238510fcSJason Evans #endif 47238510fcSJason Evans 48238510fcSJason Evans /* 49238510fcSJason Evans * Common sanity checks for cv_wait* functions. 50238510fcSJason Evans */ 51b40ce416SJulian Elischer #define CV_ASSERT(cvp, mp, td) do { \ 52a48740b6SDavid E. O'Brien KASSERT((td) != NULL, ("%s: curthread NULL", __func__)); \ 5371fad9fdSJulian Elischer KASSERT(TD_IS_RUNNING(td), ("%s: not TDS_RUNNING", __func__)); \ 54a48740b6SDavid E. O'Brien KASSERT((cvp) != NULL, ("%s: cvp NULL", __func__)); \ 55a48740b6SDavid E. O'Brien KASSERT((mp) != NULL, ("%s: mp NULL", __func__)); \ 56238510fcSJason Evans mtx_assert((mp), MA_OWNED | MA_NOTRECURSED); \ 57238510fcSJason Evans } while (0) 58238510fcSJason Evans 59e7876c09SDan Moschuk #ifdef INVARIANTS 60238510fcSJason Evans #define CV_WAIT_VALIDATE(cvp, mp) do { \ 61238510fcSJason Evans if (TAILQ_EMPTY(&(cvp)->cv_waitq)) { \ 62238510fcSJason Evans /* Only waiter. */ \ 63238510fcSJason Evans (cvp)->cv_mtx = (mp); \ 64238510fcSJason Evans } else { \ 65238510fcSJason Evans /* \ 66238510fcSJason Evans * Other waiter; assert that we're using the \ 67238510fcSJason Evans * same mutex. \ 68238510fcSJason Evans */ \ 69238510fcSJason Evans KASSERT((cvp)->cv_mtx == (mp), \ 70a48740b6SDavid E. O'Brien ("%s: Multiple mutexes", __func__)); \ 71238510fcSJason Evans } \ 72238510fcSJason Evans } while (0) 7371fad9fdSJulian Elischer 74238510fcSJason Evans #define CV_SIGNAL_VALIDATE(cvp) do { \ 75238510fcSJason Evans if (!TAILQ_EMPTY(&(cvp)->cv_waitq)) { \ 76238510fcSJason Evans KASSERT(mtx_owned((cvp)->cv_mtx), \ 77a48740b6SDavid E. O'Brien ("%s: Mutex not owned", __func__)); \ 78238510fcSJason Evans } \ 79238510fcSJason Evans } while (0) 8071fad9fdSJulian Elischer 81238510fcSJason Evans #else 82238510fcSJason Evans #define CV_WAIT_VALIDATE(cvp, mp) 83238510fcSJason Evans #define CV_SIGNAL_VALIDATE(cvp) 84238510fcSJason Evans #endif 85238510fcSJason Evans 86238510fcSJason Evans static void cv_timedwait_end(void *arg); 87238510fcSJason Evans 88238510fcSJason Evans /* 89238510fcSJason Evans * Initialize a condition variable. Must be called before use. 90238510fcSJason Evans */ 91238510fcSJason Evans void 92238510fcSJason Evans cv_init(struct cv *cvp, const char *desc) 93238510fcSJason Evans { 94238510fcSJason Evans 95238510fcSJason Evans TAILQ_INIT(&cvp->cv_waitq); 96238510fcSJason Evans cvp->cv_mtx = NULL; 97238510fcSJason Evans cvp->cv_description = desc; 98238510fcSJason Evans } 99238510fcSJason Evans 100238510fcSJason Evans /* 101238510fcSJason Evans * Destroy a condition variable. The condition variable must be re-initialized 102238510fcSJason Evans * in order to be re-used. 103238510fcSJason Evans */ 104238510fcSJason Evans void 105238510fcSJason Evans cv_destroy(struct cv *cvp) 106238510fcSJason Evans { 107238510fcSJason Evans 108a48740b6SDavid E. O'Brien KASSERT(cv_waitq_empty(cvp), ("%s: cv_waitq non-empty", __func__)); 109238510fcSJason Evans } 110238510fcSJason Evans 111238510fcSJason Evans /* 112238510fcSJason Evans * Common code for cv_wait* functions. All require sched_lock. 113238510fcSJason Evans */ 114238510fcSJason Evans 115238510fcSJason Evans /* 1169d102777SJulian Elischer * Switch context. 117e602ba25SJulian Elischer */ 1189d102777SJulian Elischer static __inline void 1199d102777SJulian Elischer cv_switch(struct thread *td) 120e602ba25SJulian Elischer { 12171fad9fdSJulian Elischer TD_SET_SLEEPING(td); 12271fad9fdSJulian Elischer td->td_proc->p_stats->p_ru.ru_nvcsw++; 123238510fcSJason Evans mi_switch(); 1249ef3a985SJohn Baldwin CTR3(KTR_PROC, "cv_switch: resume thread %p (pid %d, %s)", td, 1259ef3a985SJohn Baldwin td->td_proc->p_pid, td->td_proc->p_comm); 126238510fcSJason Evans } 127238510fcSJason Evans 128238510fcSJason Evans /* 129238510fcSJason Evans * Switch context, catching signals. 130238510fcSJason Evans */ 131238510fcSJason Evans static __inline int 132b40ce416SJulian Elischer cv_switch_catch(struct thread *td) 133238510fcSJason Evans { 1349ef3a985SJohn Baldwin struct proc *p; 135238510fcSJason Evans int sig; 136238510fcSJason Evans 137238510fcSJason Evans /* 138238510fcSJason Evans * We put ourselves on the sleep queue and start our timeout before 139628855e7SJulian Elischer * calling cursig, as we could stop there, and a wakeup or a SIGCONT (or 140238510fcSJason Evans * both) could occur while we were stopped. A SIGCONT would cause us to 141e602ba25SJulian Elischer * be marked as TDS_SLP without resuming us, thus we must be ready for 142628855e7SJulian Elischer * sleep when cursig is called. If the wakeup happens while we're 14371fad9fdSJulian Elischer * stopped, td->td_wchan will be 0 upon return from cursig, 14471fad9fdSJulian Elischer * and TD_ON_SLEEPQ() will return false. 145238510fcSJason Evans */ 146b40ce416SJulian Elischer td->td_flags |= TDF_SINTR; 1479ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 1489ef3a985SJohn Baldwin p = td->td_proc; 1499ef3a985SJohn Baldwin PROC_LOCK(p); 15090af4afaSJohn Baldwin mtx_lock(&p->p_sigacts->ps_mtx); 15171fad9fdSJulian Elischer sig = cursig(td); 15290af4afaSJohn Baldwin mtx_unlock(&p->p_sigacts->ps_mtx); 153e602ba25SJulian Elischer if (thread_suspend_check(1)) 154e602ba25SJulian Elischer sig = SIGSTOP; 1559ed346baSBosko Milekic mtx_lock_spin(&sched_lock); 156c86b6ff5SJohn Baldwin PROC_UNLOCK(p); 157238510fcSJason Evans if (sig != 0) { 15871fad9fdSJulian Elischer if (TD_ON_SLEEPQ(td)) 159b40ce416SJulian Elischer cv_waitq_remove(td); 16071fad9fdSJulian Elischer TD_SET_RUNNING(td); 16171fad9fdSJulian Elischer } else if (TD_ON_SLEEPQ(td)) { 162b40ce416SJulian Elischer cv_switch(td); 163238510fcSJason Evans } 164b40ce416SJulian Elischer td->td_flags &= ~TDF_SINTR; 165238510fcSJason Evans 166238510fcSJason Evans return sig; 167238510fcSJason Evans } 168238510fcSJason Evans 169238510fcSJason Evans /* 170b40ce416SJulian Elischer * Add a thread to the wait queue of a condition variable. 171238510fcSJason Evans */ 172238510fcSJason Evans static __inline void 173b40ce416SJulian Elischer cv_waitq_add(struct cv *cvp, struct thread *td) 174238510fcSJason Evans { 175238510fcSJason Evans 176b40ce416SJulian Elischer td->td_flags |= TDF_CVWAITQ; 17771fad9fdSJulian Elischer TD_SET_ON_SLEEPQ(td); 178b40ce416SJulian Elischer td->td_wchan = cvp; 179b40ce416SJulian Elischer td->td_wmesg = cvp->cv_description; 180b40ce416SJulian Elischer CTR3(KTR_PROC, "cv_waitq_add: thread %p (pid %d, %s)", td, 1819ef3a985SJohn Baldwin td->td_proc->p_pid, td->td_proc->p_comm); 182b40ce416SJulian Elischer TAILQ_INSERT_TAIL(&cvp->cv_waitq, td, td_slpq); 1834e997f4bSJeff Roberson sched_sleep(td, td->td_priority); 184238510fcSJason Evans } 185238510fcSJason Evans 186238510fcSJason Evans /* 187b40ce416SJulian Elischer * Wait on a condition variable. The current thread is placed on the condition 188238510fcSJason Evans * variable's wait queue and suspended. A cv_signal or cv_broadcast on the same 189b40ce416SJulian Elischer * condition variable will resume the thread. The mutex is released before 190238510fcSJason Evans * sleeping and will be held on return. It is recommended that the mutex be 191238510fcSJason Evans * held when cv_signal or cv_broadcast are called. 192238510fcSJason Evans */ 193238510fcSJason Evans void 194238510fcSJason Evans cv_wait(struct cv *cvp, struct mtx *mp) 195238510fcSJason Evans { 196b40ce416SJulian Elischer struct thread *td; 197238510fcSJason Evans WITNESS_SAVE_DECL(mp); 198238510fcSJason Evans 199b40ce416SJulian Elischer td = curthread; 200238510fcSJason Evans #ifdef KTRACE 2019ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 2029ba7fe1bSJohn Baldwin ktrcsw(1, 0); 203238510fcSJason Evans #endif 204b40ce416SJulian Elischer CV_ASSERT(cvp, mp, td); 20526306795SJohn Baldwin WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object, 20626306795SJohn Baldwin "Waiting on \"%s\"", cvp->cv_description); 20719284646SJohn Baldwin WITNESS_SAVE(&mp->mtx_object, mp); 208238510fcSJason Evans 209fe799533SAndrew Gallatin if (cold ) { 210238510fcSJason Evans /* 211fe799533SAndrew Gallatin * During autoconfiguration, just give interrupts 212fe799533SAndrew Gallatin * a chance, then just return. Don't run any other 213fe799533SAndrew Gallatin * thread or panic below, in case this is the idle 214fe799533SAndrew Gallatin * process and already asleep. 215238510fcSJason Evans */ 216238510fcSJason Evans return; 217238510fcSJason Evans } 2184bc37205SJeffrey Hsu 2194bc37205SJeffrey Hsu mtx_lock_spin(&sched_lock); 2204bc37205SJeffrey Hsu 221238510fcSJason Evans CV_WAIT_VALIDATE(cvp, mp); 222238510fcSJason Evans 223c86b6ff5SJohn Baldwin DROP_GIANT(); 224c86b6ff5SJohn Baldwin mtx_unlock(mp); 225238510fcSJason Evans 226b40ce416SJulian Elischer cv_waitq_add(cvp, td); 227b40ce416SJulian Elischer cv_switch(td); 228238510fcSJason Evans 2299ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 230238510fcSJason Evans #ifdef KTRACE 2319ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 2329ba7fe1bSJohn Baldwin ktrcsw(0, 0); 233238510fcSJason Evans #endif 234238510fcSJason Evans PICKUP_GIANT(); 2359ed346baSBosko Milekic mtx_lock(mp); 23619284646SJohn Baldwin WITNESS_RESTORE(&mp->mtx_object, mp); 237238510fcSJason Evans } 238238510fcSJason Evans 239238510fcSJason Evans /* 240238510fcSJason Evans * Wait on a condition variable, allowing interruption by signals. Return 0 if 241b40ce416SJulian Elischer * the thread was resumed with cv_signal or cv_broadcast, EINTR or ERESTART if 242238510fcSJason Evans * a signal was caught. If ERESTART is returned the system call should be 243238510fcSJason Evans * restarted if possible. 244238510fcSJason Evans */ 245238510fcSJason Evans int 246238510fcSJason Evans cv_wait_sig(struct cv *cvp, struct mtx *mp) 247238510fcSJason Evans { 248b40ce416SJulian Elischer struct thread *td; 24966f769feSPeter Wemm struct proc *p; 250238510fcSJason Evans int rval; 251238510fcSJason Evans int sig; 252238510fcSJason Evans WITNESS_SAVE_DECL(mp); 253238510fcSJason Evans 254b40ce416SJulian Elischer td = curthread; 2559ef3a985SJohn Baldwin p = td->td_proc; 256238510fcSJason Evans rval = 0; 257238510fcSJason Evans #ifdef KTRACE 2589ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 2599ba7fe1bSJohn Baldwin ktrcsw(1, 0); 260238510fcSJason Evans #endif 261b40ce416SJulian Elischer CV_ASSERT(cvp, mp, td); 26226306795SJohn Baldwin WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object, 26326306795SJohn Baldwin "Waiting on \"%s\"", cvp->cv_description); 26419284646SJohn Baldwin WITNESS_SAVE(&mp->mtx_object, mp); 265238510fcSJason Evans 266238510fcSJason Evans if (cold || panicstr) { 267238510fcSJason Evans /* 268238510fcSJason Evans * After a panic, or during autoconfiguration, just give 269238510fcSJason Evans * interrupts a chance, then just return; don't run any other 270238510fcSJason Evans * procs or panic below, in case this is the idle process and 271238510fcSJason Evans * already asleep. 272238510fcSJason Evans */ 273238510fcSJason Evans return 0; 274238510fcSJason Evans } 2754bc37205SJeffrey Hsu 2764bc37205SJeffrey Hsu mtx_lock_spin(&sched_lock); 2774bc37205SJeffrey Hsu 278238510fcSJason Evans CV_WAIT_VALIDATE(cvp, mp); 279238510fcSJason Evans 280c86b6ff5SJohn Baldwin DROP_GIANT(); 281c86b6ff5SJohn Baldwin mtx_unlock(mp); 282238510fcSJason Evans 283b40ce416SJulian Elischer cv_waitq_add(cvp, td); 284b40ce416SJulian Elischer sig = cv_switch_catch(td); 285238510fcSJason Evans 2869ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 287238510fcSJason Evans 2889ef3a985SJohn Baldwin PROC_LOCK(p); 28990af4afaSJohn Baldwin mtx_lock(&p->p_sigacts->ps_mtx); 29034178711SDavid Xu if (sig == 0) { 291e602ba25SJulian Elischer sig = cursig(td); /* XXXKSE */ 29234178711SDavid Xu if (sig == 0 && td->td_flags & TDF_INTERRUPT) 29334178711SDavid Xu rval = td->td_intrval; 29434178711SDavid Xu } 295238510fcSJason Evans if (sig != 0) { 2969ef3a985SJohn Baldwin if (SIGISMEMBER(p->p_sigacts->ps_sigintr, sig)) 297238510fcSJason Evans rval = EINTR; 298238510fcSJason Evans else 299238510fcSJason Evans rval = ERESTART; 300238510fcSJason Evans } 30190af4afaSJohn Baldwin mtx_unlock(&p->p_sigacts->ps_mtx); 302e602ba25SJulian Elischer if (p->p_flag & P_WEXIT) 303e602ba25SJulian Elischer rval = EINTR; 30453862173SJohn Baldwin PROC_UNLOCK(p); 305238510fcSJason Evans 306238510fcSJason Evans #ifdef KTRACE 3079ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 3089ba7fe1bSJohn Baldwin ktrcsw(0, 0); 309238510fcSJason Evans #endif 3109ba7fe1bSJohn Baldwin PICKUP_GIANT(); 3119ed346baSBosko Milekic mtx_lock(mp); 31219284646SJohn Baldwin WITNESS_RESTORE(&mp->mtx_object, mp); 313238510fcSJason Evans 314238510fcSJason Evans return (rval); 315238510fcSJason Evans } 316238510fcSJason Evans 317238510fcSJason Evans /* 318238510fcSJason Evans * Wait on a condition variable for at most timo/hz seconds. Returns 0 if the 319238510fcSJason Evans * process was resumed by cv_signal or cv_broadcast, EWOULDBLOCK if the timeout 320238510fcSJason Evans * expires. 321238510fcSJason Evans */ 322238510fcSJason Evans int 323238510fcSJason Evans cv_timedwait(struct cv *cvp, struct mtx *mp, int timo) 324238510fcSJason Evans { 325b40ce416SJulian Elischer struct thread *td; 326238510fcSJason Evans int rval; 327238510fcSJason Evans WITNESS_SAVE_DECL(mp); 328238510fcSJason Evans 329b40ce416SJulian Elischer td = curthread; 330238510fcSJason Evans rval = 0; 331238510fcSJason Evans #ifdef KTRACE 3329ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 3339ba7fe1bSJohn Baldwin ktrcsw(1, 0); 334238510fcSJason Evans #endif 335b40ce416SJulian Elischer CV_ASSERT(cvp, mp, td); 33626306795SJohn Baldwin WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object, 33726306795SJohn Baldwin "Waiting on \"%s\"", cvp->cv_description); 33819284646SJohn Baldwin WITNESS_SAVE(&mp->mtx_object, mp); 339238510fcSJason Evans 340238510fcSJason Evans if (cold || panicstr) { 341238510fcSJason Evans /* 342238510fcSJason Evans * After a panic, or during autoconfiguration, just give 343238510fcSJason Evans * interrupts a chance, then just return; don't run any other 344b40ce416SJulian Elischer * thread or panic below, in case this is the idle process and 345238510fcSJason Evans * already asleep. 346238510fcSJason Evans */ 347238510fcSJason Evans return 0; 348238510fcSJason Evans } 3494bc37205SJeffrey Hsu 3504bc37205SJeffrey Hsu mtx_lock_spin(&sched_lock); 3514bc37205SJeffrey Hsu 352238510fcSJason Evans CV_WAIT_VALIDATE(cvp, mp); 353238510fcSJason Evans 354c86b6ff5SJohn Baldwin DROP_GIANT(); 355c86b6ff5SJohn Baldwin mtx_unlock(mp); 356238510fcSJason Evans 357b40ce416SJulian Elischer cv_waitq_add(cvp, td); 358b40ce416SJulian Elischer callout_reset(&td->td_slpcallout, timo, cv_timedwait_end, td); 359b40ce416SJulian Elischer cv_switch(td); 360238510fcSJason Evans 361b40ce416SJulian Elischer if (td->td_flags & TDF_TIMEOUT) { 362b40ce416SJulian Elischer td->td_flags &= ~TDF_TIMEOUT; 363238510fcSJason Evans rval = EWOULDBLOCK; 364b40ce416SJulian Elischer } else if (td->td_flags & TDF_TIMOFAIL) 365b40ce416SJulian Elischer td->td_flags &= ~TDF_TIMOFAIL; 366b40ce416SJulian Elischer else if (callout_stop(&td->td_slpcallout) == 0) { 36791a4536fSJohn Baldwin /* 36891a4536fSJohn Baldwin * Work around race with cv_timedwait_end similar to that 36991a4536fSJohn Baldwin * between msleep and endtsleep. 370d5cb7e14SJulian Elischer * Go back to sleep. 37191a4536fSJohn Baldwin */ 37271fad9fdSJulian Elischer TD_SET_SLEEPING(td); 373b40ce416SJulian Elischer td->td_proc->p_stats->p_ru.ru_nivcsw++; 37491a4536fSJohn Baldwin mi_switch(); 37571fad9fdSJulian Elischer td->td_flags &= ~TDF_TIMOFAIL; 37691a4536fSJohn Baldwin } 377238510fcSJason Evans 3789ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 379238510fcSJason Evans #ifdef KTRACE 3809ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 3819ba7fe1bSJohn Baldwin ktrcsw(0, 0); 382238510fcSJason Evans #endif 383238510fcSJason Evans PICKUP_GIANT(); 3849ed346baSBosko Milekic mtx_lock(mp); 38519284646SJohn Baldwin WITNESS_RESTORE(&mp->mtx_object, mp); 386238510fcSJason Evans 387238510fcSJason Evans return (rval); 388238510fcSJason Evans } 389238510fcSJason Evans 390238510fcSJason Evans /* 391238510fcSJason Evans * Wait on a condition variable for at most timo/hz seconds, allowing 392b40ce416SJulian Elischer * interruption by signals. Returns 0 if the thread was resumed by cv_signal 393238510fcSJason Evans * or cv_broadcast, EWOULDBLOCK if the timeout expires, and EINTR or ERESTART if 394238510fcSJason Evans * a signal was caught. 395238510fcSJason Evans */ 396238510fcSJason Evans int 397238510fcSJason Evans cv_timedwait_sig(struct cv *cvp, struct mtx *mp, int timo) 398238510fcSJason Evans { 399b40ce416SJulian Elischer struct thread *td; 4009ef3a985SJohn Baldwin struct proc *p; 401238510fcSJason Evans int rval; 402238510fcSJason Evans int sig; 403238510fcSJason Evans WITNESS_SAVE_DECL(mp); 404238510fcSJason Evans 405b40ce416SJulian Elischer td = curthread; 4069ef3a985SJohn Baldwin p = td->td_proc; 407238510fcSJason Evans rval = 0; 408238510fcSJason Evans #ifdef KTRACE 4099ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 4109ba7fe1bSJohn Baldwin ktrcsw(1, 0); 411238510fcSJason Evans #endif 412b40ce416SJulian Elischer CV_ASSERT(cvp, mp, td); 41326306795SJohn Baldwin WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &mp->mtx_object, 41426306795SJohn Baldwin "Waiting on \"%s\"", cvp->cv_description); 41519284646SJohn Baldwin WITNESS_SAVE(&mp->mtx_object, mp); 416238510fcSJason Evans 417238510fcSJason Evans if (cold || panicstr) { 418238510fcSJason Evans /* 419238510fcSJason Evans * After a panic, or during autoconfiguration, just give 420238510fcSJason Evans * interrupts a chance, then just return; don't run any other 421b40ce416SJulian Elischer * thread or panic below, in case this is the idle process and 422238510fcSJason Evans * already asleep. 423238510fcSJason Evans */ 424238510fcSJason Evans return 0; 425238510fcSJason Evans } 4264bc37205SJeffrey Hsu 4274bc37205SJeffrey Hsu mtx_lock_spin(&sched_lock); 4284bc37205SJeffrey Hsu 429238510fcSJason Evans CV_WAIT_VALIDATE(cvp, mp); 430238510fcSJason Evans 431c86b6ff5SJohn Baldwin DROP_GIANT(); 432c86b6ff5SJohn Baldwin mtx_unlock(mp); 433238510fcSJason Evans 434b40ce416SJulian Elischer cv_waitq_add(cvp, td); 435b40ce416SJulian Elischer callout_reset(&td->td_slpcallout, timo, cv_timedwait_end, td); 436b40ce416SJulian Elischer sig = cv_switch_catch(td); 437238510fcSJason Evans 438b40ce416SJulian Elischer if (td->td_flags & TDF_TIMEOUT) { 439b40ce416SJulian Elischer td->td_flags &= ~TDF_TIMEOUT; 440238510fcSJason Evans rval = EWOULDBLOCK; 441b40ce416SJulian Elischer } else if (td->td_flags & TDF_TIMOFAIL) 442b40ce416SJulian Elischer td->td_flags &= ~TDF_TIMOFAIL; 443b40ce416SJulian Elischer else if (callout_stop(&td->td_slpcallout) == 0) { 44491a4536fSJohn Baldwin /* 44591a4536fSJohn Baldwin * Work around race with cv_timedwait_end similar to that 44691a4536fSJohn Baldwin * between msleep and endtsleep. 447d5cb7e14SJulian Elischer * Go back to sleep. 44891a4536fSJohn Baldwin */ 44971fad9fdSJulian Elischer TD_SET_SLEEPING(td); 450b40ce416SJulian Elischer td->td_proc->p_stats->p_ru.ru_nivcsw++; 45191a4536fSJohn Baldwin mi_switch(); 45271fad9fdSJulian Elischer td->td_flags &= ~TDF_TIMOFAIL; 45391a4536fSJohn Baldwin } 4549ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 455238510fcSJason Evans 4569ef3a985SJohn Baldwin PROC_LOCK(p); 45790af4afaSJohn Baldwin mtx_lock(&p->p_sigacts->ps_mtx); 45834178711SDavid Xu if (sig == 0) { 459e602ba25SJulian Elischer sig = cursig(td); 46034178711SDavid Xu if (sig == 0 && td->td_flags & TDF_INTERRUPT) 46134178711SDavid Xu rval = td->td_intrval; 46234178711SDavid Xu } 463238510fcSJason Evans if (sig != 0) { 4649ef3a985SJohn Baldwin if (SIGISMEMBER(p->p_sigacts->ps_sigintr, sig)) 465238510fcSJason Evans rval = EINTR; 466238510fcSJason Evans else 467238510fcSJason Evans rval = ERESTART; 468238510fcSJason Evans } 46990af4afaSJohn Baldwin mtx_unlock(&p->p_sigacts->ps_mtx); 470e602ba25SJulian Elischer if (p->p_flag & P_WEXIT) 471e602ba25SJulian Elischer rval = EINTR; 47253862173SJohn Baldwin PROC_UNLOCK(p); 473e602ba25SJulian Elischer 474238510fcSJason Evans #ifdef KTRACE 4759ba7fe1bSJohn Baldwin if (KTRPOINT(td, KTR_CSW)) 4769ba7fe1bSJohn Baldwin ktrcsw(0, 0); 477238510fcSJason Evans #endif 4789ba7fe1bSJohn Baldwin PICKUP_GIANT(); 4799ed346baSBosko Milekic mtx_lock(mp); 48019284646SJohn Baldwin WITNESS_RESTORE(&mp->mtx_object, mp); 481238510fcSJason Evans 482238510fcSJason Evans return (rval); 483238510fcSJason Evans } 484238510fcSJason Evans 485238510fcSJason Evans /* 486238510fcSJason Evans * Common code for signal and broadcast. Assumes waitq is not empty. Must be 487238510fcSJason Evans * called with sched_lock held. 488238510fcSJason Evans */ 489238510fcSJason Evans static __inline void 490238510fcSJason Evans cv_wakeup(struct cv *cvp) 491238510fcSJason Evans { 492b40ce416SJulian Elischer struct thread *td; 493238510fcSJason Evans 494981808d1SJohn Baldwin mtx_assert(&sched_lock, MA_OWNED); 495b40ce416SJulian Elischer td = TAILQ_FIRST(&cvp->cv_waitq); 496a48740b6SDavid E. O'Brien KASSERT(td->td_wchan == cvp, ("%s: bogus wchan", __func__)); 497a48740b6SDavid E. O'Brien KASSERT(td->td_flags & TDF_CVWAITQ, ("%s: not on waitq", __func__)); 49871fad9fdSJulian Elischer cv_waitq_remove(td); 49971fad9fdSJulian Elischer TD_CLR_SLEEPING(td); 50071fad9fdSJulian Elischer setrunnable(td); 501238510fcSJason Evans } 502238510fcSJason Evans 503238510fcSJason Evans /* 504b40ce416SJulian Elischer * Signal a condition variable, wakes up one waiting thread. Will also wakeup 505238510fcSJason Evans * the swapper if the process is not in memory, so that it can bring the 506b40ce416SJulian Elischer * sleeping process in. Note that this may also result in additional threads 507238510fcSJason Evans * being made runnable. Should be called with the same mutex as was passed to 508238510fcSJason Evans * cv_wait held. 509238510fcSJason Evans */ 510238510fcSJason Evans void 511238510fcSJason Evans cv_signal(struct cv *cvp) 512238510fcSJason Evans { 513238510fcSJason Evans 514a48740b6SDavid E. O'Brien KASSERT(cvp != NULL, ("%s: cvp NULL", __func__)); 5159ed346baSBosko Milekic mtx_lock_spin(&sched_lock); 516238510fcSJason Evans if (!TAILQ_EMPTY(&cvp->cv_waitq)) { 517238510fcSJason Evans CV_SIGNAL_VALIDATE(cvp); 518238510fcSJason Evans cv_wakeup(cvp); 519238510fcSJason Evans } 5209ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 521238510fcSJason Evans } 522238510fcSJason Evans 523238510fcSJason Evans /* 524b40ce416SJulian Elischer * Broadcast a signal to a condition variable. Wakes up all waiting threads. 525238510fcSJason Evans * Should be called with the same mutex as was passed to cv_wait held. 526238510fcSJason Evans */ 527238510fcSJason Evans void 528512824f8SSeigo Tanimura cv_broadcastpri(struct cv *cvp, int pri) 529238510fcSJason Evans { 530512824f8SSeigo Tanimura struct thread *td; 531238510fcSJason Evans 532a48740b6SDavid E. O'Brien KASSERT(cvp != NULL, ("%s: cvp NULL", __func__)); 5339ed346baSBosko Milekic mtx_lock_spin(&sched_lock); 534238510fcSJason Evans CV_SIGNAL_VALIDATE(cvp); 535512824f8SSeigo Tanimura while (!TAILQ_EMPTY(&cvp->cv_waitq)) { 536512824f8SSeigo Tanimura if (pri >= PRI_MIN && pri <= PRI_MAX) { 537512824f8SSeigo Tanimura td = TAILQ_FIRST(&cvp->cv_waitq); 538512824f8SSeigo Tanimura if (td->td_priority > pri) 539512824f8SSeigo Tanimura td->td_priority = pri; 540512824f8SSeigo Tanimura } 541238510fcSJason Evans cv_wakeup(cvp); 542512824f8SSeigo Tanimura } 5439ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 544238510fcSJason Evans } 545238510fcSJason Evans 546238510fcSJason Evans /* 547b40ce416SJulian Elischer * Remove a thread from the wait queue of its condition variable. This may be 548238510fcSJason Evans * called externally. 549238510fcSJason Evans */ 550238510fcSJason Evans void 551b40ce416SJulian Elischer cv_waitq_remove(struct thread *td) 552238510fcSJason Evans { 553238510fcSJason Evans struct cv *cvp; 554238510fcSJason Evans 55571fad9fdSJulian Elischer mtx_assert(&sched_lock, MA_OWNED); 556b40ce416SJulian Elischer if ((cvp = td->td_wchan) != NULL && td->td_flags & TDF_CVWAITQ) { 557b40ce416SJulian Elischer TAILQ_REMOVE(&cvp->cv_waitq, td, td_slpq); 558b40ce416SJulian Elischer td->td_flags &= ~TDF_CVWAITQ; 559b89bc9e6SHartmut Brandt td->td_wmesg = NULL; 56071fad9fdSJulian Elischer TD_CLR_ON_SLEEPQ(td); 561238510fcSJason Evans } 562238510fcSJason Evans } 563238510fcSJason Evans 564238510fcSJason Evans /* 565b40ce416SJulian Elischer * Timeout function for cv_timedwait. Put the thread on the runqueue and set 566238510fcSJason Evans * its timeout flag. 567238510fcSJason Evans */ 568238510fcSJason Evans static void 569238510fcSJason Evans cv_timedwait_end(void *arg) 570238510fcSJason Evans { 571b40ce416SJulian Elischer struct thread *td; 572238510fcSJason Evans 573b40ce416SJulian Elischer td = arg; 57471fad9fdSJulian Elischer CTR3(KTR_PROC, "cv_timedwait_end: thread %p (pid %d, %s)", 57571fad9fdSJulian Elischer td, td->td_proc->p_pid, td->td_proc->p_comm); 5769ed346baSBosko Milekic mtx_lock_spin(&sched_lock); 57771fad9fdSJulian Elischer if (TD_ON_SLEEPQ(td)) { 578b40ce416SJulian Elischer cv_waitq_remove(td); 579b40ce416SJulian Elischer td->td_flags |= TDF_TIMEOUT; 58071fad9fdSJulian Elischer } else { 581b40ce416SJulian Elischer td->td_flags |= TDF_TIMOFAIL; 58271fad9fdSJulian Elischer } 58371fad9fdSJulian Elischer TD_CLR_SLEEPING(td); 58471fad9fdSJulian Elischer setrunnable(td); 5859ed346baSBosko Milekic mtx_unlock_spin(&sched_lock); 586238510fcSJason Evans } 587e602ba25SJulian Elischer 588e602ba25SJulian Elischer /* 589e602ba25SJulian Elischer * For now only abort interruptable waits. 590e602ba25SJulian Elischer * The others will have to either complete on their own or have a timeout. 591e602ba25SJulian Elischer */ 592e602ba25SJulian Elischer void 593e602ba25SJulian Elischer cv_abort(struct thread *td) 594e602ba25SJulian Elischer { 595e602ba25SJulian Elischer 596e602ba25SJulian Elischer CTR3(KTR_PROC, "cv_abort: thread %p (pid %d, %s)", td, 59771fad9fdSJulian Elischer td->td_proc->p_pid, td->td_proc->p_comm); 598e602ba25SJulian Elischer mtx_lock_spin(&sched_lock); 599e602ba25SJulian Elischer if ((td->td_flags & (TDF_SINTR|TDF_TIMEOUT)) == TDF_SINTR) { 60071fad9fdSJulian Elischer if (TD_ON_SLEEPQ(td)) { 601e602ba25SJulian Elischer cv_waitq_remove(td); 602e602ba25SJulian Elischer } 60371fad9fdSJulian Elischer TD_CLR_SLEEPING(td); 60471fad9fdSJulian Elischer setrunnable(td); 605e602ba25SJulian Elischer } 606e602ba25SJulian Elischer mtx_unlock_spin(&sched_lock); 607e602ba25SJulian Elischer } 608e602ba25SJulian Elischer 609