kern_rwlock.c revision 1.1.2.5 1 /* $NetBSD: kern_rwlock.c,v 1.1.2.5 2002/03/22 03:27:00 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.5 2002/03/22 03:27:00 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 * note that stdarg.h and the ansi style va_start macro is used for both
58 * ansi and traditional c complers.
59 * XXX: this requires that stdarg.h define: va_alist and va_dcl
60 */
61 #include <machine/stdarg.h>
62
63 #if defined(RWLOCK_DEBUG)
64 #define RWL_LOCKED(rwl) \
65 (rwl)->rwl_debug.rwl_locked = (vaddr_t) __builtin_return_address(0)
66 #define RWL_UNLOCKED(rwl) \
67 (rwl)->rwl_debug.rwl_unlocked = (vaddr_t) __builtin_return_address(0)
68 #else
69 #define RWL_LOCKED(rwl) /* nothing */
70 #define RWL_UNLOCKED(rwl) /* nothing */
71 #endif /* RWLOCK_DEBUG */
72
73 static void
74 rwlock_abort(krwlock_t *rwl, const char *fmt, ...)
75 {
76 va_list ap;
77
78 va_start(ap, fmt);
79 vprintf(fmt, ap);
80 va_end(ap);
81
82 #if defined(RWLOCK_DEBUG)
83 printf("last locked: 0x%lx last unlocked: 0x%lx\n",
84 rwl->rwl_debug.rwl_locked, rwl->rwl_debug.rwl_unlocked);
85 #endif
86
87 panic("rwlock_abort");
88 }
89
90 /*
91 * rw_init:
92 *
93 * Initialize a rwlock for use.
94 */
95 void
96 rw_init(krwlock_t *rwl)
97 {
98
99 RWLOCK_INIT(rwl);
100 }
101
102 /*
103 * rw_destroy:
104 *
105 * Tear down a rwlock.
106 */
107 void
108 rw_destroy(krwlock_t *rwl)
109 {
110
111 /* XXX IMPLEMENT ME XXX */
112 }
113
114 /*
115 * rw_enter:
116 *
117 * Acquire a rwlock.
118 */
119 void
120 rw_enter(krwlock_t *rwl, krw_t rw)
121 {
122 struct turnstile *ts;
123 struct proc *p;
124 unsigned long owner, incr, need_wait, set_wait;
125
126 /*
127 * Ensure RW_WRITER == 0, so that machine-dependent code can
128 * make that assumption.
129 */
130 #if RW_WRITER != 0
131 #error "RW_WRITER != 0"
132 #endif
133
134 /*
135 * We play a slight trick here. If we're a reader, we want
136 * increment the read count. If we're a writer, we want to
137 * set the owner field and whe WRITE_LOCKED bit.
138 *
139 * In the latter case, we expect those bits to be zero,
140 * therefore we can use an add operation to set them, which
141 * means an add operation for both cases.
142 */
143 switch (rw) {
144 case RW_WRITER:
145 incr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
146 need_wait = RWLOCK_WRITE_LOCKED;
147 set_wait = RWLOCK_HAS_WAITERS | RWLOCK_WRITE_WANTED;
148 break;
149
150 case RW_READER:
151 incr = RWLOCK_READ_INCR;
152 need_wait = RWLOCK_WRITE_LOCKED | RWLOCK_WRITE_WANTED;
153 set_wait = RWLOCK_HAS_WAITERS;
154 break;
155 #ifdef DIAGNOSTIC
156 default:
157 rwlock_abort(rwl, "rw_enter: bad rw %d", rw);
158 #endif
159 }
160
161 for (;;) {
162 /*
163 * Read the lock owner field. If the need-to-wait
164 * indicator is clear, then try to acquire the lock.
165 */
166 owner = rwl->rwl_owner;
167 if ((owner & need_wait) == 0) {
168 if (RWLOCK_ACQUIRE(rwl, owner, owner + incr)) {
169 /* Got it! */
170 break;
171 }
172
173 /*
174 * Didn't get it -- spin around again (we'll
175 * probably sleep on the next iteration).
176 */
177 continue;
178 }
179
180 if (RWLOCK_OWNER(rwl) == curproc)
181 rwlock_abort(rwl, "rw_enter: locking against myself");
182
183 ts = turnstile_lookup(rwl);
184
185 /*
186 * Mark the rwlock as having waiters. After we do
187 * this, we need to check one more time if the lock
188 * is busy, and if not, spin around again.
189 *
190 * Note, we also need to spin again if we failed to
191 * set the has-waiters indicator (which means the
192 * lock condition changed, but more importantly, we
193 * need to try and set that indicator again).
194 */
195 RWLOCK_SET_WAITERS(rwl, need_wait, set_wait);
196 owner = rwl->rwl_owner;
197 if ((owner & need_wait) == 0 || (owner & set_wait) == 0) {
198 turnstile_exit(rwl);
199 continue;
200 }
201 /* XXXJRT p->p_priority */
202 /* XXXJRT Do not currently distinguish reader vs. writer. */
203 (void) turnstile_block(ts, TS_WRITER_Q, p->p_priority, rwl);
204
205 /*
206 * XXX Solaris Internals says that the Solaris 7
207 * rwlock implementation does a direct-handoff. We
208 * don't implement that yet, but if we did, then a
209 * thread wakes back up, i.e. arrives here, it would
210 * hold the lock as requested.
211 */
212 }
213
214 KASSERT((rw == RW_WRITER && RWLOCK_OWNER(rwl) == curproc) ||
215 (rw == RW_READER && RWLOCK_COUNT(rwl) != 0));
216 #if defined(RWLOCK_DEBUG)
217 if (rw == RW_WRITER)
218 RWL_LOCKED(rwl);
219 #endif
220 }
221
222 /*
223 * rw_tryenter:
224 *
225 * Try to acquire a rwlock.
226 */
227 int
228 rw_tryenter(krwlock_t *rwl, krw_t rw)
229 {
230 unsigned long owner, incr, need_wait;
231
232 switch (rw) {
233 case RW_WRITER:
234 incr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
235 need_wait = RWLOCK_WRITE_LOCKED;
236 break;
237
238 case RW_READER:
239 incr = RWLOCK_READ_INCR;
240 need_wait = RWLOCK_WRITE_LOCKED | RWLOCK_WRITE_WANTED;
241 break;
242 #ifdef DIAGNOSTIC
243 default:
244 rwlock_abort(rwl, "rw_tryenter: bad rw %d", rw);
245 #endif
246 }
247
248 for (;;) {
249 owner = rwl->rwl_owner;
250 if ((owner & need_wait) == 0) {
251 if (RWLOCK_ACQUIRE(rwl, owner, owner + incr)) {
252 /* Got it! */
253 break;
254 }
255 continue;
256 }
257 return (0);
258 }
259
260 KASSERT((rw == RW_WRITER && RWLOCK_OWNER(rwl) == curproc) ||
261 (rw == RW_READER && RWLOCK_COUNT(rwl) != 0));
262 #if defined(RWLOCK_DEBUG)
263 if (rw == RW_WRITER)
264 RWL_LOCKED(rwl);
265 #endif
266 return (1);
267 }
268
269 /*
270 * rw_exit:
271 *
272 * Release a rwlock.
273 */
274 void
275 rw_exit(krwlock_t *rwl)
276 {
277 struct turnstile *ts;
278 unsigned long owner, decr, new;
279
280 /*
281 * Again, we use a trick. Since we used an add operation to
282 * set the required lock bits, we can use a subtract to clear
283 * them, which makes the read-release and write-release path
284 * the same.
285 */
286 if (rwl->rwl_owner & RWLOCK_WRITE_LOCKED) {
287 if (RWLOCK_OWNER(rwl) == NULL)
288 rwlock_abort(rwl, "rw_exit: not owned");
289 else
290 rwlock_abort(rwl, "rw_exit: not owner, owner = %p, "
291 "current = %p", RWLOCK_OWNER(rwl), curproc);
292 decr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
293 RWL_UNLOCKED(rwl);
294 } else {
295 if (RWLOCK_COUNT(rwl) == 0)
296 rwlock_abort(rwl, "rw_exit: not held\n");
297 decr = RWLOCK_READ_INCR;
298 }
299
300 for (;;) {
301 /*
302 * Get this lock's turnstile. This gets the interlock on
303 * the sleep queue. Once we have that, we can perform the
304 * lock release operation.
305 */
306 ts = turnstile_lookup(rwl);
307
308 /*
309 * Compute what we expect the new value of the lock
310 * to be. Skip the wakeup step if there are no
311 * appropriate waiters.
312 */
313 owner = rwl->rwl_owner;
314 new = owner - decr;
315 if ((new & (RWLOCK_THREAD |
316 RWLOCK_HAS_WAITERS)) != RWLOCK_HAS_WAITERS) {
317 if (RWLOCK_RELEASE(rwl, owner, new)) {
318 /* Ding! */
319 turnstile_exit(rwl);
320 break;
321 }
322 turnstile_exit(rwl);
323 continue;
324 }
325
326 /* We're about to wake everybody up; clear waiter bits. */
327 new &= ~(RWLOCK_HAS_WAITERS | RWLOCK_WRITE_WANTED);
328
329 if (RWLOCK_RELEASE(rwl, owner, new) == 0) {
330 /* Oops, try again. */
331 turnstile_exit(rwl);
332 continue;
333 }
334
335 /*
336 * Wake the thundering herd.
337 * XXX Should implement direct-handoff.
338 */
339 KASSERT(ts != NULL);
340 turnstile_wakeup(ts, TS_WRITER_Q,
341 ts->ts_sleepq[TS_WRITER_Q].tsq_waiters, NULL);
342 break;
343 }
344 }
345
346 /*
347 * rw_downgrade:
348 *
349 * Downgrade a write lock to a read lock.
350 */
351 void
352 rw_downgrade(krwlock_t *rwl)
353 {
354 struct turnstile *ts;
355 unsigned long owner;
356
357 if (RWLOCK_OWNER(rwl) != curproc) {
358 if (RWLOCK_OWNER(rwl) == NULL)
359 rwlock_abort(rwl, "rw_downgrade: not owned");
360 else
361 rwlock_abort(rwl,
362 "rw_downgrade: not owner, owner = %p, "
363 "current = %p", RWLOCK_OWNER(rwl), curproc);
364 }
365
366 RWL_UNLOCKED(rwl);
367
368 /* XXX This algorithm has to change if we do direct-handoff. */
369 for (;;) {
370 ts = turnstile_lookup(rwl);
371
372 owner = rwl->rwl_owner;
373 if (RWLOCK_RELEASE(rwl, owner, RWLOCK_READ_INCR) == 0) {
374 /* Oops, try again. */
375 turnstile_exit(rwl);
376 continue;
377 }
378 if (owner & RWLOCK_HAS_WAITERS) {
379 KASSERT(ts != NULL);
380 turnstile_wakeup(ts, TS_WRITER_Q,
381 ts->ts_sleepq[TS_WRITER_Q].tsq_waiters, NULL);
382 }
383 break;
384 }
385
386 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
387 KASSERT(RWLOCK_COUNT(rwl) != 0);
388 }
389
390 /*
391 * rw_tryupgrade:
392 *
393 * Try to upgrade an read lock to a write lock.
394 */
395 int
396 rw_tryupgrade(krwlock_t *rwl)
397 {
398 unsigned long owner;
399
400 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
401 KASSERT(RWLOCK_COUNT(rwl) != 0);
402
403 for (;;) {
404 /*
405 * Since we want to favor writers, we don't bother
406 * checking for waiting writers, we just scarf it.
407 *
408 * We must be the only reader.
409 */
410 owner = rwl->rwl_owner;
411 if ((owner & RWLOCK_THREAD) != RWLOCK_READ_INCR)
412 return (0);
413 if (RWLOCK_ACQUIRE(rwl, owner,
414 ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED |
415 (owner & ~RWLOCK_THREAD))) {
416 /* Ding! */
417 break;
418 }
419 }
420
421 KASSERT(rwl->rwl_owner & RWLOCK_WRITE_LOCKED);
422 KASSERT(RWLOCK_OWNER(rwl) == curproc);
423 RWL_LOCKED(rwl);
424 return (1);
425 }
426
427 /*
428 * rw_read_held:
429 *
430 * Returns true if the rwlock is held for reading.
431 */
432 int
433 rw_read_held(krwlock_t *rwl)
434 {
435 unsigned long owner = rwl->rwl_owner;
436
437 return ((owner & RWLOCK_WRITE_LOCKED) == 0 &&
438 (owner & RWLOCK_THREAD) != 0);
439 }
440
441 /*
442 * rw_write_held:
443 *
444 * Returns true if the rwlock is held for writing.
445 */
446 int
447 rw_write_held(krwlock_t *rwl)
448 {
449 unsigned long owner = rwl->rwl_owner;
450
451 return ((owner & RWLOCK_WRITE_LOCKED) != 0);
452 }
453
454 /*
455 * rw_read_locked:
456 *
457 * Like rw_read_held(), but asserts it.
458 */
459 int
460 rw_read_locked(krwlock_t *rwl)
461 {
462 int rv = rw_read_held(rwl);
463
464 #ifdef DIAGNOSTIC
465 if (rv == 0)
466 rwlock_abort(rwl, "rw_read_locked: not held");
467 #endif
468
469 return (rv);
470 }
471
472 /*
473 * rw_write_locked:
474 *
475 * Like rw_write_held(), but asserts that we hold it.
476 */
477 int
478 rw_write_locked(krwlock_t *rwl)
479 {
480 int rv = rw_write_held(rwl);
481
482 #ifdef DIAGNOSTIC
483 if (rv == 0)
484 rwlock_abort(rwl, "rw_write_locked: not held");
485 else if (RWLOCK_OWNER(rwl) != curproc)
486 rwlock_abort(rwl, "rw_write_locked: not owner, owner = %p, "
487 "current = %p", RWLOCK_OWNER(rwl), curproc);
488 #endif
489
490 return (rv);
491 }
492
493 /*
494 * rw_owner:
495 *
496 * Return the owner of the rwlock.
497 */
498 struct proc *
499 rw_owner(krwlock_t *rwl)
500 {
501 unsigned long owner = rwl->rwl_owner;
502
503 return ((owner & RWLOCK_WRITE_LOCKED) ?
504 ((struct proc *) (owner & RWLOCK_THREAD)) : NULL);
505 }
506