| 1 | /*- |
| 2 | * SPDX-License-Identifier: BSD-2-Clause |
| 3 | * |
| 4 | * Copyright (c) 2007 Attilio Rao <attilio@freebsd.org> |
| 5 | * Copyright (c) 2001 Jason Evans <jasone@freebsd.org> |
| 6 | * All rights reserved. |
| 7 | * |
| 8 | * Redistribution and use in source and binary forms, with or without |
| 9 | * modification, are permitted provided that the following conditions |
| 10 | * are met: |
| 11 | * 1. Redistributions of source code must retain the above copyright |
| 12 | * notice(s), this list of conditions and the following disclaimer as |
| 13 | * the first lines of this file unmodified other than the possible |
| 14 | * addition of one or more copyright notices. |
| 15 | * 2. Redistributions in binary form must reproduce the above copyright |
| 16 | * notice(s), this list of conditions and the following disclaimer in the |
| 17 | * documentation and/or other materials provided with the distribution. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY |
| 20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY |
| 23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| 29 | * DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | #ifndef	_SYS_SX_H_ |
| 33 | #define	_SYS_SX_H_ |
| 34 | |
| 35 | #include <sys/_lock.h> |
| 36 | #include <sys/_sx.h> |
| 37 | |
| 38 | #ifdef	_KERNEL |
| 39 | #include <sys/pcpu.h> |
| 40 | #include <sys/lock_profile.h> |
| 41 | #include <sys/lockstat.h> |
| 42 | #include <machine/atomic.h> |
| 43 | #endif |
| 44 | |
| 45 | /* |
| 46 | * In general, the sx locks and rwlocks use very similar algorithms. |
| 47 | * The main difference in the implementations is how threads are |
| 48 | * blocked when a lock is unavailable. For this, sx locks use sleep |
| 49 | * queues which do not support priority propagation, and rwlocks use |
| 50 | * turnstiles which do. |
| 51 | * |
| 52 | * The sx_lock field consists of several fields. The low bit |
| 53 | * indicates if the lock is locked with a shared or exclusive lock. A |
| 54 | * value of 0 indicates an exclusive lock, and a value of 1 indicates |
| 55 | * a shared lock. Bit 1 is a boolean indicating if there are any |
| 56 | * threads waiting for a shared lock. Bit 2 is a boolean indicating |
| 57 | * if there are any threads waiting for an exclusive lock. Bit 3 is a |
| 58 | * boolean indicating if an exclusive lock is recursively held. The |
| 59 | * rest of the variable's definition is dependent on the value of the |
| 60 | * first bit. For an exclusive lock, it is a pointer to the thread |
| 61 | * holding the lock, similar to the mtx_lock field of mutexes. For |
| 62 | * shared locks, it is a count of read locks that are held. |
| 63 | * |
| 64 | * When the lock is not locked by any thread, it is encoded as a |
| 65 | * shared lock with zero waiters. |
| 66 | */ |
| 67 | |
| 68 | #define	SX_LOCK_SHARED			0x01 |
| 69 | #define	SX_LOCK_SHARED_WAITERS		0x02 |
| 70 | #define	SX_LOCK_EXCLUSIVE_WAITERS	0x04 |
| 71 | #define	SX_LOCK_WRITE_SPINNER		0x08 |
| 72 | #define	SX_LOCK_RECURSED		0x10 |
| 73 | #define	SX_LOCK_FLAGMASK						\ |
| 74 | 	(SX_LOCK_SHARED | SX_LOCK_SHARED_WAITERS |			\ |
| 75 | 	SX_LOCK_EXCLUSIVE_WAITERS | SX_LOCK_RECURSED | SX_LOCK_WRITE_SPINNER) |
| 76 | #define	SX_LOCK_WAITERS			(SX_LOCK_SHARED_WAITERS | SX_LOCK_EXCLUSIVE_WAITERS) |
| 77 | |
| 78 | #define	SX_OWNER(x)			((x) & ~SX_LOCK_FLAGMASK) |
| 79 | #define	SX_SHARERS_SHIFT		5 |
| 80 | #define	SX_SHARERS(x)			(SX_OWNER(x) >> SX_SHARERS_SHIFT) |
| 81 | #define	SX_SHARERS_LOCK(x)						\ |
| 82 | 	((x) << SX_SHARERS_SHIFT | SX_LOCK_SHARED) |
| 83 | #define	SX_ONE_SHARER			(1 << SX_SHARERS_SHIFT) |
| 84 | |
| 85 | #define	SX_LOCK_UNLOCKED		SX_SHARERS_LOCK(0) |
| 86 | #define	SX_LOCK_DESTROYED						\ |
| 87 | 	(SX_LOCK_SHARED_WAITERS | SX_LOCK_EXCLUSIVE_WAITERS) |
| 88 | |
| 89 | #ifdef _KERNEL |
| 90 | |
| 91 | #define	sx_recurse	lock_object.lo_data |
| 92 | |
| 93 | #define	SX_READ_VALUE(sx)	((sx)->sx_lock) |
| 94 | |
| 95 | #define	lv_sx_owner(v) \ |
| 96 | 	((v & SX_LOCK_SHARED) ? NULL : (struct thread *)SX_OWNER(v)) |
| 97 | |
| 98 | /* |
| 99 | * Function prototipes. Routines that start with an underscore are not part |
| 100 | * of the public interface and are wrappered with a macro. |
| 101 | */ |
| 102 | void	sx_sysinit(const void *arg); |
| 103 | #define	sx_init(sx, desc)	sx_init_flags((sx), (desc), 0) |
| 104 | void	sx_init_flags(struct sx *sx, const char *description, int opts); |
| 105 | void	sx_destroy(struct sx *sx); |
| 106 | int	sx_try_slock_int(struct sx *sx LOCK_FILE_LINE_ARG_DEF); |
| 107 | int	sx_try_slock_(struct sx *sx, const char *file, int line); |
| 108 | int	sx_try_xlock_int(struct sx *sx LOCK_FILE_LINE_ARG_DEF); |
| 109 | int	sx_try_xlock_(struct sx *sx, const char *file, int line); |
| 110 | int	sx_try_upgrade_int(struct sx *sx LOCK_FILE_LINE_ARG_DEF); |
| 111 | int	sx_try_upgrade_(struct sx *sx, const char *file, int line); |
| 112 | void	sx_downgrade_int(struct sx *sx LOCK_FILE_LINE_ARG_DEF); |
| 113 | void	sx_downgrade_(struct sx *sx, const char *file, int line); |
| 114 | int	_sx_slock_int(struct sx *sx, int opts LOCK_FILE_LINE_ARG_DEF); |
| 115 | int	_sx_slock(struct sx *sx, int opts, const char *file, int line); |
| 116 | int	_sx_xlock(struct sx *sx, int opts, const char *file, int line); |
| 117 | void	_sx_sunlock_int(struct sx *sx LOCK_FILE_LINE_ARG_DEF); |
| 118 | void	_sx_sunlock(struct sx *sx, const char *file, int line); |
| 119 | void	_sx_xunlock(struct sx *sx, const char *file, int line); |
| 120 | int	_sx_xlock_hard(struct sx *sx, uintptr_t x, int opts LOCK_FILE_LINE_ARG_DEF); |
| 121 | void	_sx_xunlock_hard(struct sx *sx, uintptr_t x LOCK_FILE_LINE_ARG_DEF); |
| 122 | #if defined(INVARIANTS) || defined(INVARIANT_SUPPORT) |
| 123 | void	_sx_assert(const struct sx *sx, int what, const char *file, int line); |
| 124 | #endif |
| 125 | #ifdef DDB |
| 126 | int	sx_chain(struct thread *td, struct thread **ownerp); |
| 127 | #endif |
| 128 | |
| 129 | struct sx_args { |
| 130 | 	struct sx 	*sa_sx; |
| 131 | 	const char	*sa_desc; |
| 132 | 	int		sa_flags; |
| 133 | }; |
| 134 | |
| 135 | #define	SX_SYSINIT_FLAGS(name, sxa, desc, flags)			\ |
| 136 | 	static struct sx_args name##_args = {				\ |
| 137 | 		(sxa),							\ |
| 138 | 		(desc),							\ |
| 139 | 		(flags)							\ |
| 140 | 	};								\ |
| 141 | 	SYSINIT(name##_sx_sysinit, SI_SUB_LOCK, SI_ORDER_MIDDLE,	\ |
| 142 | 	 sx_sysinit, &name##_args);					\ |
| 143 | 	SYSUNINIT(name##_sx_sysuninit, SI_SUB_LOCK, SI_ORDER_MIDDLE,	\ |
| 144 | 	 sx_destroy, (sxa)) |
| 145 | |
| 146 | #define	SX_SYSINIT(name, sxa, desc)	SX_SYSINIT_FLAGS(name, sxa, desc, 0) |
| 147 | |
| 148 | /* |
| 149 | * Full lock operations that are suitable to be inlined in non-debug kernels. |
| 150 | * If the lock can't be acquired or released trivially then the work is |
| 151 | * deferred to 'tougher' functions. |
| 152 | */ |
| 153 | |
| 154 | #if	(LOCK_DEBUG == 0) |
| 155 | /* Acquire an exclusive lock. */ |
| 156 | static __inline int |
| 157 | __sx_xlock(struct sx *sx, struct thread *td, int opts, const char *file, |
| 158 | int line) |
| 159 | { |
| 160 | 	uintptr_t tid = (uintptr_t)td; |
| 161 | 	uintptr_t v = SX_LOCK_UNLOCKED; |
| 162 | 	int error = 0; |
| 163 | |
| 164 | 	if (__predict_false(LOCKSTAT_PROFILE_ENABLED(sx__acquire) || |
| 165 | 	 !atomic_fcmpset_acq_ptr(&sx->sx_lock, &v, tid))) |
| 166 | 		error = _sx_xlock_hard(sx, v, opts); |
| 167 | |
| 168 | 	return (error); |
| 169 | } |
| 170 | |
| 171 | /* Release an exclusive lock. */ |
| 172 | static __inline void |
| 173 | __sx_xunlock(struct sx *sx, struct thread *td, const char *file, int line) |
| 174 | { |
| 175 | 	uintptr_t x = (uintptr_t)td; |
| 176 | |
| 177 | 	if (__predict_false(LOCKSTAT_PROFILE_ENABLED(sx__release) || |
| 178 | 	 !atomic_fcmpset_rel_ptr(&sx->sx_lock, &x, SX_LOCK_UNLOCKED))) |
| 179 | 		_sx_xunlock_hard(sx, x); |
| 180 | } |
| 181 | #endif |
| 182 | |
| 183 | /* |
| 184 | * Public interface for lock operations. |
| 185 | */ |
| 186 | #ifndef LOCK_DEBUG |
| 187 | #error	"LOCK_DEBUG not defined, include <sys/lock.h> before <sys/sx.h>" |
| 188 | #endif |
| 189 | #if	(LOCK_DEBUG > 0) || defined(SX_NOINLINE) |
| 190 | #define	sx_xlock_(sx, file, line)					\ |
| 191 | 	(void)_sx_xlock((sx), 0, (file), (line)) |
| 192 | #define	sx_xlock_sig_(sx, file, line)					\ |
| 193 | 	_sx_xlock((sx), SX_INTERRUPTIBLE, (file), (line)) |
| 194 | #define	sx_xunlock_(sx, file, line)					\ |
| 195 | 	_sx_xunlock((sx), (file), (line)) |
| 196 | #else |
| 197 | #define	sx_xlock_(sx, file, line)					\ |
| 198 | 	(void)__sx_xlock((sx), curthread, 0, (file), (line)) |
| 199 | #define	sx_xlock_sig_(sx, file, line)					\ |
| 200 | 	__sx_xlock((sx), curthread, SX_INTERRUPTIBLE, (file), (line)) |
| 201 | #define	sx_xunlock_(sx, file, line)					\ |
| 202 | 	__sx_xunlock((sx), curthread, (file), (line)) |
| 203 | #endif	/* LOCK_DEBUG > 0 || SX_NOINLINE */ |
| 204 | #if	(LOCK_DEBUG > 0) |
| 205 | #define	sx_slock_(sx, file, line)					\ |
| 206 | 	(void)_sx_slock((sx), 0, (file), (line)) |
| 207 | #define	sx_slock_sig_(sx, file, line)					\ |
| 208 | 	_sx_slock((sx), SX_INTERRUPTIBLE, (file) , (line)) |
| 209 | #define	sx_sunlock_(sx, file, line)					\ |
| 210 | 	_sx_sunlock((sx), (file), (line)) |
| 211 | #define	sx_try_slock(sx)	sx_try_slock_((sx), LOCK_FILE, LOCK_LINE) |
| 212 | #define	sx_try_xlock(sx)	sx_try_xlock_((sx), LOCK_FILE, LOCK_LINE) |
| 213 | #define	sx_try_upgrade(sx)	sx_try_upgrade_((sx), LOCK_FILE, LOCK_LINE) |
| 214 | #define	sx_downgrade(sx)	sx_downgrade_((sx), LOCK_FILE, LOCK_LINE) |
| 215 | #else |
| 216 | #define	sx_slock_(sx, file, line)					\ |
| 217 | 	(void)_sx_slock_int((sx), 0) |
| 218 | #define	sx_slock_sig_(sx, file, line)					\ |
| 219 | 	_sx_slock_int((sx), SX_INTERRUPTIBLE) |
| 220 | #define	sx_sunlock_(sx, file, line)					\ |
| 221 | 	_sx_sunlock_int((sx)) |
| 222 | #define	sx_try_slock(sx)	sx_try_slock_int((sx)) |
| 223 | #define	sx_try_xlock(sx)	sx_try_xlock_int((sx)) |
| 224 | #define	sx_try_upgrade(sx)	sx_try_upgrade_int((sx)) |
| 225 | #define	sx_downgrade(sx)	sx_downgrade_int((sx)) |
| 226 | #endif |
| 227 | #ifdef INVARIANTS |
| 228 | #define	sx_assert_(sx, what, file, line)				\ |
| 229 | 	_sx_assert((sx), (what), (file), (line)) |
| 230 | #else |
| 231 | #define	sx_assert_(sx, what, file, line)	(void)0 |
| 232 | #endif |
| 233 | |
| 234 | #define	sx_xlock(sx)		sx_xlock_((sx), LOCK_FILE, LOCK_LINE) |
| 235 | #define	sx_xlock_sig(sx)	sx_xlock_sig_((sx), LOCK_FILE, LOCK_LINE) |
| 236 | #define	sx_xunlock(sx)		sx_xunlock_((sx), LOCK_FILE, LOCK_LINE) |
| 237 | #define	sx_slock(sx)		sx_slock_((sx), LOCK_FILE, LOCK_LINE) |
| 238 | #define	sx_slock_sig(sx)	sx_slock_sig_((sx), LOCK_FILE, LOCK_LINE) |
| 239 | #define	sx_sunlock(sx)		sx_sunlock_((sx), LOCK_FILE, LOCK_LINE) |
| 240 | #define	sx_assert(sx, what)	sx_assert_((sx), (what), __FILE__, __LINE__) |
| 241 | |
| 242 | /* |
| 243 | * Return a pointer to the owning thread if the lock is exclusively |
| 244 | * locked. |
| 245 | */ |
| 246 | #define	sx_xholder(sx)							\ |
| 247 | 	((sx)->sx_lock & SX_LOCK_SHARED ? NULL :			\ |
| 248 | 	(struct thread *)SX_OWNER((sx)->sx_lock)) |
| 249 | |
| 250 | #define	sx_xlocked(sx)							\ |
| 251 | 	(((sx)->sx_lock & ~(SX_LOCK_FLAGMASK & ~SX_LOCK_SHARED)) ==	\ |
| 252 | 	 (uintptr_t)curthread) |
| 253 | |
| 254 | #define	sx_unlock_(sx, file, line) __extension__ ({			\ |
| 255 | 	if (sx_xlocked(sx))						\ |
| 256 | 		sx_xunlock_(sx, file, line);				\ |
| 257 | 	else								\ |
| 258 | 		sx_sunlock_(sx, file, line);				\ |
| 259 | 	(void)0; /* ensure void type for expression */			\ |
| 260 | }) |
| 261 | |
| 262 | #define	sx_unlock(sx)	sx_unlock_((sx), LOCK_FILE, LOCK_LINE) |
| 263 | |
| 264 | #define	sx_sleep(chan, sx, pri, wmesg, timo)				\ |
| 265 | 	_sleep((chan), &(sx)->lock_object, (pri), (wmesg),		\ |
| 266 | 	 tick_sbt * (timo), 0, C_HARDCLOCK) |
| 267 | |
| 268 | /* |
| 269 | * Options passed to sx_init_flags(). |
| 270 | */ |
| 271 | #define	SX_DUPOK		0x01 |
| 272 | #define	SX_NOPROFILE		0x02 |
| 273 | #define	SX_NOWITNESS		0x04 |
| 274 | #define	SX_QUIET		0x08 |
| 275 | #define	SX_RECURSE		0x20 |
| 276 | #define	SX_NEW			0x40 |
| 277 | |
| 278 | /* |
| 279 | * Options passed to sx_*lock_hard(). |
| 280 | */ |
| 281 | #define	SX_INTERRUPTIBLE	0x40 |
| 282 | |
| 283 | #if defined(INVARIANTS) || defined(INVARIANT_SUPPORT) |
| 284 | #define	SA_LOCKED		LA_LOCKED |
| 285 | #define	SA_SLOCKED		LA_SLOCKED |
| 286 | #define	SA_XLOCKED		LA_XLOCKED |
| 287 | #define	SA_UNLOCKED		LA_UNLOCKED |
| 288 | #define	SA_RECURSED		LA_RECURSED |
| 289 | #define	SA_NOTRECURSED		LA_NOTRECURSED |
| 290 | |
| 291 | /* Backwards compatibility. */ |
| 292 | #define	SX_LOCKED		LA_LOCKED |
| 293 | #define	SX_SLOCKED		LA_SLOCKED |
| 294 | #define	SX_XLOCKED		LA_XLOCKED |
| 295 | #define	SX_UNLOCKED		LA_UNLOCKED |
| 296 | #define	SX_RECURSED		LA_RECURSED |
| 297 | #define	SX_NOTRECURSED		LA_NOTRECURSED |
| 298 | #endif |
| 299 | |
| 300 | #endif /* _KERNEL */ |
| 301 | |
| 302 | #ifdef _STANDALONE |
| 303 | /* since we have no threads in the boot loader, trivially implement no-op version */ |
| 304 | #define	sx_xlock(s)	do {} while (0) |
| 305 | #define	sx_try_xlock(s)	(1) |
| 306 | #define	sx_xunlock(s)	do {} while (0) |
| 307 | #define	SX_DUPOK	0 |
| 308 | #define	SX_NEW		0 |
| 309 | #define	SX_NOWITNESS	0 |
| 310 | |
| 311 | static __inline void |
| 312 | sx_init_flags(struct sx *sx, const char *description, int opts) |
| 313 | { |
| 314 | |
| 315 | } |
| 316 | |
| 317 | static __inline void |
| 318 | sx_destroy(struct sx *sx) |
| 319 | { |
| 320 | |
| 321 | } |
| 322 | #endif /* _STANDALONE */ |
| 323 | |
| 324 | #endif /* !_SYS_SX_H_ */ |