linux_rwsem.c revision 1.3 1 /* $NetBSD: linux_rwsem.c,v 1.3 2021/12/19 11:21:45 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: linux_rwsem.c,v 1.3 2021/12/19 11:21:45 riastradh Exp $");
31
32 #include <sys/types.h>
33
34 #include <sys/condvar.h>
35 #include <sys/lwp.h>
36 #include <sys/lockdebug.h>
37 #include <sys/rwlock.h>
38
39 #include <machine/limits.h>
40
41 #include <lib/libkern/libkern.h>
42
43 #include <linux/rwsem.h>
44
45 #define RWSEM_WANTLOCK(RWSEM) \
46 LOCKDEBUG_WANTLOCK((RWSEM)->rws_debug, (RWSEM), \
47 (uintptr_t)__builtin_return_address(0), 0)
48 #define RWSEM_LOCKED_EX(RWSEM) \
49 LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL, \
50 (uintptr_t)__builtin_return_address(0), 0)
51 #define RWSEM_LOCKED_SH(RWSEM) \
52 LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL, \
53 (uintptr_t)__builtin_return_address(0), 1)
54 #define RWSEM_UNLOCKED_EX(RWSEM) \
55 LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM), \
56 (uintptr_t)__builtin_return_address(0), 0)
57 #define RWSEM_UNLOCKED_SH(RWSEM) \
58 LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM), \
59 (uintptr_t)__builtin_return_address(0), 1)
60
61 #ifdef LOCKDEBUG
62 static void
63 rwsem_dump(const volatile void *cookie, lockop_printer_t pr)
64 {
65 const volatile struct rw_semaphore *rwsem = cookie;
66
67 pr("%-13s: %p", "writer", rwsem->rws_writer);
68 pr("%-13s: %u", "readers", rwsem->rws_readers);
69 pr("%-13s: %s", "writewanted", rwsem->rws_writewanted ? "yes" : "no");
70 }
71
72 static lockops_t rwsem_lockops = {
73 .lo_name = "Linux read/write semaphore",
74 .lo_type = LOCKOPS_SLEEP,
75 .lo_dump = rwsem_dump,
76 };
77 #endif
78
79 void
80 init_rwsem(struct rw_semaphore *rwsem)
81 {
82
83 mutex_init(&rwsem->rws_lock, MUTEX_DEFAULT, IPL_VM);
84 cv_init(&rwsem->rws_cv, "lnxrwsem");
85 rwsem->rws_writer = NULL;
86 rwsem->rws_readers = 0;
87
88 #ifdef LOCKDEBUG
89 rwsem->rws_debug = LOCKDEBUG_ALLOC(rwsem, &rwsem_lockops,
90 (uintptr_t)__builtin_return_address(0));
91 #endif
92 }
93
94 void
95 destroy_rwsem(struct rw_semaphore *rwsem)
96 {
97
98 KASSERT(rwsem->rws_readers == 0);
99 KASSERT(rwsem->rws_writer == NULL);
100
101 #ifdef LOCKDEBUG
102 LOCKDEBUG_FREE(rwsem->rws_debug, rwsem);
103 #endif
104
105 cv_destroy(&rwsem->rws_cv);
106 mutex_destroy(&rwsem->rws_lock);
107 }
108
109 void
110 down_read(struct rw_semaphore *rwsem)
111 {
112
113 RWSEM_WANTLOCK(rwsem);
114
115 mutex_enter(&rwsem->rws_lock);
116 while (rwsem->rws_writer || rwsem->rws_writewanted)
117 cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
118 KASSERT(rwsem->rws_readers < UINT_MAX);
119 rwsem->rws_readers++;
120 mutex_exit(&rwsem->rws_lock);
121
122 RWSEM_LOCKED_SH(rwsem);
123 }
124
125 bool
126 down_read_trylock(struct rw_semaphore *rwsem)
127 {
128 bool ret = false;
129
130 /*
131 * Note: Linux apparently relies on down_read_trylock to
132 * quietly succeed when the caller already holds a reader lock.
133 * This is why we can't use rwlock(9), which absolutely
134 * prohibits recursive use and crashes immediately under
135 * LOCKDEBUG if you try it.
136 */
137
138 mutex_enter(&rwsem->rws_lock);
139 if (rwsem->rws_writer == NULL && !rwsem->rws_writewanted) {
140 KASSERT(rwsem->rws_readers < UINT_MAX);
141 rwsem->rws_readers++;
142 ret = true;
143 }
144 mutex_exit(&rwsem->rws_lock);
145
146 if (ret)
147 RWSEM_LOCKED_SH(rwsem);
148
149 return ret;
150 }
151
152 void
153 up_read(struct rw_semaphore *rwsem)
154 {
155
156 RWSEM_UNLOCKED_SH(rwsem);
157
158 mutex_enter(&rwsem->rws_lock);
159 KASSERT(rwsem->rws_readers);
160 KASSERT(rwsem->rws_writer == NULL);
161 if (--rwsem->rws_readers == 0)
162 cv_broadcast(&rwsem->rws_cv);
163 mutex_exit(&rwsem->rws_lock);
164 }
165
166 void
167 down_write(struct rw_semaphore *rwsem)
168 {
169
170 RWSEM_WANTLOCK(rwsem);
171
172 mutex_enter(&rwsem->rws_lock);
173
174 /* If another writer is waiting, get in the queue. */
175 while (rwsem->rws_writewanted)
176 cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
177
178 /*
179 * No other writers waiting. Our turn. Announce our intent to
180 * readers, and wait for the writer or readers to finish.
181 */
182 rwsem->rws_writewanted = true;
183 while (rwsem->rws_writer || rwsem->rws_readers) {
184 KASSERTMSG(rwsem->rws_writer != curlwp,
185 "locking against myself: rwsem=%p lwp=%p", rwsem, curlwp);
186 cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
187 }
188
189 /* At last, it is ours! */
190 KASSERT(rwsem->rws_readers == 0);
191 KASSERT(rwsem->rws_writer == NULL);
192 KASSERT(rwsem->rws_writewanted);
193 rwsem->rws_writewanted = false;
194 rwsem->rws_writer = curlwp;
195
196 mutex_exit(&rwsem->rws_lock);
197
198 RWSEM_LOCKED_EX(rwsem);
199 }
200
201 void
202 up_write(struct rw_semaphore *rwsem)
203 {
204
205 RWSEM_UNLOCKED_EX(rwsem);
206
207 mutex_enter(&rwsem->rws_lock);
208 KASSERT(rwsem->rws_writer == curlwp);
209 KASSERT(rwsem->rws_readers == 0);
210 rwsem->rws_writer = NULL;
211 cv_broadcast(&rwsem->rws_cv);
212 mutex_exit(&rwsem->rws_lock);
213 }
214
215 void
216 downgrade_write(struct rw_semaphore *rwsem)
217 {
218
219 RWSEM_UNLOCKED_EX(rwsem);
220
221 mutex_enter(&rwsem->rws_lock);
222 KASSERT(rwsem->rws_writer == curlwp);
223 KASSERT(rwsem->rws_readers == 0);
224 rwsem->rws_writer = NULL;
225 rwsem->rws_readers = 1;
226 cv_broadcast(&rwsem->rws_cv);
227 mutex_exit(&rwsem->rws_lock);
228
229 RWSEM_LOCKED_SH(rwsem);
230 }
231