Home | History | Annotate | Line # | Download | only in kern
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