aboutsummaryrefslogtreecommitdiff
path: root/sys/sys/rwlock.h
blob: 4b32e387ff47ced27a03b08eae7a4c56da3e7854 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*	$OpenBSD: rwlock.h,v 1.26 2019/07/16 01:40:49 jsg Exp $	*/
/*
 * Copyright (c) 2002 Artur Grabowski <art@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Multiple readers, single writer lock.
 *
 * Simplistic implementation modelled after rw locks in Solaris.
 *
 * The rwl_owner has the following layout:
 * [ owner or count of readers | wrlock | wrwant | wait ]
 *
 * When the WAIT bit is set (bit 0), the lock has waiters sleeping on it.
 * When the WRWANT bit is set (bit 1), at least one waiter wants a write lock.
 * When the WRLOCK bit is set (bit 2) the lock is currently write-locked.
 *
 * When write locked, the upper bits contain the struct proc * pointer to
 * the writer, otherwise they count the number of readers.
 *
 * We provide a simple machine independent implementation:
 *
 * void rw_enter_read(struct rwlock *)
 *  atomically test for RWLOCK_WRLOCK and if not set, increment the lock
 *  by RWLOCK_READ_INCR. While RWLOCK_WRLOCK is set, loop into rw_enter_wait.
 *
 * void rw_enter_write(struct rwlock *);
 *  atomically test for the lock being 0 (it's not possible to have
 *  owner/read count unset and waiter bits set) and if 0 set the owner to
 *  the proc and RWLOCK_WRLOCK. While not zero, loop into rw_enter_wait.
 *
 * void rw_exit_read(struct rwlock *);
 *  atomically decrement lock by RWLOCK_READ_INCR and unset RWLOCK_WAIT and
 *  RWLOCK_WRWANT remembering the old value of lock and if RWLOCK_WAIT was set,
 *  call rw_exit_waiters with the old contents of the lock.
 *
 * void rw_exit_write(struct rwlock *);
 *  atomically swap the contents of the lock with 0 and if RWLOCK_WAIT was
 *  set, call rw_exit_waiters with the old contents of the lock.
 */

#ifndef _SYS_RWLOCK_H
#define _SYS_RWLOCK_H

#include <sys/_lock.h>

struct proc;

struct rwlock {
	volatile unsigned long	 rwl_owner;
	const char		*rwl_name;
#ifdef WITNESS
	struct lock_object	 rwl_lock_obj;
#endif
};

#define RWLOCK_LO_FLAGS(flags) \
	((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) |			\
	 (ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) |		\
	 (ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) |		\
	 LO_INITIALIZED | LO_SLEEPABLE | LO_UPGRADABLE |		\
	 (LO_CLASS_RWLOCK << LO_CLASSSHIFT))

#define RRWLOCK_LO_FLAGS(flags) \
	((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) |			\
	 (ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) |		\
	 (ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) |		\
	 LO_INITIALIZED | LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE | \
	 (LO_CLASS_RRWLOCK << LO_CLASSSHIFT))

#define RWLOCK_LO_INITIALIZER(name, flags) \
	{ .lo_type = &(const struct lock_type){ .lt_name = name },	\
	  .lo_name = (name),						\
	  .lo_flags = RWLOCK_LO_FLAGS(flags) }

#define RWL_DUPOK		0x01
#define RWL_NOWITNESS		0x02
#define RWL_IS_VNODE		0x04

#ifdef WITNESS
#define RWLOCK_INITIALIZER(name) \
	{ 0, name, .rwl_lock_obj = RWLOCK_LO_INITIALIZER(name, 0) }
#else
#define RWLOCK_INITIALIZER(name) \
	{ 0, name }
#endif

#define RWLOCK_WAIT		0x01UL
#define RWLOCK_WRWANT		0x02UL
#define RWLOCK_WRLOCK		0x04UL
#define RWLOCK_MASK		0x07UL

#define RWLOCK_OWNER(rwl)	((struct proc *)((rwl)->rwl_owner & ~RWLOCK_MASK))

#define RWLOCK_READER_SHIFT	3UL
#define RWLOCK_READ_INCR	(1UL << RWLOCK_READER_SHIFT)

#define RW_WRITE		0x0001UL /* exclusive lock */
#define RW_READ			0x0002UL /* shared lock */
#define RW_DOWNGRADE		0x0004UL /* downgrade exclusive to shared */
#define RW_OPMASK		0x0007UL

#define RW_INTR			0x0010UL /* interruptible sleep */
#define RW_SLEEPFAIL		0x0020UL /* fail if we slept for the lock */
#define RW_NOSLEEP		0x0040UL /* don't wait for the lock */
#define RW_RECURSEFAIL		0x0080UL /* Fail on recursion for RRW locks. */
#define RW_DUPOK		0x0100UL /* Permit duplicate lock */

/*
 * for rw_status() and rrw_status() only: exclusive lock held by
 * some other thread
 */
#define RW_WRITE_OTHER		0x0100UL

/* recursive rwlocks; */
struct rrwlock {
	struct rwlock		 rrwl_lock;
	uint32_t		 rrwl_wcnt; /* # writers. */
};

#ifdef _KERNEL

void	_rw_init_flags(struct rwlock *, const char *, int,
	    const struct lock_type *);

#ifdef WITNESS
#define rw_init_flags(rwl, name, flags) do {				\
	static const struct lock_type __lock_type = { .lt_name = #rwl };\
	_rw_init_flags(rwl, name, flags, &__lock_type);			\
} while (0)
#define rw_init(rwl, name)	rw_init_flags(rwl, name, 0)
#else /* WITNESS */
#define rw_init_flags(rwl, name, flags) \
				_rw_init_flags(rwl, name, flags, NULL)
#define rw_init(rwl, name)	_rw_init_flags(rwl, name, 0, NULL)
#endif /* WITNESS */

void	rw_enter_read(struct rwlock *);
void	rw_enter_write(struct rwlock *);
void	rw_exit_read(struct rwlock *);
void	rw_exit_write(struct rwlock *);

#ifdef DIAGNOSTIC
void	rw_assert_wrlock(struct rwlock *);
void	rw_assert_rdlock(struct rwlock *);
void	rw_assert_anylock(struct rwlock *);
void	rw_assert_unlocked(struct rwlock *);
#else
#define rw_assert_wrlock(rwl)	((void)0)
#define rw_assert_rdlock(rwl)	((void)0)
#define rw_assert_anylock(rwl)	((void)0)
#define rw_assert_unlocked(rwl)	((void)0)
#endif

int	rw_enter(struct rwlock *, int);
void	rw_exit(struct rwlock *);
int	rw_status(struct rwlock *);

void	_rrw_init_flags(struct rrwlock *, const char *, int,
	    const struct lock_type *);
int	rrw_enter(struct rrwlock *, int);
void	rrw_exit(struct rrwlock *);
int	rrw_status(struct rrwlock *);

#ifdef WITNESS
#define rrw_init_flags(rrwl, name, flags) do {				\
	static const struct lock_type __lock_type = { .lt_name = #rrwl };\
	_rrw_init_flags(rrwl, name, flags, &__lock_type);		\
} while (0)
#define rrw_init(rrwl, name)	rrw_init_flags(rrwl, name, 0)
#else /* WITNESS */
#define rrw_init_flags(rrwl, name, flags) \
				_rrw_init_flags(rrwl, name, 0, NULL)
#define rrw_init(rrwl, name)	_rrw_init_flags(rrwl, name, 0, NULL)
#endif /* WITNESS */

#endif /* _KERNEL */

#endif /* _SYS_RWLOCK_H */