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