kern_rwlock.c revision 1.1.2.1 1 /* $NetBSD: kern_rwlock.c,v 1.1.2.1 2002/03/14 17:11:03 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.1 2002/03/14 17:11:03 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, ts->ts_waiters);
302 break;
303 }
304 }
305
306 /*
307 * rw_downgrade:
308 *
309 * Downgrade a write lock to a read lock.
310 */
311 void
312 rw_downgrade(krwlock_t *rwl)
313 {
314 struct turnstile *ts;
315 unsigned long owner, tmp;
316
317 if (RWLOCK_OWNER(rwl) != curproc) {
318 if (RWLOCK_OWNER(rwl) == NULL)
319 panic("rw_downgrade: not owned");
320 else
321 panic("rw_downgrade: not owner, owner = %p, "
322 "current = %p", RWLOCK_OWNER(rwl), curproc);
323 }
324
325 /* XXX This algorithm has to change if we do direct-handoff. */
326 for (;;) {
327 ts = turnstile_lookup(rwl);
328
329 owner = rwl->rwl_owner;
330 RWLOCK_RELEASE(rwl, owner, RWLOCK_READ_INCR, tmp);
331 if (tmp != owner) {
332 /* Oops, try again. */
333 turnstile_exit(rwl);
334 continue;
335 }
336 if (owner & RWLOCK_HAS_WAITERS) {
337 KASSERT(ts != NULL);
338 turnstile_wakeup(ts, TS_WRITER_Q, ts->ts_waiters);
339 }
340 break;
341 }
342
343 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
344 KASSERT(RWLOCK_COUNT(rwl) != 0);
345 }
346
347 /*
348 * rw_tryupgrade:
349 *
350 * Try to upgrade an read lock to a write lock.
351 */
352 int
353 rw_tryupgrade(krwlock_t *rwl)
354 {
355 unsigned long owner, tmp;
356
357 KASSERT((rwl->rwl_owner & RWLOCK_WRITE_LOCKED) == 0);
358 KASSERT(RWLOCK_COUNT(rwl) != 0);
359
360 for (;;) {
361 /*
362 * Since we want to favor writers, we don't bother
363 * checking for waiting writers, we just scarf it.
364 *
365 * We must be the only reader.
366 */
367 owner = rwl->rwl_owner;
368 if ((owner & RWLOCK_THREAD) != RWLOCK_READ_INCR)
369 return (0);
370 RWLOCK_ACQUIRE(rwl, owner,
371 ((unsigned long) curproc) | RWLOCK_WRITE_LOCKED |
372 (owner & ~RWLOCK_THREAD), tmp);
373 if (tmp == owner) {
374 /* Ding! */
375 break;
376 }
377 }
378
379 KASSERT(rwl->rwl_owner & RWLOCK_WRITE_LOCKED);
380 KASSERT(RWLOCK_OWNER(rwl) == curproc);
381 return (1);
382 }
383
384 /*
385 * rw_read_held:
386 *
387 * Returns true if the rwlock is held for reading.
388 */
389 int
390 rw_read_held(krwlock_t *rwl)
391 {
392 unsigned long owner = rwl->rwl_owner;
393
394 return ((owner & RWLOCK_WRITE_LOCKED) == 0 &&
395 (owner & RWLOCK_THREAD) != 0);
396 }
397
398 /*
399 * rw_write_held:
400 *
401 * Returns true if the rwlock is held for writing.
402 */
403 int
404 rw_write_held(krwlock_t *rwl)
405 {
406 unsigned long owner = rwl->rwl_owner;
407
408 return ((owner & RWLOCK_WRITE_LOCKED) != 0);
409 }
410
411 /*
412 * rw_read_locked:
413 *
414 * Like rw_read_held(), but asserts it.
415 */
416 int
417 rw_read_locked(krwlock_t *rwl)
418 {
419 int rv = rw_read_held(rwl);
420
421 #ifdef DIAGNOSTIC
422 if (rv == 0)
423 panic("rw_read_locked: not held");
424 #endif
425
426 return (rv);
427 }
428
429 /*
430 * rw_write_locked:
431 *
432 * Like rw_write_held(), but asserts that we hold it.
433 */
434 int
435 rw_write_locked(krwlock_t *rwl)
436 {
437 int rv = rw_write_held(rwl);
438
439 #ifdef DIAGNOSTIC
440 if (rv == 0)
441 panic("rw_write_locked: not held");
442 else if (RWLOCK_OWNER(rwl) != curproc)
443 panic("rw_write_locked: not owner, owner = %p, "
444 "current = %p", RWLOCK_OWNER(rwl), curproc);
445 #endif
446
447 return (rv);
448 }
449
450 /*
451 * rw_owner:
452 *
453 * Return the owner of the rwlock.
454 */
455 struct proc *
456 rw_owner(krwlock_t *rwl)
457 {
458 unsigned long owner = rwl->rwl_owner;
459
460 return ((owner & RWLOCK_WRITE_LOCKED) ?
461 ((struct proc *) (owner & RWLOCK_THREAD)) : NULL);
462 }
463