kern_rwlock.c revision 1.1.2.4 1 /* $NetBSD: kern_rwlock.c,v 1.1.2.4 2002/03/17 20:18:56 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Kernel reader/writer lock implementation, modeled after those found in
41 * Solaris, a description of which can be found in:
42 *
43 * Solaris Internals: Core Kernel Architecture, Jim Mauro and
44 * Richard McDougall.
45 */
46
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: kern_rwlock.c,v 1.1.2.4 2002/03/17 20:18:56 thorpej Exp $");
49
50 #include <sys/param.h>
51 #include <sys/proc.h>
52 #include <sys/rwlock.h>
53 #include <sys/sched.h>
54 #include <sys/systm.h>
55
56 /*
57 * rw_init:
58 *
59 * Initialize a rwlock for use.
60 */
61 void
62 rw_init(krwlock_t *rwl)
63 {
64
65 RWLOCK_INIT(rwl);
66 }
67
68 /*
69 * rw_destroy:
70 *
71 * Tear down a rwlock.
72 */
73 void
74 rw_destroy(krwlock_t *rwl)
75 {
76
77 /* XXX IMPLEMENT ME XXX */
78 }
79
80 /*
81 * rw_enter:
82 *
83 * Acquire a rwlock.
84 */
85 void
86 rw_enter(krwlock_t *rwl, krw_t rw)
87 {
88 struct turnstile *ts;
89 struct proc *p;
90 unsigned long owner, incr, need_wait, set_wait;
91
92 /*
93 * Ensure RW_WRITER == 0, so that machine-dependent code can
94 * make that assumption.
95 */
96 #if RW_WRITER != 0
97 #error "RW_WRITER != 0"
98 #endif
99
100 /*
101 * We play a slight trick here. If we're a reader, we want
102 * increment the read count. If we're a writer, we want to
103 * set the owner field and whe WRITE_LOCKED bit.
104 *
105 * In the latter case, we expect those bits to be zero,
106 * therefore we can use an add operation to set them, which
107 * means an add operation for both cases.
108 */
109 switch (rw) {
110 case RW_WRITER:
111 incr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
112 need_wait = RWLOCK_WRITE_LOCKED;
113 set_wait = RWLOCK_HAS_WAITERS | RWLOCK_WRITE_WANTED;
114 break;
115
116 case RW_READER:
117 incr = RWLOCK_READ_INCR;
118 need_wait = RWLOCK_WRITE_LOCKED | RWLOCK_WRITE_WANTED;
119 set_wait = RWLOCK_HAS_WAITERS;
120 break;
121 #ifdef DIAGNOSTIC
122 default:
123 panic("rw_enter: bad rw %d", rw);
124 #endif
125 }
126
127 for (;;) {
128 /*
129 * Read the lock owner field. If the need-to-wait
130 * indicator is clear, then try to acquire the lock.
131 */
132 owner = rwl->rwl_owner;
133 if ((owner & need_wait) == 0) {
134 if (RWLOCK_ACQUIRE(rwl, owner, owner + incr)) {
135 /* Got it! */
136 break;
137 }
138
139 /*
140 * Didn't get it -- spin around again (we'll
141 * probably sleep on the next iteration).
142 */
143 continue;
144 }
145
146 if (RWLOCK_OWNER(rwl) == curproc)
147 panic("rw_enter: locking against myself");
148
149 ts = turnstile_lookup(rwl);
150
151 /*
152 * Mark the rwlock as having waiters. After we do
153 * this, we need to check one more time if the lock
154 * is busy, and if not, spin around again.
155 *
156 * Note, we also need to spin again if we failed to
157 * set the has-waiters indicator (which means the
158 * lock condition changed, but more importantly, we
159 * need to try and set that indicator again).
160 */
161 RWLOCK_SET_WAITERS(rwl, need_wait, set_wait);
162 owner = rwl->rwl_owner;
163 if ((owner & need_wait) == 0 || (owner & set_wait) == 0) {
164 turnstile_exit(rwl);
165 continue;
166 }
167 /* XXXJRT p->p_priority */
168 /* XXXJRT Do not currently distinguish reader vs. writer. */
169 (void) turnstile_block(ts, TS_WRITER_Q, p->p_priority, rwl);
170
171 /*
172 * XXX Solaris Internals says that the Solaris 7
173 * rwlock implementation does a direct-handoff. We
174 * don't implement that yet, but if we did, then a
175 * thread wakes back up, i.e. arrives here, it would
176 * hold the lock as requested.
177 */
178 }
179
180 KASSERT((rw == RW_WRITER && RWLOCK_OWNER(rwl) == curproc) ||
181 (rw == RW_READER && RWLOCK_COUNT(rwl) != 0));
182 }
183
184 /*
185 * rw_tryenter:
186 *
187 * Try to acquire a rwlock.
188 */
189 int
190 rw_tryenter(krwlock_t *rwl, krw_t rw)
191 {
192 unsigned long owner, incr, need_wait;
193
194 switch (rw) {
195 case RW_WRITER:
196 incr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
197 need_wait = RWLOCK_WRITE_LOCKED;
198 break;
199
200 case RW_READER:
201 incr = RWLOCK_READ_INCR;
202 need_wait = RWLOCK_WRITE_LOCKED | RWLOCK_WRITE_WANTED;
203 break;
204 #ifdef DIAGNOSTIC
205 default:
206 panic("rw_tryenter: bad rw %d", rw);
207 #endif
208 }
209
210 for (;;) {
211 owner = rwl->rwl_owner;
212 if ((owner & need_wait) == 0) {
213 if (RWLOCK_ACQUIRE(rwl, owner, owner + incr)) {
214 /* Got it! */
215 break;
216 }
217 continue;
218 }
219 return (0);
220 }
221
222 KASSERT((rw == RW_WRITER && RWLOCK_OWNER(rwl) == curproc) ||
223 (rw == RW_READER && RWLOCK_COUNT(rwl) != 0));
224 return (1);
225 }
226
227 /*
228 * rw_exit:
229 *
230 * Release a rwlock.
231 */
232 void
233 rw_exit(krwlock_t *rwl)
234 {
235 struct turnstile *ts;
236 unsigned long owner, decr, new;
237
238 /*
239 * Again, we use a trick. Since we used an add operation to
240 * set the required lock bits, we can use a subtract to clear
241 * them, which makes the read-release and write-release path
242 * the same.
243 */
244 if (rwl->rwl_owner & RWLOCK_WRITE_LOCKED) {
245 if (RWLOCK_OWNER(rwl) == NULL)
246 panic("rw_exit: not owned");
247 else
248 panic("rw_exit: not owner, owner = %p, "
249 "current = %p", RWLOCK_OWNER(rwl), curproc);
250 decr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
251 } else {
252 if (RWLOCK_COUNT(rwl) == 0)
253 panic("rw_exit: not held\n");
254 decr = RWLOCK_READ_INCR;
255 }
256
257 for (;;) {
258 /*
259 * Get this lock's turnstile. This gets the interlock on
260 * the sleep queue. Once we have that, we can perform the
261 * lock release operation.
262 */
263 ts = turnstile_lookup(rwl);
264
265 /*
266 * Compute what we expect the new value of the lock
267 * to be. Skip the wakeup step if there are no
268 * appropriate waiters.
269 */
270 owner = rwl->rwl_owner;
271 new = owner - decr;
272 if ((new & (RWLOCK_THREAD |
273 RWLOCK_HAS_WAITERS)) != RWLOCK_HAS_WAITERS) {
274 if (RWLOCK_RELEASE(rwl, owner, new)) {
275 /* Ding! */
276 turnstile_exit(rwl);
277 break;
278 }
279 turnstile_exit(rwl);
280 continue;
281 }
282
283 /* We're about to wake everybody up; clear waiter bits. */
284 new &= ~(RWLOCK_HAS_WAITERS | RWLOCK_WRITE_WANTED);
285
286 if (RWLOCK_RELEASE(rwl, owner, new) == 0) {
287 /* Oops, try again. */
288 turnstile_exit(rwl);
289 continue;
290 }
291
292 /*
293 * Wake the thundering herd.
294 * XXX Should implement direct-handoff.
295 */
296 KASSERT(ts != NULL);
297 turnstile_wakeup(ts, TS_WRITER_Q,
298 ts->ts_sleepq[TS_WRITER_Q].tsq_waiters, NULL);
299 break;
300 }
301 }
302
303 /*
304 * rw_downgrade:
305 *
306 * Downgrade a write lock to a read lock.
307 */
308 void
309 rw_downgrade(krwlock_t *rwl)
310 {
311 struct turnstile *ts;
312 unsigned long owner;
313
314 if (RWLOCK_OWNER(rwl) != curproc) {
315 if (RWLOCK_OWNER(rwl) == NULL)
316 panic("rw_downgrade: not owned");
317 else
318 panic("rw_downgrade: not owner, owner = %p, "
319 "current = %p", RWLOCK_OWNER(rwl), curproc);
320 }
321
322 /* XXX This algorithm has to change if we do direct-handoff. */
323 for (;;) {
324 ts = turnstile_lookup(rwl);
325
326 owner = rwl->rwl_owner;
327 if (RWLOCK_RELEASE(rwl, owner, RWLOCK_READ_INCR) == 0) {
328 /* Oops, try again. */
329 turnstile_exit(rwl);
330 continue;
331 }
332 if (owner & RWLOCK_HAS_WAITERS) {
333 KASSERT(ts != NULL);
334 turnstile_wakeup(ts, TS_WRITER_Q,
335 ts->ts_sleepq[TS_WRITER_Q].tsq_waiters, NULL);
336 }
337 break;
338 }
339
340 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
341 KASSERT(RWLOCK_COUNT(rwl) != 0);
342 }
343
344 /*
345 * rw_tryupgrade:
346 *
347 * Try to upgrade an read lock to a write lock.
348 */
349 int
350 rw_tryupgrade(krwlock_t *rwl)
351 {
352 unsigned long owner;
353
354 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
355 KASSERT(RWLOCK_COUNT(rwl) != 0);
356
357 for (;;) {
358 /*
359 * Since we want to favor writers, we don't bother
360 * checking for waiting writers, we just scarf it.
361 *
362 * We must be the only reader.
363 */
364 owner = rwl->rwl_owner;
365 if ((owner & RWLOCK_THREAD) != RWLOCK_READ_INCR)
366 return (0);
367 if (RWLOCK_ACQUIRE(rwl, owner,
368 ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED |
369 (owner & ~RWLOCK_THREAD))) {
370 /* Ding! */
371 break;
372 }
373 }
374
375 KASSERT(rwl->rwl_owner & RWLOCK_WRITE_LOCKED);
376 KASSERT(RWLOCK_OWNER(rwl) == curproc);
377 return (1);
378 }
379
380 /*
381 * rw_read_held:
382 *
383 * Returns true if the rwlock is held for reading.
384 */
385 int
386 rw_read_held(krwlock_t *rwl)
387 {
388 unsigned long owner = rwl->rwl_owner;
389
390 return ((owner & RWLOCK_WRITE_LOCKED) == 0 &&
391 (owner & RWLOCK_THREAD) != 0);
392 }
393
394 /*
395 * rw_write_held:
396 *
397 * Returns true if the rwlock is held for writing.
398 */
399 int
400 rw_write_held(krwlock_t *rwl)
401 {
402 unsigned long owner = rwl->rwl_owner;
403
404 return ((owner & RWLOCK_WRITE_LOCKED) != 0);
405 }
406
407 /*
408 * rw_read_locked:
409 *
410 * Like rw_read_held(), but asserts it.
411 */
412 int
413 rw_read_locked(krwlock_t *rwl)
414 {
415 int rv = rw_read_held(rwl);
416
417 #ifdef DIAGNOSTIC
418 if (rv == 0)
419 panic("rw_read_locked: not held");
420 #endif
421
422 return (rv);
423 }
424
425 /*
426 * rw_write_locked:
427 *
428 * Like rw_write_held(), but asserts that we hold it.
429 */
430 int
431 rw_write_locked(krwlock_t *rwl)
432 {
433 int rv = rw_write_held(rwl);
434
435 #ifdef DIAGNOSTIC
436 if (rv == 0)
437 panic("rw_write_locked: not held");
438 else if (RWLOCK_OWNER(rwl) != curproc)
439 panic("rw_write_locked: not owner, owner = %p, "
440 "current = %p", RWLOCK_OWNER(rwl), curproc);
441 #endif
442
443 return (rv);
444 }
445
446 /*
447 * rw_owner:
448 *
449 * Return the owner of the rwlock.
450 */
451 struct proc *
452 rw_owner(krwlock_t *rwl)
453 {
454 unsigned long owner = rwl->rwl_owner;
455
456 return ((owner & RWLOCK_WRITE_LOCKED) ?
457 ((struct proc *) (owner & RWLOCK_THREAD)) : NULL);
458 }
459