1/* $OpenBSD: rwlock.h,v 1.34 2025/07/21 20:36:41 bluhm 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
59struct proc;
60
61struct 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 int rwl_traceidx;
70};
71
72#define RWLOCK_LO_FLAGS(flags) \
73 ((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) | \
74 (ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) | \
75 (ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) | \
76 LO_INITIALIZED | LO_SLEEPABLE | LO_UPGRADABLE | \
77 (LO_CLASS_RWLOCK << LO_CLASSSHIFT))
78
79#define RRWLOCK_LO_FLAGS(flags) \
80 ((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) | \
81 (ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) | \
82 (ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) | \
83 LO_INITIALIZED | LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE | \
84 (LO_CLASS_RRWLOCK << LO_CLASSSHIFT))
85
86#define RWLOCK_LO_INITIALIZER(name, flags) \
87 { .lo_type = &(const struct lock_type){ .lt_name = name }, \
88 .lo_name = (name), \
89 .lo_flags = RWLOCK_LO_FLAGS(flags) }
90
91#define RWL_DUPOK 0x01
92#define RWL_NOWITNESS 0x02
93#define RWL_IS_VNODE 0x04
94
95#ifdef WITNESS
96#define RWLOCK_INITIALIZER(name) \
97 { 0, 0, 0, name, .rwl_lock_obj = RWLOCK_LO_INITIALIZER(name, 0), 0 }
98#define RWLOCK_INITIALIZER_TRACE(name, trace) \
99 { 0, 0, 0, name, .rwl_lock_obj = RWLOCK_LO_INITIALIZER(name, 0), trace }
100#else
101#define RWLOCK_INITIALIZER(name) \
102 { 0, 0, 0, name, 0 }
103#define RWLOCK_INITIALIZER_TRACE(name, trace) \
104 { 0, 0, 0, name, trace }
105#endif
106
107#define RWLOCK_WRLOCK 0x04UL
108#define RWLOCK_MASK 0x07UL
109
110#define RWLOCK_OWNER(rwl) ((struct proc *)((rwl)->rwl_owner & ~RWLOCK_MASK))
111
112#define RWLOCK_READER_SHIFT 3UL
113#define RWLOCK_READ_INCR (1UL << RWLOCK_READER_SHIFT)
114
115#define RW_WRITE 0x0001UL /* exclusive lock */
116#define RW_READ 0x0002UL /* shared lock */
117#define RW_DOWNGRADE 0x0004UL /* downgrade exclusive to shared */
118#define RW_UPGRADE 0x0005UL
119#define RW_OPMASK 0x0007UL
120
121#define RW_INTR 0x0010UL /* interruptible sleep */
122#define RW_NOSLEEP 0x0040UL /* don't wait for the lock */
123#define RW_RECURSEFAIL 0x0080UL /* Fail on recursion for RRW locks. */
124#define RW_DUPOK 0x0100UL /* Permit duplicate lock */
125
126/*
127 * for rw_status() and rrw_status() only: exclusive lock held by
128 * some other thread
129 */
130#define RW_WRITE_OTHER 0x0100UL
131
132/* recursive rwlocks; */
133struct rrwlock {
134 struct rwlock rrwl_lock;
135 uint32_t rrwl_wcnt; /* # writers. */
136};
137
138#ifdef _KERNEL
139
140void _rw_init_flags(struct rwlock *, const char *, int,
141 const struct lock_type *, int);
142
143#ifdef WITNESS
144#define rw_init_flags_trace(rwl, name, flags, trace) do { \
145 static const struct lock_type __lock_type = { .lt_name = #rwl };\
146 _rw_init_flags(rwl, name, flags, &__lock_type, trace); \
147} while (0)
148#define rw_init_flags(rwl, name, flags) do { \
149 static const struct lock_type __lock_type = { .lt_name = #rwl };\
150 _rw_init_flags(rwl, name, flags, &__lock_type, 0); \
151} while (0)
152#define rw_init(rwl, name) rw_init_flags(rwl, name, 0)
153#else /* WITNESS */
154#define rw_init_flags_trace(rwl, name, flags, trace) \
155 _rw_init_flags(rwl, name, flags, NULL, trace)
156#define rw_init_flags(rwl, name, flags) \
157 _rw_init_flags(rwl, name, flags, NULL, 0)
158#define rw_init(rwl, name) _rw_init_flags(rwl, name, 0, NULL, 0)
159#endif /* WITNESS */
160
161void rw_enter_read(struct rwlock *);
162void rw_enter_write(struct rwlock *);
163void rw_exit_read(struct rwlock *);
164void rw_exit_write(struct rwlock *);
165
166#ifdef DIAGNOSTIC
167void rw_assert_wrlock(struct rwlock *);
168void rw_assert_rdlock(struct rwlock *);
169void rw_assert_anylock(struct rwlock *);
170void rw_assert_unlocked(struct rwlock *);
171#else
172#define rw_assert_wrlock(rwl) ((void)0)
173#define rw_assert_rdlock(rwl) ((void)0)
174#define rw_assert_anylock(rwl) ((void)0)
175#define rw_assert_unlocked(rwl) ((void)0)
176#endif
177
178int rw_enter(struct rwlock *, int);
179void rw_exit(struct rwlock *);
180int rw_status(struct rwlock *);
181
182static inline int
183rw_read_held(struct rwlock *rwl)
184{
185 return (rw_status(rwl) == RW_READ);
186}
187
188static inline int
189rw_write_held(struct rwlock *rwl)
190{
191 return (rw_status(rwl) == RW_WRITE);
192}
193
194static inline int
195rw_lock_held(struct rwlock *rwl)
196{
197 int status;
198
199 status = rw_status(rwl);
200
201 return (status == RW_READ || status == RW_WRITE);
202}
203
204
205void _rrw_init_flags(struct rrwlock *, const char *, int,
206 const struct lock_type *);
207int rrw_enter(struct rrwlock *, int);
208void rrw_exit(struct rrwlock *);
209int rrw_status(struct rrwlock *);
210
211#ifdef WITNESS
212#define rrw_init_flags(rrwl, name, flags) do { \
213 static const struct lock_type __lock_type = { .lt_name = #rrwl };\
214 _rrw_init_flags(rrwl, name, flags, &__lock_type); \
215} while (0)
216#define rrw_init(rrwl, name) rrw_init_flags(rrwl, name, 0)
217#else /* WITNESS */
218#define rrw_init_flags(rrwl, name, flags) \
219 _rrw_init_flags(rrwl, name, 0, NULL)
220#define rrw_init(rrwl, name) _rrw_init_flags(rrwl, name, 0, NULL)
221#endif /* WITNESS */
222
223
224/*
225 * Allocated, reference-counted rwlocks
226 */
227
228#ifdef WITNESS
229#define rw_obj_alloc_flags(rwl, name, flags) do { \
230 static struct lock_type __lock_type = { .lt_name = #rwl }; \
231 _rw_obj_alloc_flags(rwl, name, flags, &__lock_type); \
232} while (0)
233#else
234#define rw_obj_alloc_flags(rwl, name, flags) \
235 _rw_obj_alloc_flags(rwl, name, flags, NULL)
236#endif
237#define rw_obj_alloc(rwl, name) rw_obj_alloc_flags(rwl, name, 0)
238
239void rw_obj_init(void);
240void _rw_obj_alloc_flags(struct rwlock **, const char *, int,
241 struct lock_type *);
242void rw_obj_hold(struct rwlock *);
243int rw_obj_free(struct rwlock *);
244
245/* sorted alphabetically, keep in sync with dev/dt/dt_prov_static.c */
246#define DT_RWLOCK_IDX_NETLOCK 1
247#define DT_RWLOCK_IDX_SOLOCK 2
248
249#endif /* _KERNEL */
250
251#endif /* _SYS_RWLOCK_H */