xref: /openbsd-src/sys/kern/kern_rwlock.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
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