xref: /freebsd/sys/kern/kern_rmlock.c (revision ea77ce6ef9d2f3971d1190cf82e3b6f4d45fe433)
1f53d15feSStephan Uphoff /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
4f53d15feSStephan Uphoff  * Copyright (c) 2007 Stephan Uphoff <ups@FreeBSD.org>
5f53d15feSStephan Uphoff  * All rights reserved.
6f53d15feSStephan Uphoff  *
7f53d15feSStephan Uphoff  * Redistribution and use in source and binary forms, with or without
8f53d15feSStephan Uphoff  * modification, are permitted provided that the following conditions
9f53d15feSStephan Uphoff  * are met:
10f53d15feSStephan Uphoff  * 1. Redistributions of source code must retain the above copyright
11f53d15feSStephan Uphoff  *    notice, this list of conditions and the following disclaimer.
12f53d15feSStephan Uphoff  * 2. Redistributions in binary form must reproduce the above copyright
13f53d15feSStephan Uphoff  *    notice, this list of conditions and the following disclaimer in the
14f53d15feSStephan Uphoff  *    documentation and/or other materials provided with the distribution.
15f53d15feSStephan Uphoff  * 3. Neither the name of the author nor the names of any co-contributors
16f53d15feSStephan Uphoff  *    may be used to endorse or promote products derived from this software
17f53d15feSStephan Uphoff  *    without specific prior written permission.
18f53d15feSStephan Uphoff  *
19f53d15feSStephan Uphoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20f53d15feSStephan Uphoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21f53d15feSStephan Uphoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22f53d15feSStephan Uphoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23f53d15feSStephan Uphoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24f53d15feSStephan Uphoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25f53d15feSStephan Uphoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26f53d15feSStephan Uphoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27f53d15feSStephan Uphoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28f53d15feSStephan Uphoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29f53d15feSStephan Uphoff  * SUCH DAMAGE.
30f53d15feSStephan Uphoff  */
31f53d15feSStephan Uphoff 
32f53d15feSStephan Uphoff /*
33f53d15feSStephan Uphoff  * Machine independent bits of reader/writer lock implementation.
34f53d15feSStephan Uphoff  */
35f53d15feSStephan Uphoff 
36f53d15feSStephan Uphoff #include <sys/cdefs.h>
37f53d15feSStephan Uphoff __FBSDID("$FreeBSD$");
38f53d15feSStephan Uphoff 
39f53d15feSStephan Uphoff #include "opt_ddb.h"
40f53d15feSStephan Uphoff 
41f53d15feSStephan Uphoff #include <sys/param.h>
42f53d15feSStephan Uphoff #include <sys/systm.h>
43f53d15feSStephan Uphoff 
44f53d15feSStephan Uphoff #include <sys/kernel.h>
45cd2fe4e6SAttilio Rao #include <sys/kdb.h>
46f53d15feSStephan Uphoff #include <sys/ktr.h>
47f53d15feSStephan Uphoff #include <sys/lock.h>
48f53d15feSStephan Uphoff #include <sys/mutex.h>
49f53d15feSStephan Uphoff #include <sys/proc.h>
50f53d15feSStephan Uphoff #include <sys/rmlock.h>
51f53d15feSStephan Uphoff #include <sys/sched.h>
52f53d15feSStephan Uphoff #include <sys/smp.h>
53f53d15feSStephan Uphoff #include <sys/turnstile.h>
54f53d15feSStephan Uphoff #include <sys/lock_profile.h>
55f53d15feSStephan Uphoff #include <machine/cpu.h>
561f162fefSMateusz Guzik #include <vm/uma.h>
57f53d15feSStephan Uphoff 
58f53d15feSStephan Uphoff #ifdef DDB
59f53d15feSStephan Uphoff #include <ddb/ddb.h>
60f53d15feSStephan Uphoff #endif
61f53d15feSStephan Uphoff 
62cd32bd7aSJohn Baldwin /*
63cd32bd7aSJohn Baldwin  * A cookie to mark destroyed rmlocks.  This is stored in the head of
64cd32bd7aSJohn Baldwin  * rm_activeReaders.
65cd32bd7aSJohn Baldwin  */
66cd32bd7aSJohn Baldwin #define	RM_DESTROYED	((void *)0xdead)
67cd32bd7aSJohn Baldwin 
68cd32bd7aSJohn Baldwin #define	rm_destroyed(rm)						\
69cd32bd7aSJohn Baldwin 	(LIST_FIRST(&(rm)->rm_activeReaders) == RM_DESTROYED)
70cd32bd7aSJohn Baldwin 
71f53d15feSStephan Uphoff #define RMPF_ONQUEUE	1
72f53d15feSStephan Uphoff #define RMPF_SIGNAL	2
73f53d15feSStephan Uphoff 
74cd32bd7aSJohn Baldwin #ifndef INVARIANTS
75cd32bd7aSJohn Baldwin #define	_rm_assert(c, what, file, line)
76cd32bd7aSJohn Baldwin #endif
77f53d15feSStephan Uphoff 
78d576deedSPawel Jakub Dawidek static void	assert_rm(const struct lock_object *lock, int what);
79cd32bd7aSJohn Baldwin #ifdef DDB
80cd32bd7aSJohn Baldwin static void	db_show_rm(const struct lock_object *lock);
81cd32bd7aSJohn Baldwin #endif
827faf4d90SDavide Italiano static void	lock_rm(struct lock_object *lock, uintptr_t how);
83a5aedd68SStacey Son #ifdef KDTRACE_HOOKS
84d576deedSPawel Jakub Dawidek static int	owner_rm(const struct lock_object *lock, struct thread **owner);
85a5aedd68SStacey Son #endif
867faf4d90SDavide Italiano static uintptr_t unlock_rm(struct lock_object *lock);
87f53d15feSStephan Uphoff 
88f53d15feSStephan Uphoff struct lock_class lock_class_rm = {
89f53d15feSStephan Uphoff 	.lc_name = "rm",
90f53d15feSStephan Uphoff 	.lc_flags = LC_SLEEPLOCK | LC_RECURSABLE,
91f9721b43SAttilio Rao 	.lc_assert = assert_rm,
92f53d15feSStephan Uphoff #ifdef DDB
93cd32bd7aSJohn Baldwin 	.lc_ddb_show = db_show_rm,
94f53d15feSStephan Uphoff #endif
95cd32bd7aSJohn Baldwin 	.lc_lock = lock_rm,
96cd32bd7aSJohn Baldwin 	.lc_unlock = unlock_rm,
97cd32bd7aSJohn Baldwin #ifdef KDTRACE_HOOKS
98cd32bd7aSJohn Baldwin 	.lc_owner = owner_rm,
99cd32bd7aSJohn Baldwin #endif
100cd32bd7aSJohn Baldwin };
101cd32bd7aSJohn Baldwin 
102cd32bd7aSJohn Baldwin struct lock_class lock_class_rm_sleepable = {
103cd32bd7aSJohn Baldwin 	.lc_name = "sleepable rm",
104cd32bd7aSJohn Baldwin 	.lc_flags = LC_SLEEPLOCK | LC_SLEEPABLE | LC_RECURSABLE,
105cd32bd7aSJohn Baldwin 	.lc_assert = assert_rm,
106cd32bd7aSJohn Baldwin #ifdef DDB
107cd32bd7aSJohn Baldwin 	.lc_ddb_show = db_show_rm,
108f53d15feSStephan Uphoff #endif
109f53d15feSStephan Uphoff 	.lc_lock = lock_rm,
110f53d15feSStephan Uphoff 	.lc_unlock = unlock_rm,
111a5aedd68SStacey Son #ifdef KDTRACE_HOOKS
112a5aedd68SStacey Son 	.lc_owner = owner_rm,
113a5aedd68SStacey Son #endif
114f53d15feSStephan Uphoff };
115f53d15feSStephan Uphoff 
116f53d15feSStephan Uphoff static void
117d576deedSPawel Jakub Dawidek assert_rm(const struct lock_object *lock, int what)
118f9721b43SAttilio Rao {
119f9721b43SAttilio Rao 
120cd32bd7aSJohn Baldwin 	rm_assert((const struct rmlock *)lock, what);
121f9721b43SAttilio Rao }
122f9721b43SAttilio Rao 
123f9721b43SAttilio Rao static void
1247faf4d90SDavide Italiano lock_rm(struct lock_object *lock, uintptr_t how)
125d02add54SRobert Watson {
126cd32bd7aSJohn Baldwin 	struct rmlock *rm;
1277faf4d90SDavide Italiano 	struct rm_priotracker *tracker;
128d02add54SRobert Watson 
129cd32bd7aSJohn Baldwin 	rm = (struct rmlock *)lock;
1307faf4d90SDavide Italiano 	if (how == 0)
131cd32bd7aSJohn Baldwin 		rm_wlock(rm);
1327faf4d90SDavide Italiano 	else {
1337faf4d90SDavide Italiano 		tracker = (struct rm_priotracker *)how;
1347faf4d90SDavide Italiano 		rm_rlock(rm, tracker);
1357faf4d90SDavide Italiano 	}
136f53d15feSStephan Uphoff }
137f53d15feSStephan Uphoff 
1387faf4d90SDavide Italiano static uintptr_t
139d02add54SRobert Watson unlock_rm(struct lock_object *lock)
140d02add54SRobert Watson {
1417faf4d90SDavide Italiano 	struct thread *td;
1427faf4d90SDavide Italiano 	struct pcpu *pc;
143cd32bd7aSJohn Baldwin 	struct rmlock *rm;
1447faf4d90SDavide Italiano 	struct rm_queue *queue;
1457faf4d90SDavide Italiano 	struct rm_priotracker *tracker;
1467faf4d90SDavide Italiano 	uintptr_t how;
147d02add54SRobert Watson 
148cd32bd7aSJohn Baldwin 	rm = (struct rmlock *)lock;
1497faf4d90SDavide Italiano 	tracker = NULL;
1507faf4d90SDavide Italiano 	how = 0;
1517faf4d90SDavide Italiano 	rm_assert(rm, RA_LOCKED | RA_NOTRECURSED);
1527faf4d90SDavide Italiano 	if (rm_wowned(rm))
153cd32bd7aSJohn Baldwin 		rm_wunlock(rm);
1547faf4d90SDavide Italiano 	else {
1557faf4d90SDavide Italiano 		/*
1567faf4d90SDavide Italiano 		 * Find the right rm_priotracker structure for curthread.
1577faf4d90SDavide Italiano 		 * The guarantee about its uniqueness is given by the fact
1587faf4d90SDavide Italiano 		 * we already asserted the lock wasn't recursively acquired.
1597faf4d90SDavide Italiano 		 */
1607faf4d90SDavide Italiano 		critical_enter();
1617faf4d90SDavide Italiano 		td = curthread;
162e2a8d178SJason A. Harmening 		pc = get_pcpu();
1637faf4d90SDavide Italiano 		for (queue = pc->pc_rm_queue.rmq_next;
1647faf4d90SDavide Italiano 		    queue != &pc->pc_rm_queue; queue = queue->rmq_next) {
1657faf4d90SDavide Italiano 			tracker = (struct rm_priotracker *)queue;
1667faf4d90SDavide Italiano 				if ((tracker->rmp_rmlock == rm) &&
1677faf4d90SDavide Italiano 				    (tracker->rmp_thread == td)) {
1687faf4d90SDavide Italiano 					how = (uintptr_t)tracker;
1697faf4d90SDavide Italiano 					break;
1707faf4d90SDavide Italiano 				}
1717faf4d90SDavide Italiano 		}
1727faf4d90SDavide Italiano 		KASSERT(tracker != NULL,
1737faf4d90SDavide Italiano 		    ("rm_priotracker is non-NULL when lock held in read mode"));
1747faf4d90SDavide Italiano 		critical_exit();
1757faf4d90SDavide Italiano 		rm_runlock(rm, tracker);
1767faf4d90SDavide Italiano 	}
1777faf4d90SDavide Italiano 	return (how);
178f53d15feSStephan Uphoff }
179f53d15feSStephan Uphoff 
180a5aedd68SStacey Son #ifdef KDTRACE_HOOKS
181a5aedd68SStacey Son static int
182d576deedSPawel Jakub Dawidek owner_rm(const struct lock_object *lock, struct thread **owner)
183a5aedd68SStacey Son {
184cd32bd7aSJohn Baldwin 	const struct rmlock *rm;
185cd32bd7aSJohn Baldwin 	struct lock_class *lc;
186a5aedd68SStacey Son 
187cd32bd7aSJohn Baldwin 	rm = (const struct rmlock *)lock;
188cd32bd7aSJohn Baldwin 	lc = LOCK_CLASS(&rm->rm_wlock_object);
189cd32bd7aSJohn Baldwin 	return (lc->lc_owner(&rm->rm_wlock_object, owner));
190a5aedd68SStacey Son }
191a5aedd68SStacey Son #endif
192a5aedd68SStacey Son 
193f53d15feSStephan Uphoff static struct mtx rm_spinlock;
194f53d15feSStephan Uphoff 
195f53d15feSStephan Uphoff MTX_SYSINIT(rm_spinlock, &rm_spinlock, "rm_spinlock", MTX_SPIN);
196f53d15feSStephan Uphoff 
197f53d15feSStephan Uphoff /*
198c7ca33d1SRobert Watson  * Add or remove tracker from per-cpu list.
199d02add54SRobert Watson  *
200c7ca33d1SRobert Watson  * The per-cpu list can be traversed at any time in forward direction from an
201d02add54SRobert Watson  * interrupt on the *local* cpu.
202f53d15feSStephan Uphoff  */
203f53d15feSStephan Uphoff static void inline
204d02add54SRobert Watson rm_tracker_add(struct pcpu *pc, struct rm_priotracker *tracker)
205d02add54SRobert Watson {
206f53d15feSStephan Uphoff 	struct rm_queue *next;
207d02add54SRobert Watson 
208f53d15feSStephan Uphoff 	/* Initialize all tracker pointers */
209f53d15feSStephan Uphoff 	tracker->rmp_cpuQueue.rmq_prev = &pc->pc_rm_queue;
210f53d15feSStephan Uphoff 	next = pc->pc_rm_queue.rmq_next;
211f53d15feSStephan Uphoff 	tracker->rmp_cpuQueue.rmq_next = next;
212d02add54SRobert Watson 
213d02add54SRobert Watson 	/* rmq_prev is not used during froward traversal. */
214f53d15feSStephan Uphoff 	next->rmq_prev = &tracker->rmp_cpuQueue;
215d02add54SRobert Watson 
216d02add54SRobert Watson 	/* Update pointer to first element. */
217f53d15feSStephan Uphoff 	pc->pc_rm_queue.rmq_next = &tracker->rmp_cpuQueue;
218f53d15feSStephan Uphoff }
219f53d15feSStephan Uphoff 
220cd32bd7aSJohn Baldwin /*
221cd32bd7aSJohn Baldwin  * Return a count of the number of trackers the thread 'td' already
222cd32bd7aSJohn Baldwin  * has on this CPU for the lock 'rm'.
223cd32bd7aSJohn Baldwin  */
224cd32bd7aSJohn Baldwin static int
225cd32bd7aSJohn Baldwin rm_trackers_present(const struct pcpu *pc, const struct rmlock *rm,
226cd32bd7aSJohn Baldwin     const struct thread *td)
227cd32bd7aSJohn Baldwin {
228cd32bd7aSJohn Baldwin 	struct rm_queue *queue;
229cd32bd7aSJohn Baldwin 	struct rm_priotracker *tracker;
230cd32bd7aSJohn Baldwin 	int count;
231cd32bd7aSJohn Baldwin 
232cd32bd7aSJohn Baldwin 	count = 0;
233cd32bd7aSJohn Baldwin 	for (queue = pc->pc_rm_queue.rmq_next; queue != &pc->pc_rm_queue;
234cd32bd7aSJohn Baldwin 	    queue = queue->rmq_next) {
235cd32bd7aSJohn Baldwin 		tracker = (struct rm_priotracker *)queue;
236cd32bd7aSJohn Baldwin 		if ((tracker->rmp_rmlock == rm) && (tracker->rmp_thread == td))
237cd32bd7aSJohn Baldwin 			count++;
238cd32bd7aSJohn Baldwin 	}
239cd32bd7aSJohn Baldwin 	return (count);
240cd32bd7aSJohn Baldwin }
241cd32bd7aSJohn Baldwin 
242f53d15feSStephan Uphoff static void inline
243d02add54SRobert Watson rm_tracker_remove(struct pcpu *pc, struct rm_priotracker *tracker)
244d02add54SRobert Watson {
245f53d15feSStephan Uphoff 	struct rm_queue *next, *prev;
246d02add54SRobert Watson 
247f53d15feSStephan Uphoff 	next = tracker->rmp_cpuQueue.rmq_next;
248f53d15feSStephan Uphoff 	prev = tracker->rmp_cpuQueue.rmq_prev;
249d02add54SRobert Watson 
250d02add54SRobert Watson 	/* Not used during forward traversal. */
251f53d15feSStephan Uphoff 	next->rmq_prev = prev;
252d02add54SRobert Watson 
253d02add54SRobert Watson 	/* Remove from list. */
254f53d15feSStephan Uphoff 	prev->rmq_next = next;
255f53d15feSStephan Uphoff }
256f53d15feSStephan Uphoff 
257d02add54SRobert Watson static void
258d02add54SRobert Watson rm_cleanIPI(void *arg)
259d02add54SRobert Watson {
260f53d15feSStephan Uphoff 	struct pcpu *pc;
261f53d15feSStephan Uphoff 	struct rmlock *rm = arg;
262f53d15feSStephan Uphoff 	struct rm_priotracker *tracker;
263f53d15feSStephan Uphoff 	struct rm_queue *queue;
264e2a8d178SJason A. Harmening 	pc = get_pcpu();
265f53d15feSStephan Uphoff 
266d02add54SRobert Watson 	for (queue = pc->pc_rm_queue.rmq_next; queue != &pc->pc_rm_queue;
267f53d15feSStephan Uphoff 	    queue = queue->rmq_next) {
268f53d15feSStephan Uphoff 		tracker = (struct rm_priotracker *)queue;
269f53d15feSStephan Uphoff 		if (tracker->rmp_rmlock == rm && tracker->rmp_flags == 0) {
270f53d15feSStephan Uphoff 			tracker->rmp_flags = RMPF_ONQUEUE;
271f53d15feSStephan Uphoff 			mtx_lock_spin(&rm_spinlock);
272f53d15feSStephan Uphoff 			LIST_INSERT_HEAD(&rm->rm_activeReaders, tracker,
273f53d15feSStephan Uphoff 			    rmp_qentry);
274f53d15feSStephan Uphoff 			mtx_unlock_spin(&rm_spinlock);
275f53d15feSStephan Uphoff 		}
276f53d15feSStephan Uphoff 	}
277f53d15feSStephan Uphoff }
278f53d15feSStephan Uphoff 
279f53d15feSStephan Uphoff void
2801a109c1cSRobert Watson rm_init_flags(struct rmlock *rm, const char *name, int opts)
281f53d15feSStephan Uphoff {
282cd32bd7aSJohn Baldwin 	struct lock_class *lc;
283fd07ddcfSDmitry Chagin 	int liflags, xflags;
284d02add54SRobert Watson 
2851a109c1cSRobert Watson 	liflags = 0;
2861a109c1cSRobert Watson 	if (!(opts & RM_NOWITNESS))
2871a109c1cSRobert Watson 		liflags |= LO_WITNESS;
2881a109c1cSRobert Watson 	if (opts & RM_RECURSE)
2891a109c1cSRobert Watson 		liflags |= LO_RECURSABLE;
290fd07ddcfSDmitry Chagin 	if (opts & RM_NEW)
291fd07ddcfSDmitry Chagin 		liflags |= LO_NEW;
29236058c09SMax Laier 	rm->rm_writecpus = all_cpus;
293f53d15feSStephan Uphoff 	LIST_INIT(&rm->rm_activeReaders);
29436058c09SMax Laier 	if (opts & RM_SLEEPABLE) {
295cd32bd7aSJohn Baldwin 		liflags |= LO_SLEEPABLE;
296cd32bd7aSJohn Baldwin 		lc = &lock_class_rm_sleepable;
297fd07ddcfSDmitry Chagin 		xflags = (opts & RM_NEW ? SX_NEW : 0);
298fd07ddcfSDmitry Chagin 		sx_init_flags(&rm->rm_lock_sx, "rmlock_sx",
299fd07ddcfSDmitry Chagin 		    xflags | SX_NOWITNESS);
300cd32bd7aSJohn Baldwin 	} else {
301cd32bd7aSJohn Baldwin 		lc = &lock_class_rm;
302fd07ddcfSDmitry Chagin 		xflags = (opts & RM_NEW ? MTX_NEW : 0);
303fd07ddcfSDmitry Chagin 		mtx_init(&rm->rm_lock_mtx, name, "rmlock_mtx",
304fd07ddcfSDmitry Chagin 		    xflags | MTX_NOWITNESS);
305cd32bd7aSJohn Baldwin 	}
306cd32bd7aSJohn Baldwin 	lock_init(&rm->lock_object, lc, name, NULL, liflags);
3071a109c1cSRobert Watson }
3081a109c1cSRobert Watson 
3091a109c1cSRobert Watson void
3101a109c1cSRobert Watson rm_init(struct rmlock *rm, const char *name)
3111a109c1cSRobert Watson {
3121a109c1cSRobert Watson 
3131a109c1cSRobert Watson 	rm_init_flags(rm, name, 0);
314f53d15feSStephan Uphoff }
315f53d15feSStephan Uphoff 
316f53d15feSStephan Uphoff void
317f53d15feSStephan Uphoff rm_destroy(struct rmlock *rm)
318f53d15feSStephan Uphoff {
319d02add54SRobert Watson 
320cd32bd7aSJohn Baldwin 	rm_assert(rm, RA_UNLOCKED);
321cd32bd7aSJohn Baldwin 	LIST_FIRST(&rm->rm_activeReaders) = RM_DESTROYED;
322cd32bd7aSJohn Baldwin 	if (rm->lock_object.lo_flags & LO_SLEEPABLE)
32336058c09SMax Laier 		sx_destroy(&rm->rm_lock_sx);
32436058c09SMax Laier 	else
32536058c09SMax Laier 		mtx_destroy(&rm->rm_lock_mtx);
326f53d15feSStephan Uphoff 	lock_destroy(&rm->lock_object);
327f53d15feSStephan Uphoff }
328f53d15feSStephan Uphoff 
329433ea89aSRobert Watson int
330d576deedSPawel Jakub Dawidek rm_wowned(const struct rmlock *rm)
331433ea89aSRobert Watson {
332433ea89aSRobert Watson 
333cd32bd7aSJohn Baldwin 	if (rm->lock_object.lo_flags & LO_SLEEPABLE)
33436058c09SMax Laier 		return (sx_xlocked(&rm->rm_lock_sx));
33536058c09SMax Laier 	else
33636058c09SMax Laier 		return (mtx_owned(&rm->rm_lock_mtx));
337433ea89aSRobert Watson }
338433ea89aSRobert Watson 
339f53d15feSStephan Uphoff void
340f53d15feSStephan Uphoff rm_sysinit(void *arg)
341f53d15feSStephan Uphoff {
342755230ebSMark Johnston 	struct rm_args *args;
3431a109c1cSRobert Watson 
344755230ebSMark Johnston 	args = arg;
345755230ebSMark Johnston 	rm_init_flags(args->ra_rm, args->ra_desc, args->ra_flags);
346f53d15feSStephan Uphoff }
347f53d15feSStephan Uphoff 
34885c1b3c1SMateusz Guzik static __noinline int
34936058c09SMax Laier _rm_rlock_hard(struct rmlock *rm, struct rm_priotracker *tracker, int trylock)
350f53d15feSStephan Uphoff {
351f53d15feSStephan Uphoff 	struct pcpu *pc;
352f53d15feSStephan Uphoff 
353f53d15feSStephan Uphoff 	critical_enter();
354e2a8d178SJason A. Harmening 	pc = get_pcpu();
355f53d15feSStephan Uphoff 
356d02add54SRobert Watson 	/* Check if we just need to do a proper critical_exit. */
357a38f1f26SAttilio Rao 	if (!CPU_ISSET(pc->pc_cpuid, &rm->rm_writecpus)) {
358f53d15feSStephan Uphoff 		critical_exit();
35936058c09SMax Laier 		return (1);
360f53d15feSStephan Uphoff 	}
361f53d15feSStephan Uphoff 
362c7ca33d1SRobert Watson 	/* Remove our tracker from the per-cpu list. */
363f53d15feSStephan Uphoff 	rm_tracker_remove(pc, tracker);
364f53d15feSStephan Uphoff 
365d02add54SRobert Watson 	/* Check to see if the IPI granted us the lock after all. */
366f53d15feSStephan Uphoff 	if (tracker->rmp_flags) {
367d02add54SRobert Watson 		/* Just add back tracker - we hold the lock. */
368f53d15feSStephan Uphoff 		rm_tracker_add(pc, tracker);
369f53d15feSStephan Uphoff 		critical_exit();
37036058c09SMax Laier 		return (1);
371f53d15feSStephan Uphoff 	}
372f53d15feSStephan Uphoff 
373f53d15feSStephan Uphoff 	/*
374e3043798SPedro F. Giffuni 	 * We allow readers to acquire a lock even if a writer is blocked if
375d02add54SRobert Watson 	 * the lock is recursive and the reader already holds the lock.
376f53d15feSStephan Uphoff 	 */
377f53d15feSStephan Uphoff 	if ((rm->lock_object.lo_flags & LO_RECURSABLE) != 0) {
378f53d15feSStephan Uphoff 		/*
379c7ca33d1SRobert Watson 		 * Just grant the lock if this thread already has a tracker
380c7ca33d1SRobert Watson 		 * for this lock on the per-cpu queue.
381f53d15feSStephan Uphoff 		 */
382cd32bd7aSJohn Baldwin 		if (rm_trackers_present(pc, rm, curthread) != 0) {
383f53d15feSStephan Uphoff 			mtx_lock_spin(&rm_spinlock);
384cd32bd7aSJohn Baldwin 			LIST_INSERT_HEAD(&rm->rm_activeReaders, tracker,
385cd32bd7aSJohn Baldwin 			    rmp_qentry);
386f53d15feSStephan Uphoff 			tracker->rmp_flags = RMPF_ONQUEUE;
387f53d15feSStephan Uphoff 			mtx_unlock_spin(&rm_spinlock);
388f53d15feSStephan Uphoff 			rm_tracker_add(pc, tracker);
389f53d15feSStephan Uphoff 			critical_exit();
39036058c09SMax Laier 			return (1);
391f53d15feSStephan Uphoff 		}
392f53d15feSStephan Uphoff 	}
393f53d15feSStephan Uphoff 
394f53d15feSStephan Uphoff 	sched_unpin();
395f53d15feSStephan Uphoff 	critical_exit();
396f53d15feSStephan Uphoff 
39736058c09SMax Laier 	if (trylock) {
398cd32bd7aSJohn Baldwin 		if (rm->lock_object.lo_flags & LO_SLEEPABLE) {
39936058c09SMax Laier 			if (!sx_try_xlock(&rm->rm_lock_sx))
40036058c09SMax Laier 				return (0);
40136058c09SMax Laier 		} else {
40236058c09SMax Laier 			if (!mtx_trylock(&rm->rm_lock_mtx))
40336058c09SMax Laier 				return (0);
40436058c09SMax Laier 		}
40536058c09SMax Laier 	} else {
406e89d5f43SJohn Baldwin 		if (rm->lock_object.lo_flags & LO_SLEEPABLE) {
407e89d5f43SJohn Baldwin 			THREAD_SLEEPING_OK();
40836058c09SMax Laier 			sx_xlock(&rm->rm_lock_sx);
409e89d5f43SJohn Baldwin 			THREAD_NO_SLEEPING();
410e89d5f43SJohn Baldwin 		} else
41136058c09SMax Laier 			mtx_lock(&rm->rm_lock_mtx);
41236058c09SMax Laier 	}
413f53d15feSStephan Uphoff 
41436058c09SMax Laier 	critical_enter();
415e2a8d178SJason A. Harmening 	pc = get_pcpu();
416a38f1f26SAttilio Rao 	CPU_CLR(pc->pc_cpuid, &rm->rm_writecpus);
417f53d15feSStephan Uphoff 	rm_tracker_add(pc, tracker);
418f53d15feSStephan Uphoff 	sched_pin();
419f53d15feSStephan Uphoff 	critical_exit();
420f53d15feSStephan Uphoff 
421cd32bd7aSJohn Baldwin 	if (rm->lock_object.lo_flags & LO_SLEEPABLE)
42236058c09SMax Laier 		sx_xunlock(&rm->rm_lock_sx);
42336058c09SMax Laier 	else
42436058c09SMax Laier 		mtx_unlock(&rm->rm_lock_mtx);
42536058c09SMax Laier 
42636058c09SMax Laier 	return (1);
427f53d15feSStephan Uphoff }
428f53d15feSStephan Uphoff 
42936058c09SMax Laier int
43036058c09SMax Laier _rm_rlock(struct rmlock *rm, struct rm_priotracker *tracker, int trylock)
431f53d15feSStephan Uphoff {
432f53d15feSStephan Uphoff 	struct thread *td = curthread;
433f53d15feSStephan Uphoff 	struct pcpu *pc;
434f53d15feSStephan Uphoff 
43535370593SAndriy Gapon 	if (SCHEDULER_STOPPED())
43635370593SAndriy Gapon 		return (1);
43735370593SAndriy Gapon 
438f53d15feSStephan Uphoff 	tracker->rmp_flags  = 0;
439f53d15feSStephan Uphoff 	tracker->rmp_thread = td;
440f53d15feSStephan Uphoff 	tracker->rmp_rmlock = rm;
441f53d15feSStephan Uphoff 
442cd32bd7aSJohn Baldwin 	if (rm->lock_object.lo_flags & LO_SLEEPABLE)
443cd32bd7aSJohn Baldwin 		THREAD_NO_SLEEPING();
444cd32bd7aSJohn Baldwin 
445f53d15feSStephan Uphoff 	td->td_critnest++;	/* critical_enter(); */
446f53d15feSStephan Uphoff 
4473a473025SAttilio Rao 	__compiler_membar();
448f53d15feSStephan Uphoff 
449f53d15feSStephan Uphoff 	pc = cpuid_to_pcpu[td->td_oncpu]; /* pcpu_find(td->td_oncpu); */
450f53d15feSStephan Uphoff 
451f53d15feSStephan Uphoff 	rm_tracker_add(pc, tracker);
452f53d15feSStephan Uphoff 
45382b7a39cSRobert Watson 	sched_pin();
454f53d15feSStephan Uphoff 
4553a473025SAttilio Rao 	__compiler_membar();
456f53d15feSStephan Uphoff 
457f53d15feSStephan Uphoff 	td->td_critnest--;
458f53d15feSStephan Uphoff 
459f53d15feSStephan Uphoff 	/*
460d02add54SRobert Watson 	 * Fast path to combine two common conditions into a single
461d02add54SRobert Watson 	 * conditional jump.
462f53d15feSStephan Uphoff 	 */
46385c1b3c1SMateusz Guzik 	if (__predict_true(0 == (td->td_owepreempt |
46485c1b3c1SMateusz Guzik 	    CPU_ISSET(pc->pc_cpuid, &rm->rm_writecpus))))
46536058c09SMax Laier 		return (1);
466f53d15feSStephan Uphoff 
467d02add54SRobert Watson 	/* We do not have a read token and need to acquire one. */
46836058c09SMax Laier 	return _rm_rlock_hard(rm, tracker, trylock);
469f53d15feSStephan Uphoff }
470f53d15feSStephan Uphoff 
47185c1b3c1SMateusz Guzik static __noinline void
472f53d15feSStephan Uphoff _rm_unlock_hard(struct thread *td,struct rm_priotracker *tracker)
473f53d15feSStephan Uphoff {
474f53d15feSStephan Uphoff 
475f53d15feSStephan Uphoff 	if (td->td_owepreempt) {
476f53d15feSStephan Uphoff 		td->td_critnest++;
477f53d15feSStephan Uphoff 		critical_exit();
478f53d15feSStephan Uphoff 	}
479f53d15feSStephan Uphoff 
480d02add54SRobert Watson 	if (!tracker->rmp_flags)
481f53d15feSStephan Uphoff 		return;
482f53d15feSStephan Uphoff 
483f53d15feSStephan Uphoff 	mtx_lock_spin(&rm_spinlock);
484f53d15feSStephan Uphoff 	LIST_REMOVE(tracker, rmp_qentry);
485f53d15feSStephan Uphoff 
486f53d15feSStephan Uphoff 	if (tracker->rmp_flags & RMPF_SIGNAL) {
487f53d15feSStephan Uphoff 		struct rmlock *rm;
488f53d15feSStephan Uphoff 		struct turnstile *ts;
489f53d15feSStephan Uphoff 
490f53d15feSStephan Uphoff 		rm = tracker->rmp_rmlock;
491f53d15feSStephan Uphoff 
492f53d15feSStephan Uphoff 		turnstile_chain_lock(&rm->lock_object);
493f53d15feSStephan Uphoff 		mtx_unlock_spin(&rm_spinlock);
494f53d15feSStephan Uphoff 
495f53d15feSStephan Uphoff 		ts = turnstile_lookup(&rm->lock_object);
496f53d15feSStephan Uphoff 
497f53d15feSStephan Uphoff 		turnstile_signal(ts, TS_EXCLUSIVE_QUEUE);
498d0a22279SMateusz Guzik 		turnstile_unpend(ts);
499f53d15feSStephan Uphoff 		turnstile_chain_unlock(&rm->lock_object);
500f53d15feSStephan Uphoff 	} else
501f53d15feSStephan Uphoff 		mtx_unlock_spin(&rm_spinlock);
502f53d15feSStephan Uphoff }
503f53d15feSStephan Uphoff 
504f53d15feSStephan Uphoff void
505f53d15feSStephan Uphoff _rm_runlock(struct rmlock *rm, struct rm_priotracker *tracker)
506f53d15feSStephan Uphoff {
507f53d15feSStephan Uphoff 	struct pcpu *pc;
508f53d15feSStephan Uphoff 	struct thread *td = tracker->rmp_thread;
509f53d15feSStephan Uphoff 
51035370593SAndriy Gapon 	if (SCHEDULER_STOPPED())
51135370593SAndriy Gapon 		return;
51235370593SAndriy Gapon 
513f53d15feSStephan Uphoff 	td->td_critnest++;	/* critical_enter(); */
514f53d15feSStephan Uphoff 	pc = cpuid_to_pcpu[td->td_oncpu]; /* pcpu_find(td->td_oncpu); */
515f53d15feSStephan Uphoff 	rm_tracker_remove(pc, tracker);
516f53d15feSStephan Uphoff 	td->td_critnest--;
51782b7a39cSRobert Watson 	sched_unpin();
518f53d15feSStephan Uphoff 
519cd32bd7aSJohn Baldwin 	if (rm->lock_object.lo_flags & LO_SLEEPABLE)
520cd32bd7aSJohn Baldwin 		THREAD_SLEEPING_OK();
521cd32bd7aSJohn Baldwin 
52285c1b3c1SMateusz Guzik 	if (__predict_true(0 == (td->td_owepreempt | tracker->rmp_flags)))
523f53d15feSStephan Uphoff 		return;
524f53d15feSStephan Uphoff 
525f53d15feSStephan Uphoff 	_rm_unlock_hard(td, tracker);
526f53d15feSStephan Uphoff }
527f53d15feSStephan Uphoff 
528f53d15feSStephan Uphoff void
529f53d15feSStephan Uphoff _rm_wlock(struct rmlock *rm)
530f53d15feSStephan Uphoff {
531f53d15feSStephan Uphoff 	struct rm_priotracker *prio;
532f53d15feSStephan Uphoff 	struct turnstile *ts;
53371a19bdcSAttilio Rao 	cpuset_t readcpus;
534f53d15feSStephan Uphoff 
53535370593SAndriy Gapon 	if (SCHEDULER_STOPPED())
53635370593SAndriy Gapon 		return;
53735370593SAndriy Gapon 
538cd32bd7aSJohn Baldwin 	if (rm->lock_object.lo_flags & LO_SLEEPABLE)
53936058c09SMax Laier 		sx_xlock(&rm->rm_lock_sx);
54036058c09SMax Laier 	else
54136058c09SMax Laier 		mtx_lock(&rm->rm_lock_mtx);
542f53d15feSStephan Uphoff 
54371a19bdcSAttilio Rao 	if (CPU_CMP(&rm->rm_writecpus, &all_cpus)) {
544f53d15feSStephan Uphoff 		/* Get all read tokens back */
54571a19bdcSAttilio Rao 		readcpus = all_cpus;
5469825eadfSRyan Libby 		CPU_ANDNOT(&readcpus, &rm->rm_writecpus);
54736058c09SMax Laier 		rm->rm_writecpus = all_cpus;
548f53d15feSStephan Uphoff 
549f53d15feSStephan Uphoff 		/*
55036058c09SMax Laier 		 * Assumes rm->rm_writecpus update is visible on other CPUs
551d02add54SRobert Watson 		 * before rm_cleanIPI is called.
552f53d15feSStephan Uphoff 		 */
553f53d15feSStephan Uphoff #ifdef SMP
55436058c09SMax Laier 		smp_rendezvous_cpus(readcpus,
55567d955aaSPatrick Kelsey 		    smp_no_rendezvous_barrier,
556f53d15feSStephan Uphoff 		    rm_cleanIPI,
55767d955aaSPatrick Kelsey 		    smp_no_rendezvous_barrier,
558d02add54SRobert Watson 		    rm);
559f53d15feSStephan Uphoff 
560f53d15feSStephan Uphoff #else
561f53d15feSStephan Uphoff 		rm_cleanIPI(rm);
562f53d15feSStephan Uphoff #endif
563f53d15feSStephan Uphoff 
564f53d15feSStephan Uphoff 		mtx_lock_spin(&rm_spinlock);
565f53d15feSStephan Uphoff 		while ((prio = LIST_FIRST(&rm->rm_activeReaders)) != NULL) {
566f53d15feSStephan Uphoff 			ts = turnstile_trywait(&rm->lock_object);
567f53d15feSStephan Uphoff 			prio->rmp_flags = RMPF_ONQUEUE | RMPF_SIGNAL;
568f53d15feSStephan Uphoff 			mtx_unlock_spin(&rm_spinlock);
569f53d15feSStephan Uphoff 			turnstile_wait(ts, prio->rmp_thread,
570f53d15feSStephan Uphoff 			    TS_EXCLUSIVE_QUEUE);
571f53d15feSStephan Uphoff 			mtx_lock_spin(&rm_spinlock);
572f53d15feSStephan Uphoff 		}
573f53d15feSStephan Uphoff 		mtx_unlock_spin(&rm_spinlock);
574f53d15feSStephan Uphoff 	}
575f53d15feSStephan Uphoff }
576f53d15feSStephan Uphoff 
577f53d15feSStephan Uphoff void
578f53d15feSStephan Uphoff _rm_wunlock(struct rmlock *rm)
579f53d15feSStephan Uphoff {
580d02add54SRobert Watson 
581cd32bd7aSJohn Baldwin 	if (rm->lock_object.lo_flags & LO_SLEEPABLE)
58236058c09SMax Laier 		sx_xunlock(&rm->rm_lock_sx);
58336058c09SMax Laier 	else
58436058c09SMax Laier 		mtx_unlock(&rm->rm_lock_mtx);
585f53d15feSStephan Uphoff }
586f53d15feSStephan Uphoff 
58741f5f69fSAndrey V. Elsukov #if LOCK_DEBUG > 0
588f53d15feSStephan Uphoff 
589cd32bd7aSJohn Baldwin void
590cd32bd7aSJohn Baldwin _rm_wlock_debug(struct rmlock *rm, const char *file, int line)
591f53d15feSStephan Uphoff {
592f53d15feSStephan Uphoff 
59335370593SAndriy Gapon 	if (SCHEDULER_STOPPED())
59435370593SAndriy Gapon 		return;
59535370593SAndriy Gapon 
596cd2fe4e6SAttilio Rao 	KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(curthread),
597e3ae0dfeSAttilio Rao 	    ("rm_wlock() by idle thread %p on rmlock %s @ %s:%d",
598e3ae0dfeSAttilio Rao 	    curthread, rm->lock_object.lo_name, file, line));
599cd32bd7aSJohn Baldwin 	KASSERT(!rm_destroyed(rm),
600cd32bd7aSJohn Baldwin 	    ("rm_wlock() of destroyed rmlock @ %s:%d", file, line));
601cd32bd7aSJohn Baldwin 	_rm_assert(rm, RA_UNLOCKED, file, line);
602cd32bd7aSJohn Baldwin 
603f53d15feSStephan Uphoff 	WITNESS_CHECKORDER(&rm->lock_object, LOP_NEWORDER | LOP_EXCLUSIVE,
60441313430SJohn Baldwin 	    file, line, NULL);
605f53d15feSStephan Uphoff 
606f53d15feSStephan Uphoff 	_rm_wlock(rm);
607f53d15feSStephan Uphoff 
608f53d15feSStephan Uphoff 	LOCK_LOG_LOCK("RMWLOCK", &rm->lock_object, 0, 0, file, line);
609f53d15feSStephan Uphoff 	WITNESS_LOCK(&rm->lock_object, LOP_EXCLUSIVE, file, line);
610ce1c953eSMark Johnston 	TD_LOCKS_INC(curthread);
611f53d15feSStephan Uphoff }
612f53d15feSStephan Uphoff 
613d02add54SRobert Watson void
614d02add54SRobert Watson _rm_wunlock_debug(struct rmlock *rm, const char *file, int line)
615f53d15feSStephan Uphoff {
616d02add54SRobert Watson 
61735370593SAndriy Gapon 	if (SCHEDULER_STOPPED())
61835370593SAndriy Gapon 		return;
61935370593SAndriy Gapon 
620cd32bd7aSJohn Baldwin 	KASSERT(!rm_destroyed(rm),
621cd32bd7aSJohn Baldwin 	    ("rm_wunlock() of destroyed rmlock @ %s:%d", file, line));
622cd32bd7aSJohn Baldwin 	_rm_assert(rm, RA_WLOCKED, file, line);
623f53d15feSStephan Uphoff 	WITNESS_UNLOCK(&rm->lock_object, LOP_EXCLUSIVE, file, line);
624f53d15feSStephan Uphoff 	LOCK_LOG_LOCK("RMWUNLOCK", &rm->lock_object, 0, 0, file, line);
625f53d15feSStephan Uphoff 	_rm_wunlock(rm);
626ce1c953eSMark Johnston 	TD_LOCKS_DEC(curthread);
627f53d15feSStephan Uphoff }
628f53d15feSStephan Uphoff 
62936058c09SMax Laier int
630f53d15feSStephan Uphoff _rm_rlock_debug(struct rmlock *rm, struct rm_priotracker *tracker,
63136058c09SMax Laier     int trylock, const char *file, int line)
632f53d15feSStephan Uphoff {
63335370593SAndriy Gapon 
63435370593SAndriy Gapon 	if (SCHEDULER_STOPPED())
63535370593SAndriy Gapon 		return (1);
63635370593SAndriy Gapon 
637cd32bd7aSJohn Baldwin #ifdef INVARIANTS
638cd32bd7aSJohn Baldwin 	if (!(rm->lock_object.lo_flags & LO_RECURSABLE) && !trylock) {
639cd32bd7aSJohn Baldwin 		critical_enter();
640e2a8d178SJason A. Harmening 		KASSERT(rm_trackers_present(get_pcpu(), rm,
641cd32bd7aSJohn Baldwin 		    curthread) == 0,
642cd32bd7aSJohn Baldwin 		    ("rm_rlock: recursed on non-recursive rmlock %s @ %s:%d\n",
643cd32bd7aSJohn Baldwin 		    rm->lock_object.lo_name, file, line));
644cd32bd7aSJohn Baldwin 		critical_exit();
645cd32bd7aSJohn Baldwin 	}
646cd32bd7aSJohn Baldwin #endif
647cd2fe4e6SAttilio Rao 	KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(curthread),
648e3ae0dfeSAttilio Rao 	    ("rm_rlock() by idle thread %p on rmlock %s @ %s:%d",
649e3ae0dfeSAttilio Rao 	    curthread, rm->lock_object.lo_name, file, line));
650cd32bd7aSJohn Baldwin 	KASSERT(!rm_destroyed(rm),
651cd32bd7aSJohn Baldwin 	    ("rm_rlock() of destroyed rmlock @ %s:%d", file, line));
652cd32bd7aSJohn Baldwin 	if (!trylock) {
653cd32bd7aSJohn Baldwin 		KASSERT(!rm_wowned(rm),
654cd32bd7aSJohn Baldwin 		    ("rm_rlock: wlock already held for %s @ %s:%d",
655cd32bd7aSJohn Baldwin 		    rm->lock_object.lo_name, file, line));
65659fb4a95SRyan Libby 		WITNESS_CHECKORDER(&rm->lock_object,
65759fb4a95SRyan Libby 		    LOP_NEWORDER | LOP_NOSLEEP, file, line, NULL);
658cd32bd7aSJohn Baldwin 	}
659f53d15feSStephan Uphoff 
66036058c09SMax Laier 	if (_rm_rlock(rm, tracker, trylock)) {
661cd32bd7aSJohn Baldwin 		if (trylock)
662cd32bd7aSJohn Baldwin 			LOCK_LOG_TRY("RMRLOCK", &rm->lock_object, 0, 1, file,
663cd32bd7aSJohn Baldwin 			    line);
664cd32bd7aSJohn Baldwin 		else
665cd32bd7aSJohn Baldwin 			LOCK_LOG_LOCK("RMRLOCK", &rm->lock_object, 0, 0, file,
666cd32bd7aSJohn Baldwin 			    line);
66759fb4a95SRyan Libby 		WITNESS_LOCK(&rm->lock_object, LOP_NOSLEEP, file, line);
668ce1c953eSMark Johnston 		TD_LOCKS_INC(curthread);
66936058c09SMax Laier 		return (1);
670cd32bd7aSJohn Baldwin 	} else if (trylock)
671cd32bd7aSJohn Baldwin 		LOCK_LOG_TRY("RMRLOCK", &rm->lock_object, 0, 0, file, line);
67236058c09SMax Laier 
67336058c09SMax Laier 	return (0);
674f53d15feSStephan Uphoff }
675f53d15feSStephan Uphoff 
676f53d15feSStephan Uphoff void
677f53d15feSStephan Uphoff _rm_runlock_debug(struct rmlock *rm, struct rm_priotracker *tracker,
678d02add54SRobert Watson     const char *file, int line)
679d02add54SRobert Watson {
680d02add54SRobert Watson 
68135370593SAndriy Gapon 	if (SCHEDULER_STOPPED())
68235370593SAndriy Gapon 		return;
68335370593SAndriy Gapon 
684cd32bd7aSJohn Baldwin 	KASSERT(!rm_destroyed(rm),
685cd32bd7aSJohn Baldwin 	    ("rm_runlock() of destroyed rmlock @ %s:%d", file, line));
686cd32bd7aSJohn Baldwin 	_rm_assert(rm, RA_RLOCKED, file, line);
687f53d15feSStephan Uphoff 	WITNESS_UNLOCK(&rm->lock_object, 0, file, line);
688f53d15feSStephan Uphoff 	LOCK_LOG_LOCK("RMRUNLOCK", &rm->lock_object, 0, 0, file, line);
689f53d15feSStephan Uphoff 	_rm_runlock(rm, tracker);
690ce1c953eSMark Johnston 	TD_LOCKS_DEC(curthread);
691f53d15feSStephan Uphoff }
692f53d15feSStephan Uphoff 
693f53d15feSStephan Uphoff #else
694d02add54SRobert Watson 
695f53d15feSStephan Uphoff /*
696d02add54SRobert Watson  * Just strip out file and line arguments if no lock debugging is enabled in
697d02add54SRobert Watson  * the kernel - we are called from a kernel module.
698f53d15feSStephan Uphoff  */
699d02add54SRobert Watson void
700d02add54SRobert Watson _rm_wlock_debug(struct rmlock *rm, const char *file, int line)
701f53d15feSStephan Uphoff {
702d02add54SRobert Watson 
703f53d15feSStephan Uphoff 	_rm_wlock(rm);
704f53d15feSStephan Uphoff }
705f53d15feSStephan Uphoff 
706d02add54SRobert Watson void
707d02add54SRobert Watson _rm_wunlock_debug(struct rmlock *rm, const char *file, int line)
708f53d15feSStephan Uphoff {
709d02add54SRobert Watson 
710f53d15feSStephan Uphoff 	_rm_wunlock(rm);
711f53d15feSStephan Uphoff }
712f53d15feSStephan Uphoff 
71336058c09SMax Laier int
714f53d15feSStephan Uphoff _rm_rlock_debug(struct rmlock *rm, struct rm_priotracker *tracker,
71536058c09SMax Laier     int trylock, const char *file, int line)
716f53d15feSStephan Uphoff {
717d02add54SRobert Watson 
71836058c09SMax Laier 	return _rm_rlock(rm, tracker, trylock);
719f53d15feSStephan Uphoff }
720f53d15feSStephan Uphoff 
721f53d15feSStephan Uphoff void
722f53d15feSStephan Uphoff _rm_runlock_debug(struct rmlock *rm, struct rm_priotracker *tracker,
7231191932aSRobert Watson     const char *file, int line)
7241191932aSRobert Watson {
725d02add54SRobert Watson 
726f53d15feSStephan Uphoff 	_rm_runlock(rm, tracker);
727f53d15feSStephan Uphoff }
728f53d15feSStephan Uphoff 
729f53d15feSStephan Uphoff #endif
730cd32bd7aSJohn Baldwin 
731cd32bd7aSJohn Baldwin #ifdef INVARIANT_SUPPORT
732c64bc3a0SJohn Baldwin #ifndef INVARIANTS
733c64bc3a0SJohn Baldwin #undef _rm_assert
734c64bc3a0SJohn Baldwin #endif
735c64bc3a0SJohn Baldwin 
736cd32bd7aSJohn Baldwin /*
737cd32bd7aSJohn Baldwin  * Note that this does not need to use witness_assert() for read lock
738cd32bd7aSJohn Baldwin  * assertions since an exact count of read locks held by this thread
739cd32bd7aSJohn Baldwin  * is computable.
740cd32bd7aSJohn Baldwin  */
741cd32bd7aSJohn Baldwin void
742cd32bd7aSJohn Baldwin _rm_assert(const struct rmlock *rm, int what, const char *file, int line)
743cd32bd7aSJohn Baldwin {
744cd32bd7aSJohn Baldwin 	int count;
745cd32bd7aSJohn Baldwin 
746d54474e6SEric van Gyzen 	if (SCHEDULER_STOPPED())
747cd32bd7aSJohn Baldwin 		return;
748cd32bd7aSJohn Baldwin 	switch (what) {
749cd32bd7aSJohn Baldwin 	case RA_LOCKED:
750cd32bd7aSJohn Baldwin 	case RA_LOCKED | RA_RECURSED:
751cd32bd7aSJohn Baldwin 	case RA_LOCKED | RA_NOTRECURSED:
752cd32bd7aSJohn Baldwin 	case RA_RLOCKED:
753cd32bd7aSJohn Baldwin 	case RA_RLOCKED | RA_RECURSED:
754cd32bd7aSJohn Baldwin 	case RA_RLOCKED | RA_NOTRECURSED:
755cd32bd7aSJohn Baldwin 		/*
756cd32bd7aSJohn Baldwin 		 * Handle the write-locked case.  Unlike other
757cd32bd7aSJohn Baldwin 		 * primitives, writers can never recurse.
758cd32bd7aSJohn Baldwin 		 */
759cd32bd7aSJohn Baldwin 		if (rm_wowned(rm)) {
760cd32bd7aSJohn Baldwin 			if (what & RA_RLOCKED)
761cd32bd7aSJohn Baldwin 				panic("Lock %s exclusively locked @ %s:%d\n",
762cd32bd7aSJohn Baldwin 				    rm->lock_object.lo_name, file, line);
763cd32bd7aSJohn Baldwin 			if (what & RA_RECURSED)
764cd32bd7aSJohn Baldwin 				panic("Lock %s not recursed @ %s:%d\n",
765cd32bd7aSJohn Baldwin 				    rm->lock_object.lo_name, file, line);
766cd32bd7aSJohn Baldwin 			break;
767cd32bd7aSJohn Baldwin 		}
768cd32bd7aSJohn Baldwin 
769cd32bd7aSJohn Baldwin 		critical_enter();
770e2a8d178SJason A. Harmening 		count = rm_trackers_present(get_pcpu(), rm, curthread);
771cd32bd7aSJohn Baldwin 		critical_exit();
772cd32bd7aSJohn Baldwin 
773cd32bd7aSJohn Baldwin 		if (count == 0)
774cd32bd7aSJohn Baldwin 			panic("Lock %s not %slocked @ %s:%d\n",
775cd32bd7aSJohn Baldwin 			    rm->lock_object.lo_name, (what & RA_RLOCKED) ?
776cd32bd7aSJohn Baldwin 			    "read " : "", file, line);
777cd32bd7aSJohn Baldwin 		if (count > 1) {
778cd32bd7aSJohn Baldwin 			if (what & RA_NOTRECURSED)
779cd32bd7aSJohn Baldwin 				panic("Lock %s recursed @ %s:%d\n",
780cd32bd7aSJohn Baldwin 				    rm->lock_object.lo_name, file, line);
781cd32bd7aSJohn Baldwin 		} else if (what & RA_RECURSED)
782cd32bd7aSJohn Baldwin 			panic("Lock %s not recursed @ %s:%d\n",
783cd32bd7aSJohn Baldwin 			    rm->lock_object.lo_name, file, line);
784cd32bd7aSJohn Baldwin 		break;
785cd32bd7aSJohn Baldwin 	case RA_WLOCKED:
786cd32bd7aSJohn Baldwin 		if (!rm_wowned(rm))
787cd32bd7aSJohn Baldwin 			panic("Lock %s not exclusively locked @ %s:%d\n",
788cd32bd7aSJohn Baldwin 			    rm->lock_object.lo_name, file, line);
789cd32bd7aSJohn Baldwin 		break;
790cd32bd7aSJohn Baldwin 	case RA_UNLOCKED:
791cd32bd7aSJohn Baldwin 		if (rm_wowned(rm))
792cd32bd7aSJohn Baldwin 			panic("Lock %s exclusively locked @ %s:%d\n",
793cd32bd7aSJohn Baldwin 			    rm->lock_object.lo_name, file, line);
794cd32bd7aSJohn Baldwin 
795cd32bd7aSJohn Baldwin 		critical_enter();
796e2a8d178SJason A. Harmening 		count = rm_trackers_present(get_pcpu(), rm, curthread);
797cd32bd7aSJohn Baldwin 		critical_exit();
798cd32bd7aSJohn Baldwin 
799cd32bd7aSJohn Baldwin 		if (count != 0)
800cd32bd7aSJohn Baldwin 			panic("Lock %s read locked @ %s:%d\n",
801cd32bd7aSJohn Baldwin 			    rm->lock_object.lo_name, file, line);
802cd32bd7aSJohn Baldwin 		break;
803cd32bd7aSJohn Baldwin 	default:
804cd32bd7aSJohn Baldwin 		panic("Unknown rm lock assertion: %d @ %s:%d", what, file,
805cd32bd7aSJohn Baldwin 		    line);
806cd32bd7aSJohn Baldwin 	}
807cd32bd7aSJohn Baldwin }
808cd32bd7aSJohn Baldwin #endif /* INVARIANT_SUPPORT */
809cd32bd7aSJohn Baldwin 
810cd32bd7aSJohn Baldwin #ifdef DDB
811cd32bd7aSJohn Baldwin static void
812cd32bd7aSJohn Baldwin print_tracker(struct rm_priotracker *tr)
813cd32bd7aSJohn Baldwin {
814cd32bd7aSJohn Baldwin 	struct thread *td;
815cd32bd7aSJohn Baldwin 
816cd32bd7aSJohn Baldwin 	td = tr->rmp_thread;
817cd32bd7aSJohn Baldwin 	db_printf("   thread %p (tid %d, pid %d, \"%s\") {", td, td->td_tid,
818cd32bd7aSJohn Baldwin 	    td->td_proc->p_pid, td->td_name);
819cd32bd7aSJohn Baldwin 	if (tr->rmp_flags & RMPF_ONQUEUE) {
820cd32bd7aSJohn Baldwin 		db_printf("ONQUEUE");
821cd32bd7aSJohn Baldwin 		if (tr->rmp_flags & RMPF_SIGNAL)
822cd32bd7aSJohn Baldwin 			db_printf(",SIGNAL");
823cd32bd7aSJohn Baldwin 	} else
824cd32bd7aSJohn Baldwin 		db_printf("0");
825cd32bd7aSJohn Baldwin 	db_printf("}\n");
826cd32bd7aSJohn Baldwin }
827cd32bd7aSJohn Baldwin 
828cd32bd7aSJohn Baldwin static void
829cd32bd7aSJohn Baldwin db_show_rm(const struct lock_object *lock)
830cd32bd7aSJohn Baldwin {
831cd32bd7aSJohn Baldwin 	struct rm_priotracker *tr;
832cd32bd7aSJohn Baldwin 	struct rm_queue *queue;
833cd32bd7aSJohn Baldwin 	const struct rmlock *rm;
834cd32bd7aSJohn Baldwin 	struct lock_class *lc;
835cd32bd7aSJohn Baldwin 	struct pcpu *pc;
836cd32bd7aSJohn Baldwin 
837cd32bd7aSJohn Baldwin 	rm = (const struct rmlock *)lock;
838cd32bd7aSJohn Baldwin 	db_printf(" writecpus: ");
839cd32bd7aSJohn Baldwin 	ddb_display_cpuset(__DEQUALIFY(const cpuset_t *, &rm->rm_writecpus));
840cd32bd7aSJohn Baldwin 	db_printf("\n");
841cd32bd7aSJohn Baldwin 	db_printf(" per-CPU readers:\n");
842cd32bd7aSJohn Baldwin 	STAILQ_FOREACH(pc, &cpuhead, pc_allcpu)
843cd32bd7aSJohn Baldwin 		for (queue = pc->pc_rm_queue.rmq_next;
844cd32bd7aSJohn Baldwin 		    queue != &pc->pc_rm_queue; queue = queue->rmq_next) {
845cd32bd7aSJohn Baldwin 			tr = (struct rm_priotracker *)queue;
846cd32bd7aSJohn Baldwin 			if (tr->rmp_rmlock == rm)
847cd32bd7aSJohn Baldwin 				print_tracker(tr);
848cd32bd7aSJohn Baldwin 		}
849cd32bd7aSJohn Baldwin 	db_printf(" active readers:\n");
850cd32bd7aSJohn Baldwin 	LIST_FOREACH(tr, &rm->rm_activeReaders, rmp_qentry)
851cd32bd7aSJohn Baldwin 		print_tracker(tr);
852cd32bd7aSJohn Baldwin 	lc = LOCK_CLASS(&rm->rm_wlock_object);
853cd32bd7aSJohn Baldwin 	db_printf("Backing write-lock (%s):\n", lc->lc_name);
854cd32bd7aSJohn Baldwin 	lc->lc_ddb_show(&rm->rm_wlock_object);
855cd32bd7aSJohn Baldwin }
856cd32bd7aSJohn Baldwin #endif
8571f162fefSMateusz Guzik 
8581f162fefSMateusz Guzik /*
8591f162fefSMateusz Guzik  * Read-mostly sleepable locks.
8601f162fefSMateusz Guzik  *
8611f162fefSMateusz Guzik  * These primitives allow both readers and writers to sleep. However, neither
8621f162fefSMateusz Guzik  * readers nor writers are tracked and subsequently there is no priority
8631f162fefSMateusz Guzik  * propagation.
8641f162fefSMateusz Guzik  *
8651f162fefSMateusz Guzik  * They are intended to be only used when write-locking is almost never needed
8661f162fefSMateusz Guzik  * (e.g., they can guard against unloading a kernel module) while read-locking
8671f162fefSMateusz Guzik  * happens all the time.
8681f162fefSMateusz Guzik  *
8691f162fefSMateusz Guzik  * Concurrent writers take turns taking the lock while going off cpu. If this is
8701f162fefSMateusz Guzik  * of concern for your usecase, this is not the right primitive.
8711f162fefSMateusz Guzik  *
8721f162fefSMateusz Guzik  * Neither rms_rlock nor rms_runlock use fences. Instead compiler barriers are
8731f162fefSMateusz Guzik  * inserted to prevert reordering of generated code. Execution ordering is
8741f162fefSMateusz Guzik  * provided with the use of an IPI handler.
8751f162fefSMateusz Guzik  */
8761f162fefSMateusz Guzik 
8771f162fefSMateusz Guzik void
8781f162fefSMateusz Guzik rms_init(struct rmslock *rms, const char *name)
8791f162fefSMateusz Guzik {
8801f162fefSMateusz Guzik 
8811f162fefSMateusz Guzik 	rms->writers = 0;
8821f162fefSMateusz Guzik 	rms->readers = 0;
8831f162fefSMateusz Guzik 	mtx_init(&rms->mtx, name, NULL, MTX_DEF | MTX_NEW);
8841f162fefSMateusz Guzik 	rms->readers_pcpu = uma_zalloc_pcpu(pcpu_zone_int, M_WAITOK | M_ZERO);
8851f162fefSMateusz Guzik 	rms->readers_influx = uma_zalloc_pcpu(pcpu_zone_int, M_WAITOK | M_ZERO);
8861f162fefSMateusz Guzik }
8871f162fefSMateusz Guzik 
8881f162fefSMateusz Guzik void
8891f162fefSMateusz Guzik rms_destroy(struct rmslock *rms)
8901f162fefSMateusz Guzik {
8911f162fefSMateusz Guzik 
8921f162fefSMateusz Guzik 	MPASS(rms->writers == 0);
8931f162fefSMateusz Guzik 	MPASS(rms->readers == 0);
8941f162fefSMateusz Guzik 	mtx_destroy(&rms->mtx);
8951f162fefSMateusz Guzik 	uma_zfree_pcpu(pcpu_zone_int, rms->readers_pcpu);
8961f162fefSMateusz Guzik 	uma_zfree_pcpu(pcpu_zone_int, rms->readers_influx);
8971f162fefSMateusz Guzik }
8981f162fefSMateusz Guzik 
8991f162fefSMateusz Guzik static void __noinline
9001f162fefSMateusz Guzik rms_rlock_fallback(struct rmslock *rms)
9011f162fefSMateusz Guzik {
9021f162fefSMateusz Guzik 
903*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 0);
9041f162fefSMateusz Guzik 	critical_exit();
9051f162fefSMateusz Guzik 
9061f162fefSMateusz Guzik 	mtx_lock(&rms->mtx);
9071f162fefSMateusz Guzik 	MPASS(*zpcpu_get(rms->readers_pcpu) == 0);
9081f162fefSMateusz Guzik 	while (rms->writers > 0)
9091f162fefSMateusz Guzik 		msleep(&rms->readers, &rms->mtx, PUSER - 1, mtx_name(&rms->mtx), 0);
910*ea77ce6eSMateusz Guzik 	critical_enter();
911*ea77ce6eSMateusz Guzik 	zpcpu_add_protected(rms->readers_pcpu, 1);
9121f162fefSMateusz Guzik 	mtx_unlock(&rms->mtx);
913*ea77ce6eSMateusz Guzik 	critical_exit();
9141f162fefSMateusz Guzik }
9151f162fefSMateusz Guzik 
9161f162fefSMateusz Guzik void
9171f162fefSMateusz Guzik rms_rlock(struct rmslock *rms)
9181f162fefSMateusz Guzik {
9191f162fefSMateusz Guzik 
9201f162fefSMateusz Guzik 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__);
9211f162fefSMateusz Guzik 
9221f162fefSMateusz Guzik 	critical_enter();
923*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 1);
9241f162fefSMateusz Guzik 	__compiler_membar();
9251f162fefSMateusz Guzik 	if (__predict_false(rms->writers > 0)) {
9261f162fefSMateusz Guzik 		rms_rlock_fallback(rms);
9271f162fefSMateusz Guzik 		return;
9281f162fefSMateusz Guzik 	}
9291f162fefSMateusz Guzik 	__compiler_membar();
930*ea77ce6eSMateusz Guzik 	zpcpu_add_protected(rms->readers_pcpu, 1);
9311f162fefSMateusz Guzik 	__compiler_membar();
932*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 0);
9331f162fefSMateusz Guzik 	critical_exit();
9341f162fefSMateusz Guzik }
9351f162fefSMateusz Guzik 
9361a78ac24SMateusz Guzik int
9371a78ac24SMateusz Guzik rms_try_rlock(struct rmslock *rms)
9381a78ac24SMateusz Guzik {
9391a78ac24SMateusz Guzik 
9401a78ac24SMateusz Guzik 	critical_enter();
941*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 1);
9421a78ac24SMateusz Guzik 	__compiler_membar();
9431a78ac24SMateusz Guzik 	if (__predict_false(rms->writers > 0)) {
9441a78ac24SMateusz Guzik 		__compiler_membar();
945*ea77ce6eSMateusz Guzik 		zpcpu_set_protected(rms->readers_influx, 0);
9461a78ac24SMateusz Guzik 		critical_exit();
9471a78ac24SMateusz Guzik 		return (0);
9481a78ac24SMateusz Guzik 	}
9491a78ac24SMateusz Guzik 	__compiler_membar();
950*ea77ce6eSMateusz Guzik 	zpcpu_add_protected(rms->readers_pcpu, 1);
9511a78ac24SMateusz Guzik 	__compiler_membar();
952*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 0);
9531a78ac24SMateusz Guzik 	critical_exit();
9541a78ac24SMateusz Guzik 	return (1);
9551a78ac24SMateusz Guzik }
9561a78ac24SMateusz Guzik 
9571f162fefSMateusz Guzik static void __noinline
9581f162fefSMateusz Guzik rms_runlock_fallback(struct rmslock *rms)
9591f162fefSMateusz Guzik {
9601f162fefSMateusz Guzik 
961*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 0);
9621f162fefSMateusz Guzik 	critical_exit();
9631f162fefSMateusz Guzik 
9641f162fefSMateusz Guzik 	mtx_lock(&rms->mtx);
9651f162fefSMateusz Guzik 	MPASS(*zpcpu_get(rms->readers_pcpu) == 0);
9661f162fefSMateusz Guzik 	MPASS(rms->writers > 0);
9671f162fefSMateusz Guzik 	MPASS(rms->readers > 0);
9681f162fefSMateusz Guzik 	rms->readers--;
9691f162fefSMateusz Guzik 	if (rms->readers == 0)
9701f162fefSMateusz Guzik 		wakeup_one(&rms->writers);
9711f162fefSMateusz Guzik 	mtx_unlock(&rms->mtx);
9721f162fefSMateusz Guzik }
9731f162fefSMateusz Guzik 
9741f162fefSMateusz Guzik void
9751f162fefSMateusz Guzik rms_runlock(struct rmslock *rms)
9761f162fefSMateusz Guzik {
9771f162fefSMateusz Guzik 
9781f162fefSMateusz Guzik 	critical_enter();
979*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 1);
9801f162fefSMateusz Guzik 	__compiler_membar();
9811f162fefSMateusz Guzik 	if (__predict_false(rms->writers > 0)) {
9821f162fefSMateusz Guzik 		rms_runlock_fallback(rms);
9831f162fefSMateusz Guzik 		return;
9841f162fefSMateusz Guzik 	}
9851f162fefSMateusz Guzik 	__compiler_membar();
986*ea77ce6eSMateusz Guzik 	zpcpu_sub_protected(rms->readers_pcpu, 1);
9871f162fefSMateusz Guzik 	__compiler_membar();
988*ea77ce6eSMateusz Guzik 	zpcpu_set_protected(rms->readers_influx, 0);
9891f162fefSMateusz Guzik 	critical_exit();
9901f162fefSMateusz Guzik }
9911f162fefSMateusz Guzik 
9921f162fefSMateusz Guzik struct rmslock_ipi {
9931f162fefSMateusz Guzik 	struct rmslock *rms;
9941f162fefSMateusz Guzik 	cpuset_t signal;
9951f162fefSMateusz Guzik };
9961f162fefSMateusz Guzik 
9971f162fefSMateusz Guzik static void
9981f162fefSMateusz Guzik rms_wlock_IPI(void *arg)
9991f162fefSMateusz Guzik {
10001f162fefSMateusz Guzik 	struct rmslock_ipi *rmsipi;
10011f162fefSMateusz Guzik 	struct rmslock *rms;
10021f162fefSMateusz Guzik 	int readers;
10031f162fefSMateusz Guzik 
10041f162fefSMateusz Guzik 	rmsipi = arg;
10051f162fefSMateusz Guzik 	rms = rmsipi->rms;
10061f162fefSMateusz Guzik 
10071f162fefSMateusz Guzik 	if (*zpcpu_get(rms->readers_influx))
10081f162fefSMateusz Guzik 		return;
10091f162fefSMateusz Guzik 	readers = zpcpu_replace(rms->readers_pcpu, 0);
10101f162fefSMateusz Guzik 	if (readers != 0)
10111f162fefSMateusz Guzik 		atomic_add_int(&rms->readers, readers);
10121f162fefSMateusz Guzik 	CPU_CLR_ATOMIC(curcpu, &rmsipi->signal);
10131f162fefSMateusz Guzik }
10141f162fefSMateusz Guzik 
10151f162fefSMateusz Guzik static void
10161f162fefSMateusz Guzik rms_wlock_switch(struct rmslock *rms)
10171f162fefSMateusz Guzik {
10181f162fefSMateusz Guzik 	struct rmslock_ipi rmsipi;
10191f162fefSMateusz Guzik 	int *in_op;
10201f162fefSMateusz Guzik 	int cpu;
10211f162fefSMateusz Guzik 
10221f162fefSMateusz Guzik 	MPASS(rms->readers == 0);
10231f162fefSMateusz Guzik 	MPASS(rms->writers == 1);
10241f162fefSMateusz Guzik 
10251f162fefSMateusz Guzik 	rmsipi.rms = rms;
10261f162fefSMateusz Guzik 
10271f162fefSMateusz Guzik 	/*
10281f162fefSMateusz Guzik 	 * Publishes rms->writers. rlock and runlock will get this ordered
10291f162fefSMateusz Guzik 	 * via IPI in the worst case.
10301f162fefSMateusz Guzik 	 */
10311f162fefSMateusz Guzik 	atomic_thread_fence_rel();
10321f162fefSMateusz Guzik 
10331f162fefSMateusz Guzik 	/*
10341f162fefSMateusz Guzik 	 * Collect reader counts from all CPUs using an IPI. The handler can
10351f162fefSMateusz Guzik 	 * find itself running while the interrupted CPU was doing either
10361f162fefSMateusz Guzik 	 * rlock or runlock in which case it will fail.
10371f162fefSMateusz Guzik 	 *
10381f162fefSMateusz Guzik 	 * Successful attempts clear the cpu id in the bitmap.
10391f162fefSMateusz Guzik 	 *
10401f162fefSMateusz Guzik 	 * In case of failure we observe all failing CPUs not executing there to
10411f162fefSMateusz Guzik 	 * determine when to make the next attempt. Note that threads having
10421f162fefSMateusz Guzik 	 * the var set have preemption disabled.  Setting of readers_influx
10431f162fefSMateusz Guzik 	 * only uses compiler barriers making these loads unreliable, which is
10441f162fefSMateusz Guzik 	 * fine -- the IPI handler will always see the correct result.
10451f162fefSMateusz Guzik 	 *
10461f162fefSMateusz Guzik 	 * We retry until all counts are collected. Forward progress is
10471f162fefSMateusz Guzik 	 * guaranteed by that fact that the total number of threads which can
10481f162fefSMateusz Guzik 	 * be caught like this is finite and they all are going to block on
10491f162fefSMateusz Guzik 	 * their own.
10501f162fefSMateusz Guzik 	 */
10511f162fefSMateusz Guzik 	CPU_COPY(&all_cpus, &rmsipi.signal);
10521f162fefSMateusz Guzik 	for (;;) {
10531f162fefSMateusz Guzik 		smp_rendezvous_cpus(
10541f162fefSMateusz Guzik 		    rmsipi.signal,
10551f162fefSMateusz Guzik 		    smp_no_rendezvous_barrier,
10561f162fefSMateusz Guzik 		    rms_wlock_IPI,
10571f162fefSMateusz Guzik 		    smp_no_rendezvous_barrier,
10581f162fefSMateusz Guzik 		    &rmsipi);
10591f162fefSMateusz Guzik 
10601f162fefSMateusz Guzik 		if (CPU_EMPTY(&rmsipi.signal))
10611f162fefSMateusz Guzik 			break;
10621f162fefSMateusz Guzik 
10631f162fefSMateusz Guzik 		CPU_FOREACH(cpu) {
10641f162fefSMateusz Guzik 			if (!CPU_ISSET(cpu, &rmsipi.signal))
10651f162fefSMateusz Guzik 				continue;
10661f162fefSMateusz Guzik 			in_op = zpcpu_get_cpu(rms->readers_influx, cpu);
10671f162fefSMateusz Guzik 			while (atomic_load_int(in_op))
10681f162fefSMateusz Guzik 				cpu_spinwait();
10691f162fefSMateusz Guzik 		}
10701f162fefSMateusz Guzik 	}
10711f162fefSMateusz Guzik }
10721f162fefSMateusz Guzik 
10731f162fefSMateusz Guzik void
10741f162fefSMateusz Guzik rms_wlock(struct rmslock *rms)
10751f162fefSMateusz Guzik {
10761f162fefSMateusz Guzik 
10771f162fefSMateusz Guzik 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__);
10781f162fefSMateusz Guzik 
10791f162fefSMateusz Guzik 	mtx_lock(&rms->mtx);
10801f162fefSMateusz Guzik 	rms->writers++;
10811f162fefSMateusz Guzik 	if (rms->writers > 1) {
10823983dc32SMateusz Guzik 		msleep(&rms->writers, &rms->mtx, (PUSER - 1) | PDROP,
10833983dc32SMateusz Guzik 		    mtx_name(&rms->mtx), 0);
10841f162fefSMateusz Guzik 		MPASS(rms->readers == 0);
10851f162fefSMateusz Guzik 		return;
10861f162fefSMateusz Guzik 	}
10871f162fefSMateusz Guzik 
10881f162fefSMateusz Guzik 	rms_wlock_switch(rms);
10891f162fefSMateusz Guzik 
10901f162fefSMateusz Guzik 	if (rms->readers > 0)
10913983dc32SMateusz Guzik 		msleep(&rms->writers, &rms->mtx, (PUSER - 1) | PDROP,
10923983dc32SMateusz Guzik 		    mtx_name(&rms->mtx), 0);
10931f162fefSMateusz Guzik 	else
10941f162fefSMateusz Guzik 		mtx_unlock(&rms->mtx);
10951f162fefSMateusz Guzik 	MPASS(rms->readers == 0);
10961f162fefSMateusz Guzik }
10971f162fefSMateusz Guzik 
10981f162fefSMateusz Guzik void
10991f162fefSMateusz Guzik rms_wunlock(struct rmslock *rms)
11001f162fefSMateusz Guzik {
11011f162fefSMateusz Guzik 
11021f162fefSMateusz Guzik 	mtx_lock(&rms->mtx);
11031f162fefSMateusz Guzik 	MPASS(rms->writers >= 1);
11041f162fefSMateusz Guzik 	MPASS(rms->readers == 0);
11051f162fefSMateusz Guzik 	rms->writers--;
11061f162fefSMateusz Guzik 	if (rms->writers > 0)
11071f162fefSMateusz Guzik 		wakeup_one(&rms->writers);
11081f162fefSMateusz Guzik 	else
11091f162fefSMateusz Guzik 		wakeup(&rms->readers);
11101f162fefSMateusz Guzik 	mtx_unlock(&rms->mtx);
11111f162fefSMateusz Guzik }
1112