1 /* $OpenBSD: kern_rwlock.c,v 1.2 2003/11/18 18:12:14 tedu Exp $ */ 2 /* 3 * Copyright (c) 2002, 2003 Artur Grabowski <art@openbsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/proc.h> 30 #include <sys/rwlock.h> 31 32 /* XXX - temporary measure until proc0 is properly aligned */ 33 #define RW_PROC(p) (((unsigned long)p) & ~RWLOCK_MASK) 34 35 #ifndef __HAVE_MD_RWLOCK 36 /* 37 * Simple cases that should be in MD code and atomic. 38 */ 39 void 40 rw_enter_read(struct rwlock *rwl) 41 { 42 while (__predict_false(rwl->rwl_owner & RWLOCK_WRLOCK)) { 43 /* 44 * Not the simple case, go to slow path. 45 */ 46 rw_enter_wait(rwl, curproc, RW_READ); 47 } 48 rwl->rwl_owner += RWLOCK_READ_INCR; 49 } 50 51 void 52 rw_enter_write(struct rwlock *rwl, struct proc *p) 53 { 54 while (__predict_false(rwl->rwl_owner != 0)) { 55 /* 56 * Not the simple case, go to slow path. 57 */ 58 rw_enter_wait(rwl, p, RW_WRITE); 59 } 60 rwl->rwl_owner = RW_PROC(p) | RWLOCK_WRLOCK; 61 } 62 63 void 64 rw_exit_read(struct rwlock *rwl) 65 { 66 unsigned long owner = rwl->rwl_owner; 67 unsigned long decr = (owner & (RWLOCK_WAIT|RWLOCK_WRWANT)) | 68 RWLOCK_READ_INCR; 69 70 rwl->rwl_owner -= decr; 71 /* 72 * Potential MP race here. If the owner had WRWANT set we cleared 73 * it and a reader can sneak in before a writer. Do we care? 74 */ 75 if (__predict_false(owner & RWLOCK_WAIT)) 76 rw_exit_waiters(rwl, owner); 77 } 78 79 void 80 rw_exit_write(struct rwlock *rwl) 81 { 82 unsigned long owner = rwl->rwl_owner; 83 84 rwl->rwl_owner = 0; 85 /* 86 * Potential MP race here. If the owner had WRWANT set we cleared 87 * it and a reader can sneak in before a writer. Do we care? 88 */ 89 if (__predict_false(owner & RWLOCK_WAIT)) 90 rw_exit_waiters(rwl, owner); 91 } 92 #endif 93 94 void 95 rw_init(struct rwlock *rwl) 96 { 97 rwl->rwl_owner = 0; 98 } 99 100 void 101 rw_enter_wait(struct rwlock *rwl, struct proc *p, int how) 102 { 103 unsigned long need_wait, set_wait; 104 int wait_prio; 105 106 #ifdef DIAGNOSTIC 107 if (p == NULL) 108 panic("rw_enter_wait: NULL proc"); 109 #endif 110 111 /* 112 * XXX - this function needs a lot of help to become MP safe. 113 */ 114 115 switch (how) { 116 case RW_READ: 117 /* 118 * Let writers through before obtaining read lock. 119 */ 120 need_wait = RWLOCK_WRLOCK | RWLOCK_WRWANT; 121 set_wait = RWLOCK_WAIT; 122 wait_prio = PLOCK; 123 break; 124 case RW_WRITE: 125 need_wait = ~0UL; 126 set_wait = RWLOCK_WAIT | RWLOCK_WRWANT; 127 wait_prio = PLOCK - 4; 128 if (RW_PROC(RWLOCK_OWNER(rwl)) == RW_PROC(p)) { 129 panic("rw_enter: locking against myself"); 130 } 131 break; 132 } 133 134 while (rwl->rwl_owner & need_wait) { 135 rwl->rwl_owner |= set_wait; 136 tsleep(rwl, wait_prio, "rwlock", 0); 137 } 138 } 139 140 void 141 rw_exit_waiters(struct rwlock *rwl, unsigned long owner) 142 { 143 #ifdef DIAGNOSTIC 144 if ((owner & RWLOCK_WAIT) == 0) 145 panic("rw_exit_waiters: no waiter"); 146 #endif 147 /* We wake up all waiters because we can't know how many they are. */ 148 wakeup(rwl); 149 } 150 151 #ifdef RWLOCK_TEST 152 #include <sys/kthread.h> 153 154 void rwlock_test(void); 155 156 void rwlock_testp1(void *); 157 void rwlock_testp2(void *); 158 void rwlock_testp3(void *); 159 160 struct rwlock rw_test = RWLOCK_INITIALIZER; 161 162 void 163 rwlock_test(void) 164 { 165 kthread_create(rwlock_testp1, NULL, NULL, "rw1"); 166 kthread_create(rwlock_testp2, NULL, NULL, "rw2"); 167 kthread_create(rwlock_testp3, NULL, NULL, "rw3"); 168 } 169 170 void 171 rwlock_testp1(void *a) 172 { 173 int local; 174 175 printf("rwlock test1 start\n"); 176 rw_enter_read(&rw_test); 177 printf("rwlock test1 obtained\n"); 178 tsleep(&local, PWAIT, "rw1", 4); 179 rw_exit_read(&rw_test); 180 printf("rwlock test1 released\n"); 181 tsleep(&local, PWAIT, "rw1/2", 3); 182 rw_enter_read(&rw_test); 183 printf("rwlock test1 obtained\n"); 184 rw_exit_read(&rw_test); 185 printf("rwlock test1 released\n"); 186 kthread_exit(0); 187 } 188 189 void 190 rwlock_testp2(void *a) 191 { 192 int local; 193 194 printf("rwlock test2 start\n"); 195 rw_enter_read(&rw_test); 196 printf("rwlock test2 obtained\n"); 197 tsleep(&local, PWAIT, "rw2", 4); 198 rw_exit_read(&rw_test); 199 printf("rwlock test2 released\n"); 200 kthread_exit(0); 201 } 202 203 void 204 rwlock_testp3(void *a) 205 { 206 int local; 207 208 printf("rwlock test3 start\n"); 209 tsleep(&local, PWAIT, "rw3", 2); 210 printf("rwlock test3 exited waiting\n"); 211 rw_enter_write(&rw_test, curproc); 212 printf("rwlock test3 obtained\n"); 213 tsleep(&local, PWAIT, "rw3/2", 4); 214 rw_exit_write(&rw_test); 215 printf("rwlock test3 released\n"); 216 kthread_exit(0); 217 } 218 #endif 219