xref: /dflybsd-src/sys/kern/kern_mutex.c (revision 6d0742ae7aea551e633fc7147abd5de001c40346)
133b0b87cSMatthew Dillon /*
233b0b87cSMatthew Dillon  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
333b0b87cSMatthew Dillon  *
433b0b87cSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
533b0b87cSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
633b0b87cSMatthew Dillon  *
733b0b87cSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
833b0b87cSMatthew Dillon  * modification, are permitted provided that the following conditions
933b0b87cSMatthew Dillon  * are met:
1033b0b87cSMatthew Dillon  *
1133b0b87cSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
1233b0b87cSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1333b0b87cSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
1433b0b87cSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
1533b0b87cSMatthew Dillon  *    the documentation and/or other materials provided with the
1633b0b87cSMatthew Dillon  *    distribution.
1733b0b87cSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
1833b0b87cSMatthew Dillon  *    contributors may be used to endorse or promote products derived
1933b0b87cSMatthew Dillon  *    from this software without specific, prior written permission.
2033b0b87cSMatthew Dillon  *
2133b0b87cSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2233b0b87cSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2333b0b87cSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2433b0b87cSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2533b0b87cSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2633b0b87cSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2733b0b87cSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2833b0b87cSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2933b0b87cSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3033b0b87cSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3133b0b87cSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3233b0b87cSMatthew Dillon  * SUCH DAMAGE.
3333b0b87cSMatthew Dillon  */
3433b0b87cSMatthew Dillon /*
3533b0b87cSMatthew Dillon  * Implement fast persistent locks based on atomic_cmpset_int() with
3633b0b87cSMatthew Dillon  * semantics similar to lockmgr locks but faster and taking up much less
3733b0b87cSMatthew Dillon  * space.  Taken from HAMMER's lock implementation.
3833b0b87cSMatthew Dillon  *
3933b0b87cSMatthew Dillon  * These are meant to complement our LWKT tokens.  Tokens are only held
4033b0b87cSMatthew Dillon  * while the thread is running.  Mutexes can be held across blocking
4133b0b87cSMatthew Dillon  * conditions.
4233b0b87cSMatthew Dillon  *
43d66b88f3SMatthew Dillon  * - Exclusive priority over shared to prevent SMP starvation.
44d66b88f3SMatthew Dillon  * - locks can be aborted (async callback, if any, will be made w/ENOLCK).
45d66b88f3SMatthew Dillon  * - locks can be asynchronous.
46d66b88f3SMatthew Dillon  * - synchronous fast path if no blocking occurs (async callback is not
47d66b88f3SMatthew Dillon  *   made in this case).
48d66b88f3SMatthew Dillon  *
49d66b88f3SMatthew Dillon  * Generally speaking any caller-supplied link state must be properly
50d66b88f3SMatthew Dillon  * initialized before use.
51d66b88f3SMatthew Dillon  *
5233b0b87cSMatthew Dillon  * Most of the support is in sys/mutex[2].h.  We mostly provide backoff
5333b0b87cSMatthew Dillon  * functions here.
5433b0b87cSMatthew Dillon  */
5533b0b87cSMatthew Dillon 
5633b0b87cSMatthew Dillon #include <sys/param.h>
5733b0b87cSMatthew Dillon #include <sys/systm.h>
5833b0b87cSMatthew Dillon #include <sys/kernel.h>
5933b0b87cSMatthew Dillon #include <sys/sysctl.h>
605b49787bSMatthew Dillon #include <sys/indefinite.h>
6133b0b87cSMatthew Dillon #include <sys/thread.h>
6233b0b87cSMatthew Dillon 
6333b0b87cSMatthew Dillon #include <machine/cpufunc.h>
6433b0b87cSMatthew Dillon 
6533b0b87cSMatthew Dillon #include <sys/thread2.h>
6633b0b87cSMatthew Dillon #include <sys/mutex2.h>
675b49787bSMatthew Dillon #include <sys/indefinite2.h>
6833b0b87cSMatthew Dillon 
69d66b88f3SMatthew Dillon static int mtx_chain_link_ex(mtx_t *mtx, u_int olock);
70124e15f9SMatthew Dillon static int mtx_chain_link_sh(mtx_t *mtx, u_int olock);
71d66b88f3SMatthew Dillon static void mtx_delete_link(mtx_t *mtx, mtx_link_t *link);
72685ebdabSMatthew Dillon 
7333b0b87cSMatthew Dillon /*
74d66b88f3SMatthew Dillon  * Exclusive-lock a mutex, block until acquired unless link is async.
75d66b88f3SMatthew Dillon  * Recursion is allowed.
767355baa5SMatthew Dillon  *
77d66b88f3SMatthew Dillon  * Returns 0 on success, the tsleep() return code on failure, EINPROGRESS
78d66b88f3SMatthew Dillon  * if async.  If immediately successful an async exclusive lock will return 0
79d66b88f3SMatthew Dillon  * and not issue the async callback or link the link structure.  The caller
80d66b88f3SMatthew Dillon  * must handle this case (typically this is an optimal code path).
81d66b88f3SMatthew Dillon  *
82d66b88f3SMatthew Dillon  * A tsleep() error can only be returned if PCATCH is specified in the flags.
8333b0b87cSMatthew Dillon  */
847355baa5SMatthew Dillon static __inline int
__mtx_lock_ex(mtx_t * mtx,mtx_link_t * link,int flags,int to)85cabfc9f6SMatthew Dillon __mtx_lock_ex(mtx_t *mtx, mtx_link_t *link, int flags, int to)
8633b0b87cSMatthew Dillon {
87d66b88f3SMatthew Dillon 	thread_t td;
8833b0b87cSMatthew Dillon 	u_int	lock;
8933b0b87cSMatthew Dillon 	u_int	nlock;
907355baa5SMatthew Dillon 	int	error;
91d66b88f3SMatthew Dillon 	int	isasync;
9233b0b87cSMatthew Dillon 
9333b0b87cSMatthew Dillon 	for (;;) {
9433b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
95d66b88f3SMatthew Dillon 		cpu_ccfence();
96d66b88f3SMatthew Dillon 
9733b0b87cSMatthew Dillon 		if (lock == 0) {
9833b0b87cSMatthew Dillon 			nlock = MTX_EXCLUSIVE | 1;
9933b0b87cSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, 0, nlock)) {
1007355baa5SMatthew Dillon 				mtx->mtx_owner = curthread;
101a9a062cdSMatthew Dillon 				cpu_sfence();
102d66b88f3SMatthew Dillon 				link->state = MTX_LINK_ACQUIRED;
1037355baa5SMatthew Dillon 				error = 0;
1047355baa5SMatthew Dillon 				break;
10533b0b87cSMatthew Dillon 			}
106d66b88f3SMatthew Dillon 			continue;
107d66b88f3SMatthew Dillon 		}
108d66b88f3SMatthew Dillon 		if ((lock & MTX_EXCLUSIVE) && mtx->mtx_owner == curthread) {
10933b0b87cSMatthew Dillon 			KKASSERT((lock & MTX_MASK) != MTX_MASK);
110685ebdabSMatthew Dillon 			nlock = lock + 1;
111685ebdabSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock)) {
112a9a062cdSMatthew Dillon 				cpu_sfence();
113d66b88f3SMatthew Dillon 				link->state = MTX_LINK_ACQUIRED;
1147355baa5SMatthew Dillon 				error = 0;
1157355baa5SMatthew Dillon 				break;
1167355baa5SMatthew Dillon 			}
117d66b88f3SMatthew Dillon 			continue;
118d66b88f3SMatthew Dillon 		}
11977912481SMatthew Dillon 
120d66b88f3SMatthew Dillon 		/*
121d66b88f3SMatthew Dillon 		 * We need MTX_LINKSPIN to manipulate exlink or
122d66b88f3SMatthew Dillon 		 * shlink.
123d66b88f3SMatthew Dillon 		 *
124d66b88f3SMatthew Dillon 		 * We must set MTX_EXWANTED with MTX_LINKSPIN to indicate
125124e15f9SMatthew Dillon 		 * pending exclusive requests.  It cannot be set as a separate
126d66b88f3SMatthew Dillon 		 * operation prior to acquiring MTX_LINKSPIN.
127d66b88f3SMatthew Dillon 		 *
128d66b88f3SMatthew Dillon 		 * To avoid unnecessary cpu cache traffic we poll
129d66b88f3SMatthew Dillon 		 * for collisions.  It is also possible that EXWANTED
130d66b88f3SMatthew Dillon 		 * state failing the above test was spurious, so all the
131d66b88f3SMatthew Dillon 		 * tests must be repeated if we cannot obtain LINKSPIN
132d66b88f3SMatthew Dillon 		 * with the prior state tests intact (i.e. don't reload
133d66b88f3SMatthew Dillon 		 * the (lock) variable here, for heaven's sake!).
134d66b88f3SMatthew Dillon 		 */
135d66b88f3SMatthew Dillon 		if (lock & MTX_LINKSPIN) {
136685ebdabSMatthew Dillon 			cpu_pause();
137685ebdabSMatthew Dillon 			continue;
138685ebdabSMatthew Dillon 		}
13977912481SMatthew Dillon 		td = curthread;
140d66b88f3SMatthew Dillon 		nlock = lock | MTX_EXWANTED | MTX_LINKSPIN;
141e8b1691fSMatthew Dillon 		crit_enter_quick(td);
142d66b88f3SMatthew Dillon 		if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock) == 0) {
143e8b1691fSMatthew Dillon 			crit_exit_quick(td);
144d66b88f3SMatthew Dillon 			continue;
145d66b88f3SMatthew Dillon 		}
146d66b88f3SMatthew Dillon 
147685ebdabSMatthew Dillon 		/*
148d66b88f3SMatthew Dillon 		 * Check for early abort.
149685ebdabSMatthew Dillon 		 */
150685ebdabSMatthew Dillon 		if (link->state == MTX_LINK_ABORTED) {
151d66b88f3SMatthew Dillon 			if (mtx->mtx_exlink == NULL) {
152685ebdabSMatthew Dillon 				atomic_clear_int(&mtx->mtx_lock,
153d66b88f3SMatthew Dillon 						 MTX_LINKSPIN |
154685ebdabSMatthew Dillon 						 MTX_EXWANTED);
155d66b88f3SMatthew Dillon 			} else {
156d66b88f3SMatthew Dillon 				atomic_clear_int(&mtx->mtx_lock,
157d66b88f3SMatthew Dillon 						 MTX_LINKSPIN);
158685ebdabSMatthew Dillon 			}
159e8b1691fSMatthew Dillon 			crit_exit_quick(td);
160d66b88f3SMatthew Dillon 			link->state = MTX_LINK_IDLE;
161d66b88f3SMatthew Dillon 			error = ENOLCK;
1627355baa5SMatthew Dillon 			break;
1637355baa5SMatthew Dillon 		}
164685ebdabSMatthew Dillon 
165685ebdabSMatthew Dillon 		/*
166d66b88f3SMatthew Dillon 		 * Add our link to the exlink list and release LINKSPIN.
167685ebdabSMatthew Dillon 		 */
16877912481SMatthew Dillon 		link->owner = td;
169d66b88f3SMatthew Dillon 		link->state = MTX_LINK_LINKED_EX;
170d66b88f3SMatthew Dillon 		if (mtx->mtx_exlink) {
171d66b88f3SMatthew Dillon 			link->next = mtx->mtx_exlink;
172685ebdabSMatthew Dillon 			link->prev = link->next->prev;
173685ebdabSMatthew Dillon 			link->next->prev = link;
174685ebdabSMatthew Dillon 			link->prev->next = link;
17554d51ad1SMatthew Dillon 		} else {
176685ebdabSMatthew Dillon 			link->next = link;
177685ebdabSMatthew Dillon 			link->prev = link;
178d66b88f3SMatthew Dillon 			mtx->mtx_exlink = link;
179685ebdabSMatthew Dillon 		}
180d66b88f3SMatthew Dillon 		isasync = (link->callback != NULL);
181d66b88f3SMatthew Dillon 		atomic_clear_int(&mtx->mtx_lock, MTX_LINKSPIN);
182e8b1691fSMatthew Dillon 		crit_exit_quick(td);
183685ebdabSMatthew Dillon 
184685ebdabSMatthew Dillon 		/*
185d66b88f3SMatthew Dillon 		 * If asynchronous lock request return without
186d66b88f3SMatthew Dillon 		 * blocking, leave link structure linked.
187685ebdabSMatthew Dillon 		 */
188d66b88f3SMatthew Dillon 		if (isasync) {
189d66b88f3SMatthew Dillon 			error = EINPROGRESS;
190685ebdabSMatthew Dillon 			break;
191685ebdabSMatthew Dillon 		}
192685ebdabSMatthew Dillon 
193685ebdabSMatthew Dillon 		/*
194d66b88f3SMatthew Dillon 		 * Wait for lock
195685ebdabSMatthew Dillon 		 */
196cabfc9f6SMatthew Dillon 		error = mtx_wait_link(mtx, link, flags, to);
197685ebdabSMatthew Dillon 		break;
198685ebdabSMatthew Dillon 	}
1997355baa5SMatthew Dillon 	return (error);
2007355baa5SMatthew Dillon }
2017355baa5SMatthew Dillon 
2027355baa5SMatthew Dillon int
_mtx_lock_ex_link(mtx_t * mtx,mtx_link_t * link,int flags,int to)203cabfc9f6SMatthew Dillon _mtx_lock_ex_link(mtx_t *mtx, mtx_link_t *link, int flags, int to)
204685ebdabSMatthew Dillon {
205cabfc9f6SMatthew Dillon 	return(__mtx_lock_ex(mtx, link, flags, to));
206685ebdabSMatthew Dillon }
207685ebdabSMatthew Dillon 
208685ebdabSMatthew Dillon int
_mtx_lock_ex(mtx_t * mtx,int flags,int to)209cabfc9f6SMatthew Dillon _mtx_lock_ex(mtx_t *mtx, int flags, int to)
2107355baa5SMatthew Dillon {
211d66b88f3SMatthew Dillon 	mtx_link_t link;
212685ebdabSMatthew Dillon 
213685ebdabSMatthew Dillon 	mtx_link_init(&link);
214cabfc9f6SMatthew Dillon 	return(__mtx_lock_ex(mtx, &link, flags, to));
2157355baa5SMatthew Dillon }
2167355baa5SMatthew Dillon 
2177355baa5SMatthew Dillon int
_mtx_lock_ex_quick(mtx_t * mtx)218cabfc9f6SMatthew Dillon _mtx_lock_ex_quick(mtx_t *mtx)
2197355baa5SMatthew Dillon {
220d66b88f3SMatthew Dillon 	mtx_link_t link;
221685ebdabSMatthew Dillon 
222685ebdabSMatthew Dillon 	mtx_link_init(&link);
223cabfc9f6SMatthew Dillon 	return(__mtx_lock_ex(mtx, &link, 0, 0));
22433b0b87cSMatthew Dillon }
22533b0b87cSMatthew Dillon 
22633b0b87cSMatthew Dillon /*
22733b0b87cSMatthew Dillon  * Share-lock a mutex, block until acquired.  Recursion is allowed.
2287355baa5SMatthew Dillon  *
2297355baa5SMatthew Dillon  * Returns 0 on success, or the tsleep() return code on failure.
2307355baa5SMatthew Dillon  * An error can only be returned if PCATCH is specified in the flags.
231685ebdabSMatthew Dillon  *
232685ebdabSMatthew Dillon  * NOTE: Shared locks get a mass-wakeup so if the tsleep fails we
233685ebdabSMatthew Dillon  *	 do not have to chain the wakeup().
23433b0b87cSMatthew Dillon  */
2357355baa5SMatthew Dillon static __inline int
__mtx_lock_sh(mtx_t * mtx,mtx_link_t * link,int flags,int to)236cabfc9f6SMatthew Dillon __mtx_lock_sh(mtx_t *mtx, mtx_link_t *link, int flags, int to)
23733b0b87cSMatthew Dillon {
238d66b88f3SMatthew Dillon 	thread_t td;
23933b0b87cSMatthew Dillon 	u_int	lock;
24033b0b87cSMatthew Dillon 	u_int	nlock;
2417355baa5SMatthew Dillon 	int	error;
242d66b88f3SMatthew Dillon 	int	isasync;
24333b0b87cSMatthew Dillon 
24433b0b87cSMatthew Dillon 	for (;;) {
24533b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
246d66b88f3SMatthew Dillon 		cpu_ccfence();
247d66b88f3SMatthew Dillon 
248d66b88f3SMatthew Dillon 		if (lock == 0) {
249d66b88f3SMatthew Dillon 			nlock = 1;
250d66b88f3SMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, 0, nlock)) {
251d66b88f3SMatthew Dillon 				error = 0;
252a9a062cdSMatthew Dillon 				cpu_sfence();
253d66b88f3SMatthew Dillon 				link->state = MTX_LINK_ACQUIRED;
254d66b88f3SMatthew Dillon 				break;
255d66b88f3SMatthew Dillon 			}
256d66b88f3SMatthew Dillon 			continue;
257d66b88f3SMatthew Dillon 		}
258d66b88f3SMatthew Dillon 		if ((lock & (MTX_EXCLUSIVE | MTX_EXWANTED)) == 0) {
25933b0b87cSMatthew Dillon 			KKASSERT((lock & MTX_MASK) != MTX_MASK);
26033b0b87cSMatthew Dillon 			nlock = lock + 1;
2617355baa5SMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock)) {
2627355baa5SMatthew Dillon 				error = 0;
263a9a062cdSMatthew Dillon 				cpu_sfence();
264d66b88f3SMatthew Dillon 				link->state = MTX_LINK_ACQUIRED;
2657355baa5SMatthew Dillon 				break;
2667355baa5SMatthew Dillon 			}
267d66b88f3SMatthew Dillon 			continue;
26833b0b87cSMatthew Dillon 		}
269d66b88f3SMatthew Dillon 
270d66b88f3SMatthew Dillon 		/*
271d66b88f3SMatthew Dillon 		 * We need MTX_LINKSPIN to manipulate exlink or
272d66b88f3SMatthew Dillon 		 * shlink.
273d66b88f3SMatthew Dillon 		 *
274d66b88f3SMatthew Dillon 		 * We must set MTX_SHWANTED with MTX_LINKSPIN to indicate
275d66b88f3SMatthew Dillon 		 * pending shared requests.  It cannot be set as a separate
276d66b88f3SMatthew Dillon 		 * operation prior to acquiring MTX_LINKSPIN.
277d66b88f3SMatthew Dillon 		 *
278d66b88f3SMatthew Dillon 		 * To avoid unnecessary cpu cache traffic we poll
279d66b88f3SMatthew Dillon 		 * for collisions.  It is also possible that EXWANTED
280d66b88f3SMatthew Dillon 		 * state failing the above test was spurious, so all the
281d66b88f3SMatthew Dillon 		 * tests must be repeated if we cannot obtain LINKSPIN
282d66b88f3SMatthew Dillon 		 * with the prior state tests intact (i.e. don't reload
283d66b88f3SMatthew Dillon 		 * the (lock) variable here, for heaven's sake!).
284d66b88f3SMatthew Dillon 		 */
285d66b88f3SMatthew Dillon 		if (lock & MTX_LINKSPIN) {
286d66b88f3SMatthew Dillon 			cpu_pause();
287d66b88f3SMatthew Dillon 			continue;
288d66b88f3SMatthew Dillon 		}
289d66b88f3SMatthew Dillon 		td = curthread;
290d66b88f3SMatthew Dillon 		nlock = lock | MTX_SHWANTED | MTX_LINKSPIN;
291e8b1691fSMatthew Dillon 		crit_enter_quick(td);
292d66b88f3SMatthew Dillon 		if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock) == 0) {
293e8b1691fSMatthew Dillon 			crit_exit_quick(td);
294d66b88f3SMatthew Dillon 			continue;
295d66b88f3SMatthew Dillon 		}
296d66b88f3SMatthew Dillon 
297d66b88f3SMatthew Dillon 		/*
298124e15f9SMatthew Dillon 		 * Check for early abort.  Other shared lock requestors
299124e15f9SMatthew Dillon 		 * could have sneaked in before we set LINKSPIN so make
300124e15f9SMatthew Dillon 		 * sure we undo the state properly.
301d66b88f3SMatthew Dillon 		 */
302d66b88f3SMatthew Dillon 		if (link->state == MTX_LINK_ABORTED) {
303124e15f9SMatthew Dillon 			if (mtx->mtx_shlink) {
304124e15f9SMatthew Dillon 				atomic_clear_int(&mtx->mtx_lock,
305124e15f9SMatthew Dillon 						 MTX_LINKSPIN);
306124e15f9SMatthew Dillon 			} else {
307d66b88f3SMatthew Dillon 				atomic_clear_int(&mtx->mtx_lock,
308d66b88f3SMatthew Dillon 						 MTX_LINKSPIN |
309d66b88f3SMatthew Dillon 						 MTX_SHWANTED);
310d66b88f3SMatthew Dillon 			}
311e8b1691fSMatthew Dillon 			crit_exit_quick(td);
312d66b88f3SMatthew Dillon 			link->state = MTX_LINK_IDLE;
313d66b88f3SMatthew Dillon 			error = ENOLCK;
314d66b88f3SMatthew Dillon 			break;
315d66b88f3SMatthew Dillon 		}
316d66b88f3SMatthew Dillon 
317d66b88f3SMatthew Dillon 		/*
318124e15f9SMatthew Dillon 		 * Add our link to the shlink list and release LINKSPIN.
319d66b88f3SMatthew Dillon 		 */
320d66b88f3SMatthew Dillon 		link->owner = td;
321d66b88f3SMatthew Dillon 		link->state = MTX_LINK_LINKED_SH;
322d66b88f3SMatthew Dillon 		if (mtx->mtx_shlink) {
323d66b88f3SMatthew Dillon 			link->next = mtx->mtx_shlink;
324d66b88f3SMatthew Dillon 			link->prev = link->next->prev;
325d66b88f3SMatthew Dillon 			link->next->prev = link;
326d66b88f3SMatthew Dillon 			link->prev->next = link;
327d66b88f3SMatthew Dillon 		} else {
328d66b88f3SMatthew Dillon 			link->next = link;
329d66b88f3SMatthew Dillon 			link->prev = link;
330d66b88f3SMatthew Dillon 			mtx->mtx_shlink = link;
331d66b88f3SMatthew Dillon 		}
332d66b88f3SMatthew Dillon 		isasync = (link->callback != NULL);
333d66b88f3SMatthew Dillon 		atomic_clear_int(&mtx->mtx_lock, MTX_LINKSPIN);
334e8b1691fSMatthew Dillon 		crit_exit_quick(td);
335d66b88f3SMatthew Dillon 
336d66b88f3SMatthew Dillon 		/*
337d66b88f3SMatthew Dillon 		 * If asynchronous lock request return without
338d66b88f3SMatthew Dillon 		 * blocking, leave link structure linked.
339d66b88f3SMatthew Dillon 		 */
340d66b88f3SMatthew Dillon 		if (isasync) {
341d66b88f3SMatthew Dillon 			error = EINPROGRESS;
342d66b88f3SMatthew Dillon 			break;
343d66b88f3SMatthew Dillon 		}
344d66b88f3SMatthew Dillon 
345d66b88f3SMatthew Dillon 		/*
346d66b88f3SMatthew Dillon 		 * Wait for lock
347d66b88f3SMatthew Dillon 		 */
348cabfc9f6SMatthew Dillon 		error = mtx_wait_link(mtx, link, flags, to);
349d66b88f3SMatthew Dillon 		break;
35033b0b87cSMatthew Dillon 	}
3517355baa5SMatthew Dillon 	return (error);
3527355baa5SMatthew Dillon }
3537355baa5SMatthew Dillon 
3547355baa5SMatthew Dillon int
_mtx_lock_sh_link(mtx_t * mtx,mtx_link_t * link,int flags,int to)355cabfc9f6SMatthew Dillon _mtx_lock_sh_link(mtx_t *mtx, mtx_link_t *link, int flags, int to)
3567355baa5SMatthew Dillon {
357cabfc9f6SMatthew Dillon 	return(__mtx_lock_sh(mtx, link, flags, to));
3587355baa5SMatthew Dillon }
3597355baa5SMatthew Dillon 
3607355baa5SMatthew Dillon int
_mtx_lock_sh(mtx_t * mtx,int flags,int to)361cabfc9f6SMatthew Dillon _mtx_lock_sh(mtx_t *mtx, int flags, int to)
3627355baa5SMatthew Dillon {
363d66b88f3SMatthew Dillon 	mtx_link_t link;
364d66b88f3SMatthew Dillon 
365d66b88f3SMatthew Dillon 	mtx_link_init(&link);
366cabfc9f6SMatthew Dillon 	return(__mtx_lock_sh(mtx, &link, flags, to));
367d66b88f3SMatthew Dillon }
368d66b88f3SMatthew Dillon 
369d66b88f3SMatthew Dillon int
_mtx_lock_sh_quick(mtx_t * mtx)370cabfc9f6SMatthew Dillon _mtx_lock_sh_quick(mtx_t *mtx)
371d66b88f3SMatthew Dillon {
372d66b88f3SMatthew Dillon 	mtx_link_t link;
373d66b88f3SMatthew Dillon 
374d66b88f3SMatthew Dillon 	mtx_link_init(&link);
375cabfc9f6SMatthew Dillon 	return(__mtx_lock_sh(mtx, &link, 0, 0));
37633b0b87cSMatthew Dillon }
37733b0b87cSMatthew Dillon 
37838c3ee9bSMatthew Dillon /*
37938c3ee9bSMatthew Dillon  * Get an exclusive spinlock the hard way.
38038c3ee9bSMatthew Dillon  */
38133b0b87cSMatthew Dillon void
_mtx_spinlock(mtx_t * mtx)382d66b88f3SMatthew Dillon _mtx_spinlock(mtx_t *mtx)
38333b0b87cSMatthew Dillon {
38433b0b87cSMatthew Dillon 	u_int	lock;
38533b0b87cSMatthew Dillon 	u_int	nlock;
38633b0b87cSMatthew Dillon 	int	bb = 1;
38733b0b87cSMatthew Dillon 	int	bo;
38833b0b87cSMatthew Dillon 
38933b0b87cSMatthew Dillon 	for (;;) {
39033b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
39133b0b87cSMatthew Dillon 		if (lock == 0) {
39233b0b87cSMatthew Dillon 			nlock = MTX_EXCLUSIVE | 1;
39333b0b87cSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, 0, nlock)) {
394685ebdabSMatthew Dillon 				mtx->mtx_owner = curthread;
395685ebdabSMatthew Dillon 				break;
39633b0b87cSMatthew Dillon 			}
39733b0b87cSMatthew Dillon 		} else if ((lock & MTX_EXCLUSIVE) &&
39833b0b87cSMatthew Dillon 			   mtx->mtx_owner == curthread) {
39933b0b87cSMatthew Dillon 			KKASSERT((lock & MTX_MASK) != MTX_MASK);
400685ebdabSMatthew Dillon 			nlock = lock + 1;
401685ebdabSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
402685ebdabSMatthew Dillon 				break;
40333b0b87cSMatthew Dillon 		} else {
40433b0b87cSMatthew Dillon 			/* MWAIT here */
40533b0b87cSMatthew Dillon 			if (bb < 1000)
40633b0b87cSMatthew Dillon 				++bb;
40733b0b87cSMatthew Dillon 			cpu_pause();
40833b0b87cSMatthew Dillon 			for (bo = 0; bo < bb; ++bo)
40933b0b87cSMatthew Dillon 				;
41033b0b87cSMatthew Dillon 		}
411685ebdabSMatthew Dillon 		cpu_pause();
41233b0b87cSMatthew Dillon 	}
41333b0b87cSMatthew Dillon }
41433b0b87cSMatthew Dillon 
41538c3ee9bSMatthew Dillon /*
41638c3ee9bSMatthew Dillon  * Attempt to acquire a spinlock, if we fail we must undo the
4170846e4ceSMatthew Dillon  * gd->gd_spinlocks/gd->gd_curthead->td_critcount predisposition.
41838c3ee9bSMatthew Dillon  *
41938c3ee9bSMatthew Dillon  * Returns 0 on success, EAGAIN on failure.
42038c3ee9bSMatthew Dillon  */
42138c3ee9bSMatthew Dillon int
_mtx_spinlock_try(mtx_t * mtx)422d66b88f3SMatthew Dillon _mtx_spinlock_try(mtx_t *mtx)
42338c3ee9bSMatthew Dillon {
42438c3ee9bSMatthew Dillon 	globaldata_t gd = mycpu;
42538c3ee9bSMatthew Dillon 	u_int	lock;
42638c3ee9bSMatthew Dillon 	u_int	nlock;
42738c3ee9bSMatthew Dillon 	int	res = 0;
42838c3ee9bSMatthew Dillon 
42938c3ee9bSMatthew Dillon 	for (;;) {
43038c3ee9bSMatthew Dillon 		lock = mtx->mtx_lock;
43138c3ee9bSMatthew Dillon 		if (lock == 0) {
43238c3ee9bSMatthew Dillon 			nlock = MTX_EXCLUSIVE | 1;
43338c3ee9bSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, 0, nlock)) {
43438c3ee9bSMatthew Dillon 				mtx->mtx_owner = gd->gd_curthread;
43538c3ee9bSMatthew Dillon 				break;
43638c3ee9bSMatthew Dillon 			}
43738c3ee9bSMatthew Dillon 		} else if ((lock & MTX_EXCLUSIVE) &&
43838c3ee9bSMatthew Dillon 			   mtx->mtx_owner == gd->gd_curthread) {
43938c3ee9bSMatthew Dillon 			KKASSERT((lock & MTX_MASK) != MTX_MASK);
44038c3ee9bSMatthew Dillon 			nlock = lock + 1;
44138c3ee9bSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
44238c3ee9bSMatthew Dillon 				break;
44338c3ee9bSMatthew Dillon 		} else {
4440846e4ceSMatthew Dillon 			--gd->gd_spinlocks;
44538c3ee9bSMatthew Dillon 			cpu_ccfence();
446e8b1691fSMatthew Dillon 			crit_exit_quick(gd->gd_curthread);
44738c3ee9bSMatthew Dillon 			res = EAGAIN;
44838c3ee9bSMatthew Dillon 			break;
44938c3ee9bSMatthew Dillon 		}
45038c3ee9bSMatthew Dillon 		cpu_pause();
45138c3ee9bSMatthew Dillon 	}
45238c3ee9bSMatthew Dillon 	return res;
45338c3ee9bSMatthew Dillon }
45438c3ee9bSMatthew Dillon 
45538c3ee9bSMatthew Dillon #if 0
45638c3ee9bSMatthew Dillon 
45733b0b87cSMatthew Dillon void
458d66b88f3SMatthew Dillon _mtx_spinlock_sh(mtx_t *mtx)
45933b0b87cSMatthew Dillon {
46033b0b87cSMatthew Dillon 	u_int	lock;
46133b0b87cSMatthew Dillon 	u_int	nlock;
46233b0b87cSMatthew Dillon 	int	bb = 1;
46333b0b87cSMatthew Dillon 	int	bo;
46433b0b87cSMatthew Dillon 
46533b0b87cSMatthew Dillon 	for (;;) {
46633b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
46733b0b87cSMatthew Dillon 		if ((lock & MTX_EXCLUSIVE) == 0) {
46833b0b87cSMatthew Dillon 			KKASSERT((lock & MTX_MASK) != MTX_MASK);
46933b0b87cSMatthew Dillon 			nlock = lock + 1;
47033b0b87cSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
471685ebdabSMatthew Dillon 				break;
47233b0b87cSMatthew Dillon 		} else {
47333b0b87cSMatthew Dillon 			/* MWAIT here */
47433b0b87cSMatthew Dillon 			if (bb < 1000)
47533b0b87cSMatthew Dillon 				++bb;
47633b0b87cSMatthew Dillon 			cpu_pause();
47733b0b87cSMatthew Dillon 			for (bo = 0; bo < bb; ++bo)
47833b0b87cSMatthew Dillon 				;
47933b0b87cSMatthew Dillon 		}
480685ebdabSMatthew Dillon 		cpu_pause();
48133b0b87cSMatthew Dillon 	}
48233b0b87cSMatthew Dillon }
48333b0b87cSMatthew Dillon 
48438c3ee9bSMatthew Dillon #endif
48538c3ee9bSMatthew Dillon 
48633b0b87cSMatthew Dillon int
_mtx_lock_ex_try(mtx_t * mtx)487d66b88f3SMatthew Dillon _mtx_lock_ex_try(mtx_t *mtx)
48833b0b87cSMatthew Dillon {
48933b0b87cSMatthew Dillon 	u_int	lock;
49033b0b87cSMatthew Dillon 	u_int	nlock;
491d66b88f3SMatthew Dillon 	int	error;
49233b0b87cSMatthew Dillon 
49333b0b87cSMatthew Dillon 	for (;;) {
49433b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
49533b0b87cSMatthew Dillon 		if (lock == 0) {
49633b0b87cSMatthew Dillon 			nlock = MTX_EXCLUSIVE | 1;
49733b0b87cSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, 0, nlock)) {
498685ebdabSMatthew Dillon 				mtx->mtx_owner = curthread;
499d66b88f3SMatthew Dillon 				error = 0;
50033b0b87cSMatthew Dillon 				break;
50133b0b87cSMatthew Dillon 			}
50233b0b87cSMatthew Dillon 		} else if ((lock & MTX_EXCLUSIVE) &&
50333b0b87cSMatthew Dillon 			   mtx->mtx_owner == curthread) {
50433b0b87cSMatthew Dillon 			KKASSERT((lock & MTX_MASK) != MTX_MASK);
505685ebdabSMatthew Dillon 			nlock = lock + 1;
506d66b88f3SMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock)) {
507d66b88f3SMatthew Dillon 				error = 0;
50833b0b87cSMatthew Dillon 				break;
509d66b88f3SMatthew Dillon 			}
51033b0b87cSMatthew Dillon 		} else {
51133b0b87cSMatthew Dillon 			error = EAGAIN;
51233b0b87cSMatthew Dillon 			break;
51333b0b87cSMatthew Dillon 		}
514685ebdabSMatthew Dillon 		cpu_pause();
51533b0b87cSMatthew Dillon 	}
51633b0b87cSMatthew Dillon 	return (error);
51733b0b87cSMatthew Dillon }
51833b0b87cSMatthew Dillon 
51933b0b87cSMatthew Dillon int
_mtx_lock_sh_try(mtx_t * mtx)520d66b88f3SMatthew Dillon _mtx_lock_sh_try(mtx_t *mtx)
52133b0b87cSMatthew Dillon {
52233b0b87cSMatthew Dillon 	u_int	lock;
52333b0b87cSMatthew Dillon 	u_int	nlock;
52433b0b87cSMatthew Dillon 	int	error = 0;
52533b0b87cSMatthew Dillon 
52633b0b87cSMatthew Dillon 	for (;;) {
52733b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
52833b0b87cSMatthew Dillon 		if ((lock & MTX_EXCLUSIVE) == 0) {
52933b0b87cSMatthew Dillon 			KKASSERT((lock & MTX_MASK) != MTX_MASK);
53033b0b87cSMatthew Dillon 			nlock = lock + 1;
53133b0b87cSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
53233b0b87cSMatthew Dillon 				break;
53333b0b87cSMatthew Dillon 		} else {
53433b0b87cSMatthew Dillon 			error = EAGAIN;
53533b0b87cSMatthew Dillon 			break;
53633b0b87cSMatthew Dillon 		}
537685ebdabSMatthew Dillon 		cpu_pause();
53833b0b87cSMatthew Dillon 	}
53933b0b87cSMatthew Dillon 	return (error);
54033b0b87cSMatthew Dillon }
54133b0b87cSMatthew Dillon 
54233b0b87cSMatthew Dillon /*
54333b0b87cSMatthew Dillon  * If the lock is held exclusively it must be owned by the caller.  If the
54433b0b87cSMatthew Dillon  * lock is already a shared lock this operation is a NOP.  A panic will
54533b0b87cSMatthew Dillon  * occur if the lock is not held either shared or exclusive.
54633b0b87cSMatthew Dillon  *
54733b0b87cSMatthew Dillon  * The exclusive count is converted to a shared count.
54833b0b87cSMatthew Dillon  */
54933b0b87cSMatthew Dillon void
_mtx_downgrade(mtx_t * mtx)550d66b88f3SMatthew Dillon _mtx_downgrade(mtx_t *mtx)
55133b0b87cSMatthew Dillon {
55233b0b87cSMatthew Dillon 	u_int	lock;
55333b0b87cSMatthew Dillon 	u_int	nlock;
55433b0b87cSMatthew Dillon 
55533b0b87cSMatthew Dillon 	for (;;) {
55633b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
557d66b88f3SMatthew Dillon 		cpu_ccfence();
558d66b88f3SMatthew Dillon 
559d66b88f3SMatthew Dillon 		/*
560d66b88f3SMatthew Dillon 		 * NOP if already shared.
561d66b88f3SMatthew Dillon 		 */
56233b0b87cSMatthew Dillon 		if ((lock & MTX_EXCLUSIVE) == 0) {
56333b0b87cSMatthew Dillon 			KKASSERT((lock & MTX_MASK) > 0);
56433b0b87cSMatthew Dillon 			break;
56533b0b87cSMatthew Dillon 		}
566d66b88f3SMatthew Dillon 
567d66b88f3SMatthew Dillon 		/*
568d66b88f3SMatthew Dillon 		 * Transfer count to shared.  Any additional pending shared
569d66b88f3SMatthew Dillon 		 * waiters must be woken up.
570d66b88f3SMatthew Dillon 		 */
57133b0b87cSMatthew Dillon 		if (lock & MTX_SHWANTED) {
572124e15f9SMatthew Dillon 			if (mtx_chain_link_sh(mtx, lock))
57333b0b87cSMatthew Dillon 				break;
574d66b88f3SMatthew Dillon 			/* retry */
575d66b88f3SMatthew Dillon 		} else {
576d66b88f3SMatthew Dillon 			nlock = lock & ~MTX_EXCLUSIVE;
577d66b88f3SMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
578d66b88f3SMatthew Dillon 				break;
579d66b88f3SMatthew Dillon 			/* retry */
58033b0b87cSMatthew Dillon 		}
581685ebdabSMatthew Dillon 		cpu_pause();
58233b0b87cSMatthew Dillon 	}
58333b0b87cSMatthew Dillon }
58433b0b87cSMatthew Dillon 
58533b0b87cSMatthew Dillon /*
58633b0b87cSMatthew Dillon  * Upgrade a shared lock to an exclusive lock.  The upgrade will fail if
58733b0b87cSMatthew Dillon  * the shared lock has a count other then 1.  Optimize the most likely case
58833b0b87cSMatthew Dillon  * but note that a single cmpset can fail due to WANTED races.
58933b0b87cSMatthew Dillon  *
59033b0b87cSMatthew Dillon  * If the lock is held exclusively it must be owned by the caller and
59133b0b87cSMatthew Dillon  * this function will simply return without doing anything.   A panic will
59233b0b87cSMatthew Dillon  * occur if the lock is held exclusively by someone other then the caller.
59333b0b87cSMatthew Dillon  *
59433b0b87cSMatthew Dillon  * Returns 0 on success, EDEADLK on failure.
59533b0b87cSMatthew Dillon  */
59633b0b87cSMatthew Dillon int
_mtx_upgrade_try(mtx_t * mtx)597d66b88f3SMatthew Dillon _mtx_upgrade_try(mtx_t *mtx)
59833b0b87cSMatthew Dillon {
59933b0b87cSMatthew Dillon 	u_int	lock;
60033b0b87cSMatthew Dillon 	u_int	nlock;
60133b0b87cSMatthew Dillon 	int	error = 0;
60233b0b87cSMatthew Dillon 
60333b0b87cSMatthew Dillon 	for (;;) {
60433b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
605a9a062cdSMatthew Dillon 		cpu_ccfence();
60633b0b87cSMatthew Dillon 
60733b0b87cSMatthew Dillon 		if ((lock & ~MTX_EXWANTED) == 1) {
60833b0b87cSMatthew Dillon 			nlock = lock | MTX_EXCLUSIVE;
60933b0b87cSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock)) {
61033b0b87cSMatthew Dillon 				mtx->mtx_owner = curthread;
61133b0b87cSMatthew Dillon 				break;
61233b0b87cSMatthew Dillon 			}
61333b0b87cSMatthew Dillon 		} else if (lock & MTX_EXCLUSIVE) {
61433b0b87cSMatthew Dillon 			KKASSERT(mtx->mtx_owner == curthread);
61533b0b87cSMatthew Dillon 			break;
61633b0b87cSMatthew Dillon 		} else {
61733b0b87cSMatthew Dillon 			error = EDEADLK;
61833b0b87cSMatthew Dillon 			break;
61933b0b87cSMatthew Dillon 		}
620685ebdabSMatthew Dillon 		cpu_pause();
62133b0b87cSMatthew Dillon 	}
62233b0b87cSMatthew Dillon 	return (error);
62333b0b87cSMatthew Dillon }
62433b0b87cSMatthew Dillon 
62533b0b87cSMatthew Dillon /*
62633b0b87cSMatthew Dillon  * Unlock a lock.  The caller must hold the lock either shared or exclusive.
627685ebdabSMatthew Dillon  *
628d66b88f3SMatthew Dillon  * On the last release we handle any pending chains.
62933b0b87cSMatthew Dillon  */
63033b0b87cSMatthew Dillon void
_mtx_unlock(mtx_t * mtx)631d66b88f3SMatthew Dillon _mtx_unlock(mtx_t *mtx)
63233b0b87cSMatthew Dillon {
633ae344ea7SSepherosa Ziehau 	thread_t td __debugvar = curthread;
63433b0b87cSMatthew Dillon 	u_int	lock;
63533b0b87cSMatthew Dillon 	u_int	nlock;
63633b0b87cSMatthew Dillon 
63733b0b87cSMatthew Dillon 	for (;;) {
63833b0b87cSMatthew Dillon 		lock = mtx->mtx_lock;
639d66b88f3SMatthew Dillon 		cpu_ccfence();
640685ebdabSMatthew Dillon 
641d66b88f3SMatthew Dillon 		switch(lock) {
642d66b88f3SMatthew Dillon 		case MTX_EXCLUSIVE | 1:
643685ebdabSMatthew Dillon 			/*
644d66b88f3SMatthew Dillon 			 * Last release, exclusive lock.
645d66b88f3SMatthew Dillon 			 * No exclusive or shared requests pending.
646685ebdabSMatthew Dillon 			 */
647b5c75996SMatthew Dillon 			KKASSERT(mtx->mtx_owner == td ||
648b5c75996SMatthew Dillon 				 mtx->mtx_owner == NULL);
64933b0b87cSMatthew Dillon 			mtx->mtx_owner = NULL;
650d66b88f3SMatthew Dillon 			nlock = 0;
651d66b88f3SMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
652d66b88f3SMatthew Dillon 				goto done;
65333b0b87cSMatthew Dillon 			break;
654d66b88f3SMatthew Dillon 		case MTX_EXCLUSIVE | MTX_EXWANTED | 1:
655d66b88f3SMatthew Dillon 		case MTX_EXCLUSIVE | MTX_EXWANTED | MTX_SHWANTED | 1:
656685ebdabSMatthew Dillon 			/*
657d66b88f3SMatthew Dillon 			 * Last release, exclusive lock.
658d66b88f3SMatthew Dillon 			 * Exclusive requests pending.
659d66b88f3SMatthew Dillon 			 * Exclusive requests have priority over shared reqs.
660685ebdabSMatthew Dillon 			 */
661b5c75996SMatthew Dillon 			KKASSERT(mtx->mtx_owner == td ||
662b5c75996SMatthew Dillon 				 mtx->mtx_owner == NULL);
663b5c75996SMatthew Dillon 			mtx->mtx_owner = NULL;
664d66b88f3SMatthew Dillon 			if (mtx_chain_link_ex(mtx, lock))
665d66b88f3SMatthew Dillon 				goto done;
666685ebdabSMatthew Dillon 			break;
667d66b88f3SMatthew Dillon 		case MTX_EXCLUSIVE | MTX_SHWANTED | 1:
668685ebdabSMatthew Dillon 			/*
669d66b88f3SMatthew Dillon 			 * Last release, exclusive lock.
670685ebdabSMatthew Dillon 			 *
671d66b88f3SMatthew Dillon 			 * Shared requests are pending.  Transfer our count (1)
672d66b88f3SMatthew Dillon 			 * to the first shared request, wakeup all shared reqs.
673685ebdabSMatthew Dillon 			 */
674b5c75996SMatthew Dillon 			KKASSERT(mtx->mtx_owner == td ||
675b5c75996SMatthew Dillon 				 mtx->mtx_owner == NULL);
676b5c75996SMatthew Dillon 			mtx->mtx_owner = NULL;
677124e15f9SMatthew Dillon 			if (mtx_chain_link_sh(mtx, lock))
678d66b88f3SMatthew Dillon 				goto done;
679d66b88f3SMatthew Dillon 			break;
680d66b88f3SMatthew Dillon 		case 1:
681d66b88f3SMatthew Dillon 			/*
682d66b88f3SMatthew Dillon 			 * Last release, shared lock.
683d66b88f3SMatthew Dillon 			 * No exclusive or shared requests pending.
684d66b88f3SMatthew Dillon 			 */
685d66b88f3SMatthew Dillon 			nlock = 0;
686d66b88f3SMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
687d66b88f3SMatthew Dillon 				goto done;
688d66b88f3SMatthew Dillon 			break;
689d66b88f3SMatthew Dillon 		case MTX_EXWANTED | 1:
690d66b88f3SMatthew Dillon 		case MTX_EXWANTED | MTX_SHWANTED | 1:
691d66b88f3SMatthew Dillon 			/*
692d66b88f3SMatthew Dillon 			 * Last release, shared lock.
693d66b88f3SMatthew Dillon 			 *
694a9a062cdSMatthew Dillon 			 * Exclusive requests are pending.  Upgrade this
695a9a062cdSMatthew Dillon 			 * final shared lock to exclusive and transfer our
696d66b88f3SMatthew Dillon 			 * count (1) to the next exclusive request.
697d66b88f3SMatthew Dillon 			 *
698d66b88f3SMatthew Dillon 			 * Exclusive requests have priority over shared reqs.
699d66b88f3SMatthew Dillon 			 */
700d66b88f3SMatthew Dillon 			if (mtx_chain_link_ex(mtx, lock))
701d66b88f3SMatthew Dillon 				goto done;
702d66b88f3SMatthew Dillon 			break;
703d66b88f3SMatthew Dillon 		case MTX_SHWANTED | 1:
704d66b88f3SMatthew Dillon 			/*
705d66b88f3SMatthew Dillon 			 * Last release, shared lock.
706d66b88f3SMatthew Dillon 			 * Shared requests pending.
707d66b88f3SMatthew Dillon 			 */
708124e15f9SMatthew Dillon 			if (mtx_chain_link_sh(mtx, lock))
709d66b88f3SMatthew Dillon 				goto done;
710d66b88f3SMatthew Dillon 			break;
711d66b88f3SMatthew Dillon 		default:
712d66b88f3SMatthew Dillon 			/*
713d66b88f3SMatthew Dillon 			 * We have to loop if this is the last release but
714d66b88f3SMatthew Dillon 			 * someone is fiddling with LINKSPIN.
715d66b88f3SMatthew Dillon 			 */
716d66b88f3SMatthew Dillon 			if ((lock & MTX_MASK) == 1) {
717d66b88f3SMatthew Dillon 				KKASSERT(lock & MTX_LINKSPIN);
718685ebdabSMatthew Dillon 				break;
719685ebdabSMatthew Dillon 			}
720d66b88f3SMatthew Dillon 
721685ebdabSMatthew Dillon 			/*
722685ebdabSMatthew Dillon 			 * Not the last release (shared or exclusive)
723685ebdabSMatthew Dillon 			 */
72433b0b87cSMatthew Dillon 			nlock = lock - 1;
72533b0b87cSMatthew Dillon 			KKASSERT((nlock & MTX_MASK) != MTX_MASK);
72633b0b87cSMatthew Dillon 			if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
727d66b88f3SMatthew Dillon 				goto done;
72833b0b87cSMatthew Dillon 			break;
72933b0b87cSMatthew Dillon 		}
730d66b88f3SMatthew Dillon 		/* loop try again */
731685ebdabSMatthew Dillon 		cpu_pause();
73233b0b87cSMatthew Dillon 	}
733d66b88f3SMatthew Dillon done:
734d66b88f3SMatthew Dillon 	;
73533b0b87cSMatthew Dillon }
736685ebdabSMatthew Dillon 
737685ebdabSMatthew Dillon /*
738d66b88f3SMatthew Dillon  * Chain pending links.  Called on the last release of an exclusive or
739d66b88f3SMatthew Dillon  * shared lock when the appropriate WANTED bit is set.  mtx_lock old state
740d66b88f3SMatthew Dillon  * is passed in with the count left at 1, which we can inherit, and other
741d66b88f3SMatthew Dillon  * bits which we must adjust in a single atomic operation.
742d66b88f3SMatthew Dillon  *
743d66b88f3SMatthew Dillon  * Return non-zero on success, 0 if caller needs to retry.
744d66b88f3SMatthew Dillon  *
745d66b88f3SMatthew Dillon  * NOTE: It's ok if MTX_EXWANTED is in an indeterminant state while we are
746d66b88f3SMatthew Dillon  *	 acquiring LINKSPIN as all other cases will also need to acquire
747d66b88f3SMatthew Dillon  *	 LINKSPIN when handling the EXWANTED case.
748685ebdabSMatthew Dillon  */
749d66b88f3SMatthew Dillon static int
mtx_chain_link_ex(mtx_t * mtx,u_int olock)750d66b88f3SMatthew Dillon mtx_chain_link_ex(mtx_t *mtx, u_int olock)
751685ebdabSMatthew Dillon {
752d66b88f3SMatthew Dillon 	thread_t td = curthread;
753d66b88f3SMatthew Dillon 	mtx_link_t *link;
754685ebdabSMatthew Dillon 	u_int	nlock;
755685ebdabSMatthew Dillon 
756d66b88f3SMatthew Dillon 	olock &= ~MTX_LINKSPIN;
757a9a062cdSMatthew Dillon 	nlock = olock | MTX_LINKSPIN | MTX_EXCLUSIVE;	/* upgrade if necc */
758e8b1691fSMatthew Dillon 	crit_enter_quick(td);
759d66b88f3SMatthew Dillon 	if (atomic_cmpset_int(&mtx->mtx_lock, olock, nlock)) {
760d66b88f3SMatthew Dillon 		link = mtx->mtx_exlink;
761d66b88f3SMatthew Dillon 		KKASSERT(link != NULL);
762685ebdabSMatthew Dillon 		if (link->next == link) {
763d66b88f3SMatthew Dillon 			mtx->mtx_exlink = NULL;
764d66b88f3SMatthew Dillon 			nlock = MTX_LINKSPIN | MTX_EXWANTED;	/* to clear */
765685ebdabSMatthew Dillon 		} else {
766d66b88f3SMatthew Dillon 			mtx->mtx_exlink = link->next;
767685ebdabSMatthew Dillon 			link->next->prev = link->prev;
768685ebdabSMatthew Dillon 			link->prev->next = link->next;
769d66b88f3SMatthew Dillon 			nlock = MTX_LINKSPIN;			/* to clear */
770685ebdabSMatthew Dillon 		}
771d66b88f3SMatthew Dillon 		KKASSERT(link->state == MTX_LINK_LINKED_EX);
772685ebdabSMatthew Dillon 		mtx->mtx_owner = link->owner;
773d66b88f3SMatthew Dillon 		cpu_sfence();
774d66b88f3SMatthew Dillon 
775d66b88f3SMatthew Dillon 		/*
776d66b88f3SMatthew Dillon 		 * WARNING! The callback can only be safely
777d66b88f3SMatthew Dillon 		 *	    made with LINKSPIN still held
778d66b88f3SMatthew Dillon 		 *	    and in a critical section.
779d66b88f3SMatthew Dillon 		 *
780d66b88f3SMatthew Dillon 		 * WARNING! The link can go away after the
781d66b88f3SMatthew Dillon 		 *	    state is set, or after the
782d66b88f3SMatthew Dillon 		 *	    callback.
783d66b88f3SMatthew Dillon 		 */
784d66b88f3SMatthew Dillon 		if (link->callback) {
785d66b88f3SMatthew Dillon 			link->state = MTX_LINK_CALLEDBACK;
786d66b88f3SMatthew Dillon 			link->callback(link, link->arg, 0);
787685ebdabSMatthew Dillon 		} else {
788d66b88f3SMatthew Dillon 			link->state = MTX_LINK_ACQUIRED;
789685ebdabSMatthew Dillon 			wakeup(link);
790685ebdabSMatthew Dillon 		}
791d66b88f3SMatthew Dillon 		atomic_clear_int(&mtx->mtx_lock, nlock);
792e8b1691fSMatthew Dillon 		crit_exit_quick(td);
793d66b88f3SMatthew Dillon 		return 1;
794d66b88f3SMatthew Dillon 	}
795d66b88f3SMatthew Dillon 	/* retry */
796e8b1691fSMatthew Dillon 	crit_exit_quick(td);
797a4d95680SMatthew Dillon 
798d66b88f3SMatthew Dillon 	return 0;
799d66b88f3SMatthew Dillon }
800d66b88f3SMatthew Dillon 
801d66b88f3SMatthew Dillon /*
802d66b88f3SMatthew Dillon  * Flush waiting shared locks.  The lock's prior state is passed in and must
803124e15f9SMatthew Dillon  * be adjusted atomically only if it matches and LINKSPIN is not set.
804d66b88f3SMatthew Dillon  *
805124e15f9SMatthew Dillon  * IMPORTANT! The caller has left one active count on the lock for us to
806124e15f9SMatthew Dillon  *	      consume.  We will apply this to the first link, but must add
807124e15f9SMatthew Dillon  *	      additional counts for any other links.
808d66b88f3SMatthew Dillon  */
809d66b88f3SMatthew Dillon static int
mtx_chain_link_sh(mtx_t * mtx,u_int olock)810124e15f9SMatthew Dillon mtx_chain_link_sh(mtx_t *mtx, u_int olock)
811d66b88f3SMatthew Dillon {
812d66b88f3SMatthew Dillon 	thread_t td = curthread;
813d66b88f3SMatthew Dillon 	mtx_link_t *link;
814124e15f9SMatthew Dillon 	u_int	addcount;
815d66b88f3SMatthew Dillon 	u_int	nlock;
816d66b88f3SMatthew Dillon 
817d66b88f3SMatthew Dillon 	olock &= ~MTX_LINKSPIN;
818d66b88f3SMatthew Dillon 	nlock = olock | MTX_LINKSPIN;
819d66b88f3SMatthew Dillon 	nlock &= ~MTX_EXCLUSIVE;
820e8b1691fSMatthew Dillon 	crit_enter_quick(td);
821d66b88f3SMatthew Dillon 	if (atomic_cmpset_int(&mtx->mtx_lock, olock, nlock)) {
822124e15f9SMatthew Dillon 		/*
823124e15f9SMatthew Dillon 		 * It should not be possible for SHWANTED to be set without
824124e15f9SMatthew Dillon 		 * any links pending.
825124e15f9SMatthew Dillon 		 */
826d66b88f3SMatthew Dillon 		KKASSERT(mtx->mtx_shlink != NULL);
827124e15f9SMatthew Dillon 
828124e15f9SMatthew Dillon 		/*
829124e15f9SMatthew Dillon 		 * We have to process the count for all shared locks before
830124e15f9SMatthew Dillon 		 * we process any of the links.  Count the additional shared
831124e15f9SMatthew Dillon 		 * locks beyond the first link (which is already accounted
832124e15f9SMatthew Dillon 		 * for) and associate the full count with the lock
833124e15f9SMatthew Dillon 		 * immediately.
834124e15f9SMatthew Dillon 		 */
835124e15f9SMatthew Dillon 		addcount = 0;
836124e15f9SMatthew Dillon 		for (link = mtx->mtx_shlink->next; link != mtx->mtx_shlink;
837124e15f9SMatthew Dillon 		     link = link->next) {
838124e15f9SMatthew Dillon 			++addcount;
839124e15f9SMatthew Dillon 		}
840124e15f9SMatthew Dillon 		if (addcount > 0)
841d66b88f3SMatthew Dillon 			atomic_add_int(&mtx->mtx_lock, addcount);
842124e15f9SMatthew Dillon 
843124e15f9SMatthew Dillon 		/*
844124e15f9SMatthew Dillon 		 * We can wakeup all waiting shared locks.
845124e15f9SMatthew Dillon 		 */
846124e15f9SMatthew Dillon 		while ((link = mtx->mtx_shlink) != NULL) {
847d66b88f3SMatthew Dillon 			KKASSERT(link->state == MTX_LINK_LINKED_SH);
848d66b88f3SMatthew Dillon 			if (link->next == link) {
849d66b88f3SMatthew Dillon 				mtx->mtx_shlink = NULL;
850124e15f9SMatthew Dillon 			} else {
851124e15f9SMatthew Dillon 				mtx->mtx_shlink = link->next;
852124e15f9SMatthew Dillon 				link->next->prev = link->prev;
853124e15f9SMatthew Dillon 				link->prev->next = link->next;
854124e15f9SMatthew Dillon 			}
855124e15f9SMatthew Dillon 			link->next = NULL;
856124e15f9SMatthew Dillon 			link->prev = NULL;
857d66b88f3SMatthew Dillon 			cpu_sfence();
858d66b88f3SMatthew Dillon 			if (link->callback) {
859d66b88f3SMatthew Dillon 				link->state = MTX_LINK_CALLEDBACK;
860d66b88f3SMatthew Dillon 				link->callback(link, link->arg, 0);
861d66b88f3SMatthew Dillon 			} else {
862a9a062cdSMatthew Dillon 				cpu_sfence();
863d66b88f3SMatthew Dillon 				link->state = MTX_LINK_ACQUIRED;
864d66b88f3SMatthew Dillon 				wakeup(link);
865d66b88f3SMatthew Dillon 			}
866685ebdabSMatthew Dillon 		}
867d66b88f3SMatthew Dillon 		atomic_clear_int(&mtx->mtx_lock, MTX_LINKSPIN |
868d66b88f3SMatthew Dillon 						 MTX_SHWANTED);
869e8b1691fSMatthew Dillon 		crit_exit_quick(td);
870d66b88f3SMatthew Dillon 		return 1;
871d66b88f3SMatthew Dillon 	}
872d66b88f3SMatthew Dillon 	/* retry */
873e8b1691fSMatthew Dillon 	crit_exit_quick(td);
874a4d95680SMatthew Dillon 
875d66b88f3SMatthew Dillon 	return 0;
876685ebdabSMatthew Dillon }
877685ebdabSMatthew Dillon 
878685ebdabSMatthew Dillon /*
879685ebdabSMatthew Dillon  * Delete a link structure after tsleep has failed.  This code is not
880685ebdabSMatthew Dillon  * in the critical path as most exclusive waits are chained.
881685ebdabSMatthew Dillon  */
882685ebdabSMatthew Dillon static
883685ebdabSMatthew Dillon void
mtx_delete_link(mtx_t * mtx,mtx_link_t * link)884d66b88f3SMatthew Dillon mtx_delete_link(mtx_t *mtx, mtx_link_t *link)
885685ebdabSMatthew Dillon {
88677912481SMatthew Dillon 	thread_t td = curthread;
887685ebdabSMatthew Dillon 	u_int	lock;
888685ebdabSMatthew Dillon 	u_int	nlock;
889685ebdabSMatthew Dillon 
890685ebdabSMatthew Dillon 	/*
891d66b88f3SMatthew Dillon 	 * Acquire MTX_LINKSPIN.
892685ebdabSMatthew Dillon 	 *
893d66b88f3SMatthew Dillon 	 * Do not use cmpxchg to wait for LINKSPIN to clear as this might
894685ebdabSMatthew Dillon 	 * result in too much cpu cache traffic.
895685ebdabSMatthew Dillon 	 */
896e8b1691fSMatthew Dillon 	crit_enter_quick(td);
897685ebdabSMatthew Dillon 	for (;;) {
898685ebdabSMatthew Dillon 		lock = mtx->mtx_lock;
899d66b88f3SMatthew Dillon 		if (lock & MTX_LINKSPIN) {
900685ebdabSMatthew Dillon 			cpu_pause();
901685ebdabSMatthew Dillon 			continue;
902685ebdabSMatthew Dillon 		}
903d66b88f3SMatthew Dillon 		nlock = lock | MTX_LINKSPIN;
904685ebdabSMatthew Dillon 		if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
905685ebdabSMatthew Dillon 			break;
906685ebdabSMatthew Dillon 		cpu_pause();
907685ebdabSMatthew Dillon 	}
908685ebdabSMatthew Dillon 
909685ebdabSMatthew Dillon 	/*
910d66b88f3SMatthew Dillon 	 * Delete the link and release LINKSPIN.
911685ebdabSMatthew Dillon 	 */
912d66b88f3SMatthew Dillon 	nlock = MTX_LINKSPIN;	/* to clear */
913d66b88f3SMatthew Dillon 
914d66b88f3SMatthew Dillon 	switch(link->state) {
915d66b88f3SMatthew Dillon 	case MTX_LINK_LINKED_EX:
916685ebdabSMatthew Dillon 		if (link->next == link) {
917d66b88f3SMatthew Dillon 			mtx->mtx_exlink = NULL;
918d66b88f3SMatthew Dillon 			nlock |= MTX_EXWANTED;	/* to clear */
919685ebdabSMatthew Dillon 		} else {
920d66b88f3SMatthew Dillon 			mtx->mtx_exlink = link->next;
921685ebdabSMatthew Dillon 			link->next->prev = link->prev;
922685ebdabSMatthew Dillon 			link->prev->next = link->next;
923685ebdabSMatthew Dillon 		}
924d66b88f3SMatthew Dillon 		break;
925d66b88f3SMatthew Dillon 	case MTX_LINK_LINKED_SH:
926d66b88f3SMatthew Dillon 		if (link->next == link) {
927d66b88f3SMatthew Dillon 			mtx->mtx_shlink = NULL;
928d66b88f3SMatthew Dillon 			nlock |= MTX_SHWANTED;	/* to clear */
929d66b88f3SMatthew Dillon 		} else {
930d66b88f3SMatthew Dillon 			mtx->mtx_shlink = link->next;
931d66b88f3SMatthew Dillon 			link->next->prev = link->prev;
932d66b88f3SMatthew Dillon 			link->prev->next = link->next;
933685ebdabSMatthew Dillon 		}
934d66b88f3SMatthew Dillon 		break;
935d66b88f3SMatthew Dillon 	default:
936d66b88f3SMatthew Dillon 		/* no change */
937d66b88f3SMatthew Dillon 		break;
938d66b88f3SMatthew Dillon 	}
939d66b88f3SMatthew Dillon 	atomic_clear_int(&mtx->mtx_lock, nlock);
940e8b1691fSMatthew Dillon 	crit_exit_quick(td);
941685ebdabSMatthew Dillon }
942685ebdabSMatthew Dillon 
943685ebdabSMatthew Dillon /*
944d66b88f3SMatthew Dillon  * Wait for async lock completion or abort.  Returns ENOLCK if an abort
945d66b88f3SMatthew Dillon  * occurred.
946d66b88f3SMatthew Dillon  */
947d66b88f3SMatthew Dillon int
mtx_wait_link(mtx_t * mtx,mtx_link_t * link,int flags,int to)948cabfc9f6SMatthew Dillon mtx_wait_link(mtx_t *mtx, mtx_link_t *link, int flags, int to)
949d66b88f3SMatthew Dillon {
950b1793cc6SMatthew Dillon 	indefinite_info_t info;
951d66b88f3SMatthew Dillon 	int error;
952d66b88f3SMatthew Dillon 
953*6d0742aeSMatthew Dillon 	indefinite_init(&info, mtx, mtx->mtx_ident, 1,
9545b49787bSMatthew Dillon 			((link->state & MTX_LINK_LINKED_SH) ? 'm' : 'M'));
9555b49787bSMatthew Dillon 
956d66b88f3SMatthew Dillon 	/*
957d66b88f3SMatthew Dillon 	 * Sleep.  Handle false wakeups, interruptions, etc.
958a9a062cdSMatthew Dillon 	 * The link may also have been aborted.  The LINKED
959a9a062cdSMatthew Dillon 	 * bit was set by this cpu so we can test it without
960a9a062cdSMatthew Dillon 	 * fences.
961d66b88f3SMatthew Dillon 	 */
962d66b88f3SMatthew Dillon 	error = 0;
963d66b88f3SMatthew Dillon 	while (link->state & MTX_LINK_LINKED) {
964d66b88f3SMatthew Dillon 		tsleep_interlock(link, 0);
965d66b88f3SMatthew Dillon 		cpu_lfence();
966d66b88f3SMatthew Dillon 		if (link->state & MTX_LINK_LINKED) {
967d66b88f3SMatthew Dillon 			error = tsleep(link, flags | PINTERLOCKED,
968cabfc9f6SMatthew Dillon 				       mtx->mtx_ident, to);
969d66b88f3SMatthew Dillon 			if (error)
970d66b88f3SMatthew Dillon 				break;
971d66b88f3SMatthew Dillon 		}
9725b49787bSMatthew Dillon 		if ((mtx->mtx_flags & MTXF_NOCOLLSTATS) == 0)
973b1793cc6SMatthew Dillon 			indefinite_check(&info);
974d66b88f3SMatthew Dillon 	}
975d66b88f3SMatthew Dillon 
976d66b88f3SMatthew Dillon 	/*
977a9a062cdSMatthew Dillon 	 * We need at least a lfence (load fence) to ensure our cpu does not
978a9a062cdSMatthew Dillon 	 * reorder loads (of data outside the lock structure) prior to the
979a9a062cdSMatthew Dillon 	 * remote cpu's release, since the above test may have run without
980a9a062cdSMatthew Dillon 	 * any atomic interactions.
981a9a062cdSMatthew Dillon 	 *
982a9a062cdSMatthew Dillon 	 * If we do not do this then state updated by the other cpu before
983a9a062cdSMatthew Dillon 	 * releasing its lock may not be read cleanly by our cpu when this
984a9a062cdSMatthew Dillon 	 * function returns.  Even though the other cpu ordered its stores,
985a9a062cdSMatthew Dillon 	 * our loads can still be out of order.
986a9a062cdSMatthew Dillon 	 */
987a9a062cdSMatthew Dillon 	cpu_mfence();
988a9a062cdSMatthew Dillon 
989a9a062cdSMatthew Dillon 	/*
990d66b88f3SMatthew Dillon 	 * We are done, make sure the link structure is unlinked.
991d66b88f3SMatthew Dillon 	 * It may still be on the list due to e.g. EINTR or
992d66b88f3SMatthew Dillon 	 * EWOULDBLOCK.
993d66b88f3SMatthew Dillon 	 *
994d66b88f3SMatthew Dillon 	 * It is possible for the tsleep to race an ABORT and cause
995d66b88f3SMatthew Dillon 	 * error to be 0.
996d66b88f3SMatthew Dillon 	 *
997d66b88f3SMatthew Dillon 	 * The tsleep() can be woken up for numerous reasons and error
998d66b88f3SMatthew Dillon 	 * might be zero in situations where we intend to return an error.
999d66b88f3SMatthew Dillon 	 *
1000d66b88f3SMatthew Dillon 	 * (This is the synchronous case so state cannot be CALLEDBACK)
1001d66b88f3SMatthew Dillon 	 */
1002d66b88f3SMatthew Dillon 	switch(link->state) {
1003d66b88f3SMatthew Dillon 	case MTX_LINK_ACQUIRED:
1004d66b88f3SMatthew Dillon 	case MTX_LINK_CALLEDBACK:
1005d66b88f3SMatthew Dillon 		error = 0;
1006d66b88f3SMatthew Dillon 		break;
1007d66b88f3SMatthew Dillon 	case MTX_LINK_ABORTED:
1008d66b88f3SMatthew Dillon 		error = ENOLCK;
1009d66b88f3SMatthew Dillon 		break;
1010d66b88f3SMatthew Dillon 	case MTX_LINK_LINKED_EX:
1011d66b88f3SMatthew Dillon 	case MTX_LINK_LINKED_SH:
1012d66b88f3SMatthew Dillon 		mtx_delete_link(mtx, link);
1013d66b88f3SMatthew Dillon 		/* fall through */
1014d66b88f3SMatthew Dillon 	default:
1015d66b88f3SMatthew Dillon 		if (error == 0)
1016d66b88f3SMatthew Dillon 			error = EWOULDBLOCK;
1017d66b88f3SMatthew Dillon 		break;
1018d66b88f3SMatthew Dillon 	}
1019d66b88f3SMatthew Dillon 
1020d66b88f3SMatthew Dillon 	/*
1021d66b88f3SMatthew Dillon 	 * Clear state on status returned.
1022d66b88f3SMatthew Dillon 	 */
1023d66b88f3SMatthew Dillon 	link->state = MTX_LINK_IDLE;
1024d66b88f3SMatthew Dillon 
10255b49787bSMatthew Dillon 	if ((mtx->mtx_flags & MTXF_NOCOLLSTATS) == 0)
1026b1793cc6SMatthew Dillon 		indefinite_done(&info);
10275b49787bSMatthew Dillon 
1028d66b88f3SMatthew Dillon 	return error;
1029d66b88f3SMatthew Dillon }
1030d66b88f3SMatthew Dillon 
1031d66b88f3SMatthew Dillon /*
1032685ebdabSMatthew Dillon  * Abort a mutex locking operation, causing mtx_lock_ex_link() to
1033d66b88f3SMatthew Dillon  * return ENOLCK.  This may be called at any time after the mtx_link
1034d66b88f3SMatthew Dillon  * is initialized or the status from a previous lock has been
1035d66b88f3SMatthew Dillon  * returned.  If called prior to the next (non-try) lock attempt, the
1036d66b88f3SMatthew Dillon  * next lock attempt using this link structure will abort instantly.
1037d66b88f3SMatthew Dillon  *
1038d66b88f3SMatthew Dillon  * Caller must still wait for the operation to complete, either from a
1039d66b88f3SMatthew Dillon  * blocking call that is still in progress or by calling mtx_wait_link().
1040d66b88f3SMatthew Dillon  *
1041d66b88f3SMatthew Dillon  * If an asynchronous lock request is possibly in-progress, the caller
1042d66b88f3SMatthew Dillon  * should call mtx_wait_link() synchronously.  Note that the asynchronous
1043d66b88f3SMatthew Dillon  * lock callback will NOT be called if a successful abort occurred. XXX
1044685ebdabSMatthew Dillon  */
1045685ebdabSMatthew Dillon void
mtx_abort_link(mtx_t * mtx,mtx_link_t * link)1046d66b88f3SMatthew Dillon mtx_abort_link(mtx_t *mtx, mtx_link_t *link)
1047685ebdabSMatthew Dillon {
104877912481SMatthew Dillon 	thread_t td = curthread;
1049685ebdabSMatthew Dillon 	u_int	lock;
1050685ebdabSMatthew Dillon 	u_int	nlock;
1051685ebdabSMatthew Dillon 
1052685ebdabSMatthew Dillon 	/*
1053d66b88f3SMatthew Dillon 	 * Acquire MTX_LINKSPIN
1054685ebdabSMatthew Dillon 	 */
1055e8b1691fSMatthew Dillon 	crit_enter_quick(td);
1056685ebdabSMatthew Dillon 	for (;;) {
1057685ebdabSMatthew Dillon 		lock = mtx->mtx_lock;
1058d66b88f3SMatthew Dillon 		if (lock & MTX_LINKSPIN) {
1059685ebdabSMatthew Dillon 			cpu_pause();
1060685ebdabSMatthew Dillon 			continue;
1061685ebdabSMatthew Dillon 		}
1062d66b88f3SMatthew Dillon 		nlock = lock | MTX_LINKSPIN;
1063685ebdabSMatthew Dillon 		if (atomic_cmpset_int(&mtx->mtx_lock, lock, nlock))
1064685ebdabSMatthew Dillon 			break;
1065685ebdabSMatthew Dillon 		cpu_pause();
1066685ebdabSMatthew Dillon 	}
1067685ebdabSMatthew Dillon 
1068685ebdabSMatthew Dillon 	/*
1069d66b88f3SMatthew Dillon 	 * Do the abort.
1070d66b88f3SMatthew Dillon 	 *
1071d66b88f3SMatthew Dillon 	 * WARNING! Link structure can disappear once link->state is set.
1072685ebdabSMatthew Dillon 	 */
1073d66b88f3SMatthew Dillon 	nlock = MTX_LINKSPIN;	/* to clear */
1074d66b88f3SMatthew Dillon 
1075685ebdabSMatthew Dillon 	switch(link->state) {
1076685ebdabSMatthew Dillon 	case MTX_LINK_IDLE:
1077685ebdabSMatthew Dillon 		/*
1078685ebdabSMatthew Dillon 		 * Link not started yet
1079685ebdabSMatthew Dillon 		 */
1080685ebdabSMatthew Dillon 		link->state = MTX_LINK_ABORTED;
1081685ebdabSMatthew Dillon 		break;
1082d66b88f3SMatthew Dillon 	case MTX_LINK_LINKED_EX:
1083685ebdabSMatthew Dillon 		/*
1084d66b88f3SMatthew Dillon 		 * de-link, mark aborted, and potentially wakeup the thread
1085d66b88f3SMatthew Dillon 		 * or issue the callback.
1086685ebdabSMatthew Dillon 		 */
1087685ebdabSMatthew Dillon 		if (link->next == link) {
1088d66b88f3SMatthew Dillon 			if (mtx->mtx_exlink == link) {
1089d66b88f3SMatthew Dillon 				mtx->mtx_exlink = NULL;
1090d66b88f3SMatthew Dillon 				nlock |= MTX_EXWANTED;	/* to clear */
1091d66b88f3SMatthew Dillon 			}
1092685ebdabSMatthew Dillon 		} else {
1093d66b88f3SMatthew Dillon 			if (mtx->mtx_exlink == link)
1094d66b88f3SMatthew Dillon 				mtx->mtx_exlink = link->next;
1095685ebdabSMatthew Dillon 			link->next->prev = link->prev;
1096685ebdabSMatthew Dillon 			link->prev->next = link->next;
1097685ebdabSMatthew Dillon 		}
1098d66b88f3SMatthew Dillon 
1099d66b88f3SMatthew Dillon 		/*
1100d66b88f3SMatthew Dillon 		 * When aborting the async callback is still made.  We must
1101d66b88f3SMatthew Dillon 		 * not set the link status to ABORTED in the callback case
1102d66b88f3SMatthew Dillon 		 * since there is nothing else to clear its status if the
1103d66b88f3SMatthew Dillon 		 * link is reused.
1104d66b88f3SMatthew Dillon 		 */
1105d66b88f3SMatthew Dillon 		if (link->callback) {
1106d66b88f3SMatthew Dillon 			link->state = MTX_LINK_CALLEDBACK;
1107d66b88f3SMatthew Dillon 			link->callback(link, link->arg, ENOLCK);
1108d66b88f3SMatthew Dillon 		} else {
1109685ebdabSMatthew Dillon 			link->state = MTX_LINK_ABORTED;
1110685ebdabSMatthew Dillon 			wakeup(link);
1111d66b88f3SMatthew Dillon 		}
1112d66b88f3SMatthew Dillon 		break;
1113d66b88f3SMatthew Dillon 	case MTX_LINK_LINKED_SH:
1114d66b88f3SMatthew Dillon 		/*
1115d66b88f3SMatthew Dillon 		 * de-link, mark aborted, and potentially wakeup the thread
1116d66b88f3SMatthew Dillon 		 * or issue the callback.
1117d66b88f3SMatthew Dillon 		 */
1118d66b88f3SMatthew Dillon 		if (link->next == link) {
1119d66b88f3SMatthew Dillon 			if (mtx->mtx_shlink == link) {
1120d66b88f3SMatthew Dillon 				mtx->mtx_shlink = NULL;
1121d66b88f3SMatthew Dillon 				nlock |= MTX_SHWANTED;	/* to clear */
1122d66b88f3SMatthew Dillon 			}
1123d66b88f3SMatthew Dillon 		} else {
1124d66b88f3SMatthew Dillon 			if (mtx->mtx_shlink == link)
1125d66b88f3SMatthew Dillon 				mtx->mtx_shlink = link->next;
1126d66b88f3SMatthew Dillon 			link->next->prev = link->prev;
1127d66b88f3SMatthew Dillon 			link->prev->next = link->next;
1128d66b88f3SMatthew Dillon 		}
1129d66b88f3SMatthew Dillon 
1130d66b88f3SMatthew Dillon 		/*
1131d66b88f3SMatthew Dillon 		 * When aborting the async callback is still made.  We must
1132d66b88f3SMatthew Dillon 		 * not set the link status to ABORTED in the callback case
1133d66b88f3SMatthew Dillon 		 * since there is nothing else to clear its status if the
1134d66b88f3SMatthew Dillon 		 * link is reused.
1135d66b88f3SMatthew Dillon 		 */
1136d66b88f3SMatthew Dillon 		if (link->callback) {
1137d66b88f3SMatthew Dillon 			link->state = MTX_LINK_CALLEDBACK;
1138d66b88f3SMatthew Dillon 			link->callback(link, link->arg, ENOLCK);
1139d66b88f3SMatthew Dillon 		} else {
1140d66b88f3SMatthew Dillon 			link->state = MTX_LINK_ABORTED;
1141d66b88f3SMatthew Dillon 			wakeup(link);
1142d66b88f3SMatthew Dillon 		}
1143685ebdabSMatthew Dillon 		break;
1144685ebdabSMatthew Dillon 	case MTX_LINK_ACQUIRED:
1145d66b88f3SMatthew Dillon 	case MTX_LINK_CALLEDBACK:
1146685ebdabSMatthew Dillon 		/*
1147685ebdabSMatthew Dillon 		 * Too late, the lock was acquired.  Let it complete.
1148685ebdabSMatthew Dillon 		 */
1149685ebdabSMatthew Dillon 		break;
1150685ebdabSMatthew Dillon 	default:
1151685ebdabSMatthew Dillon 		/*
1152685ebdabSMatthew Dillon 		 * link already aborted, do nothing.
1153685ebdabSMatthew Dillon 		 */
1154685ebdabSMatthew Dillon 		break;
1155685ebdabSMatthew Dillon 	}
1156d66b88f3SMatthew Dillon 	atomic_clear_int(&mtx->mtx_lock, nlock);
1157e8b1691fSMatthew Dillon 	crit_exit_quick(td);
1158685ebdabSMatthew Dillon }
1159