Home | History | Annotate | Line # | Download | only in kern
kern_rwlock.c revision 1.1.2.3
      1 /*	$NetBSD: kern_rwlock.c,v 1.1.2.3 2002/03/16 20:57:42 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.3 2002/03/16 20:57:42 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, NULL);
    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, NULL);
    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