diff options
author | Ted Unangst <tedu@cvs.openbsd.org> | 2003-11-18 06:11:11 +0000 |
---|---|---|
committer | Ted Unangst <tedu@cvs.openbsd.org> | 2003-11-18 06:11:11 +0000 |
commit | db9713dbdab2e5ce91f8694a9b55c4907ee9fb27 (patch) | |
tree | 8f0b746ad0ca4c40995c14c7ce24f3489fcfa458 /sys | |
parent | 9722162346d7941d8bcdc6b3813022efb2f5a67c (diff) |
add lightwight reader/writer locks from art@. we will be using these
later. have been looked over for quite some time now.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/kern_rwlock.c | 220 | ||||
-rw-r--r-- | sys/sys/rwlock.h | 98 |
2 files changed, 318 insertions, 0 deletions
diff --git a/sys/kern/kern_rwlock.c b/sys/kern/kern_rwlock.c new file mode 100644 index 00000000000..113456a95d7 --- /dev/null +++ b/sys/kern/kern_rwlock.c @@ -0,0 +1,220 @@ +/* $OpenBSD: kern_rwlock.c,v 1.1 2003/11/18 06:11:10 tedu Exp $ */ +/* + * Copyright (c) 2002, 2003 Artur Grabowski <art@openbsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/rwlock.h> + +/* XXX - temporary measure until proc0 is properly aligned */ +#define RW_PROC(p) (((unsigned long)p) & ~RWLOCK_MASK) + +#define RWLOCK_TEST + +#ifndef __HAVE_MD_RWLOCK +/* + * Simple cases that should be in MD code and atomic. + */ +void +rw_enter_read(struct rwlock *rwl) +{ + while (__predict_false(rwl->rwl_owner & RWLOCK_WRLOCK)) { + /* + * Not the simple case, go to slow path. + */ + rw_enter_wait(rwl, curproc, RW_READ); + } + rwl->rwl_owner += RWLOCK_READ_INCR; +} + +void +rw_enter_write(struct rwlock *rwl, struct proc *p) +{ + while (__predict_false(rwl->rwl_owner != 0)) { + /* + * Not the simple case, go to slow path. + */ + rw_enter_wait(rwl, p, RW_WRITE); + } + rwl->rwl_owner = RW_PROC(p) | RWLOCK_WRLOCK; +} + +void +rw_exit_read(struct rwlock *rwl) +{ + unsigned long owner = rwl->rwl_owner; + unsigned long decr = (owner & (RWLOCK_WAIT|RWLOCK_WRWANT)) | + RWLOCK_READ_INCR; + + rwl->rwl_owner -= decr; + /* + * Potential MP race here. If the owner had WRWANT set we cleared + * it and a reader can sneak in before a writer. Do we care? + */ + if (__predict_false(owner & RWLOCK_WAIT)) + rw_exit_waiters(rwl, owner); +} + +void +rw_exit_write(struct rwlock *rwl) +{ + unsigned long owner = rwl->rwl_owner; + + rwl->rwl_owner = 0; + /* + * Potential MP race here. If the owner had WRWANT set we cleared + * it and a reader can sneak in before a writer. Do we care? + */ + if (__predict_false(owner & RWLOCK_WAIT)) + rw_exit_waiters(rwl, owner); +} +#endif + +void +rw_init(struct rwlock *rwl) +{ + rwl->rwl_owner = 0; +} + +void +rw_enter_wait(struct rwlock *rwl, struct proc *p, int how) +{ + unsigned long need_wait, set_wait; + int wait_prio; + +#ifdef DIAGNOSTIC + if (p == NULL) + panic("rw_enter_wait: NULL proc"); +#endif + + /* + * XXX - this function needs a lot of help to become MP safe. + */ + + switch (how) { + case RW_READ: + /* + * Let writers through before obtaining read lock. + */ + need_wait = RWLOCK_WRLOCK | RWLOCK_WRWANT; + set_wait = RWLOCK_WAIT; + wait_prio = PLOCK; + break; + case RW_WRITE: + need_wait = ~0UL; + set_wait = RWLOCK_WAIT | RWLOCK_WRWANT; + wait_prio = PLOCK - 4; + if (RW_PROC(RWLOCK_OWNER(rwl)) == RW_PROC(p)) { + panic("rw_enter: locking against myself"); + } + break; + } + + while (rwl->rwl_owner & need_wait) { + rwl->rwl_owner |= set_wait; + tsleep(rwl, wait_prio, "rwlock", 0); + } +} + +void +rw_exit_waiters(struct rwlock *rwl, unsigned long owner) +{ +#ifdef DIAGNOSTIC + if ((owner & RWLOCK_WAIT) == 0) + panic("rw_exit_waiters: no waiter"); +#endif + /* We wake up all waiters because we can't know how many they are. */ + wakeup(rwl); +} + +#ifdef RWLOCK_TEST +#include <sys/kthread.h> + +void rwlock_test(void); + +void rwlock_testp1(void *); +void rwlock_testp2(void *); +void rwlock_testp3(void *); + +struct rwlock rw_test = RWLOCK_INITIALIZER; + +void +rwlock_test(void) +{ + kthread_create(rwlock_testp1, NULL, NULL, "rw1"); + kthread_create(rwlock_testp2, NULL, NULL, "rw2"); + kthread_create(rwlock_testp3, NULL, NULL, "rw3"); +} + +void +rwlock_testp1(void *a) +{ + int local; + + printf("rwlock test1 start\n"); + rw_enter_read(&rw_test); + printf("rwlock test1 obtained\n"); + tsleep(&local, PWAIT, "rw1", 4); + rw_exit_read(&rw_test); + printf("rwlock test1 released\n"); + tsleep(&local, PWAIT, "rw1/2", 3); + rw_enter_read(&rw_test); + printf("rwlock test1 obtained\n"); + rw_exit_read(&rw_test); + printf("rwlock test1 released\n"); + kthread_exit(0); +} + +void +rwlock_testp2(void *a) +{ + int local; + + printf("rwlock test2 start\n"); + rw_enter_read(&rw_test); + printf("rwlock test2 obtained\n"); + tsleep(&local, PWAIT, "rw2", 4); + rw_exit_read(&rw_test); + printf("rwlock test2 released\n"); + kthread_exit(0); +} + +void +rwlock_testp3(void *a) +{ + int local; + + printf("rwlock test3 start\n"); + tsleep(&local, PWAIT, "rw3", 2); + printf("rwlock test3 exited waiting\n"); + rw_enter_write(&rw_test, curproc); + printf("rwlock test3 obtained\n"); + tsleep(&local, PWAIT, "rw3/2", 4); + rw_exit_write(&rw_test); + printf("rwlock test3 released\n"); + kthread_exit(0); +} +#endif diff --git a/sys/sys/rwlock.h b/sys/sys/rwlock.h new file mode 100644 index 00000000000..1c25afe5cf8 --- /dev/null +++ b/sys/sys/rwlock.h @@ -0,0 +1,98 @@ +/* $OpenBSD: rwlock.h,v 1.1 2003/11/18 06:11:10 tedu Exp $ */ +/* + * Copyright (c) 2002 Artur Grabowski <art@openbsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Multiple readers, single writer lock. + * + * Simplistic implementation modelled after rw locks in Solaris. + * + * The rwl_owner has the following layout: + * [ owner or count of readers | wrlock | wrwant | wait ] + * + * When the WAIT bit is set (bit 0), the lock has waiters sleeping on it. + * When the WRWANT bit is set (bit 1), at least one waiter wants a write lock. + * When the WRLOCK bit is set (bit 2) the lock is currently write-locked. + * + * When write locked, the upper bits contain the struct proc * pointer to + * the writer, otherwise they count the number of readers. + * + * We provide a simple machine independent implementation that can be + * optimized by machine dependent code when __HAVE_MD_RWLOCK is defined. + * + * MD code that defines __HAVE_MD_RWLOCK and implement four functions: + * + * void rw_enter_read(struct rwlock *) + * atomically test for RWLOCK_WRLOCK and if not set, increment the lock + * by RWLOCK_READ_INCR. While RWLOCK_WRLOCK is set, loop into rw_enter_wait. + * + * void rw_enter_write(struct rwlock *, struct proc *); + * atomically test for the lock being 0 (it's not possible to have + * owner/read count unset and waiter bits set) and if 0 set the owner to + * the proc and RWLOCK_WRLOCK. While not zero, loop into rw_enter_wait. + * + * void rw_exit_read(struct rwlock *); + * atomically decrement lock by RWLOCK_READ_INCR and unset RWLOCK_WAIT and + * RWLOCK_WRWANT remembering the old value of lock and if RWLOCK_WAIT was set, + * call rw_exit_waiters with the old contents of the lock. + * + * void rw_exit_write(struct rwlock *); + * atomically swap the contents of the lock with 0 and if RWLOCK_WAIT was + * set, call rw_exit_waiters with the old contents of the lock. + * + * (XXX - the rest of the API for this is not invented yet). + */ + +struct rwlock { + __volatile unsigned long rwl_owner; +}; + +#define RWLOCK_INITIALIZER { 0 } + +#define RWLOCK_WAIT 0x01 +#define RWLOCK_WRWANT 0x02 +#define RWLOCK_WRLOCK 0x04 +#define RWLOCK_MASK 0x07 + +#define RWLOCK_OWNER(rwl) ((struct proc *)((rwl)->rwl_owner & ~RWLOCK_MASK)) + +#define RWLOCK_READER_SHIFT 3 +#define RWLOCK_READ_INCR (1 << RWLOCK_READER_SHIFT) + +void rw_init(struct rwlock *); + +void rw_enter_read(struct rwlock *); +void rw_enter_write(struct rwlock *, struct proc *); +void rw_exit_read(struct rwlock *); +void rw_exit_write(struct rwlock *); + +/* + * Internal API. + */ +#define RW_WRITE 0x00 +#define RW_READ 0x01 +void rw_enter_wait(struct rwlock *, struct proc *, int); +void rw_exit_waiters(struct rwlock *, unsigned long); + |