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