kern_rwlock.c revision 1.1.2.2 1 /* $NetBSD: kern_rwlock.c,v 1.1.2.2 2002/03/16 03:46:38 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.2 2002/03/16 03:46:38 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, tmp, 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 RWLOCK_ACQUIRE(rwl, owner, owner + incr, tmp);
135 if (tmp == owner) {
136 /* Got it! */
137 break;
138 }
139
140 /*
141 * Didn't get it -- spin around again (we'll
142 * probably sleep on the next iteration).
143 */
144 continue;
145 }
146
147 if (RWLOCK_OWNER(rwl) == curproc)
148 panic("rw_enter: locking against myself");
149
150 ts = turnstile_lookup(rwl);
151
152 /*
153 * Mark the rwlock as having waiters. After we do
154 * this, we need to check one more time if the lock
155 * is busy, and if not, spin around again.
156 *
157 * Note, we also need to spin again if we failed to
158 * set the has-waiters indicator (which means the
159 * lock condition changed, but more importantly, we
160 * need to try and set that indicator again).
161 */
162 RWLOCK_SET_WAITERS(rwl, need_wait, set_wait);
163 owner = rwl->rwl_owner;
164 if ((owner & need_wait) == 0 || (owner & set_wait) == 0) {
165 turnstile_exit(rwl);
166 continue;
167 }
168 /* XXXJRT p->p_priority */
169 /* XXXJRT Do not currently distinguish reader vs. writer. */
170 (void) turnstile_block(ts, TS_WRITER_Q, p->p_priority, rwl);
171
172 /*
173 * XXX Solaris Internals says that the Solaris 7
174 * rwlock implementation does a direct-handoff. We
175 * don't implement that yet, but if we did, then a
176 * thread wakes back up, i.e. arrives here, it would
177 * hold the lock as requested.
178 */
179 }
180
181 KASSERT((rw == RW_WRITER && RWLOCK_OWNER(rwl) == curproc) ||
182 (rw == RW_READER && RWLOCK_COUNT(rwl) != 0));
183 }
184
185 /*
186 * rw_tryenter:
187 *
188 * Try to acquire a rwlock.
189 */
190 int
191 rw_tryenter(krwlock_t *rwl, krw_t rw)
192 {
193 unsigned long owner, tmp, incr, need_wait;
194
195 switch (rw) {
196 case RW_WRITER:
197 incr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
198 need_wait = RWLOCK_WRITE_LOCKED;
199 break;
200
201 case RW_READER:
202 incr = RWLOCK_READ_INCR;
203 need_wait = RWLOCK_WRITE_LOCKED | RWLOCK_WRITE_WANTED;
204 break;
205 #ifdef DIAGNOSTIC
206 default:
207 panic("rw_tryenter: bad rw %d", rw);
208 #endif
209 }
210
211 for (;;) {
212 owner = rwl->rwl_owner;
213 if ((owner & need_wait) == 0) {
214 RWLOCK_ACQUIRE(rwl, owner, owner + incr, tmp);
215 if (tmp == owner) {
216 /* Got it! */
217 break;
218 }
219 continue;
220 }
221 return (0);
222 }
223
224 KASSERT((rw == RW_WRITER && RWLOCK_OWNER(rwl) == curproc) ||
225 (rw == RW_READER && RWLOCK_COUNT(rwl) != 0));
226 return (1);
227 }
228
229 /*
230 * rw_exit:
231 *
232 * Release a rwlock.
233 */
234 void
235 rw_exit(krwlock_t *rwl)
236 {
237 struct turnstile *ts;
238 unsigned long owner, tmp, decr, new;
239
240 /*
241 * Again, we use a trick. Since we used an add operation to
242 * set the required lock bits, we can use a subtract to clear
243 * them, which makes the read-release and write-release path
244 * the same.
245 */
246 if (rwl->rwl_owner & RWLOCK_WRITE_LOCKED) {
247 if (RWLOCK_OWNER(rwl) == NULL)
248 panic("rw_exit: not owned");
249 else
250 panic("rw_exit: not owner, owner = %p, "
251 "current = %p", RWLOCK_OWNER(rwl), curproc);
252 decr = ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED;
253 } else {
254 if (RWLOCK_COUNT(rwl) == 0)
255 panic("rw_exit: not held\n");
256 decr = RWLOCK_READ_INCR;
257 }
258
259 for (;;) {
260 /*
261 * Get this lock's turnstile. This gets the interlock on
262 * the sleep queue. Once we have that, we can perform the
263 * lock release operation.
264 */
265 ts = turnstile_lookup(rwl);
266
267 /*
268 * Compute what we expect the new value of the lock
269 * to be. Skip the wakeup step if there are no
270 * appropriate waiters.
271 */
272 owner = rwl->rwl_owner;
273 new = owner - decr;
274 if ((new & (RWLOCK_THREAD |
275 RWLOCK_HAS_WAITERS)) != RWLOCK_HAS_WAITERS) {
276 RWLOCK_RELEASE(rwl, owner, new, tmp);
277 if (tmp == owner) {
278 /* Ding! */
279 turnstile_exit(rwl);
280 break;
281 }
282 turnstile_exit(rwl);
283 continue;
284 }
285
286 /* We're about to wake everybody up; clear waiter bits. */
287 new &= ~(RWLOCK_HAS_WAITERS | RWLOCK_WRITE_WANTED);
288
289 RWLOCK_RELEASE(rwl, owner, new, tmp);
290 if (tmp != owner) {
291 /* Oops, try again. */
292 turnstile_exit(rwl);
293 continue;
294 }
295
296 /*
297 * Wake the thundering herd.
298 * XXX Should implement direct-handoff.
299 */
300 KASSERT(ts != NULL);
301 turnstile_wakeup(ts, TS_WRITER_Q,
302 ts->ts_sleepq[TS_WRITER_Q].tsq_waiters);
303 break;
304 }
305 }
306
307 /*
308 * rw_downgrade:
309 *
310 * Downgrade a write lock to a read lock.
311 */
312 void
313 rw_downgrade(krwlock_t *rwl)
314 {
315 struct turnstile *ts;
316 unsigned long owner, tmp;
317
318 if (RWLOCK_OWNER(rwl) != curproc) {
319 if (RWLOCK_OWNER(rwl) == NULL)
320 panic("rw_downgrade: not owned");
321 else
322 panic("rw_downgrade: not owner, owner = %p, "
323 "current = %p", RWLOCK_OWNER(rwl), curproc);
324 }
325
326 /* XXX This algorithm has to change if we do direct-handoff. */
327 for (;;) {
328 ts = turnstile_lookup(rwl);
329
330 owner = rwl->rwl_owner;
331 RWLOCK_RELEASE(rwl, owner, RWLOCK_READ_INCR, tmp);
332 if (tmp != owner) {
333 /* Oops, try again. */
334 turnstile_exit(rwl);
335 continue;
336 }
337 if (owner & RWLOCK_HAS_WAITERS) {
338 KASSERT(ts != NULL);
339 turnstile_wakeup(ts, TS_WRITER_Q,
340 ts->ts_sleepq[TS_WRITER_Q].tsq_waiters);
341 }
342 break;
343 }
344
345 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
346 KASSERT(RWLOCK_COUNT(rwl) != 0);
347 }
348
349 /*
350 * rw_tryupgrade:
351 *
352 * Try to upgrade an read lock to a write lock.
353 */
354 int
355 rw_tryupgrade(krwlock_t *rwl)
356 {
357 unsigned long owner, tmp;
358
359 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
360 KASSERT(RWLOCK_COUNT(rwl) != 0);
361
362 for (;;) {
363 /*
364 * Since we want to favor writers, we don't bother
365 * checking for waiting writers, we just scarf it.
366 *
367 * We must be the only reader.
368 */
369 owner = rwl->rwl_owner;
370 if ((owner & RWLOCK_THREAD) != RWLOCK_READ_INCR)
371 return (0);
372 RWLOCK_ACQUIRE(rwl, owner,
373 ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED |
374 (owner & ~RWLOCK_THREAD), tmp);
375 if (tmp == owner) {
376 /* Ding! */
377 break;
378 }
379 }
380
381 KASSERT(rwl->rwl_owner & RWLOCK_WRITE_LOCKED);
382 KASSERT(RWLOCK_OWNER(rwl) == curproc);
383 return (1);
384 }
385
386 /*
387 * rw_read_held:
388 *
389 * Returns true if the rwlock is held for reading.
390 */
391 int
392 rw_read_held(krwlock_t *rwl)
393 {
394 unsigned long owner = rwl->rwl_owner;
395
396 return ((owner & RWLOCK_WRITE_LOCKED) == 0 &&
397 (owner & RWLOCK_THREAD) != 0);
398 }
399
400 /*
401 * rw_write_held:
402 *
403 * Returns true if the rwlock is held for writing.
404 */
405 int
406 rw_write_held(krwlock_t *rwl)
407 {
408 unsigned long owner = rwl->rwl_owner;
409
410 return ((owner & RWLOCK_WRITE_LOCKED) != 0);
411 }
412
413 /*
414 * rw_read_locked:
415 *
416 * Like rw_read_held(), but asserts it.
417 */
418 int
419 rw_read_locked(krwlock_t *rwl)
420 {
421 int rv = rw_read_held(rwl);
422
423 #ifdef DIAGNOSTIC
424 if (rv == 0)
425 panic("rw_read_locked: not held");
426 #endif
427
428 return (rv);
429 }
430
431 /*
432 * rw_write_locked:
433 *
434 * Like rw_write_held(), but asserts that we hold it.
435 */
436 int
437 rw_write_locked(krwlock_t *rwl)
438 {
439 int rv = rw_write_held(rwl);
440
441 #ifdef DIAGNOSTIC
442 if (rv == 0)
443 panic("rw_write_locked: not held");
444 else if (RWLOCK_OWNER(rwl) != curproc)
445 panic("rw_write_locked: not owner, owner = %p, "
446 "current = %p", RWLOCK_OWNER(rwl), curproc);
447 #endif
448
449 return (rv);
450 }
451
452 /*
453 * rw_owner:
454 *
455 * Return the owner of the rwlock.
456 */
457 struct proc *
458 rw_owner(krwlock_t *rwl)
459 {
460 unsigned long owner = rwl->rwl_owner;
461
462 return ((owner & RWLOCK_WRITE_LOCKED) ?
463 ((struct proc *) (owner & RWLOCK_THREAD)) : NULL);
464 }
465