xref: /openbsd-src/sys/sys/rwlock.h (revision 0c0478dcf387c6bf881f64e03b2ced90788ebda2)
1 /*	$OpenBSD: rwlock.h,v 1.32 2025/01/29 15:10:09 mpi Exp $	*/
2 /*
3  * Copyright (c) 2002 Artur Grabowski <art@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*
19  * Multiple readers, single writer lock.
20  *
21  * Simplistic implementation modelled after rw locks in Solaris.
22  *
23  * The rwl_owner has the following layout:
24  * [ owner or count of readers | wrlock | wrwant | wait ]
25  *
26  * When the WAIT bit is set (bit 0), the lock has waiters sleeping on it.
27  * When the WRWANT bit is set (bit 1), at least one waiter wants a write lock.
28  * When the WRLOCK bit is set (bit 2) the lock is currently write-locked.
29  *
30  * When write locked, the upper bits contain the struct proc * pointer to
31  * the writer, otherwise they count the number of readers.
32  *
33  * We provide a simple machine independent implementation:
34  *
35  * void rw_enter_read(struct rwlock *)
36  *  atomically test for RWLOCK_WRLOCK and if not set, increment the lock
37  *  by RWLOCK_READ_INCR. While RWLOCK_WRLOCK is set, loop into rw_enter_wait.
38  *
39  * void rw_enter_write(struct rwlock *);
40  *  atomically test for the lock being 0 (it's not possible to have
41  *  owner/read count unset and waiter bits set) and if 0 set the owner to
42  *  the proc and RWLOCK_WRLOCK. While not zero, loop into rw_enter_wait.
43  *
44  * void rw_exit_read(struct rwlock *);
45  *  atomically decrement lock by RWLOCK_READ_INCR and unset RWLOCK_WAIT and
46  *  RWLOCK_WRWANT remembering the old value of lock and if RWLOCK_WAIT was set,
47  *  call rw_exit_waiters with the old contents of the lock.
48  *
49  * void rw_exit_write(struct rwlock *);
50  *  atomically swap the contents of the lock with 0 and if RWLOCK_WAIT was
51  *  set, call rw_exit_waiters with the old contents of the lock.
52  */
53 
54 #ifndef _SYS_RWLOCK_H
55 #define _SYS_RWLOCK_H
56 
57 #include <sys/_lock.h>
58 
59 struct proc;
60 
61 struct rwlock {
62 	volatile unsigned long	 rwl_owner;
63 	volatile unsigned int	 rwl_waiters;
64 	volatile unsigned int	 rwl_readers;
65 	const char		*rwl_name;
66 #ifdef WITNESS
67 	struct lock_object	 rwl_lock_obj;
68 #endif
69 };
70 
71 #define RWLOCK_LO_FLAGS(flags) \
72 	((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) |			\
73 	 (ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) |		\
74 	 (ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) |		\
75 	 LO_INITIALIZED | LO_SLEEPABLE | LO_UPGRADABLE |		\
76 	 (LO_CLASS_RWLOCK << LO_CLASSSHIFT))
77 
78 #define RRWLOCK_LO_FLAGS(flags) \
79 	((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) |			\
80 	 (ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) |		\
81 	 (ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) |		\
82 	 LO_INITIALIZED | LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE | \
83 	 (LO_CLASS_RRWLOCK << LO_CLASSSHIFT))
84 
85 #define RWLOCK_LO_INITIALIZER(name, flags) \
86 	{ .lo_type = &(const struct lock_type){ .lt_name = name },	\
87 	  .lo_name = (name),						\
88 	  .lo_flags = RWLOCK_LO_FLAGS(flags) }
89 
90 #define RWL_DUPOK		0x01
91 #define RWL_NOWITNESS		0x02
92 #define RWL_IS_VNODE		0x04
93 
94 #ifdef WITNESS
95 #define RWLOCK_INITIALIZER(name) \
96 	{ 0, 0, 0, name, .rwl_lock_obj = RWLOCK_LO_INITIALIZER(name, 0) }
97 #else
98 #define RWLOCK_INITIALIZER(name) \
99 	{ 0, 0, 0, name }
100 #endif
101 
102 #define RWLOCK_WRLOCK		0x04UL
103 #define RWLOCK_MASK		0x07UL
104 
105 #define RWLOCK_OWNER(rwl)	((struct proc *)((rwl)->rwl_owner & ~RWLOCK_MASK))
106 
107 #define RWLOCK_READER_SHIFT	3UL
108 #define RWLOCK_READ_INCR	(1UL << RWLOCK_READER_SHIFT)
109 
110 #define RW_WRITE		0x0001UL /* exclusive lock */
111 #define RW_READ			0x0002UL /* shared lock */
112 #define RW_DOWNGRADE		0x0004UL /* downgrade exclusive to shared */
113 #define RW_UPGRADE		0x0005UL
114 #define RW_OPMASK		0x0007UL
115 
116 #define RW_INTR			0x0010UL /* interruptible sleep */
117 #define RW_SLEEPFAIL		0x0020UL /* fail if we slept for the lock */
118 #define RW_NOSLEEP		0x0040UL /* don't wait for the lock */
119 #define RW_RECURSEFAIL		0x0080UL /* Fail on recursion for RRW locks. */
120 #define RW_DUPOK		0x0100UL /* Permit duplicate lock */
121 
122 /*
123  * for rw_status() and rrw_status() only: exclusive lock held by
124  * some other thread
125  */
126 #define RW_WRITE_OTHER		0x0100UL
127 
128 /* recursive rwlocks; */
129 struct rrwlock {
130 	struct rwlock		 rrwl_lock;
131 	uint32_t		 rrwl_wcnt; /* # writers. */
132 };
133 
134 #ifdef _KERNEL
135 
136 void	_rw_init_flags(struct rwlock *, const char *, int,
137 	    const struct lock_type *);
138 
139 #ifdef WITNESS
140 #define rw_init_flags(rwl, name, flags) do {				\
141 	static const struct lock_type __lock_type = { .lt_name = #rwl };\
142 	_rw_init_flags(rwl, name, flags, &__lock_type);			\
143 } while (0)
144 #define rw_init(rwl, name)	rw_init_flags(rwl, name, 0)
145 #else /* WITNESS */
146 #define rw_init_flags(rwl, name, flags) \
147 				_rw_init_flags(rwl, name, flags, NULL)
148 #define rw_init(rwl, name)	_rw_init_flags(rwl, name, 0, NULL)
149 #endif /* WITNESS */
150 
151 void	rw_enter_read(struct rwlock *);
152 void	rw_enter_write(struct rwlock *);
153 void	rw_exit_read(struct rwlock *);
154 void	rw_exit_write(struct rwlock *);
155 
156 #ifdef DIAGNOSTIC
157 void	rw_assert_wrlock(struct rwlock *);
158 void	rw_assert_rdlock(struct rwlock *);
159 void	rw_assert_anylock(struct rwlock *);
160 void	rw_assert_unlocked(struct rwlock *);
161 #else
162 #define rw_assert_wrlock(rwl)	((void)0)
163 #define rw_assert_rdlock(rwl)	((void)0)
164 #define rw_assert_anylock(rwl)	((void)0)
165 #define rw_assert_unlocked(rwl)	((void)0)
166 #endif
167 
168 int	rw_enter(struct rwlock *, int);
169 void	rw_exit(struct rwlock *);
170 int	rw_status(struct rwlock *);
171 
172 static inline int
173 rw_read_held(struct rwlock *rwl)
174 {
175 	return (rw_status(rwl) == RW_READ);
176 }
177 
178 static inline int
179 rw_write_held(struct rwlock *rwl)
180 {
181 	return (rw_status(rwl) == RW_WRITE);
182 }
183 
184 static inline int
185 rw_lock_held(struct rwlock *rwl)
186 {
187 	int status;
188 
189 	status = rw_status(rwl);
190 
191 	return (status == RW_READ || status == RW_WRITE);
192 }
193 
194 
195 void	_rrw_init_flags(struct rrwlock *, const char *, int,
196 	    const struct lock_type *);
197 int	rrw_enter(struct rrwlock *, int);
198 void	rrw_exit(struct rrwlock *);
199 int	rrw_status(struct rrwlock *);
200 
201 #ifdef WITNESS
202 #define rrw_init_flags(rrwl, name, flags) do {				\
203 	static const struct lock_type __lock_type = { .lt_name = #rrwl };\
204 	_rrw_init_flags(rrwl, name, flags, &__lock_type);		\
205 } while (0)
206 #define rrw_init(rrwl, name)	rrw_init_flags(rrwl, name, 0)
207 #else /* WITNESS */
208 #define rrw_init_flags(rrwl, name, flags) \
209 				_rrw_init_flags(rrwl, name, 0, NULL)
210 #define rrw_init(rrwl, name)	_rrw_init_flags(rrwl, name, 0, NULL)
211 #endif /* WITNESS */
212 
213 
214 /*
215  * Allocated, reference-counted rwlocks
216  */
217 
218 #ifdef WITNESS
219 #define rw_obj_alloc_flags(rwl, name, flags) do {			\
220 	static struct lock_type __lock_type = { .lt_name = #rwl };	\
221 	_rw_obj_alloc_flags(rwl, name, flags, &__lock_type);		\
222 } while (0)
223 #else
224 #define rw_obj_alloc_flags(rwl, name, flags) \
225 			_rw_obj_alloc_flags(rwl, name, flags, NULL)
226 #endif
227 #define rw_obj_alloc(rwl, name)		rw_obj_alloc_flags(rwl, name, 0)
228 
229 void	rw_obj_init(void);
230 void	_rw_obj_alloc_flags(struct rwlock **, const char *, int,
231 		struct lock_type *);
232 void	rw_obj_hold(struct rwlock *);
233 int	rw_obj_free(struct rwlock *);
234 
235 #endif /* _KERNEL */
236 
237 #endif /* _SYS_RWLOCK_H */
238