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