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
rw_enter_read(struct rwlock * rwl)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
rw_enter_write(struct rwlock * rwl)52 rw_enter_write(struct rwlock *rwl)
53 {
54 struct proc *p = curproc;
55
56 while (__predict_false(rwl->rwl_owner != 0)) {
57 /*
58 * Not the simple case, go to slow path.
59 */
60 rw_enter_wait(rwl, p, RW_WRITE);
61 }
62 rwl->rwl_owner = RW_PROC(p) | RWLOCK_WRLOCK;
63 }
64
65 void
rw_exit_read(struct rwlock * rwl)66 rw_exit_read(struct rwlock *rwl)
67 {
68 unsigned long owner = rwl->rwl_owner;
69 unsigned long decr = (owner & (RWLOCK_WAIT|RWLOCK_WRWANT)) |
70 RWLOCK_READ_INCR;
71
72 rwl->rwl_owner -= decr;
73 /*
74 * Potential MP race here. If the owner had WRWANT set we cleared
75 * it and a reader can sneak in before a writer. Do we care?
76 */
77 if (__predict_false(owner & RWLOCK_WAIT))
78 rw_exit_waiters(rwl, owner);
79 }
80
81 void
rw_exit_write(struct rwlock * rwl)82 rw_exit_write(struct rwlock *rwl)
83 {
84 unsigned long owner = rwl->rwl_owner;
85
86 rwl->rwl_owner = 0;
87 /*
88 * Potential MP race here. If the owner had WRWANT set we cleared
89 * it and a reader can sneak in before a writer. Do we care?
90 */
91 if (__predict_false(owner & RWLOCK_WAIT))
92 rw_exit_waiters(rwl, owner);
93 }
94 #endif
95
96 void
rw_init(struct rwlock * rwl)97 rw_init(struct rwlock *rwl)
98 {
99 rwl->rwl_owner = 0;
100 }
101
102 void
rw_enter_wait(struct rwlock * rwl,struct proc * p,int how)103 rw_enter_wait(struct rwlock *rwl, struct proc *p, int how)
104 {
105 unsigned long need_wait, set_wait;
106 int wait_prio;
107
108 #ifdef DIAGNOSTIC
109 if (p == NULL)
110 panic("rw_enter_wait: NULL proc");
111 #endif
112
113 /*
114 * XXX - this function needs a lot of help to become MP safe.
115 */
116
117 switch (how) {
118 case RW_READ:
119 /*
120 * Let writers through before obtaining read lock.
121 */
122 need_wait = RWLOCK_WRLOCK | RWLOCK_WRWANT;
123 set_wait = RWLOCK_WAIT;
124 wait_prio = PLOCK;
125 break;
126 case RW_WRITE:
127 need_wait = ~0UL;
128 set_wait = RWLOCK_WAIT | RWLOCK_WRWANT;
129 wait_prio = PLOCK - 4;
130 if (RW_PROC(RWLOCK_OWNER(rwl)) == RW_PROC(p)) {
131 panic("rw_enter: locking against myself");
132 }
133 break;
134 }
135
136 while (rwl->rwl_owner & need_wait) {
137 rwl->rwl_owner |= set_wait;
138 tsleep(rwl, wait_prio, "rwlock", 0);
139 }
140 }
141
142 void
rw_exit_waiters(struct rwlock * rwl,unsigned long owner)143 rw_exit_waiters(struct rwlock *rwl, unsigned long owner)
144 {
145 #ifdef DIAGNOSTIC
146 if ((owner & RWLOCK_WAIT) == 0)
147 panic("rw_exit_waiters: no waiter");
148 #endif
149 /* We wake up all waiters because we can't know how many they are. */
150 wakeup(rwl);
151 }
152
153 #ifdef RWLOCK_TEST
154 #include <sys/kthread.h>
155
156 void rwlock_test(void);
157
158 void rwlock_testp1(void *);
159 void rwlock_testp2(void *);
160 void rwlock_testp3(void *);
161
162 struct rwlock rw_test = RWLOCK_INITIALIZER;
163
164 void
rwlock_test(void)165 rwlock_test(void)
166 {
167 kthread_create(rwlock_testp1, NULL, NULL, "rw1");
168 kthread_create(rwlock_testp2, NULL, NULL, "rw2");
169 kthread_create(rwlock_testp3, NULL, NULL, "rw3");
170 }
171
172 void
rwlock_testp1(void * a)173 rwlock_testp1(void *a)
174 {
175 int local;
176
177 printf("rwlock test1 start\n");
178 rw_enter_read(&rw_test);
179 printf("rwlock test1 obtained\n");
180 tsleep(&local, PWAIT, "rw1", 4);
181 rw_exit_read(&rw_test);
182 printf("rwlock test1 released\n");
183 tsleep(&local, PWAIT, "rw1/2", 3);
184 rw_enter_read(&rw_test);
185 printf("rwlock test1 obtained\n");
186 rw_exit_read(&rw_test);
187 printf("rwlock test1 released\n");
188 kthread_exit(0);
189 }
190
191 void
rwlock_testp2(void * a)192 rwlock_testp2(void *a)
193 {
194 int local;
195
196 printf("rwlock test2 start\n");
197 rw_enter_read(&rw_test);
198 printf("rwlock test2 obtained\n");
199 tsleep(&local, PWAIT, "rw2", 4);
200 rw_exit_read(&rw_test);
201 printf("rwlock test2 released\n");
202 kthread_exit(0);
203 }
204
205 void
rwlock_testp3(void * a)206 rwlock_testp3(void *a)
207 {
208 int local;
209
210 printf("rwlock test3 start\n");
211 tsleep(&local, PWAIT, "rw3", 2);
212 printf("rwlock test3 exited waiting\n");
213 rw_enter_write(&rw_test);
214 printf("rwlock test3 obtained\n");
215 tsleep(&local, PWAIT, "rw3/2", 4);
216 rw_exit_write(&rw_test);
217 printf("rwlock test3 released\n");
218 kthread_exit(0);
219 }
220 #endif
221