Changeset View
Standalone View
sys/kern/subr_sleepqueue.c
Show First 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/sbuf.h> | #include <sys/sbuf.h> | ||||
#include <sys/sched.h> | #include <sys/sched.h> | ||||
#include <sys/sdt.h> | #include <sys/sdt.h> | ||||
#include <sys/signalvar.h> | #include <sys/signalvar.h> | ||||
#include <sys/sleepqueue.h> | #include <sys/sleepqueue.h> | ||||
#include <sys/stack.h> | #include <sys/stack.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/time.h> | |||||
#include <machine/atomic.h> | |||||
#include <vm/uma.h> | #include <vm/uma.h> | ||||
#ifdef DDB | #ifdef DDB | ||||
#include <ddb/ddb.h> | #include <ddb/ddb.h> | ||||
#endif | #endif | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 444 Lines • ▼ Show 20 Lines | |||||
* Returns with thread lock. | * Returns with thread lock. | ||||
*/ | */ | ||||
static void | static void | ||||
sleepq_switch(void *wchan, int pri) | sleepq_switch(void *wchan, int pri) | ||||
{ | { | ||||
struct sleepqueue_chain *sc; | struct sleepqueue_chain *sc; | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct thread *td; | struct thread *td; | ||||
bool rtc_changed; | |||||
td = curthread; | td = curthread; | ||||
sc = SC_LOOKUP(wchan); | sc = SC_LOOKUP(wchan); | ||||
mtx_assert(&sc->sc_lock, MA_OWNED); | mtx_assert(&sc->sc_lock, MA_OWNED); | ||||
THREAD_LOCK_ASSERT(td, MA_OWNED); | THREAD_LOCK_ASSERT(td, MA_OWNED); | ||||
/* | /* | ||||
* If we have a sleep queue, then we've already been woken up, so | * If we have a sleep queue, then we've already been woken up, so | ||||
* just return. | * just return. | ||||
*/ | */ | ||||
if (td->td_sleepqueue != NULL) { | if (td->td_sleepqueue != NULL) { | ||||
mtx_unlock_spin(&sc->sc_lock); | mtx_unlock_spin(&sc->sc_lock); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
* If TDF_TIMEOUT is set, then our sleep has been timed out | * If TDF_TIMEOUT is set, then our sleep has been timed out | ||||
* already but we are still on the sleep queue, so dequeue the | * already but we are still on the sleep queue, so dequeue the | ||||
* thread and return. | * thread and return. | ||||
* | |||||
* Do the same if the real-time clock has been adjusted since this | |||||
kib: I think this is a right place to provide some reasoning why the code correctly covers the race… | |||||
Done Inline ActionsWill do. vangyzen: Will do. | |||||
* thread calculated its timeout based on that clock. This handles | |||||
* the following race: | |||||
* - The Ts thread needs to sleep until an absolute real-clock time. | |||||
* It copies the global rtc_generation into curthread->td_rtcgen, | |||||
* reads the RTC, and calculates a sleep duration based on that time. | |||||
* See umtxq_sleep() for an example. | |||||
Not Done Inline ActionsI think it is a prereq for the race that Ts is not put to sleep yet. In fact, there is very fine difference between TD_ON_SLEEPQ() which means that sleepq_add() was done, and TD_IS_SLEEPING(), which mean that the thread is really asleep. TD_IS_SLEEPING() condition is set as the last action in sleepq_switch() (this function) right before the call to mi_switch(). When I asked for the comment, I mean just mention this race and also to explain how the use of sleep chain spinlocks makes the td_rtcgen accesses correctly synchronized. kib: I think it is a prereq for the race that Ts is not put to sleep yet. In fact, there is very… | |||||
* - The Tc thread adjusts the RTC, bumps rtc_generation, and wakes | |||||
* threads that are sleeping until an absolute real-clock time. | |||||
* See tc_setclock() and the POSIX specification of clock_settime(). | |||||
* - Ts reaches the code below. It holds the sleepqueue chain lock, | |||||
* so Tc has finished waking, so this thread must test td_rtcgen. | |||||
* (The declaration of td_rtcgen refers to this comment.) | |||||
*/ | */ | ||||
if (td->td_flags & TDF_TIMEOUT) { | rtc_changed = td->td_rtcgen != 0 && td->td_rtcgen != rtc_generation; | ||||
if ((td->td_flags & TDF_TIMEOUT) || rtc_changed) { | |||||
if (rtc_changed) { | |||||
td->td_rtcgen = 0; | |||||
} | |||||
MPASS(TD_ON_SLEEPQ(td)); | MPASS(TD_ON_SLEEPQ(td)); | ||||
sq = sleepq_lookup(wchan); | sq = sleepq_lookup(wchan); | ||||
if (sleepq_resume_thread(sq, td, 0)) { | if (sleepq_resume_thread(sq, td, 0)) { | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
/* | /* | ||||
* This thread hasn't gone to sleep yet, so it | * This thread hasn't gone to sleep yet, so it | ||||
* should not be swapped out. | * should not be swapped out. | ||||
*/ | */ | ||||
panic("not waking up swapper"); | panic("not waking up swapper"); | ||||
#endif | #endif | ||||
} | } | ||||
mtx_unlock_spin(&sc->sc_lock); | mtx_unlock_spin(&sc->sc_lock); | ||||
return; | return; | ||||
Done Inline ActionsAs I asked before, please commit the style and comment grammar fixes now. kib: As I asked before, please commit the style and comment grammar fixes now. | |||||
} | } | ||||
#ifdef SLEEPQUEUE_PROFILING | #ifdef SLEEPQUEUE_PROFILING | ||||
if (prof_enabled) | if (prof_enabled) | ||||
sleepq_profile(td->td_wmesg); | sleepq_profile(td->td_wmesg); | ||||
#endif | #endif | ||||
MPASS(td->td_sleepqueue == NULL); | MPASS(td->td_sleepqueue == NULL); | ||||
sched_sleep(td, pri); | sched_sleep(td, pri); | ||||
thread_lock_set(td, &sc->sc_lock); | thread_lock_set(td, &sc->sc_lock); | ||||
▲ Show 20 Lines • Show All 297 Lines • ▼ Show 20 Lines | sleepq_signal(void *wchan, int flags, int pri, int queue) | ||||
} | } | ||||
MPASS(besttd != NULL); | MPASS(besttd != NULL); | ||||
thread_lock(besttd); | thread_lock(besttd); | ||||
wakeup_swapper = sleepq_resume_thread(sq, besttd, pri); | wakeup_swapper = sleepq_resume_thread(sq, besttd, pri); | ||||
thread_unlock(besttd); | thread_unlock(besttd); | ||||
return (wakeup_swapper); | return (wakeup_swapper); | ||||
} | } | ||||
static bool | |||||
match_any(struct thread *td __unused) | |||||
{ | |||||
Done Inline ActionsStyle(9) requires a blank line after the '{' if no local vars is defined. kib: Style(9) requires a blank line after the '{' if no local vars is defined. | |||||
return (true); | |||||
} | |||||
/* | /* | ||||
* Resume all threads sleeping on a specified wait channel. | * Resume all threads sleeping on a specified wait channel. | ||||
*/ | */ | ||||
int | int | ||||
sleepq_broadcast(void *wchan, int flags, int pri, int queue) | sleepq_broadcast(void *wchan, int flags, int pri, int queue) | ||||
{ | { | ||||
struct sleepqueue *sq; | struct sleepqueue *sq; | ||||
struct thread *td, *tdn; | |||||
int wakeup_swapper; | |||||
CTR2(KTR_PROC, "sleepq_broadcast(%p, %d)", wchan, flags); | CTR2(KTR_PROC, "sleepq_broadcast(%p, %d)", wchan, flags); | ||||
KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | KASSERT(wchan != NULL, ("%s: invalid NULL wait channel", __func__)); | ||||
MPASS((queue >= 0) && (queue < NR_SLEEPQS)); | MPASS((queue >= 0) && (queue < NR_SLEEPQS)); | ||||
sq = sleepq_lookup(wchan); | sq = sleepq_lookup(wchan); | ||||
if (sq == NULL) | if (sq == NULL) | ||||
return (0); | return (0); | ||||
KASSERT(sq->sq_type == (flags & SLEEPQ_TYPE), | KASSERT(sq->sq_type == (flags & SLEEPQ_TYPE), | ||||
("%s: mismatch between sleep/wakeup and cv_*", __func__)); | ("%s: mismatch between sleep/wakeup and cv_*", __func__)); | ||||
return (sleepq_remove_matching(sq, queue, match_any, pri)); | |||||
Done Inline ActionsI would prefer to remove special case for matcher == NULL and instead provided a trivial matcher always returning true there. kib: I would prefer to remove special case for matcher == NULL and instead provided a trivial… | |||||
} | |||||
/* | /* | ||||
* Resume all blocked threads on the sleep queue. The last thread will | * Resume threads on the sleep queue that match the given predicate. | ||||
* be given ownership of sq and may re-enqueue itself before | |||||
* sleepq_resume_thread() returns, so we must cache the "next" queue | |||||
* item at the beginning of the final iteration. | |||||
*/ | */ | ||||
int | |||||
sleepq_remove_matching(struct sleepqueue *sq, int queue, | |||||
bool (*matches)(struct thread *), int pri) | |||||
{ | |||||
struct thread *td, *tdn; | |||||
int wakeup_swapper; | |||||
/* | |||||
* The last thread will be given ownership of sq and may | |||||
* re-enqueue itself before sleepq_resume_thread() returns, | |||||
* so we must cache the "next" queue item at the beginning | |||||
* of the final iteration. | |||||
*/ | |||||
wakeup_swapper = 0; | wakeup_swapper = 0; | ||||
TAILQ_FOREACH_SAFE(td, &sq->sq_blocked[queue], td_slpq, tdn) { | TAILQ_FOREACH_SAFE(td, &sq->sq_blocked[queue], td_slpq, tdn) { | ||||
thread_lock(td); | thread_lock(td); | ||||
if (matches(td)) | |||||
wakeup_swapper |= sleepq_resume_thread(sq, td, pri); | wakeup_swapper |= sleepq_resume_thread(sq, td, pri); | ||||
thread_unlock(td); | thread_unlock(td); | ||||
} | } | ||||
return (wakeup_swapper); | return (wakeup_swapper); | ||||
} | } | ||||
/* | /* | ||||
* Time sleeping threads out. When the timeout expires, the thread is | * Time sleeping threads out. When the timeout expires, the thread is | ||||
* removed from the sleep queue and made runnable if it is still asleep. | * removed from the sleep queue and made runnable if it is still asleep. | ||||
*/ | */ | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | if (!TD_IS_SLEEPING(td)) | ||||
return (0); | return (0); | ||||
wchan = td->td_wchan; | wchan = td->td_wchan; | ||||
MPASS(wchan != NULL); | MPASS(wchan != NULL); | ||||
sq = sleepq_lookup(wchan); | sq = sleepq_lookup(wchan); | ||||
MPASS(sq != NULL); | MPASS(sq != NULL); | ||||
/* Thread is asleep on sleep queue sq, so wake it up. */ | /* Thread is asleep on sleep queue sq, so wake it up. */ | ||||
return (sleepq_resume_thread(sq, td, 0)); | return (sleepq_resume_thread(sq, td, 0)); | ||||
} | |||||
void | |||||
sleepq_chains_remove_matching(bool (*matches)(struct thread *)) | |||||
{ | |||||
struct sleepqueue_chain *sc; | |||||
struct sleepqueue *sq; | |||||
int i, wakeup_swapper; | |||||
wakeup_swapper = 0; | |||||
for (sc = &sleepq_chains[0]; sc < sleepq_chains + SC_TABLESIZE; ++sc) { | |||||
if (LIST_EMPTY(&sc->sc_queues)) { | |||||
continue; | |||||
} | |||||
mtx_lock_spin(&sc->sc_lock); | |||||
LIST_FOREACH(sq, &sc->sc_queues, sq_hash) { | |||||
for (i = 0; i < NR_SLEEPQS; ++i) { | |||||
wakeup_swapper |= sleepq_remove_matching(sq, i, | |||||
matches, 0); | |||||
Done Inline ActionsI suggest to re-factor this code fragment from sleepq_broadcast() and reuse the call. kib: I suggest to re-factor this code fragment from sleepq_broadcast() and reuse the call. | |||||
Done Inline ActionsGood idea. Done. vangyzen: Good idea. Done. | |||||
} | |||||
} | |||||
mtx_unlock_spin(&sc->sc_lock); | |||||
} | |||||
if (wakeup_swapper) { | |||||
Done Inline ActionsNo need in {}. kib: No need in {}. | |||||
Done Inline ActionsTrue, but I very strongly prefer using such braces. vangyzen: True, but I very strongly prefer using such braces. | |||||
kick_proc0(); | |||||
} | |||||
} | } | ||||
/* | /* | ||||
* Prints the stacks of all threads presently sleeping on wchan/queue to | * Prints the stacks of all threads presently sleeping on wchan/queue to | ||||
* the sbuf sb. Sets count_stacks_printed to the number of stacks actually | * the sbuf sb. Sets count_stacks_printed to the number of stacks actually | ||||
* printed. Typically, this will equal the number of threads sleeping on the | * printed. Typically, this will equal the number of threads sleeping on the | ||||
Done Inline ActionsIt is enough to do one kick_proc0() after waking up all chains. kib: It is enough to do one kick_proc0() after waking up all chains. | |||||
Done Inline ActionsI was wondering about that. Thanks. vangyzen: I was wondering about that. Thanks. | |||||
* queue, but may be less if sb overflowed before all stacks were printed. | * queue, but may be less if sb overflowed before all stacks were printed. | ||||
*/ | */ | ||||
#ifdef STACK | #ifdef STACK | ||||
int | int | ||||
sleepq_sbuf_print_stacks(struct sbuf *sb, void *wchan, int queue, | sleepq_sbuf_print_stacks(struct sbuf *sb, void *wchan, int queue, | ||||
int *count_stacks_printed) | int *count_stacks_printed) | ||||
{ | { | ||||
struct thread *td, *td_next; | struct thread *td, *td_next; | ||||
▲ Show 20 Lines • Show All 316 Lines • Show Last 20 Lines |
I think this is a right place to provide some reasoning why the code correctly covers the race where tc_windup()->sleeping_on_old_rtc() missed us. Some note about sleepq lock used as interlock there is due, IMO.