xref: /netbsd/sys/rump/librump/rumpkern/locks.c (revision 6550d01e)
1 /*	$NetBSD: locks.c,v 1.50 2011/01/28 17:04:39 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008 Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Finnish Cultural Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: locks.c,v 1.50 2011/01/28 17:04:39 pooka Exp $");
33 
34 #include <sys/param.h>
35 #include <sys/kmem.h>
36 #include <sys/mutex.h>
37 #include <sys/rwlock.h>
38 
39 #include <rump/rumpuser.h>
40 
41 #include "rump_private.h"
42 
43 /*
44  * Simple lockdebug.  If it's compiled in, it's always active.
45  * Currently available only for mtx/rwlock.
46  */
47 #ifdef LOCKDEBUG
48 #include <sys/lockdebug.h>
49 
50 static lockops_t mutex_lockops = {
51 	"mutex",
52 	LOCKOPS_SLEEP,
53 	NULL
54 };
55 static lockops_t rw_lockops = {
56 	"rwlock",
57 	LOCKOPS_SLEEP,
58 	NULL
59 };
60 
61 #define ALLOCK(lock, ops)		\
62     lockdebug_alloc(lock, ops, (uintptr_t)__builtin_return_address(0))
63 #define FREELOCK(lock)			\
64     lockdebug_free(lock)
65 #define WANTLOCK(lock, shar, try)	\
66     lockdebug_wantlock(lock, (uintptr_t)__builtin_return_address(0), shar, try)
67 #define LOCKED(lock, shar)		\
68     lockdebug_locked(lock, NULL, (uintptr_t)__builtin_return_address(0), shar)
69 #define UNLOCKED(lock, shar)		\
70     lockdebug_unlocked(lock, (uintptr_t)__builtin_return_address(0), shar)
71 #else
72 #define ALLOCK(a, b)
73 #define FREELOCK(a)
74 #define WANTLOCK(a, b, c)
75 #define LOCKED(a, b)
76 #define UNLOCKED(a, b)
77 #endif
78 
79 /*
80  * We map locks to pthread routines.  The difference between kernel
81  * and rumpuser routines is that while the kernel uses static
82  * storage, rumpuser allocates the object from the heap.  This
83  * indirection is necessary because we don't know the size of
84  * pthread objects here.  It is also beneficial, since we can
85  * be easily compatible with the kernel ABI because all kernel
86  * objects regardless of machine architecture are always at least
87  * the size of a pointer.  The downside, of course, is a performance
88  * penalty.
89  */
90 
91 #define RUMPMTX(mtx) (*(struct rumpuser_mtx **)(mtx))
92 
93 void
94 mutex_init(kmutex_t *mtx, kmutex_type_t type, int ipl)
95 {
96 
97 	CTASSERT(sizeof(kmutex_t) >= sizeof(void *));
98 
99 	rumpuser_mutex_init_kmutex((struct rumpuser_mtx **)mtx);
100 	ALLOCK(mtx, &mutex_lockops);
101 }
102 
103 void
104 mutex_destroy(kmutex_t *mtx)
105 {
106 
107 	FREELOCK(mtx);
108 	rumpuser_mutex_destroy(RUMPMTX(mtx));
109 }
110 
111 void
112 mutex_enter(kmutex_t *mtx)
113 {
114 
115 	WANTLOCK(mtx, false, false);
116 	rumpuser_mutex_enter(RUMPMTX(mtx));
117 	LOCKED(mtx, false);
118 }
119 __strong_alias(mutex_spin_enter,mutex_enter);
120 
121 int
122 mutex_tryenter(kmutex_t *mtx)
123 {
124 	int rv;
125 
126 	rv = rumpuser_mutex_tryenter(RUMPMTX(mtx));
127 	if (rv) {
128 		WANTLOCK(mtx, false, true);
129 		LOCKED(mtx, false);
130 	}
131 	return rv;
132 }
133 
134 void
135 mutex_exit(kmutex_t *mtx)
136 {
137 
138 	UNLOCKED(mtx, false);
139 	rumpuser_mutex_exit(RUMPMTX(mtx));
140 }
141 __strong_alias(mutex_spin_exit,mutex_exit);
142 
143 int
144 mutex_owned(kmutex_t *mtx)
145 {
146 
147 	return mutex_owner(mtx) == curlwp;
148 }
149 
150 struct lwp *
151 mutex_owner(kmutex_t *mtx)
152 {
153 
154 	return rumpuser_mutex_owner(RUMPMTX(mtx));
155 }
156 
157 #define RUMPRW(rw) (*(struct rumpuser_rw **)(rw))
158 
159 /* reader/writer locks */
160 
161 void
162 rw_init(krwlock_t *rw)
163 {
164 
165 	CTASSERT(sizeof(krwlock_t) >= sizeof(void *));
166 
167 	rumpuser_rw_init((struct rumpuser_rw **)rw);
168 	ALLOCK(rw, &rw_lockops);
169 }
170 
171 void
172 rw_destroy(krwlock_t *rw)
173 {
174 
175 	FREELOCK(rw);
176 	rumpuser_rw_destroy(RUMPRW(rw));
177 }
178 
179 void
180 rw_enter(krwlock_t *rw, const krw_t op)
181 {
182 
183 
184 	WANTLOCK(rw, op == RW_READER, false);
185 	rumpuser_rw_enter(RUMPRW(rw), op == RW_WRITER);
186 	LOCKED(rw, op == RW_READER);
187 }
188 
189 int
190 rw_tryenter(krwlock_t *rw, const krw_t op)
191 {
192 	int rv;
193 
194 	rv = rumpuser_rw_tryenter(RUMPRW(rw), op == RW_WRITER);
195 	if (rv) {
196 		WANTLOCK(rw, op == RW_READER, true);
197 		LOCKED(rw, op == RW_READER);
198 	}
199 	return rv;
200 }
201 
202 void
203 rw_exit(krwlock_t *rw)
204 {
205 
206 #ifdef LOCKDEBUG
207 	bool shared = !rw_write_held(rw);
208 
209 	if (shared)
210 		KASSERT(rw_read_held(rw));
211 	UNLOCKED(rw, shared);
212 #endif
213 	rumpuser_rw_exit(RUMPRW(rw));
214 }
215 
216 /* always fails */
217 int
218 rw_tryupgrade(krwlock_t *rw)
219 {
220 
221 	return 0;
222 }
223 
224 void
225 rw_downgrade(krwlock_t *rw)
226 {
227 
228 #ifdef LOCKDEBUG
229 	KASSERT(!rw_write_held(rw));
230 #endif
231 	/*
232 	 * XXX HACK: How we can downgrade re lock in rump properly.
233 	 */
234 	rw_exit(rw);
235 	rw_enter(rw, RW_READER);
236 	return;
237 }
238 
239 int
240 rw_write_held(krwlock_t *rw)
241 {
242 
243 	return rumpuser_rw_wrheld(RUMPRW(rw));
244 }
245 
246 int
247 rw_read_held(krwlock_t *rw)
248 {
249 
250 	return rumpuser_rw_rdheld(RUMPRW(rw));
251 }
252 
253 int
254 rw_lock_held(krwlock_t *rw)
255 {
256 
257 	return rumpuser_rw_held(RUMPRW(rw));
258 }
259 
260 /* curriculum vitaes */
261 
262 #define RUMPCV(cv) (*(struct rumpuser_cv **)(cv))
263 
264 void
265 cv_init(kcondvar_t *cv, const char *msg)
266 {
267 
268 	CTASSERT(sizeof(kcondvar_t) >= sizeof(void *));
269 
270 	rumpuser_cv_init((struct rumpuser_cv **)cv);
271 }
272 
273 void
274 cv_destroy(kcondvar_t *cv)
275 {
276 
277 	rumpuser_cv_destroy(RUMPCV(cv));
278 }
279 
280 static int
281 docvwait(kcondvar_t *cv, kmutex_t *mtx, struct timespec *ts)
282 {
283 	struct lwp *l = curlwp;
284 	int rv;
285 
286 	if (__predict_false(l->l_flag & LW_RUMP_DYING)) {
287 		/*
288 		 * yield() here, someone might want the cpu
289 		 * to set a condition.  otherwise we'll just
290 		 * loop forever.
291 		 */
292 		yield();
293 		return EINTR;
294 	}
295 
296 	UNLOCKED(mtx, false);
297 
298 	l->l_private = cv;
299 	rv = 0;
300 	if (ts) {
301 		if (rumpuser_cv_timedwait(RUMPCV(cv), RUMPMTX(mtx),
302 		    ts->tv_sec, ts->tv_nsec))
303 			rv = EWOULDBLOCK;
304 	} else {
305 		rumpuser_cv_wait(RUMPCV(cv), RUMPMTX(mtx));
306 	}
307 
308 	/*
309 	 * Check for DYING.  if so, we need to wait here until we
310 	 * are allowed to exit.
311 	 */
312 	if (__predict_false(l->l_flag & LW_RUMP_DYING)) {
313 		struct proc *p = l->l_proc;
314 
315 		mutex_exit(mtx); /* drop and retake later */
316 
317 		mutex_enter(p->p_lock);
318 		while (p->p_stat != SDYING) {
319 			/* avoid recursion */
320 			rumpuser_cv_wait(RUMPCV(&p->p_waitcv),
321 			    RUMPMTX(p->p_lock));
322 		}
323 		KASSERT(p->p_stat == SDYING);
324 		mutex_exit(p->p_lock);
325 
326 		/* ok, we can exit and remove "reference" to l->private */
327 
328 		mutex_enter(mtx);
329 		rv = EINTR;
330 	}
331 	l->l_private = NULL;
332 
333 	LOCKED(mtx, false);
334 
335 	return rv;
336 }
337 
338 void
339 cv_wait(kcondvar_t *cv, kmutex_t *mtx)
340 {
341 
342 	if (__predict_false(rump_threads == 0))
343 		panic("cv_wait without threads");
344 	(void) docvwait(cv, mtx, NULL);
345 }
346 
347 int
348 cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx)
349 {
350 
351 	if (__predict_false(rump_threads == 0))
352 		panic("cv_wait without threads");
353 	return docvwait(cv, mtx, NULL);
354 }
355 
356 int
357 cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int ticks)
358 {
359 	struct timespec ts, tick;
360 	extern int hz;
361 	int rv;
362 
363 	if (ticks == 0) {
364 		rv = cv_wait_sig(cv, mtx);
365 	} else {
366 		/*
367 		 * XXX: this fetches rump kernel time, but
368 		 * rumpuser_cv_timedwait uses host time.
369 		 */
370 		nanotime(&ts);
371 		tick.tv_sec = ticks / hz;
372 		tick.tv_nsec = (ticks % hz) * (1000000000/hz);
373 		timespecadd(&ts, &tick, &ts);
374 
375 		rv = docvwait(cv, mtx, &ts);
376 	}
377 
378 	return rv;
379 }
380 __strong_alias(cv_timedwait_sig,cv_timedwait);
381 
382 void
383 cv_signal(kcondvar_t *cv)
384 {
385 
386 	rumpuser_cv_signal(RUMPCV(cv));
387 }
388 
389 void
390 cv_broadcast(kcondvar_t *cv)
391 {
392 
393 	rumpuser_cv_broadcast(RUMPCV(cv));
394 }
395 
396 bool
397 cv_has_waiters(kcondvar_t *cv)
398 {
399 
400 	return rumpuser_cv_has_waiters(RUMPCV(cv));
401 }
402 
403 /* this is not much of an attempt, but ... */
404 bool
405 cv_is_valid(kcondvar_t *cv)
406 {
407 
408 	return RUMPCV(cv) != NULL;
409 }
410