diff options
author | 2006-01-06 06:50:31 +0000 | |
---|---|---|
committer | 2006-01-06 06:50:31 +0000 | |
commit | 55ed33a69145d691b6d4f89db5ccb9380d2f7be3 (patch) | |
tree | 16966448e08991b8dd5ca2de226f21bf3a63162c /sys/kern/kern_rwlock.c | |
parent | take interp[MAXPATHLEN] off the stack. from mickey, ok otto (diff) | |
download | wireguard-openbsd-55ed33a69145d691b6d4f89db5ccb9380d2f7be3.tar.xz wireguard-openbsd-55ed33a69145d691b6d4f89db5ccb9380d2f7be3.zip |
check in of "rwlock.20051230" from art.
mostly cleanup and simplification, though now also supporting
upgrade and downgrade via the magic wand.
Diffstat (limited to 'sys/kern/kern_rwlock.c')
-rw-r--r-- | sys/kern/kern_rwlock.c | 260 |
1 files changed, 145 insertions, 115 deletions
diff --git a/sys/kern/kern_rwlock.c b/sys/kern/kern_rwlock.c index 833d879d98c..6543ed94b66 100644 --- a/sys/kern/kern_rwlock.c +++ b/sys/kern/kern_rwlock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_rwlock.c,v 1.3 2004/07/21 12:10:20 art Exp $ */ +/* $OpenBSD: kern_rwlock.c,v 1.4 2006/01/06 06:50:31 tedu Exp $ */ /* * Copyright (c) 2002, 2003 Artur Grabowski <art@openbsd.org> * All rights reserved. @@ -28,9 +28,63 @@ #include <sys/systm.h> #include <sys/proc.h> #include <sys/rwlock.h> +#include <sys/limits.h> /* XXX - temporary measure until proc0 is properly aligned */ -#define RW_PROC(p) (((unsigned long)p) & ~RWLOCK_MASK) +#define RW_PROC(p) (((long)p) & ~RWLOCK_MASK) + +/* + * Magic wand for lock operations. Every operation checks if certain + * flags are set and if they aren't, it increments the lock with some + * value (that might need some computing in a few cases). If the operation + * fails, we need to set certain flags while waiting for the lock. + * + * RW_WRITE The lock must be completly empty. We increment it with + * RWLOCK_WRLOCK and the proc pointer of the holder. + * Sets RWLOCK_WAIT|RWLOCK_WRWANT while waiting. + * RW_READ RWLOCK_WRLOCK|RWLOCK_WRWANT may not be set. We increment + * with RWLOCK_READ_INCR. RWLOCK_WAIT while waiting. + * RW_UPGRADE There must be exactly one holder of the read lock. + * We increment with what's needed for RW_WRITE - RW_READ. + * RWLOCK_WAIT|RWLOCK_WRWANT while waiting. + * RW_DOWNGRADE Always doable. Increment with -RW_WRITE + RW_READ. + */ +static const struct rwlock_op { + unsigned long inc; + unsigned long check; + unsigned long wait_set; + long proc_mult; + int wait_prio; +} rw_ops[] = { + { /* RW_WRITE */ + RWLOCK_WRLOCK, + ULONG_MAX, + RWLOCK_WAIT | RWLOCK_WRWANT, + 1, + PLOCK - 4 + }, + { /* RW_READ */ + RWLOCK_READ_INCR, + RWLOCK_WRLOCK, + RWLOCK_WAIT, + 0, + PLOCK + }, + { /* RW_UPGRADE */ + RWLOCK_WRLOCK-RWLOCK_READ_INCR, + ~(RWLOCK_READ_INCR | RWLOCK_WAIT | RWLOCK_WRWANT), + RWLOCK_WAIT|RWLOCK_WRLOCK, + 1, + PLOCK - 4 + }, + { /* RW_DOWNGRADE */ + -RWLOCK_WRLOCK + RWLOCK_READ_INCR, + 0, + 0, + -1, + 0 + } +}; #ifndef __HAVE_MD_RWLOCK /* @@ -39,13 +93,10 @@ 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; + if (__predict_false(rwl->rwl_owner & RWLOCK_WRLOCK)) + rw_enter(rwl, RW_READ); + else + rwl->rwl_owner += RWLOCK_READ_INCR; } void @@ -53,13 +104,10 @@ rw_enter_write(struct rwlock *rwl) { struct proc *p = curproc; - 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; + if (__predict_false(rwl->rwl_owner != 0)) + rw_enter(rwl, RW_WRITE); + else + rwl->rwl_owner = RW_PROC(p) | RWLOCK_WRLOCK; } void @@ -91,130 +139,112 @@ rw_exit_write(struct rwlock *rwl) 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) +int +rw_test_and_set(volatile unsigned long *p, unsigned long o, unsigned long n) { - unsigned long need_wait, set_wait; - int wait_prio; + if (*p != o) + return (1); + *p = n; -#ifdef DIAGNOSTIC - if (p == NULL) - panic("rw_enter_wait: NULL proc"); + return (0); +} #endif - /* - * XXX - this function needs a lot of help to become MP safe. - */ - - switch (how) { +#ifdef DIAGNOSTIC +/* + * Put the diagnostic functions here to keep the main code free + * from ifdef clutter. + */ +static void +rw_enter_diag(struct rwlock *rwl, int flags) +{ + switch (flags & RW_OPMASK) { + case RW_WRITE: case RW_READ: + if (RW_PROC(curproc) == RW_PROC(rwl->rwl_owner)) + panic("rw_enter: locking against myself"); + break; + case RW_UPGRADE: /* - * Let writers through before obtaining read lock. + * Since we're holding the read lock, it can't possibly + * be write locked. */ - 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"); - } + if (rwl->rwl_owner & RWLOCK_WRLOCK) + panic("rw_enter: upgraded lock write locked"); break; - } - - while (rwl->rwl_owner & need_wait) { - rwl->rwl_owner |= set_wait; - tsleep(rwl, wait_prio, "rwlock", 0); + case RW_DOWNGRADE: + /* + * If we're downgrading, we much hold the write lock. + */ + if (RW_PROC(curproc) != RW_PROC(rwl->rwl_owner)) + panic("rw_enter: not holder"); + default: + panic("rw_enter: unknown op 0x%x", flags); } } -void -rw_exit_waiters(struct rwlock *rwl, unsigned long owner) +static void +rw_exit_diag(struct rwlock *rwl, int 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); + panic("rw_exit: no waiter"); } -#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; +#else +#define rw_enter_diag(r, f) +#define rw_exit_diag(r, o) +#endif void -rwlock_test(void) +rw_init(struct rwlock *rwl) { - kthread_create(rwlock_testp1, NULL, NULL, "rw1"); - kthread_create(rwlock_testp2, NULL, NULL, "rw2"); - kthread_create(rwlock_testp3, NULL, NULL, "rw3"); + rwl->rwl_owner = 0; } -void -rwlock_testp1(void *a) +/* + * You are supposed to understand this. + */ +int +rw_enter(struct rwlock *rwl, int flags) { - 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); -} + const struct rwlock_op *op; + unsigned long inc, o; + int error, prio; + + op = &rw_ops[flags & RW_OPMASK]; + + inc = op->inc; + if (op->proc_mult == -1) + inc -= RW_PROC(curproc); + else if (op->proc_mult == 1) + inc += RW_PROC(curproc); + prio = op->wait_prio; + if (flags & RW_INTR) + prio |= PCATCH; +retry: + while (__predict_false(((o = rwl->rwl_owner) & op->check) != 0)) { + if (rw_test_and_set(&rwl->rwl_owner, o, o | op->wait_set)) + continue; + + rw_enter_diag(rwl, flags); + + if ((error = tsleep(rwl, op->wait_prio, "rwlock", 0)) != 0) + return (error); + if (flags & RW_SLEEPFAIL) + return (EAGAIN); + } -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); + if (__predict_false(rw_test_and_set(&rwl->rwl_owner, o, o + inc))) + goto retry; + + return (0); } void -rwlock_testp3(void *a) +rw_exit_waiters(struct rwlock *rwl, unsigned long owner) { - int local; - - printf("rwlock test3 start\n"); - tsleep(&local, PWAIT, "rw3", 2); - printf("rwlock test3 exited waiting\n"); - rw_enter_write(&rw_test); - printf("rwlock test3 obtained\n"); - tsleep(&local, PWAIT, "rw3/2", 4); - rw_exit_write(&rw_test); - printf("rwlock test3 released\n"); - kthread_exit(0); + rw_exit_diag(rwl, owner); + /* We wake up all waiters because we can't know how many they are. */ + wakeup(rwl); } -#endif |