16d3dff5fSMatthew Dillon /*
26d3dff5fSMatthew Dillon * Copyright (c) 2023 The DragonFly Project. All rights reserved.
36d3dff5fSMatthew Dillon *
46d3dff5fSMatthew Dillon * This code is derived from software contributed to The DragonFly Project
56d3dff5fSMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
66d3dff5fSMatthew Dillon *
76d3dff5fSMatthew Dillon * Redistribution and use in source and binary forms, with or without
86d3dff5fSMatthew Dillon * modification, are permitted provided that the following conditions
96d3dff5fSMatthew Dillon * are met:
106d3dff5fSMatthew Dillon *
116d3dff5fSMatthew Dillon * 1. Redistributions of source code must retain the above copyright
126d3dff5fSMatthew Dillon * notice, this list of conditions and the following disclaimer.
136d3dff5fSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
146d3dff5fSMatthew Dillon * notice, this list of conditions and the following disclaimer in
156d3dff5fSMatthew Dillon * the documentation and/or other materials provided with the
166d3dff5fSMatthew Dillon * distribution.
176d3dff5fSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
186d3dff5fSMatthew Dillon * contributors may be used to endorse or promote products derived
196d3dff5fSMatthew Dillon * from this software without specific, prior written permission.
206d3dff5fSMatthew Dillon *
216d3dff5fSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
226d3dff5fSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
236d3dff5fSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
246d3dff5fSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
256d3dff5fSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
266d3dff5fSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
276d3dff5fSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
286d3dff5fSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
296d3dff5fSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
306d3dff5fSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
316d3dff5fSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
326d3dff5fSMatthew Dillon * SUCH DAMAGE.
336d3dff5fSMatthew Dillon */
346d3dff5fSMatthew Dillon /*
356d3dff5fSMatthew Dillon * DragonFly implementation of the FreeBSD sleepq*() API. FreeBSD's sleepq*()
366d3dff5fSMatthew Dillon * API differs from our tsleep*() API in several major ways.
376d3dff5fSMatthew Dillon *
386d3dff5fSMatthew Dillon * ONLY USE THIS FOR FREEBSD COMPATIBILITY, E.G. THE LINUX KPI
396d3dff5fSMatthew Dillon *
406d3dff5fSMatthew Dillon * - Explicit interlock via sleepq_lock(wchan)
416d3dff5fSMatthew Dillon * - Single global wchan hash table
426d3dff5fSMatthew Dillon * - Explicit blocking counts
436d3dff5fSMatthew Dillon * - Expects all blockers on the same wchan to be of the same type
446d3dff5fSMatthew Dillon *
456d3dff5fSMatthew Dillon * In addition, the API calls have a ton of unnecessary redundancy which
466d3dff5fSMatthew Dillon * creates problems with using on-stack structures for things. To make
476d3dff5fSMatthew Dillon * ends meat, DragonFly records information in the thread structure and
486d3dff5fSMatthew Dillon * only enacts the actual operation when the thread issues a *wait*() API
496d3dff5fSMatthew Dillon * call. Only the spin interlock is handled in real-time.
506d3dff5fSMatthew Dillon */
516d3dff5fSMatthew Dillon
526d3dff5fSMatthew Dillon #include "opt_ddb.h"
536d3dff5fSMatthew Dillon
546d3dff5fSMatthew Dillon #include <sys/cdefs.h>
556d3dff5fSMatthew Dillon #include <sys/param.h>
566d3dff5fSMatthew Dillon #include <sys/systm.h>
576d3dff5fSMatthew Dillon #include <sys/lock.h>
58*2b3f93eaSMatthew Dillon #include <sys/caps.h>
596d3dff5fSMatthew Dillon #include <sys/malloc.h>
606d3dff5fSMatthew Dillon #include <sys/queue.h>
616d3dff5fSMatthew Dillon #include <sys/sleepqueue.h>
626d3dff5fSMatthew Dillon #include <sys/objcache.h>
636d3dff5fSMatthew Dillon #include <sys/sysctl.h>
646d3dff5fSMatthew Dillon
656d3dff5fSMatthew Dillon #include <sys/time.h>
666d3dff5fSMatthew Dillon
676d3dff5fSMatthew Dillon #include <machine/atomic.h>
686d3dff5fSMatthew Dillon
696d3dff5fSMatthew Dillon #ifdef DDB
706d3dff5fSMatthew Dillon #include <ddb/ddb.h>
716d3dff5fSMatthew Dillon #endif
726d3dff5fSMatthew Dillon
736d3dff5fSMatthew Dillon #include <sys/signal2.h>
746d3dff5fSMatthew Dillon #include <sys/thread2.h>
756d3dff5fSMatthew Dillon #include <sys/spinlock2.h>
766d3dff5fSMatthew Dillon #include <sys/mutex2.h>
776d3dff5fSMatthew Dillon
786d3dff5fSMatthew Dillon #include <vm/vm_extern.h>
796d3dff5fSMatthew Dillon
806d3dff5fSMatthew Dillon #define SLEEPQ_HSIZE 1024
816d3dff5fSMatthew Dillon #define SLEEPQ_HMASK (SLEEPQ_HSIZE - 1)
826d3dff5fSMatthew Dillon #define SLEEPQ_HASH(wchan) ((((uintptr_t)(wchan) >> 10) ^ \
836d3dff5fSMatthew Dillon ((uintptr_t)(wchan) & SLEEPQ_HMASK)))
846d3dff5fSMatthew Dillon
856d3dff5fSMatthew Dillon #define SLEEPQ_LOOKUP(wchan) &sleepq_chains[SLEEPQ_HASH(wchan)]
866d3dff5fSMatthew Dillon #define SLEEPQ_NRQUEUES 2
876d3dff5fSMatthew Dillon #define SLEEPQ_FREEPERSLOT 4
886d3dff5fSMatthew Dillon
896d3dff5fSMatthew Dillon struct sleepqueue_wchan;
906d3dff5fSMatthew Dillon
916d3dff5fSMatthew Dillon struct sleepqueue_chain {
926d3dff5fSMatthew Dillon struct spinlock sc_spin;
936d3dff5fSMatthew Dillon TAILQ_HEAD(, sleepqueue_wchan) sc_wchead;
946d3dff5fSMatthew Dillon u_int sc_free_count;
956d3dff5fSMatthew Dillon };
966d3dff5fSMatthew Dillon
976d3dff5fSMatthew Dillon struct sleepqueue_wchan {
986d3dff5fSMatthew Dillon TAILQ_ENTRY(sleepqueue_wchan) wc_entry;
996d3dff5fSMatthew Dillon const void *wc_wchan;
1006d3dff5fSMatthew Dillon struct sleepqueue_chain *wc_sc;
1016d3dff5fSMatthew Dillon u_int wc_refs;
1026d3dff5fSMatthew Dillon int wc_type;
1036d3dff5fSMatthew Dillon u_int wc_blocked[SLEEPQ_NRQUEUES];
1046d3dff5fSMatthew Dillon };
1056d3dff5fSMatthew Dillon
1066d3dff5fSMatthew Dillon static struct sleepqueue_chain sleepq_chains[SLEEPQ_HSIZE];
1076d3dff5fSMatthew Dillon static MALLOC_DEFINE(M_SLEEPQ, "sleepq", "fbsd sleepq api");
1086d3dff5fSMatthew Dillon static struct objcache *sleepq_wc_cache;
1096d3dff5fSMatthew Dillon
1106d3dff5fSMatthew Dillon /*
1116d3dff5fSMatthew Dillon * Return existing wchan, assert not NULL.
1126d3dff5fSMatthew Dillon */
1136d3dff5fSMatthew Dillon static __inline
1146d3dff5fSMatthew Dillon struct sleepqueue_wchan *
sleepq_wclookup(const void * wchan)1156d3dff5fSMatthew Dillon sleepq_wclookup(const void *wchan)
1166d3dff5fSMatthew Dillon {
1176d3dff5fSMatthew Dillon struct sleepqueue_chain *sc;
1186d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
1196d3dff5fSMatthew Dillon
1206d3dff5fSMatthew Dillon sc = SLEEPQ_LOOKUP(wchan);
1216d3dff5fSMatthew Dillon KKASSERT(spin_held(&sc->sc_spin));
1226d3dff5fSMatthew Dillon TAILQ_FOREACH(wc, &sc->sc_wchead, wc_entry) {
1236d3dff5fSMatthew Dillon if (wc->wc_wchan == wchan)
1246d3dff5fSMatthew Dillon return(wc);
1256d3dff5fSMatthew Dillon }
1266d3dff5fSMatthew Dillon panic("sleepq_wclookup: wchan %p not found\n", wc);
1276d3dff5fSMatthew Dillon
1286d3dff5fSMatthew Dillon return NULL; /* not reached */
1296d3dff5fSMatthew Dillon }
1306d3dff5fSMatthew Dillon
1316d3dff5fSMatthew Dillon /*
1326d3dff5fSMatthew Dillon * Early initialization of sleep queues
1336d3dff5fSMatthew Dillon */
1346d3dff5fSMatthew Dillon static void
init_sleepqueues(void)1356d3dff5fSMatthew Dillon init_sleepqueues(void)
1366d3dff5fSMatthew Dillon {
1376d3dff5fSMatthew Dillon int i;
1386d3dff5fSMatthew Dillon
1396d3dff5fSMatthew Dillon for (i = 0; i < SLEEPQ_HSIZE; ++i) {
1406d3dff5fSMatthew Dillon spin_init(&sleepq_chains[i].sc_spin, "sleepq");
1416d3dff5fSMatthew Dillon TAILQ_INIT(&sleepq_chains[i].sc_wchead);
1426d3dff5fSMatthew Dillon }
1436d3dff5fSMatthew Dillon /*thread0.td_sleepqueue = sleepq_alloc();*/
1446d3dff5fSMatthew Dillon
1456d3dff5fSMatthew Dillon sleepq_wc_cache = objcache_create_simple(M_SLEEPQ,
1466d3dff5fSMatthew Dillon sizeof(struct sleepqueue_wchan));
1476d3dff5fSMatthew Dillon
1486d3dff5fSMatthew Dillon }
1496d3dff5fSMatthew Dillon SYSINIT(sysinit_sleepqueues, SI_BOOT2_LWKT_INIT, SI_ORDER_SECOND,
1506d3dff5fSMatthew Dillon init_sleepqueues, NULL);
1516d3dff5fSMatthew Dillon
1526d3dff5fSMatthew Dillon /*
1536d3dff5fSMatthew Dillon * DragonFlyBSD Interface Functions
1546d3dff5fSMatthew Dillon *
1556d3dff5fSMatthew Dillon * Setup and teardown sleepq related thread structures
1566d3dff5fSMatthew Dillon *
1576d3dff5fSMatthew Dillon * sleepq_alloc() - not applicable
1586d3dff5fSMatthew Dillon * sleepq_free() - not applicable
1596d3dff5fSMatthew Dillon */
1606d3dff5fSMatthew Dillon void
sleepq_setup_thread(struct thread * td)1616d3dff5fSMatthew Dillon sleepq_setup_thread(struct thread *td)
1626d3dff5fSMatthew Dillon {
1636d3dff5fSMatthew Dillon }
1646d3dff5fSMatthew Dillon
1656d3dff5fSMatthew Dillon void
sleepq_teardown_thread(struct thread * td)1666d3dff5fSMatthew Dillon sleepq_teardown_thread(struct thread *td)
1676d3dff5fSMatthew Dillon {
1686d3dff5fSMatthew Dillon }
1696d3dff5fSMatthew Dillon
1706d3dff5fSMatthew Dillon /*
1716d3dff5fSMatthew Dillon * Lock the wchan, creating the structure if necessary. Returns with
1726d3dff5fSMatthew Dillon * the spin-lock held.
1736d3dff5fSMatthew Dillon *
1746d3dff5fSMatthew Dillon * Also used as an interlock, so we need to actually lock something here.
1756d3dff5fSMatthew Dillon */
1766d3dff5fSMatthew Dillon void
sleepq_lock(const void * wchan)1776d3dff5fSMatthew Dillon sleepq_lock(const void *wchan)
1786d3dff5fSMatthew Dillon {
1796d3dff5fSMatthew Dillon struct sleepqueue_chain *sc;
1806d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
1816d3dff5fSMatthew Dillon
1826d3dff5fSMatthew Dillon sc = SLEEPQ_LOOKUP(wchan);
1836d3dff5fSMatthew Dillon spin_lock(&sc->sc_spin);
1846d3dff5fSMatthew Dillon
1856d3dff5fSMatthew Dillon for (;;) {
1866d3dff5fSMatthew Dillon /*
1876d3dff5fSMatthew Dillon * Look for the wchan, if not found then allocate one from
1886d3dff5fSMatthew Dillon * the existing in-list cache if available.
1896d3dff5fSMatthew Dillon */
1906d3dff5fSMatthew Dillon TAILQ_FOREACH(wc, &sc->sc_wchead, wc_entry) {
1916d3dff5fSMatthew Dillon if (wc->wc_wchan == wchan) {
1926d3dff5fSMatthew Dillon ++wc->wc_refs;
1936d3dff5fSMatthew Dillon return;
1946d3dff5fSMatthew Dillon }
1956d3dff5fSMatthew Dillon if (wc->wc_wchan == NULL) {
1966d3dff5fSMatthew Dillon wc->wc_wchan = wchan;
1976d3dff5fSMatthew Dillon ++wc->wc_refs;
1986d3dff5fSMatthew Dillon --sc->sc_free_count;
1996d3dff5fSMatthew Dillon return;
2006d3dff5fSMatthew Dillon }
2016d3dff5fSMatthew Dillon }
2026d3dff5fSMatthew Dillon
2036d3dff5fSMatthew Dillon /*
2046d3dff5fSMatthew Dillon * Not found and no free entries available, allocate
2056d3dff5fSMatthew Dillon * a new, free wc, then relock and repeat the search.
2066d3dff5fSMatthew Dillon */
2076d3dff5fSMatthew Dillon spin_unlock(&sc->sc_spin);
2086d3dff5fSMatthew Dillon wc = objcache_get(sleepq_wc_cache, M_WAITOK);
2096d3dff5fSMatthew Dillon KKASSERT(wc->wc_wchan == NULL && wc->wc_refs == 0);
2106d3dff5fSMatthew Dillon wc->wc_sc = sc;
2116d3dff5fSMatthew Dillon spin_lock(&sc->sc_spin);
2126d3dff5fSMatthew Dillon TAILQ_INSERT_TAIL(&sc->sc_wchead, wc, wc_entry);
2136d3dff5fSMatthew Dillon ++sc->sc_free_count;
2146d3dff5fSMatthew Dillon /* loop re-search */
2156d3dff5fSMatthew Dillon }
2166d3dff5fSMatthew Dillon }
2176d3dff5fSMatthew Dillon
2186d3dff5fSMatthew Dillon /*
2196d3dff5fSMatthew Dillon * Unlock the sleep queue chain associated with a given wait channel.
2206d3dff5fSMatthew Dillon */
2216d3dff5fSMatthew Dillon void
sleepq_release(const void * wchan)2226d3dff5fSMatthew Dillon sleepq_release(const void *wchan)
2236d3dff5fSMatthew Dillon {
2246d3dff5fSMatthew Dillon struct sleepqueue_chain *sc;
2256d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
2266d3dff5fSMatthew Dillon
2276d3dff5fSMatthew Dillon wc = sleepq_wclookup(wchan);
2286d3dff5fSMatthew Dillon sc = wc->wc_sc;
2296d3dff5fSMatthew Dillon KKASSERT(wc->wc_refs > 0);
2306d3dff5fSMatthew Dillon if (--wc->wc_refs == 0) {
2316d3dff5fSMatthew Dillon /* just sanity-check one for brevity */
2326d3dff5fSMatthew Dillon KKASSERT(wc->wc_blocked[0] == 0);
2336d3dff5fSMatthew Dillon wc->wc_wchan = NULL;
2346d3dff5fSMatthew Dillon wc->wc_type = 0;
2356d3dff5fSMatthew Dillon ++sc->sc_free_count;
2366d3dff5fSMatthew Dillon }
2376d3dff5fSMatthew Dillon
2386d3dff5fSMatthew Dillon spin_unlock(&sc->sc_spin);
2396d3dff5fSMatthew Dillon }
2406d3dff5fSMatthew Dillon
2416d3dff5fSMatthew Dillon /*
2426d3dff5fSMatthew Dillon * Place the current thread on the specified wait channel and sub-queue.
2436d3dff5fSMatthew Dillon *
2446d3dff5fSMatthew Dillon * The caller must be holding the wchan lock and it will remain locked
2456d3dff5fSMatthew Dillon * on return. The caller must follow-up with a sleepq_*wait*() call.
2466d3dff5fSMatthew Dillon *
2476d3dff5fSMatthew Dillon * If INVARIANTS is enabled, then it associates the passed in lock with
2486d3dff5fSMatthew Dillon * the sleepq to make sure it is held when that sleep queue is woken up
2496d3dff5fSMatthew Dillon * (XXX not implemented)
2506d3dff5fSMatthew Dillon */
2516d3dff5fSMatthew Dillon void
sleepq_add(const void * wchan,struct lock_object * lock,const char * wmesg,int flags,int queue)2526d3dff5fSMatthew Dillon sleepq_add(const void *wchan, struct lock_object *lock, const char *wmesg,
2536d3dff5fSMatthew Dillon int flags, int queue)
2546d3dff5fSMatthew Dillon {
2556d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
2566d3dff5fSMatthew Dillon struct thread *td;
2576d3dff5fSMatthew Dillon int domain;
2586d3dff5fSMatthew Dillon
2596d3dff5fSMatthew Dillon /*
2606d3dff5fSMatthew Dillon * Locate the wc (also asserts that the lock is held). Bump
2616d3dff5fSMatthew Dillon * wc->wc_refs and wc->wc_blocked[queue] to indicate that the
2626d3dff5fSMatthew Dillon * thread has been placed on the sleepq.
2636d3dff5fSMatthew Dillon */
2646d3dff5fSMatthew Dillon wc = sleepq_wclookup(wchan);
2656d3dff5fSMatthew Dillon ++wc->wc_refs;
2666d3dff5fSMatthew Dillon ++wc->wc_blocked[queue];
2676d3dff5fSMatthew Dillon wc->wc_type = flags & SLEEPQ_TYPE;
2686d3dff5fSMatthew Dillon
2696d3dff5fSMatthew Dillon /*
2706d3dff5fSMatthew Dillon * tsleep_interlock() sets TDF_TSLEEPQ, sets td_wchan, and td_wdomain,
2716d3dff5fSMatthew Dillon * and places the thread on the appropriate sleepq.
2726d3dff5fSMatthew Dillon */
2736d3dff5fSMatthew Dillon td = curthread;
2746d3dff5fSMatthew Dillon td->td_wmesg = wmesg;
2756d3dff5fSMatthew Dillon domain = PDOMAIN_FBSD0 + queue * PDOMAIN_FBSDINC;
2766d3dff5fSMatthew Dillon tsleep_interlock(wchan, domain);
2776d3dff5fSMatthew Dillon
2786d3dff5fSMatthew Dillon /*
2796d3dff5fSMatthew Dillon * Clear timeout to discern a possible later sleepq_set_timeout_sbt()
2806d3dff5fSMatthew Dillon * call.
2816d3dff5fSMatthew Dillon */
2826d3dff5fSMatthew Dillon td->td_sqwc = wc;
2836d3dff5fSMatthew Dillon td->td_sqtimo = 0;
2846d3dff5fSMatthew Dillon td->td_sqqueue = queue;
2856d3dff5fSMatthew Dillon }
2866d3dff5fSMatthew Dillon
2876d3dff5fSMatthew Dillon /*
2886d3dff5fSMatthew Dillon * Set a timeout for the thread after it has been added to a wait channel.
2896d3dff5fSMatthew Dillon */
2906d3dff5fSMatthew Dillon void
sleepq_set_timeout_sbt(const void * wchan,sbintime_t sbt,sbintime_t pr,int flags)2916d3dff5fSMatthew Dillon sleepq_set_timeout_sbt(const void *wchan, sbintime_t sbt, sbintime_t pr,
2926d3dff5fSMatthew Dillon int flags)
2936d3dff5fSMatthew Dillon {
2946d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
2956d3dff5fSMatthew Dillon struct thread *td;
2966d3dff5fSMatthew Dillon
2976d3dff5fSMatthew Dillon td = curthread;
2986d3dff5fSMatthew Dillon wc = td->td_sqwc;
2996d3dff5fSMatthew Dillon KKASSERT(wc && wc->wc_wchan == wchan);
3006d3dff5fSMatthew Dillon td->td_sqtimo = sbticks + sbt;
3016d3dff5fSMatthew Dillon }
3026d3dff5fSMatthew Dillon
3036d3dff5fSMatthew Dillon /*
3046d3dff5fSMatthew Dillon * Return the number of actual sleepers for the specified queue.
3056d3dff5fSMatthew Dillon *
3066d3dff5fSMatthew Dillon * Caller must be holding the wchan locked
3076d3dff5fSMatthew Dillon */
3086d3dff5fSMatthew Dillon u_int
sleepq_sleepcnt(const void * wchan,int queue)3096d3dff5fSMatthew Dillon sleepq_sleepcnt(const void *wchan, int queue)
3106d3dff5fSMatthew Dillon {
3116d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
3126d3dff5fSMatthew Dillon
3136d3dff5fSMatthew Dillon wc = sleepq_wclookup(wchan);
3146d3dff5fSMatthew Dillon return (wc->wc_blocked[queue]);
3156d3dff5fSMatthew Dillon }
3166d3dff5fSMatthew Dillon
3176d3dff5fSMatthew Dillon static __inline
3186d3dff5fSMatthew Dillon int
_sleepq_wait_begin(struct thread * td,struct sleepqueue_chain * sc,struct sleepqueue_wchan * wc,sbintime_t timo,int tflags)3196d3dff5fSMatthew Dillon _sleepq_wait_begin(struct thread *td, struct sleepqueue_chain *sc,
3206d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc, sbintime_t timo, int tflags)
3216d3dff5fSMatthew Dillon {
3226d3dff5fSMatthew Dillon int domain;
3236d3dff5fSMatthew Dillon int ret;
3246d3dff5fSMatthew Dillon
3256d3dff5fSMatthew Dillon spin_unlock(&sc->sc_spin);
3266d3dff5fSMatthew Dillon
3276d3dff5fSMatthew Dillon domain = PDOMAIN_FBSD0 + td->td_sqqueue * PDOMAIN_FBSDINC;
3286d3dff5fSMatthew Dillon if (timo) {
3296d3dff5fSMatthew Dillon timo -= sbticks;
3306d3dff5fSMatthew Dillon if (timo > 0) {
3316d3dff5fSMatthew Dillon ret = tsleep(td->td_wchan, tflags, td->td_wmesg, timo);
3326d3dff5fSMatthew Dillon } else {
3336d3dff5fSMatthew Dillon ret = EWOULDBLOCK;
3346d3dff5fSMatthew Dillon }
3356d3dff5fSMatthew Dillon } else {
3366d3dff5fSMatthew Dillon ret = tsleep(td->td_wchan, tflags, td->td_wmesg, 0);
3376d3dff5fSMatthew Dillon }
3386d3dff5fSMatthew Dillon
3396d3dff5fSMatthew Dillon return ret;
3406d3dff5fSMatthew Dillon }
3416d3dff5fSMatthew Dillon
3426d3dff5fSMatthew Dillon /*
3436d3dff5fSMatthew Dillon * Wait for completion
3446d3dff5fSMatthew Dillon */
3456d3dff5fSMatthew Dillon static __inline
3466d3dff5fSMatthew Dillon void
_sleepq_wait_complete(struct thread * td,struct sleepqueue_chain * sc,struct sleepqueue_wchan * wc)3476d3dff5fSMatthew Dillon _sleepq_wait_complete(struct thread *td, struct sleepqueue_chain *sc,
3486d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc)
3496d3dff5fSMatthew Dillon {
3506d3dff5fSMatthew Dillon struct sleepqueue_wchan *wcn;
3516d3dff5fSMatthew Dillon
3526d3dff5fSMatthew Dillon td->td_sqwc = NULL;
3536d3dff5fSMatthew Dillon spin_lock(&sc->sc_spin);
3546d3dff5fSMatthew Dillon --wc->wc_blocked[td->td_sqqueue];
3556d3dff5fSMatthew Dillon if (--wc->wc_refs == 0) {
3566d3dff5fSMatthew Dillon wc->wc_wchan = NULL;
3576d3dff5fSMatthew Dillon wc->wc_type = 0;
3586d3dff5fSMatthew Dillon ++sc->sc_free_count;
3596d3dff5fSMatthew Dillon if (sc->sc_free_count <= SLEEPQ_FREEPERSLOT) {
3606d3dff5fSMatthew Dillon wcn = TAILQ_NEXT(wc, wc_entry);
3616d3dff5fSMatthew Dillon if (wcn && wcn->wc_wchan) {
3626d3dff5fSMatthew Dillon TAILQ_REMOVE(&sc->sc_wchead, wc, wc_entry);
3636d3dff5fSMatthew Dillon TAILQ_INSERT_TAIL(&sc->sc_wchead, wc, wc_entry);
3646d3dff5fSMatthew Dillon }
3656d3dff5fSMatthew Dillon spin_unlock(&sc->sc_spin);
3666d3dff5fSMatthew Dillon } else {
3676d3dff5fSMatthew Dillon --sc->sc_free_count;
3686d3dff5fSMatthew Dillon TAILQ_REMOVE(&sc->sc_wchead, wc, wc_entry);
3696d3dff5fSMatthew Dillon spin_unlock(&sc->sc_spin);
3706d3dff5fSMatthew Dillon objcache_put(sleepq_wc_cache, wc);
3716d3dff5fSMatthew Dillon }
3726d3dff5fSMatthew Dillon } else {
3736d3dff5fSMatthew Dillon spin_unlock(&sc->sc_spin);
3746d3dff5fSMatthew Dillon }
3756d3dff5fSMatthew Dillon }
3766d3dff5fSMatthew Dillon
3776d3dff5fSMatthew Dillon /*
3786d3dff5fSMatthew Dillon * Various combinations of wait until unblocked, with and without
3796d3dff5fSMatthew Dillon * signaling and timeouts.
3806d3dff5fSMatthew Dillon *
3816d3dff5fSMatthew Dillon * The wchan lock must be held by the caller and will be released upon
3826d3dff5fSMatthew Dillon * return.
3836d3dff5fSMatthew Dillon *
3846d3dff5fSMatthew Dillon * NOTE: tsleep_interlock() was already issued by sleepq_add().
3856d3dff5fSMatthew Dillon */
3866d3dff5fSMatthew Dillon void
sleepq_wait(const void * wchan,int pri)3876d3dff5fSMatthew Dillon sleepq_wait(const void *wchan, int pri)
3886d3dff5fSMatthew Dillon {
3896d3dff5fSMatthew Dillon struct sleepqueue_chain *sc;
3906d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
3916d3dff5fSMatthew Dillon struct thread *td;
3926d3dff5fSMatthew Dillon
3936d3dff5fSMatthew Dillon td = curthread;
3946d3dff5fSMatthew Dillon wc = td->td_sqwc;
3956d3dff5fSMatthew Dillon KKASSERT(wc != NULL && wc->wc_wchan == wchan);
3966d3dff5fSMatthew Dillon sc = wc->wc_sc;
3976d3dff5fSMatthew Dillon
3986d3dff5fSMatthew Dillon (void)_sleepq_wait_begin(td, sc, wc, 0, PINTERLOCKED);
3996d3dff5fSMatthew Dillon _sleepq_wait_complete(td, sc, wc);
4006d3dff5fSMatthew Dillon }
4016d3dff5fSMatthew Dillon
4026d3dff5fSMatthew Dillon int
sleepq_wait_sig(const void * wchan,int pri)4036d3dff5fSMatthew Dillon sleepq_wait_sig(const void *wchan, int pri)
4046d3dff5fSMatthew Dillon {
4056d3dff5fSMatthew Dillon struct sleepqueue_chain *sc;
4066d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
4076d3dff5fSMatthew Dillon struct thread *td;
4086d3dff5fSMatthew Dillon int ret;
4096d3dff5fSMatthew Dillon
4106d3dff5fSMatthew Dillon td = curthread;
4116d3dff5fSMatthew Dillon wc = td->td_sqwc;
4126d3dff5fSMatthew Dillon KKASSERT(wc != NULL && wc->wc_wchan == wchan);
4136d3dff5fSMatthew Dillon sc = wc->wc_sc;
4146d3dff5fSMatthew Dillon
4156d3dff5fSMatthew Dillon ret = _sleepq_wait_begin(td, sc, wc, 0, PINTERLOCKED | PCATCH);
4166d3dff5fSMatthew Dillon _sleepq_wait_complete(td, sc, wc);
4176d3dff5fSMatthew Dillon
4186d3dff5fSMatthew Dillon return ret;
4196d3dff5fSMatthew Dillon }
4206d3dff5fSMatthew Dillon
4216d3dff5fSMatthew Dillon int
sleepq_timedwait(const void * wchan,int pri)4226d3dff5fSMatthew Dillon sleepq_timedwait(const void *wchan, int pri)
4236d3dff5fSMatthew Dillon {
4246d3dff5fSMatthew Dillon struct sleepqueue_chain *sc;
4256d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
4266d3dff5fSMatthew Dillon struct thread *td;
4276d3dff5fSMatthew Dillon int ret;
4286d3dff5fSMatthew Dillon
4296d3dff5fSMatthew Dillon td = curthread;
4306d3dff5fSMatthew Dillon wc = td->td_sqwc;
4316d3dff5fSMatthew Dillon KKASSERT(wc != NULL && wc->wc_wchan == wchan);
4326d3dff5fSMatthew Dillon sc = wc->wc_sc;
4336d3dff5fSMatthew Dillon
4346d3dff5fSMatthew Dillon ret = _sleepq_wait_begin(td, sc, wc, td->td_sqtimo, PINTERLOCKED);
4356d3dff5fSMatthew Dillon _sleepq_wait_complete(td, sc, wc);
4366d3dff5fSMatthew Dillon
4376d3dff5fSMatthew Dillon return ret;
4386d3dff5fSMatthew Dillon }
4396d3dff5fSMatthew Dillon
4406d3dff5fSMatthew Dillon int
sleepq_timedwait_sig(const void * wchan,int pri)4416d3dff5fSMatthew Dillon sleepq_timedwait_sig(const void *wchan, int pri)
4426d3dff5fSMatthew Dillon {
4436d3dff5fSMatthew Dillon struct sleepqueue_chain *sc;
4446d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
4456d3dff5fSMatthew Dillon struct thread *td;
4466d3dff5fSMatthew Dillon int ret;
4476d3dff5fSMatthew Dillon
4486d3dff5fSMatthew Dillon td = curthread;
4496d3dff5fSMatthew Dillon wc = td->td_sqwc;
4506d3dff5fSMatthew Dillon KKASSERT(wc != NULL && wc->wc_wchan == wchan);
4516d3dff5fSMatthew Dillon sc = wc->wc_sc;
4526d3dff5fSMatthew Dillon
4536d3dff5fSMatthew Dillon ret = _sleepq_wait_begin(td, sc, wc, td->td_sqtimo,
4546d3dff5fSMatthew Dillon PINTERLOCKED | PCATCH);
4556d3dff5fSMatthew Dillon _sleepq_wait_complete(td, sc, wc);
4566d3dff5fSMatthew Dillon
4576d3dff5fSMatthew Dillon return ret;
4586d3dff5fSMatthew Dillon }
4596d3dff5fSMatthew Dillon
4606d3dff5fSMatthew Dillon /*
4616d3dff5fSMatthew Dillon * Returns the type of sleepqueue given a waitchannel. Returns 0 if
4626d3dff5fSMatthew Dillon * sleepq_add() has not been called no the wchan.
4636d3dff5fSMatthew Dillon *
4646d3dff5fSMatthew Dillon * wchan must be locked.
4656d3dff5fSMatthew Dillon */
4666d3dff5fSMatthew Dillon int
sleepq_type(const void * wchan)4676d3dff5fSMatthew Dillon sleepq_type(const void *wchan)
4686d3dff5fSMatthew Dillon {
4696d3dff5fSMatthew Dillon struct sleepqueue_wchan *wc;
4706d3dff5fSMatthew Dillon int type;
4716d3dff5fSMatthew Dillon
4726d3dff5fSMatthew Dillon wc = sleepq_wclookup(wchan);
4736d3dff5fSMatthew Dillon type = wc->wc_type;
4746d3dff5fSMatthew Dillon
4756d3dff5fSMatthew Dillon return type;
4766d3dff5fSMatthew Dillon }
4776d3dff5fSMatthew Dillon
4786d3dff5fSMatthew Dillon /*
4796d3dff5fSMatthew Dillon * Wakeup one thread sleeping on a wait channel.
4806d3dff5fSMatthew Dillon *
4816d3dff5fSMatthew Dillon * DragonFly: Presumably callers also deal with multiple wakeups,
4826d3dff5fSMatthew Dillon * our wakeup_domaoin_one() function is a bit non-deterministic.
4836d3dff5fSMatthew Dillon *
4846d3dff5fSMatthew Dillon * Nominally returns whether the swapper should be woken up which
4856d3dff5fSMatthew Dillon * is not applicable to DragonFlyBSD.
4866d3dff5fSMatthew Dillon */
4876d3dff5fSMatthew Dillon int
sleepq_signal(const void * wchan,int flags,int pri,int queue)4886d3dff5fSMatthew Dillon sleepq_signal(const void *wchan, int flags, int pri, int queue)
4896d3dff5fSMatthew Dillon {
4906d3dff5fSMatthew Dillon int domain;
4916d3dff5fSMatthew Dillon
4926d3dff5fSMatthew Dillon domain = PDOMAIN_FBSD0 + queue * PDOMAIN_FBSDINC;
4936d3dff5fSMatthew Dillon wakeup_domain_one(wchan, domain);
4946d3dff5fSMatthew Dillon
4956d3dff5fSMatthew Dillon return 0;
4966d3dff5fSMatthew Dillon }
4976d3dff5fSMatthew Dillon
4986d3dff5fSMatthew Dillon /*
4996d3dff5fSMatthew Dillon * Resume everything sleeping on the specified wait channel and queue.
5006d3dff5fSMatthew Dillon *
5016d3dff5fSMatthew Dillon * Nominally returns whether the swapper should be woken up which is not
5026d3dff5fSMatthew Dillon * applicable to DragonFlyBSD.
5036d3dff5fSMatthew Dillon */
5046d3dff5fSMatthew Dillon int
sleepq_broadcast(const void * wchan,int flags,int pri,int queue)5056d3dff5fSMatthew Dillon sleepq_broadcast(const void *wchan, int flags, int pri, int queue)
5066d3dff5fSMatthew Dillon {
5076d3dff5fSMatthew Dillon int domain;
5086d3dff5fSMatthew Dillon
5096d3dff5fSMatthew Dillon domain = PDOMAIN_FBSD0 + queue * PDOMAIN_FBSDINC;
5106d3dff5fSMatthew Dillon wakeup_domain(wchan, domain);
5116d3dff5fSMatthew Dillon
5126d3dff5fSMatthew Dillon return 0;
5136d3dff5fSMatthew Dillon }
514