1139825Simp/*-
2168191Sjhb * Copyright (c) 2007 Attilio Rao <attilio@freebsd.org>
3168191Sjhb * Copyright (c) 2001 Jason Evans <jasone@freebsd.org>
4168191Sjhb * All rights reserved.
573782Sjasone *
673782Sjasone * Redistribution and use in source and binary forms, with or without
773782Sjasone * modification, are permitted provided that the following conditions
873782Sjasone * are met:
973782Sjasone * 1. Redistributions of source code must retain the above copyright
1073782Sjasone *    notice(s), this list of conditions and the following disclaimer as
1173782Sjasone *    the first lines of this file unmodified other than the possible
1273782Sjasone *    addition of one or more copyright notices.
1373782Sjasone * 2. Redistributions in binary form must reproduce the above copyright
1473782Sjasone *    notice(s), this list of conditions and the following disclaimer in the
1573782Sjasone *    documentation and/or other materials provided with the distribution.
1673782Sjasone *
1773782Sjasone * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
1873782Sjasone * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1973782Sjasone * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2073782Sjasone * DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
2173782Sjasone * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2273782Sjasone * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2373782Sjasone * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
2473782Sjasone * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2573782Sjasone * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2673782Sjasone * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2773782Sjasone * DAMAGE.
2873782Sjasone *
2973782Sjasone * $FreeBSD: stable/10/sys/sys/sx.h 323870 2017-09-21 19:24:11Z marius $
3073782Sjasone */
3173782Sjasone
3273782Sjasone#ifndef	_SYS_SX_H_
3373782Sjasone#define	_SYS_SX_H_
3473782Sjasone
3583103Sjhb#include <sys/_lock.h>
36168191Sjhb#include <sys/_sx.h>
3773782Sjasone
38168191Sjhb#ifdef	_KERNEL
39181682Sed#include <sys/pcpu.h>
40181682Sed#include <sys/lock_profile.h>
41192853Ssson#include <sys/lockstat.h>
42168191Sjhb#include <machine/atomic.h>
43168191Sjhb#endif
4473782Sjasone
45168191Sjhb/*
46168191Sjhb * In general, the sx locks and rwlocks use very similar algorithms.
47168191Sjhb * The main difference in the implementations is how threads are
48168191Sjhb * blocked when a lock is unavailable.  For this, sx locks use sleep
49168191Sjhb * queues which do not support priority propagation, and rwlocks use
50168191Sjhb * turnstiles which do.
51168191Sjhb *
52168191Sjhb * The sx_lock field consists of several fields.  The low bit
53168191Sjhb * indicates if the lock is locked with a shared or exclusive lock.  A
54168191Sjhb * value of 0 indicates an exclusive lock, and a value of 1 indicates
55168191Sjhb * a shared lock.  Bit 1 is a boolean indicating if there are any
56168191Sjhb * threads waiting for a shared lock.  Bit 2 is a boolean indicating
57168191Sjhb * if there are any threads waiting for an exclusive lock.  Bit 3 is a
58168191Sjhb * boolean indicating if an exclusive lock is recursively held.  The
59168191Sjhb * rest of the variable's definition is dependent on the value of the
60168191Sjhb * first bit.  For an exclusive lock, it is a pointer to the thread
61168191Sjhb * holding the lock, similar to the mtx_lock field of mutexes.  For
62168191Sjhb * shared locks, it is a count of read locks that are held.
63168191Sjhb *
64168191Sjhb * When the lock is not locked by any thread, it is encoded as a
65168191Sjhb * shared lock with zero waiters.
66168191Sjhb */
67168191Sjhb
68168191Sjhb#define	SX_LOCK_SHARED			0x01
69168191Sjhb#define	SX_LOCK_SHARED_WAITERS		0x02
70168191Sjhb#define	SX_LOCK_EXCLUSIVE_WAITERS	0x04
71168191Sjhb#define	SX_LOCK_RECURSED		0x08
72168191Sjhb#define	SX_LOCK_FLAGMASK						\
73168191Sjhb	(SX_LOCK_SHARED | SX_LOCK_SHARED_WAITERS |			\
74168191Sjhb	SX_LOCK_EXCLUSIVE_WAITERS | SX_LOCK_RECURSED)
75168191Sjhb
76168191Sjhb#define	SX_OWNER(x)			((x) & ~SX_LOCK_FLAGMASK)
77168191Sjhb#define	SX_SHARERS_SHIFT		4
78168191Sjhb#define	SX_SHARERS(x)			(SX_OWNER(x) >> SX_SHARERS_SHIFT)
79168191Sjhb#define	SX_SHARERS_LOCK(x)						\
80168191Sjhb	((x) << SX_SHARERS_SHIFT | SX_LOCK_SHARED)
81168191Sjhb#define	SX_ONE_SHARER			(1 << SX_SHARERS_SHIFT)
82168191Sjhb
83168191Sjhb#define	SX_LOCK_UNLOCKED		SX_SHARERS_LOCK(0)
84169394Sjhb#define	SX_LOCK_DESTROYED						\
85169394Sjhb	(SX_LOCK_SHARED_WAITERS | SX_LOCK_EXCLUSIVE_WAITERS)
86168191Sjhb
8773782Sjasone#ifdef _KERNEL
88168191Sjhb
89168191Sjhb/*
90168191Sjhb * Function prototipes.  Routines that start with an underscore are not part
91168191Sjhb * of the public interface and are wrappered with a macro.
92168191Sjhb */
9393672Sarrvoid	sx_sysinit(void *arg);
94168191Sjhb#define	sx_init(sx, desc)	sx_init_flags((sx), (desc), 0)
95168191Sjhbvoid	sx_init_flags(struct sx *sx, const char *description, int opts);
9673782Sjasonevoid	sx_destroy(struct sx *sx);
97227788Sattilioint	sx_try_slock_(struct sx *sx, const char *file, int line);
98227788Sattilioint	sx_try_xlock_(struct sx *sx, const char *file, int line);
99227788Sattilioint	sx_try_upgrade_(struct sx *sx, const char *file, int line);
100227788Sattiliovoid	sx_downgrade_(struct sx *sx, const char *file, int line);
101170149Sattilioint	_sx_slock(struct sx *sx, int opts, const char *file, int line);
102170149Sattilioint	_sx_xlock(struct sx *sx, int opts, const char *file, int line);
10374912Sjhbvoid	_sx_sunlock(struct sx *sx, const char *file, int line);
10474912Sjhbvoid	_sx_xunlock(struct sx *sx, const char *file, int line);
105170149Sattilioint	_sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts,
106170149Sattilio	    const char *file, int line);
107170149Sattilioint	_sx_slock_hard(struct sx *sx, int opts, const char *file, int line);
108168191Sjhbvoid	_sx_xunlock_hard(struct sx *sx, uintptr_t tid, const char *file, int
109168191Sjhb	    line);
110168191Sjhbvoid	_sx_sunlock_hard(struct sx *sx, const char *file, int line);
111161721Sjhb#if defined(INVARIANTS) || defined(INVARIANT_SUPPORT)
112227588Spjdvoid	_sx_assert(const struct sx *sx, int what, const char *file, int line);
11393688Sarr#endif
114161337Sjhb#ifdef DDB
115161337Sjhbint	sx_chain(struct thread *td, struct thread **ownerp);
116161337Sjhb#endif
11793672Sarr
11893672Sarrstruct sx_args {
11993672Sarr	struct sx 	*sa_sx;
12093672Sarr	const char	*sa_desc;
121219819Sjeff	int		sa_flags;
12293672Sarr};
12393672Sarr
124219819Sjeff#define	SX_SYSINIT_FLAGS(name, sxa, desc, flags)			\
12593672Sarr	static struct sx_args name##_args = {				\
126149739Sjhb		(sxa),							\
127219819Sjeff		(desc),							\
128219819Sjeff		(flags)							\
12993672Sarr	};								\
13093672Sarr	SYSINIT(name##_sx_sysinit, SI_SUB_LOCK, SI_ORDER_MIDDLE,	\
131149739Sjhb	    sx_sysinit, &name##_args);					\
132149739Sjhb	SYSUNINIT(name##_sx_sysuninit, SI_SUB_LOCK, SI_ORDER_MIDDLE,	\
133149739Sjhb	    sx_destroy, (sxa))
13473782Sjasone
135219819Sjeff#define	SX_SYSINIT(name, sxa, desc)	SX_SYSINIT_FLAGS(name, sxa, desc, 0)
136219819Sjeff
137168191Sjhb/*
138170149Sattilio * Full lock operations that are suitable to be inlined in non-debug kernels.
139170149Sattilio * If the lock can't be acquired or released trivially then the work is
140170149Sattilio * deferred to 'tougher' functions.
141170149Sattilio */
142170149Sattilio
143170149Sattilio/* Acquire an exclusive lock. */
144170149Sattiliostatic __inline int
145170149Sattilio__sx_xlock(struct sx *sx, struct thread *td, int opts, const char *file,
146170149Sattilio    int line)
147170149Sattilio{
148170149Sattilio	uintptr_t tid = (uintptr_t)td;
149170149Sattilio	int error = 0;
150170149Sattilio
151310979Smjg	if (sx->sx_lock != SX_LOCK_UNLOCKED ||
152310979Smjg	    !atomic_cmpset_acq_ptr(&sx->sx_lock, SX_LOCK_UNLOCKED, tid))
153170149Sattilio		error = _sx_xlock_hard(sx, tid, opts, file, line);
154192853Ssson	else
155192853Ssson		LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(LS_SX_XLOCK_ACQUIRE,
156192853Ssson		    sx, 0, 0, file, line);
157170149Sattilio
158170149Sattilio	return (error);
159170149Sattilio}
160170149Sattilio
161170149Sattilio/* Release an exclusive lock. */
162170149Sattiliostatic __inline void
163170149Sattilio__sx_xunlock(struct sx *sx, struct thread *td, const char *file, int line)
164170149Sattilio{
165170149Sattilio	uintptr_t tid = (uintptr_t)td;
166170149Sattilio
167310979Smjg	if (sx->sx_lock != tid ||
168310979Smjg	    !atomic_cmpset_rel_ptr(&sx->sx_lock, tid, SX_LOCK_UNLOCKED))
169170149Sattilio		_sx_xunlock_hard(sx, tid, file, line);
170170149Sattilio}
171170149Sattilio
172170149Sattilio/* Acquire a shared lock. */
173170149Sattiliostatic __inline int
174170149Sattilio__sx_slock(struct sx *sx, int opts, const char *file, int line)
175170149Sattilio{
176170149Sattilio	uintptr_t x = sx->sx_lock;
177170149Sattilio	int error = 0;
178170149Sattilio
179170149Sattilio	if (!(x & SX_LOCK_SHARED) ||
180170149Sattilio	    !atomic_cmpset_acq_ptr(&sx->sx_lock, x, x + SX_ONE_SHARER))
181170149Sattilio		error = _sx_slock_hard(sx, opts, file, line);
182174629Sjeff	else
183192853Ssson		LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(LS_SX_SLOCK_ACQUIRE, sx, 0,
184192853Ssson		    0, file, line);
185170149Sattilio
186170149Sattilio	return (error);
187170149Sattilio}
188170149Sattilio
189170149Sattilio/*
190170149Sattilio * Release a shared lock.  We can just drop a single shared lock so
191170149Sattilio * long as we aren't trying to drop the last shared lock when other
192170149Sattilio * threads are waiting for an exclusive lock.  This takes advantage of
193170149Sattilio * the fact that an unlocked lock is encoded as a shared lock with a
194170149Sattilio * count of 0.
195170149Sattilio */
196170149Sattiliostatic __inline void
197170149Sattilio__sx_sunlock(struct sx *sx, const char *file, int line)
198170149Sattilio{
199170149Sattilio	uintptr_t x = sx->sx_lock;
200170149Sattilio
201170149Sattilio	if (x == (SX_SHARERS_LOCK(1) | SX_LOCK_EXCLUSIVE_WAITERS) ||
202197643Sattilio	    !atomic_cmpset_rel_ptr(&sx->sx_lock, x, x - SX_ONE_SHARER))
203170149Sattilio		_sx_sunlock_hard(sx, file, line);
204170149Sattilio}
205170149Sattilio
206170149Sattilio/*
207168191Sjhb * Public interface for lock operations.
208168191Sjhb */
209168191Sjhb#ifndef LOCK_DEBUG
210168191Sjhb#error	"LOCK_DEBUG not defined, include <sys/lock.h> before <sys/sx.h>"
211168191Sjhb#endif
212168191Sjhb#if	(LOCK_DEBUG > 0) || defined(SX_NOINLINE)
213227788Sattilio#define	sx_xlock_(sx, file, line)					\
214227788Sattilio	(void)_sx_xlock((sx), 0, (file), (line))
215227788Sattilio#define	sx_xlock_sig_(sx, file, line)					\
216227788Sattilio	_sx_xlock((sx), SX_INTERRUPTIBLE, (file), (line))
217227788Sattilio#define	sx_xunlock_(sx, file, line)					\
218227788Sattilio	_sx_xunlock((sx), (file), (line))
219227788Sattilio#define	sx_slock_(sx, file, line)					\
220227788Sattilio	(void)_sx_slock((sx), 0, (file), (line))
221227788Sattilio#define	sx_slock_sig_(sx, file, line)					\
222227788Sattilio	_sx_slock((sx), SX_INTERRUPTIBLE, (file) , (line))
223227788Sattilio#define	sx_sunlock_(sx, file, line)					\
224227788Sattilio	_sx_sunlock((sx), (file), (line))
225168191Sjhb#else
226227788Sattilio#define	sx_xlock_(sx, file, line)					\
227227788Sattilio	(void)__sx_xlock((sx), curthread, 0, (file), (line))
228227788Sattilio#define	sx_xlock_sig_(sx, file, line)					\
229227788Sattilio	__sx_xlock((sx), curthread, SX_INTERRUPTIBLE, (file), (line))
230227788Sattilio#define	sx_xunlock_(sx, file, line)					\
231227788Sattilio	__sx_xunlock((sx), curthread, (file), (line))
232227788Sattilio#define	sx_slock_(sx, file, line)					\
233227788Sattilio	(void)__sx_slock((sx), 0, (file), (line))
234227788Sattilio#define	sx_slock_sig_(sx, file, line)					\
235227788Sattilio	__sx_slock((sx), SX_INTERRUPTIBLE, (file), (line))
236227788Sattilio#define	sx_sunlock_(sx, file, line)					\
237227788Sattilio	__sx_sunlock((sx), (file), (line))
238168191Sjhb#endif	/* LOCK_DEBUG > 0 || SX_NOINLINE */
239227788Sattilio#define	sx_try_slock(sx)	sx_try_slock_((sx), LOCK_FILE, LOCK_LINE)
240227788Sattilio#define	sx_try_xlock(sx)	sx_try_xlock_((sx), LOCK_FILE, LOCK_LINE)
241227788Sattilio#define	sx_try_upgrade(sx)	sx_try_upgrade_((sx), LOCK_FILE, LOCK_LINE)
242227788Sattilio#define	sx_downgrade(sx)	sx_downgrade_((sx), LOCK_FILE, LOCK_LINE)
243227788Sattilio#ifdef INVARIANTS
244227788Sattilio#define	sx_assert_(sx, what, file, line)				\
245227788Sattilio	_sx_assert((sx), (what), (file), (line))
246227788Sattilio#else
247227788Sattilio#define	sx_assert_(sx, what, file, line)	(void)0
248227788Sattilio#endif
249168191Sjhb
250227788Sattilio#define	sx_xlock(sx)		sx_xlock_((sx), LOCK_FILE, LOCK_LINE)
251227788Sattilio#define	sx_xlock_sig(sx)	sx_xlock_sig_((sx), LOCK_FILE, LOCK_LINE)
252227788Sattilio#define	sx_xunlock(sx)		sx_xunlock_((sx), LOCK_FILE, LOCK_LINE)
253227788Sattilio#define	sx_slock(sx)		sx_slock_((sx), LOCK_FILE, LOCK_LINE)
254227788Sattilio#define	sx_slock_sig(sx)	sx_slock_sig_((sx), LOCK_FILE, LOCK_LINE)
255227788Sattilio#define	sx_sunlock(sx)		sx_sunlock_((sx), LOCK_FILE, LOCK_LINE)
256227788Sattilio#define	sx_assert(sx, what)	sx_assert_((sx), (what), __FILE__, __LINE__)
257227788Sattilio
258169776Sjhb/*
259169776Sjhb * Return a pointer to the owning thread if the lock is exclusively
260169776Sjhb * locked.
261169776Sjhb */
262169776Sjhb#define	sx_xholder(sx)							\
263169776Sjhb	((sx)->sx_lock & SX_LOCK_SHARED ? NULL :			\
264169776Sjhb	(struct thread *)SX_OWNER((sx)->sx_lock))
265169776Sjhb
266168191Sjhb#define	sx_xlocked(sx)							\
267168191Sjhb	(((sx)->sx_lock & ~(SX_LOCK_FLAGMASK & ~SX_LOCK_SHARED)) ==	\
268168191Sjhb	    (uintptr_t)curthread)
269168191Sjhb
270227788Sattilio#define	sx_unlock_(sx, file, line) do {					\
271159844Sjhb	if (sx_xlocked(sx))						\
272227788Sattilio		sx_xunlock_(sx, file, line);				\
273157296Sjhb	else								\
274227788Sattilio		sx_sunlock_(sx, file, line);				\
275157296Sjhb} while (0)
276168191Sjhb
277227788Sattilio#define	sx_unlock(sx)	sx_unlock_((sx), LOCK_FILE, LOCK_LINE)
278227788Sattilio
279167387Sjhb#define	sx_sleep(chan, sx, pri, wmesg, timo)				\
280247787Sdavide	_sleep((chan), &(sx)->lock_object, (pri), (wmesg),		\
281247787Sdavide	    tick_sbt * (timo), 0,  C_HARDCLOCK)
28274912Sjhb
283168191Sjhb/*
284168191Sjhb * Options passed to sx_init_flags().
285168191Sjhb */
286168191Sjhb#define	SX_DUPOK		0x01
287168191Sjhb#define	SX_NOPROFILE		0x02
288168191Sjhb#define	SX_NOWITNESS		0x04
289168191Sjhb#define	SX_QUIET		0x08
290193011Sattilio#define	SX_NOADAPTIVE		0x10
291169769Sjhb#define	SX_RECURSE		0x20
292323870Smarius#define	SX_NEW			0x40
293168191Sjhb
294170149Sattilio/*
295170149Sattilio * Options passed to sx_*lock_hard().
296170149Sattilio */
297170149Sattilio#define	SX_INTERRUPTIBLE	0x40
298170149Sattilio
29985412Sjhb#if defined(INVARIANTS) || defined(INVARIANT_SUPPORT)
300169780Sjhb#define	SA_LOCKED		LA_LOCKED
301169780Sjhb#define	SA_SLOCKED		LA_SLOCKED
302169780Sjhb#define	SA_XLOCKED		LA_XLOCKED
303169780Sjhb#define	SA_UNLOCKED		LA_UNLOCKED
304169780Sjhb#define	SA_RECURSED		LA_RECURSED
305169780Sjhb#define	SA_NOTRECURSED		LA_NOTRECURSED
306169780Sjhb
307300060Spfg/* Backwards compatibility. */
30885388Sjhb#define	SX_LOCKED		LA_LOCKED
30985388Sjhb#define	SX_SLOCKED		LA_SLOCKED
31085388Sjhb#define	SX_XLOCKED		LA_XLOCKED
311125419Spjd#define	SX_UNLOCKED		LA_UNLOCKED
312168191Sjhb#define	SX_RECURSED		LA_RECURSED
313168191Sjhb#define	SX_NOTRECURSED		LA_NOTRECURSED
314125444Sbde#endif
31573863Sbmilekic
316125444Sbde#endif /* _KERNEL */
317125444Sbde
318125444Sbde#endif /* !_SYS_SX_H_ */
319