Annotation of sys/kern/kern_rwlock.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: kern_rwlock.c,v 1.13 2007/05/13 04:52:32 tedu Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2002, 2003 Artur Grabowski <art@openbsd.org>
! 5: * All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: *
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. The name of the author may not be used to endorse or promote products
! 14: * derived from this software without specific prior written permission.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 17: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 18: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 19: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 20: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 21: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 22: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 23: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 24: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 25: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 26: */
! 27:
! 28: #include <sys/param.h>
! 29: #include <sys/systm.h>
! 30: #include <sys/proc.h>
! 31: #include <sys/rwlock.h>
! 32: #include <sys/limits.h>
! 33:
! 34: #include <machine/lock.h>
! 35:
! 36: /* XXX - temporary measure until proc0 is properly aligned */
! 37: #define RW_PROC(p) (((long)p) & ~RWLOCK_MASK)
! 38:
! 39: /*
! 40: * Magic wand for lock operations. Every operation checks if certain
! 41: * flags are set and if they aren't, it increments the lock with some
! 42: * value (that might need some computing in a few cases). If the operation
! 43: * fails, we need to set certain flags while waiting for the lock.
! 44: *
! 45: * RW_WRITE The lock must be completely empty. We increment it with
! 46: * RWLOCK_WRLOCK and the proc pointer of the holder.
! 47: * Sets RWLOCK_WAIT|RWLOCK_WRWANT while waiting.
! 48: * RW_READ RWLOCK_WRLOCK|RWLOCK_WRWANT may not be set. We increment
! 49: * with RWLOCK_READ_INCR. RWLOCK_WAIT while waiting.
! 50: */
! 51: static const struct rwlock_op {
! 52: unsigned long inc;
! 53: unsigned long check;
! 54: unsigned long wait_set;
! 55: long proc_mult;
! 56: int wait_prio;
! 57: } rw_ops[] = {
! 58: { /* RW_WRITE */
! 59: RWLOCK_WRLOCK,
! 60: ULONG_MAX,
! 61: RWLOCK_WAIT | RWLOCK_WRWANT,
! 62: 1,
! 63: PLOCK - 4
! 64: },
! 65: { /* RW_READ */
! 66: RWLOCK_READ_INCR,
! 67: RWLOCK_WRLOCK,
! 68: RWLOCK_WAIT,
! 69: 0,
! 70: PLOCK
! 71: },
! 72: { /* RW_DOWNGRADE */
! 73: RWLOCK_READ_INCR - RWLOCK_WRLOCK,
! 74: 0,
! 75: 0,
! 76: -1,
! 77: PLOCK
! 78: },
! 79: };
! 80:
! 81: #ifndef __HAVE_MD_RWLOCK
! 82: /*
! 83: * Simple cases that should be in MD code and atomic.
! 84: */
! 85: void
! 86: rw_enter_read(struct rwlock *rwl)
! 87: {
! 88: unsigned long owner = rwl->rwl_owner;
! 89:
! 90: if (__predict_false((owner & RWLOCK_WRLOCK) ||
! 91: rw_cas(&rwl->rwl_owner, owner, owner + RWLOCK_READ_INCR)))
! 92: rw_enter(rwl, RW_READ);
! 93: }
! 94:
! 95: void
! 96: rw_enter_write(struct rwlock *rwl)
! 97: {
! 98: struct proc *p = curproc;
! 99:
! 100: if (__predict_false(rw_cas(&rwl->rwl_owner, 0,
! 101: RW_PROC(p) | RWLOCK_WRLOCK)))
! 102: rw_enter(rwl, RW_WRITE);
! 103: }
! 104:
! 105: void
! 106: rw_exit_read(struct rwlock *rwl)
! 107: {
! 108: unsigned long owner = rwl->rwl_owner;
! 109:
! 110: if (__predict_false((owner & RWLOCK_WAIT) ||
! 111: rw_cas(&rwl->rwl_owner, owner, owner - RWLOCK_READ_INCR)))
! 112: rw_exit(rwl);
! 113: }
! 114:
! 115: void
! 116: rw_exit_write(struct rwlock *rwl)
! 117: {
! 118: unsigned long owner = rwl->rwl_owner;
! 119:
! 120: if (__predict_false((owner & RWLOCK_WAIT) ||
! 121: rw_cas(&rwl->rwl_owner, owner, 0)))
! 122: rw_exit(rwl);
! 123: }
! 124:
! 125: #ifndef rw_cas
! 126: int
! 127: rw_cas(volatile unsigned long *p, unsigned long o, unsigned long n)
! 128: {
! 129: if (*p != o)
! 130: return (1);
! 131: *p = n;
! 132:
! 133: return (0);
! 134: }
! 135: #endif
! 136:
! 137: #endif
! 138:
! 139: #ifdef DIAGNOSTIC
! 140: /*
! 141: * Put the diagnostic functions here to keep the main code free
! 142: * from ifdef clutter.
! 143: */
! 144: static void
! 145: rw_enter_diag(struct rwlock *rwl, int flags)
! 146: {
! 147: switch (flags & RW_OPMASK) {
! 148: case RW_WRITE:
! 149: case RW_READ:
! 150: if (RW_PROC(curproc) == RW_PROC(rwl->rwl_owner))
! 151: panic("rw_enter: %s locking against myself",
! 152: rwl->rwl_name);
! 153: break;
! 154: case RW_DOWNGRADE:
! 155: /*
! 156: * If we're downgrading, we must hold the write lock.
! 157: */
! 158: if ((rwl->rwl_owner & RWLOCK_WRLOCK) == 0)
! 159: panic("rw_enter: %s downgrade of non-write lock",
! 160: rwl->rwl_name);
! 161: if (RW_PROC(curproc) != RW_PROC(rwl->rwl_owner))
! 162: panic("rw_enter: %s downgrade, not holder",
! 163: rwl->rwl_name);
! 164: break;
! 165:
! 166: default:
! 167: panic("rw_enter: unknown op 0x%x", flags);
! 168: }
! 169: }
! 170:
! 171: #else
! 172: #define rw_enter_diag(r, f)
! 173: #endif
! 174:
! 175: void
! 176: rw_init(struct rwlock *rwl, const char *name)
! 177: {
! 178: rwl->rwl_owner = 0;
! 179: rwl->rwl_name = name;
! 180: }
! 181:
! 182: int
! 183: rw_enter(struct rwlock *rwl, int flags)
! 184: {
! 185: const struct rwlock_op *op;
! 186: struct sleep_state sls;
! 187: unsigned long inc, o;
! 188: int error;
! 189:
! 190: op = &rw_ops[flags & RW_OPMASK];
! 191:
! 192: inc = op->inc + RW_PROC(curproc) * op->proc_mult;
! 193: retry:
! 194: while (__predict_false(((o = rwl->rwl_owner) & op->check) != 0)) {
! 195: unsigned long set = o | op->wait_set;
! 196: int do_sleep;
! 197:
! 198: rw_enter_diag(rwl, flags);
! 199:
! 200: if (flags & RW_NOSLEEP)
! 201: return (EBUSY);
! 202:
! 203: sleep_setup(&sls, rwl, op->wait_prio, rwl->rwl_name);
! 204: if (flags & RW_INTR)
! 205: sleep_setup_signal(&sls, op->wait_prio | PCATCH);
! 206:
! 207: do_sleep = !rw_cas(&rwl->rwl_owner, o, set);
! 208:
! 209: sleep_finish(&sls, do_sleep);
! 210: if ((flags & RW_INTR) &&
! 211: (error = sleep_finish_signal(&sls)) != 0)
! 212: return (error);
! 213: if (flags & RW_SLEEPFAIL)
! 214: return (EAGAIN);
! 215: }
! 216:
! 217: if (__predict_false(rw_cas(&rwl->rwl_owner, o, o + inc)))
! 218: goto retry;
! 219:
! 220: /*
! 221: * If old lock had RWLOCK_WAIT and RWLOCK_WRLOCK set, it means we
! 222: * downgraded a write lock and had possible read waiter, wake them
! 223: * to let them retry the lock.
! 224: */
! 225: if (__predict_false((o & (RWLOCK_WRLOCK|RWLOCK_WAIT)) ==
! 226: (RWLOCK_WRLOCK|RWLOCK_WAIT)))
! 227: wakeup(rwl);
! 228:
! 229: return (0);
! 230: }
! 231:
! 232: void
! 233: rw_exit(struct rwlock *rwl)
! 234: {
! 235: unsigned long owner = rwl->rwl_owner;
! 236: int wrlock = owner & RWLOCK_WRLOCK;
! 237: unsigned long set;
! 238:
! 239: do {
! 240: owner = rwl->rwl_owner;
! 241: if (wrlock)
! 242: set = 0;
! 243: else
! 244: set = (owner - RWLOCK_READ_INCR) &
! 245: ~(RWLOCK_WAIT|RWLOCK_WRWANT);
! 246: } while (rw_cas(&rwl->rwl_owner, owner, set));
! 247:
! 248: if (owner & RWLOCK_WAIT)
! 249: wakeup(rwl);
! 250: }
CVSweb