xref: /dflybsd-src/sys/kern/subr_sleepqueue.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
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