xref: /freebsd/sys/kern/kern_sx.c (revision ae8dde30c26dc1596e19515cb9b222726ffca625)
19454b2d8SWarner Losh /*-
26281b30aSJason Evans  * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.  All rights reserved.
36281b30aSJason Evans  *
46281b30aSJason Evans  * Redistribution and use in source and binary forms, with or without
56281b30aSJason Evans  * modification, are permitted provided that the following conditions
66281b30aSJason Evans  * are met:
76281b30aSJason Evans  * 1. Redistributions of source code must retain the above copyright
86281b30aSJason Evans  *    notice(s), this list of conditions and the following disclaimer as
96281b30aSJason Evans  *    the first lines of this file unmodified other than the possible
106281b30aSJason Evans  *    addition of one or more copyright notices.
116281b30aSJason Evans  * 2. Redistributions in binary form must reproduce the above copyright
126281b30aSJason Evans  *    notice(s), this list of conditions and the following disclaimer in the
136281b30aSJason Evans  *    documentation and/or other materials provided with the distribution.
146281b30aSJason Evans  *
156281b30aSJason Evans  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
166281b30aSJason Evans  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
176281b30aSJason Evans  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
186281b30aSJason Evans  * DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
196281b30aSJason Evans  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
206281b30aSJason Evans  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
216281b30aSJason Evans  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
226281b30aSJason Evans  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
236281b30aSJason Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
246281b30aSJason Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
256281b30aSJason Evans  * DAMAGE.
266281b30aSJason Evans  */
276281b30aSJason Evans 
286281b30aSJason Evans /*
296281b30aSJason Evans  * Shared/exclusive locks.  This implementation assures deterministic lock
306281b30aSJason Evans  * granting behavior, so that slocks and xlocks are interleaved.
316281b30aSJason Evans  *
326281b30aSJason Evans  * Priority propagation will not generally raise the priority of lock holders,
336281b30aSJason Evans  * so should not be relied upon in combination with sx locks.
346281b30aSJason Evans  */
356281b30aSJason Evans 
36677b542eSDavid E. O'Brien #include <sys/cdefs.h>
37677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
38677b542eSDavid E. O'Brien 
39d272fe53SJohn Baldwin #include "opt_ddb.h"
40d272fe53SJohn Baldwin 
416281b30aSJason Evans #include <sys/param.h>
426281b30aSJason Evans #include <sys/systm.h>
436281b30aSJason Evans #include <sys/ktr.h>
44d272fe53SJohn Baldwin #include <sys/linker_set.h>
456281b30aSJason Evans #include <sys/condvar.h>
4619284646SJohn Baldwin #include <sys/lock.h>
476281b30aSJason Evans #include <sys/mutex.h>
48d272fe53SJohn Baldwin #include <sys/proc.h>
496281b30aSJason Evans #include <sys/sx.h>
507c0435b9SKip Macy #include <sys/lock_profile.h>
516281b30aSJason Evans 
52462a7addSJohn Baldwin #ifdef DDB
53d272fe53SJohn Baldwin #include <ddb/ddb.h>
54d272fe53SJohn Baldwin 
55d272fe53SJohn Baldwin static void	db_show_sx(struct lock_object *lock);
56d272fe53SJohn Baldwin #endif
57d272fe53SJohn Baldwin 
5819284646SJohn Baldwin struct lock_class lock_class_sx = {
59ae8dde30SJohn Baldwin 	.lc_name = "sx",
60ae8dde30SJohn Baldwin 	.lc_flags = LC_SLEEPLOCK | LC_SLEEPABLE | LC_RECURSABLE | LC_UPGRADABLE,
61d272fe53SJohn Baldwin #ifdef DDB
62ae8dde30SJohn Baldwin 	.lc_ddb_show = db_show_sx,
63d272fe53SJohn Baldwin #endif
6419284646SJohn Baldwin };
6519284646SJohn Baldwin 
66781a35dfSJohn Baldwin #ifndef INVARIANTS
67781a35dfSJohn Baldwin #define	_sx_assert(sx, what, file, line)
68781a35dfSJohn Baldwin #endif
69781a35dfSJohn Baldwin 
706281b30aSJason Evans void
71c27b5699SAndrew R. Reiter sx_sysinit(void *arg)
72c27b5699SAndrew R. Reiter {
73c27b5699SAndrew R. Reiter 	struct sx_args *sargs = arg;
74c27b5699SAndrew R. Reiter 
75c27b5699SAndrew R. Reiter 	sx_init(sargs->sa_sx, sargs->sa_desc);
76c27b5699SAndrew R. Reiter }
77c27b5699SAndrew R. Reiter 
78c27b5699SAndrew R. Reiter void
796281b30aSJason Evans sx_init(struct sx *sx, const char *description)
806281b30aSJason Evans {
816281b30aSJason Evans 
82857d9c60SDon Lewis 	sx->sx_lock = mtx_pool_find(mtxpool_lockbuilder, sx);
836281b30aSJason Evans 	sx->sx_cnt = 0;
846281b30aSJason Evans 	cv_init(&sx->sx_shrd_cv, description);
856281b30aSJason Evans 	sx->sx_shrd_wcnt = 0;
866281b30aSJason Evans 	cv_init(&sx->sx_excl_cv, description);
876281b30aSJason Evans 	sx->sx_excl_wcnt = 0;
88af761449SBosko Milekic 	sx->sx_xholder = NULL;
8961bd5e21SKip Macy 	lock_profile_object_init(&sx->sx_object, &lock_class_sx, description);
9083a81bcbSJohn Baldwin 	lock_init(&sx->sx_object, &lock_class_sx, description, NULL,
9183a81bcbSJohn Baldwin 	    LO_WITNESS | LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE);
926281b30aSJason Evans }
936281b30aSJason Evans 
946281b30aSJason Evans void
956281b30aSJason Evans sx_destroy(struct sx *sx)
966281b30aSJason Evans {
976281b30aSJason Evans 
986281b30aSJason Evans 	KASSERT((sx->sx_cnt == 0 && sx->sx_shrd_wcnt == 0 && sx->sx_excl_wcnt ==
99a48740b6SDavid E. O'Brien 	    0), ("%s (%s): holders or waiters\n", __func__,
10019284646SJohn Baldwin 	    sx->sx_object.lo_name));
1016281b30aSJason Evans 
102f2860039SMatthew Dillon 	sx->sx_lock = NULL;
1036281b30aSJason Evans 	cv_destroy(&sx->sx_shrd_cv);
1046281b30aSJason Evans 	cv_destroy(&sx->sx_excl_cv);
10519284646SJohn Baldwin 
1067c0435b9SKip Macy 	lock_profile_object_destroy(&sx->sx_object);
10783a81bcbSJohn Baldwin 	lock_destroy(&sx->sx_object);
1086281b30aSJason Evans }
1096281b30aSJason Evans 
1106281b30aSJason Evans void
11119284646SJohn Baldwin _sx_slock(struct sx *sx, const char *file, int line)
1126281b30aSJason Evans {
1137c0435b9SKip Macy 	uint64_t waittime = 0;
114fe68a916SKip Macy 	int contested = 0;
1156281b30aSJason Evans 
116f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
117b40ce416SJulian Elischer 	KASSERT(sx->sx_xholder != curthread,
118a48740b6SDavid E. O'Brien 	    ("%s (%s): slock while xlock is held @ %s:%d\n", __func__,
1195f36700aSJohn Baldwin 	    sx->sx_object.lo_name, file, line));
1208d768e76SJohn Baldwin 	WITNESS_CHECKORDER(&sx->sx_object, LOP_NEWORDER, file, line);
1216281b30aSJason Evans 
1226281b30aSJason Evans 	/*
1236281b30aSJason Evans 	 * Loop in case we lose the race for lock acquisition.
1246281b30aSJason Evans 	 */
1256281b30aSJason Evans 	while (sx->sx_cnt < 0) {
1266281b30aSJason Evans 		sx->sx_shrd_wcnt++;
127fe68a916SKip Macy 		lock_profile_obtain_lock_failed(&sx->sx_object, &contested, &waittime);
128f2860039SMatthew Dillon 		cv_wait(&sx->sx_shrd_cv, sx->sx_lock);
1296281b30aSJason Evans 		sx->sx_shrd_wcnt--;
1306281b30aSJason Evans 	}
1316281b30aSJason Evans 
1326281b30aSJason Evans 	/* Acquire a shared lock. */
1336281b30aSJason Evans 	sx->sx_cnt++;
1346281b30aSJason Evans 
1357c0435b9SKip Macy         if (sx->sx_cnt == 1)
136fe68a916SKip Macy 		lock_profile_obtain_lock_success(&sx->sx_object, contested, waittime, file, line);
1377c0435b9SKip Macy 
13819284646SJohn Baldwin 	LOCK_LOG_LOCK("SLOCK", &sx->sx_object, 0, 0, file, line);
13919284646SJohn Baldwin 	WITNESS_LOCK(&sx->sx_object, 0, file, line);
140764e4d54SJohn Baldwin 	curthread->td_locks++;
14119284646SJohn Baldwin 
142f2860039SMatthew Dillon 	mtx_unlock(sx->sx_lock);
1436281b30aSJason Evans }
1446281b30aSJason Evans 
1455f36700aSJohn Baldwin int
1465f36700aSJohn Baldwin _sx_try_slock(struct sx *sx, const char *file, int line)
1475f36700aSJohn Baldwin {
1485f36700aSJohn Baldwin 
149f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
1505f36700aSJohn Baldwin 	if (sx->sx_cnt >= 0) {
1515f36700aSJohn Baldwin 		sx->sx_cnt++;
1525f36700aSJohn Baldwin 		LOCK_LOG_TRY("SLOCK", &sx->sx_object, 0, 1, file, line);
1535f36700aSJohn Baldwin 		WITNESS_LOCK(&sx->sx_object, LOP_TRYLOCK, file, line);
154764e4d54SJohn Baldwin 		curthread->td_locks++;
155f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
1565f36700aSJohn Baldwin 		return (1);
1575f36700aSJohn Baldwin 	} else {
1585f36700aSJohn Baldwin 		LOCK_LOG_TRY("SLOCK", &sx->sx_object, 0, 0, file, line);
159f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
1605f36700aSJohn Baldwin 		return (0);
1615f36700aSJohn Baldwin 	}
1625f36700aSJohn Baldwin }
1635f36700aSJohn Baldwin 
1646281b30aSJason Evans void
16519284646SJohn Baldwin _sx_xlock(struct sx *sx, const char *file, int line)
1666281b30aSJason Evans {
167fe68a916SKip Macy 	int contested = 0;
1687c0435b9SKip Macy 	uint64_t waittime = 0;
1696281b30aSJason Evans 
170f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
1716281b30aSJason Evans 
172af761449SBosko Milekic 	/*
173af761449SBosko Milekic 	 * With sx locks, we're absolutely not permitted to recurse on
174af761449SBosko Milekic 	 * xlocks, as it is fatal (deadlock). Normally, recursion is handled
175af761449SBosko Milekic 	 * by WITNESS, but as it is not semantically correct to hold the
176af761449SBosko Milekic 	 * xlock while in here, we consider it API abuse and put it under
177af761449SBosko Milekic 	 * INVARIANTS.
178af761449SBosko Milekic 	 */
179b40ce416SJulian Elischer 	KASSERT(sx->sx_xholder != curthread,
180a48740b6SDavid E. O'Brien 	    ("%s (%s): xlock already held @ %s:%d", __func__,
18119284646SJohn Baldwin 	    sx->sx_object.lo_name, file, line));
1828d768e76SJohn Baldwin 	WITNESS_CHECKORDER(&sx->sx_object, LOP_NEWORDER | LOP_EXCLUSIVE, file,
1838d768e76SJohn Baldwin 	    line);
184af761449SBosko Milekic 
1856281b30aSJason Evans 	/* Loop in case we lose the race for lock acquisition. */
1866281b30aSJason Evans 	while (sx->sx_cnt != 0) {
1876281b30aSJason Evans 		sx->sx_excl_wcnt++;
188fe68a916SKip Macy 		lock_profile_obtain_lock_failed(&sx->sx_object, &contested, &waittime);
189f2860039SMatthew Dillon 		cv_wait(&sx->sx_excl_cv, sx->sx_lock);
1906281b30aSJason Evans 		sx->sx_excl_wcnt--;
1916281b30aSJason Evans 	}
1926281b30aSJason Evans 
193af761449SBosko Milekic 	MPASS(sx->sx_cnt == 0);
194af761449SBosko Milekic 
1956281b30aSJason Evans 	/* Acquire an exclusive lock. */
1966281b30aSJason Evans 	sx->sx_cnt--;
197b40ce416SJulian Elischer 	sx->sx_xholder = curthread;
1986281b30aSJason Evans 
199fe68a916SKip Macy 	lock_profile_obtain_lock_success(&sx->sx_object, contested, waittime, file, line);
20019284646SJohn Baldwin 	LOCK_LOG_LOCK("XLOCK", &sx->sx_object, 0, 0, file, line);
2012d96f0b1SJohn Baldwin 	WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line);
202764e4d54SJohn Baldwin 	curthread->td_locks++;
20319284646SJohn Baldwin 
204f2860039SMatthew Dillon 	mtx_unlock(sx->sx_lock);
2056281b30aSJason Evans }
2066281b30aSJason Evans 
2075f36700aSJohn Baldwin int
2085f36700aSJohn Baldwin _sx_try_xlock(struct sx *sx, const char *file, int line)
2095f36700aSJohn Baldwin {
2105f36700aSJohn Baldwin 
211f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
2125f36700aSJohn Baldwin 	if (sx->sx_cnt == 0) {
2135f36700aSJohn Baldwin 		sx->sx_cnt--;
214b40ce416SJulian Elischer 		sx->sx_xholder = curthread;
2155f36700aSJohn Baldwin 		LOCK_LOG_TRY("XLOCK", &sx->sx_object, 0, 1, file, line);
2165f36700aSJohn Baldwin 		WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE | LOP_TRYLOCK, file,
2175f36700aSJohn Baldwin 		    line);
218764e4d54SJohn Baldwin 		curthread->td_locks++;
219f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
2205f36700aSJohn Baldwin 		return (1);
2215f36700aSJohn Baldwin 	} else {
2225f36700aSJohn Baldwin 		LOCK_LOG_TRY("XLOCK", &sx->sx_object, 0, 0, file, line);
223f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
2245f36700aSJohn Baldwin 		return (0);
2255f36700aSJohn Baldwin 	}
2265f36700aSJohn Baldwin }
2275f36700aSJohn Baldwin 
2286281b30aSJason Evans void
22919284646SJohn Baldwin _sx_sunlock(struct sx *sx, const char *file, int line)
2306281b30aSJason Evans {
2314e5e677bSJohn Baldwin 	_sx_assert(sx, SX_SLOCKED, file, line);
232f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
2336281b30aSJason Evans 
234764e4d54SJohn Baldwin 	curthread->td_locks--;
23519284646SJohn Baldwin 	WITNESS_UNLOCK(&sx->sx_object, 0, file, line);
23619284646SJohn Baldwin 
2376281b30aSJason Evans 	/* Release. */
2386281b30aSJason Evans 	sx->sx_cnt--;
2396281b30aSJason Evans 
240f183910bSKip Macy 	if (sx->sx_cnt == 0) {
241c66d7606SKip Macy 		lock_profile_release_lock(&sx->sx_object);
242f183910bSKip Macy 	}
243c66d7606SKip Macy 
2446281b30aSJason Evans 	/*
2456281b30aSJason Evans 	 * If we just released the last shared lock, wake any waiters up, giving
2466281b30aSJason Evans 	 * exclusive lockers precedence.  In order to make sure that exclusive
2476281b30aSJason Evans 	 * lockers won't be blocked forever, don't wake shared lock waiters if
2486281b30aSJason Evans 	 * there are exclusive lock waiters.
2496281b30aSJason Evans 	 */
2506281b30aSJason Evans 	if (sx->sx_excl_wcnt > 0) {
2516281b30aSJason Evans 		if (sx->sx_cnt == 0)
2526281b30aSJason Evans 			cv_signal(&sx->sx_excl_cv);
2536281b30aSJason Evans 	} else if (sx->sx_shrd_wcnt > 0)
2546281b30aSJason Evans 		cv_broadcast(&sx->sx_shrd_cv);
2556281b30aSJason Evans 
25619284646SJohn Baldwin 	LOCK_LOG_LOCK("SUNLOCK", &sx->sx_object, 0, 0, file, line);
25719284646SJohn Baldwin 
258f2860039SMatthew Dillon 	mtx_unlock(sx->sx_lock);
2596281b30aSJason Evans }
2606281b30aSJason Evans 
2616281b30aSJason Evans void
26219284646SJohn Baldwin _sx_xunlock(struct sx *sx, const char *file, int line)
2636281b30aSJason Evans {
2644e5e677bSJohn Baldwin 	_sx_assert(sx, SX_XLOCKED, file, line);
265f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
266af761449SBosko Milekic 	MPASS(sx->sx_cnt == -1);
2676281b30aSJason Evans 
268764e4d54SJohn Baldwin 	curthread->td_locks--;
2692d96f0b1SJohn Baldwin 	WITNESS_UNLOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line);
27019284646SJohn Baldwin 
2716281b30aSJason Evans 	/* Release. */
2726281b30aSJason Evans 	sx->sx_cnt++;
273af761449SBosko Milekic 	sx->sx_xholder = NULL;
2746281b30aSJason Evans 
2756281b30aSJason Evans 	/*
2766281b30aSJason Evans 	 * Wake up waiters if there are any.  Give precedence to slock waiters.
2776281b30aSJason Evans 	 */
2786281b30aSJason Evans 	if (sx->sx_shrd_wcnt > 0)
2796281b30aSJason Evans 		cv_broadcast(&sx->sx_shrd_cv);
2806281b30aSJason Evans 	else if (sx->sx_excl_wcnt > 0)
2816281b30aSJason Evans 		cv_signal(&sx->sx_excl_cv);
2826281b30aSJason Evans 
28319284646SJohn Baldwin 	LOCK_LOG_LOCK("XUNLOCK", &sx->sx_object, 0, 0, file, line);
28419284646SJohn Baldwin 
285c66d7606SKip Macy 	lock_profile_release_lock(&sx->sx_object);
286f2860039SMatthew Dillon 	mtx_unlock(sx->sx_lock);
2876281b30aSJason Evans }
288d55229b7SJason Evans 
289d55229b7SJason Evans int
290d55229b7SJason Evans _sx_try_upgrade(struct sx *sx, const char *file, int line)
291d55229b7SJason Evans {
292d55229b7SJason Evans 
2934e5e677bSJohn Baldwin 	_sx_assert(sx, SX_SLOCKED, file, line);
294f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
295d55229b7SJason Evans 
296d55229b7SJason Evans 	if (sx->sx_cnt == 1) {
297d55229b7SJason Evans 		sx->sx_cnt = -1;
298b40ce416SJulian Elischer 		sx->sx_xholder = curthread;
299d55229b7SJason Evans 
300d55229b7SJason Evans 		LOCK_LOG_TRY("XUPGRADE", &sx->sx_object, 0, 1, file, line);
301b0b7cb50SJohn Baldwin 		WITNESS_UPGRADE(&sx->sx_object, LOP_EXCLUSIVE | LOP_TRYLOCK,
302b0b7cb50SJohn Baldwin 		    file, line);
303d55229b7SJason Evans 
304f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
305d55229b7SJason Evans 		return (1);
306d55229b7SJason Evans 	} else {
307d55229b7SJason Evans 		LOCK_LOG_TRY("XUPGRADE", &sx->sx_object, 0, 0, file, line);
308f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
309d55229b7SJason Evans 		return (0);
310d55229b7SJason Evans 	}
311d55229b7SJason Evans }
312d55229b7SJason Evans 
313d55229b7SJason Evans void
314d55229b7SJason Evans _sx_downgrade(struct sx *sx, const char *file, int line)
315d55229b7SJason Evans {
316d55229b7SJason Evans 
3174e5e677bSJohn Baldwin 	_sx_assert(sx, SX_XLOCKED, file, line);
318f2860039SMatthew Dillon 	mtx_lock(sx->sx_lock);
319d55229b7SJason Evans 	MPASS(sx->sx_cnt == -1);
320d55229b7SJason Evans 
321b0b7cb50SJohn Baldwin 	WITNESS_DOWNGRADE(&sx->sx_object, 0, file, line);
322d55229b7SJason Evans 
323d55229b7SJason Evans 	sx->sx_cnt = 1;
324e2870579SJohn Baldwin 	sx->sx_xholder = NULL;
325d55229b7SJason Evans         if (sx->sx_shrd_wcnt > 0)
326d55229b7SJason Evans                 cv_broadcast(&sx->sx_shrd_cv);
327d55229b7SJason Evans 
328d55229b7SJason Evans 	LOCK_LOG_LOCK("XDOWNGRADE", &sx->sx_object, 0, 0, file, line);
329d55229b7SJason Evans 
330f2860039SMatthew Dillon 	mtx_unlock(sx->sx_lock);
331d55229b7SJason Evans }
3324e5e677bSJohn Baldwin 
3334e5e677bSJohn Baldwin #ifdef INVARIANT_SUPPORT
334781a35dfSJohn Baldwin #ifndef INVARIANTS
335781a35dfSJohn Baldwin #undef	_sx_assert
336781a35dfSJohn Baldwin #endif
337781a35dfSJohn Baldwin 
3384e5e677bSJohn Baldwin /*
3394e5e677bSJohn Baldwin  * In the non-WITNESS case, sx_assert() can only detect that at least
3404e5e677bSJohn Baldwin  * *some* thread owns an slock, but it cannot guarantee that *this*
3414e5e677bSJohn Baldwin  * thread owns an slock.
3424e5e677bSJohn Baldwin  */
3434e5e677bSJohn Baldwin void
3444e5e677bSJohn Baldwin _sx_assert(struct sx *sx, int what, const char *file, int line)
3454e5e677bSJohn Baldwin {
3464e5e677bSJohn Baldwin 
34703129ba9SJohn Baldwin 	if (panicstr != NULL)
34803129ba9SJohn Baldwin 		return;
3494e5e677bSJohn Baldwin 	switch (what) {
3504e5e677bSJohn Baldwin 	case SX_LOCKED:
3514e5e677bSJohn Baldwin 	case SX_SLOCKED:
3524e5e677bSJohn Baldwin #ifdef WITNESS
3534e5e677bSJohn Baldwin 		witness_assert(&sx->sx_object, what, file, line);
3544e5e677bSJohn Baldwin #else
355f2860039SMatthew Dillon 		mtx_lock(sx->sx_lock);
3564e5e677bSJohn Baldwin 		if (sx->sx_cnt <= 0 &&
35798bf25aaSSeigo Tanimura 		    (what == SX_SLOCKED || sx->sx_xholder != curthread))
35803129ba9SJohn Baldwin 			panic("Lock %s not %slocked @ %s:%d\n",
3594e5e677bSJohn Baldwin 			    sx->sx_object.lo_name, (what == SX_SLOCKED) ?
3604e5e677bSJohn Baldwin 			    "share " : "", file, line);
361f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
3624e5e677bSJohn Baldwin #endif
3634e5e677bSJohn Baldwin 		break;
3644e5e677bSJohn Baldwin 	case SX_XLOCKED:
365f2860039SMatthew Dillon 		mtx_lock(sx->sx_lock);
3664e5e677bSJohn Baldwin 		if (sx->sx_xholder != curthread)
36703129ba9SJohn Baldwin 			panic("Lock %s not exclusively locked @ %s:%d\n",
3684e5e677bSJohn Baldwin 			    sx->sx_object.lo_name, file, line);
369f2860039SMatthew Dillon 		mtx_unlock(sx->sx_lock);
3704e5e677bSJohn Baldwin 		break;
37119b0efd3SPawel Jakub Dawidek 	case SX_UNLOCKED:
37219b0efd3SPawel Jakub Dawidek #ifdef WITNESS
37319b0efd3SPawel Jakub Dawidek 		witness_assert(&sx->sx_object, what, file, line);
37419b0efd3SPawel Jakub Dawidek #else
375f6739b1dSPawel Jakub Dawidek 		/*
376f6739b1dSPawel Jakub Dawidek 		 * We are able to check only exclusive lock here,
377f6739b1dSPawel Jakub Dawidek 		 * we cannot assert that *this* thread owns slock.
378f6739b1dSPawel Jakub Dawidek 		 */
37919b0efd3SPawel Jakub Dawidek 		mtx_lock(sx->sx_lock);
380f6739b1dSPawel Jakub Dawidek 		if (sx->sx_xholder == curthread)
38103129ba9SJohn Baldwin 			panic("Lock %s exclusively locked @ %s:%d\n",
38219b0efd3SPawel Jakub Dawidek 			    sx->sx_object.lo_name, file, line);
38319b0efd3SPawel Jakub Dawidek 		mtx_unlock(sx->sx_lock);
38419b0efd3SPawel Jakub Dawidek #endif
38519b0efd3SPawel Jakub Dawidek 		break;
3864e5e677bSJohn Baldwin 	default:
3874e5e677bSJohn Baldwin 		panic("Unknown sx lock assertion: %d @ %s:%d", what, file,
3884e5e677bSJohn Baldwin 		    line);
3894e5e677bSJohn Baldwin 	}
3904e5e677bSJohn Baldwin }
3914e5e677bSJohn Baldwin #endif	/* INVARIANT_SUPPORT */
392d272fe53SJohn Baldwin 
393d272fe53SJohn Baldwin #ifdef DDB
394d272fe53SJohn Baldwin void
395d272fe53SJohn Baldwin db_show_sx(struct lock_object *lock)
396d272fe53SJohn Baldwin {
397d272fe53SJohn Baldwin 	struct thread *td;
398d272fe53SJohn Baldwin 	struct sx *sx;
399d272fe53SJohn Baldwin 
400d272fe53SJohn Baldwin 	sx = (struct sx *)lock;
401d272fe53SJohn Baldwin 
402d272fe53SJohn Baldwin 	db_printf(" state: ");
403d272fe53SJohn Baldwin 	if (sx->sx_cnt < 0) {
404d272fe53SJohn Baldwin 		td = sx->sx_xholder;
405d272fe53SJohn Baldwin 		db_printf("XLOCK: %p (tid %d, pid %d, \"%s\")\n", td,
406d272fe53SJohn Baldwin 		    td->td_tid, td->td_proc->p_pid, td->td_proc->p_comm);
407d272fe53SJohn Baldwin 	} else if (sx->sx_cnt > 0)
408d272fe53SJohn Baldwin 		db_printf("SLOCK: %d locks\n", sx->sx_cnt);
409d272fe53SJohn Baldwin 	else
410d272fe53SJohn Baldwin 		db_printf("UNLOCKED\n");
411d272fe53SJohn Baldwin 	db_printf(" waiters: %d shared, %d exclusive\n", sx->sx_shrd_wcnt,
412d272fe53SJohn Baldwin 	    sx->sx_excl_wcnt);
413d272fe53SJohn Baldwin }
414462a7addSJohn Baldwin 
415462a7addSJohn Baldwin /*
416462a7addSJohn Baldwin  * Check to see if a thread that is blocked on a sleep queue is actually
417462a7addSJohn Baldwin  * blocked on an sx lock.  If so, output some details and return true.
418462a7addSJohn Baldwin  * If the lock has an exclusive owner, return that in *ownerp.
419462a7addSJohn Baldwin  */
420462a7addSJohn Baldwin int
421462a7addSJohn Baldwin sx_chain(struct thread *td, struct thread **ownerp)
422462a7addSJohn Baldwin {
423462a7addSJohn Baldwin 	struct sx *sx;
424462a7addSJohn Baldwin 	struct cv *cv;
425462a7addSJohn Baldwin 
426462a7addSJohn Baldwin 	/*
427462a7addSJohn Baldwin 	 * First, see if it looks like td is blocked on a condition
428462a7addSJohn Baldwin 	 * variable.
429462a7addSJohn Baldwin 	 */
430462a7addSJohn Baldwin 	cv = td->td_wchan;
431462a7addSJohn Baldwin 	if (cv->cv_description != td->td_wmesg)
432462a7addSJohn Baldwin 		return (0);
433462a7addSJohn Baldwin 
434462a7addSJohn Baldwin 	/*
435462a7addSJohn Baldwin 	 * Ok, see if it looks like td is blocked on the exclusive
436462a7addSJohn Baldwin 	 * condition variable.
437462a7addSJohn Baldwin 	 */
438462a7addSJohn Baldwin 	sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_excl_cv));
439462a7addSJohn Baldwin 	if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx &&
440462a7addSJohn Baldwin 	    sx->sx_excl_wcnt > 0)
441462a7addSJohn Baldwin 		goto ok;
442462a7addSJohn Baldwin 
443462a7addSJohn Baldwin 	/*
444462a7addSJohn Baldwin 	 * Second, see if it looks like td is blocked on the shared
445462a7addSJohn Baldwin 	 * condition variable.
446462a7addSJohn Baldwin 	 */
447462a7addSJohn Baldwin 	sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_shrd_cv));
448462a7addSJohn Baldwin 	if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx &&
449462a7addSJohn Baldwin 	    sx->sx_shrd_wcnt > 0)
450462a7addSJohn Baldwin 		goto ok;
451462a7addSJohn Baldwin 
452462a7addSJohn Baldwin 	/* Doesn't seem to be an sx lock. */
453462a7addSJohn Baldwin 	return (0);
454462a7addSJohn Baldwin 
455462a7addSJohn Baldwin ok:
456462a7addSJohn Baldwin 	/* We think we have an sx lock, so output some details. */
457462a7addSJohn Baldwin 	db_printf("blocked on sx \"%s\" ", td->td_wmesg);
458462a7addSJohn Baldwin 	if (sx->sx_cnt >= 0) {
459462a7addSJohn Baldwin 		db_printf("SLOCK (count %d)\n", sx->sx_cnt);
460462a7addSJohn Baldwin 		*ownerp = NULL;
461462a7addSJohn Baldwin 	} else {
462462a7addSJohn Baldwin 		db_printf("XLOCK\n");
463462a7addSJohn Baldwin 		*ownerp = sx->sx_xholder;
464462a7addSJohn Baldwin 	}
465462a7addSJohn Baldwin 	return (1);
466462a7addSJohn Baldwin }
467d272fe53SJohn Baldwin #endif
468