rumpuser_pth.c revision 1.16 1 /* $NetBSD: rumpuser_pth.c,v 1.16 2013/04/28 13:37:51 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007-2010 Antti Kantee. 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 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include "rumpuser_port.h"
29
30 #if !defined(lint)
31 __RCSID("$NetBSD: rumpuser_pth.c,v 1.16 2013/04/28 13:37:51 pooka Exp $");
32 #endif /* !lint */
33
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <pthread.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdint.h>
42 #include <unistd.h>
43
44 #include <rump/rumpuser.h>
45
46 #include "rumpuser_int.h"
47
48 static pthread_key_t curlwpkey;
49
50 #define NOFAIL(a) do {if (!(a)) abort();} while (/*CONSTCOND*/0)
51 #define NOFAIL_ERRNO(a) \
52 do { \
53 int fail_rv = (a); \
54 if (fail_rv) { \
55 printf("panic: rumpuser fatal failure %d (%s)\n", \
56 fail_rv, strerror(fail_rv)); \
57 abort(); \
58 } \
59 } while (/*CONSTCOND*/0)
60
61 struct rumpuser_mtx {
62 pthread_mutex_t pthmtx;
63 struct lwp *owner;
64 int flags;
65 };
66
67 #define RURW_AMWRITER(rw) (rw->writer == rumpuser_get_curlwp() \
68 && rw->readers == -1)
69 #define RURW_HASREAD(rw) (rw->readers > 0)
70
71 #define RURW_SETWRITE(rw) \
72 do { \
73 assert(rw->readers == 0); \
74 rw->writer = rumpuser_get_curlwp(); \
75 rw->readers = -1; \
76 } while (/*CONSTCOND*/0)
77 #define RURW_CLRWRITE(rw) \
78 do { \
79 assert(rw->readers == -1 && RURW_AMWRITER(rw)); \
80 rw->readers = 0; \
81 } while (/*CONSTCOND*/0)
82 #define RURW_INCREAD(rw) \
83 do { \
84 pthread_spin_lock(&rw->spin); \
85 assert(rw->readers >= 0); \
86 ++(rw)->readers; \
87 pthread_spin_unlock(&rw->spin); \
88 } while (/*CONSTCOND*/0)
89 #define RURW_DECREAD(rw) \
90 do { \
91 pthread_spin_lock(&rw->spin); \
92 assert(rw->readers > 0); \
93 --(rw)->readers; \
94 pthread_spin_unlock(&rw->spin); \
95 } while (/*CONSTCOND*/0)
96
97 struct rumpuser_rw {
98 pthread_rwlock_t pthrw;
99 pthread_spinlock_t spin;
100 int readers;
101 struct lwp *writer;
102 };
103
104 struct rumpuser_cv {
105 pthread_cond_t pthcv;
106 int nwaiters;
107 };
108
109 struct rumpuser_mtx rumpuser_aio_mtx;
110 struct rumpuser_cv rumpuser_aio_cv;
111 int rumpuser_aio_head, rumpuser_aio_tail;
112 struct rumpuser_aio rumpuser_aios[N_AIOS];
113
114 void
115 rumpuser__thrinit(void)
116 {
117
118 pthread_mutex_init(&rumpuser_aio_mtx.pthmtx, NULL);
119 pthread_cond_init(&rumpuser_aio_cv.pthcv, NULL);
120 pthread_key_create(&curlwpkey, NULL);
121 }
122
123 void
124 /*ARGSUSED*/
125 rumpuser_biothread(void *arg)
126 {
127 struct rumpuser_aio *rua;
128 rump_biodone_fn biodone = arg;
129 ssize_t rv;
130 int error, dummy;
131
132 /* unschedule from CPU. we reschedule before running the interrupt */
133 rumpuser__unschedule(0, &dummy, NULL);
134 assert(dummy == 0);
135
136 NOFAIL_ERRNO(pthread_mutex_lock(&rumpuser_aio_mtx.pthmtx));
137 for (;;) {
138 while (rumpuser_aio_head == rumpuser_aio_tail) {
139 NOFAIL_ERRNO(pthread_cond_wait(&rumpuser_aio_cv.pthcv,
140 &rumpuser_aio_mtx.pthmtx));
141 }
142
143 rua = &rumpuser_aios[rumpuser_aio_tail];
144 assert(rua->rua_bp != NULL);
145 pthread_mutex_unlock(&rumpuser_aio_mtx.pthmtx);
146
147 if (rua->rua_op & RUA_OP_READ) {
148 error = 0;
149 rv = pread(rua->rua_fd, rua->rua_data,
150 rua->rua_dlen, rua->rua_off);
151 if (rv < 0) {
152 rv = 0;
153 error = errno;
154 }
155 } else {
156 error = 0;
157 rv = pwrite(rua->rua_fd, rua->rua_data,
158 rua->rua_dlen, rua->rua_off);
159 if (rv < 0) {
160 rv = 0;
161 error = errno;
162 } else if (rua->rua_op & RUA_OP_SYNC) {
163 #ifdef __NetBSD__
164 fsync_range(rua->rua_fd, FDATASYNC,
165 rua->rua_off, rua->rua_dlen);
166 #else
167 fsync(rua->rua_fd);
168 #endif
169 }
170 }
171 rumpuser__reschedule(0, NULL);
172 biodone(rua->rua_bp, (size_t)rv, error);
173 rumpuser__unschedule(0, &dummy, NULL);
174
175 rua->rua_bp = NULL;
176
177 NOFAIL_ERRNO(pthread_mutex_lock(&rumpuser_aio_mtx.pthmtx));
178 rumpuser_aio_tail = (rumpuser_aio_tail+1) % N_AIOS;
179 pthread_cond_signal(&rumpuser_aio_cv.pthcv);
180 }
181
182 /*NOTREACHED*/
183 fprintf(stderr, "error: rumpuser_biothread reached unreachable\n");
184 abort();
185 }
186
187 int
188 rumpuser_thread_create(void *(*f)(void *), void *arg, const char *thrname,
189 int joinable, void **ptcookie)
190 {
191 pthread_t ptid;
192 pthread_t *ptidp;
193 pthread_attr_t pattr;
194 int rv;
195
196 if ((rv = pthread_attr_init(&pattr)) != 0)
197 return rv;
198
199 if (joinable) {
200 NOFAIL(ptidp = malloc(sizeof(*ptidp)));
201 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
202 } else {
203 ptidp = &ptid;
204 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
205 }
206
207 rv = pthread_create(ptidp, &pattr, f, arg);
208 #if defined(__NetBSD__)
209 if (rv == 0 && thrname)
210 pthread_setname_np(ptid, thrname, NULL);
211 #elif defined(__linux__)
212 /*
213 * The pthread_setname_np() call varies from one Linux distro to
214 * another. Comment out the call pending autoconf support.
215 */
216 #if 0
217 if (rv == 0 && thrname)
218 pthread_setname_np(ptid, thrname);
219 #endif
220 #endif
221
222 if (joinable) {
223 assert(ptcookie);
224 *ptcookie = ptidp;
225 }
226
227 pthread_attr_destroy(&pattr);
228
229 return rv;
230 }
231
232 __dead void
233 rumpuser_thread_exit(void)
234 {
235
236 pthread_exit(NULL);
237 }
238
239 int
240 rumpuser_thread_join(void *ptcookie)
241 {
242 pthread_t *pt = ptcookie;
243 int rv;
244
245 KLOCK_WRAP((rv = pthread_join(*pt, NULL)));
246 if (rv == 0)
247 free(pt);
248
249 return rv;
250 }
251
252 void
253 rumpuser_mutex_init(struct rumpuser_mtx **mtx, int flags)
254 {
255 pthread_mutexattr_t att;
256
257 NOFAIL(*mtx = malloc(sizeof(struct rumpuser_mtx)));
258
259 pthread_mutexattr_init(&att);
260 pthread_mutexattr_settype(&att, PTHREAD_MUTEX_ERRORCHECK);
261 NOFAIL_ERRNO(pthread_mutex_init(&((*mtx)->pthmtx), &att));
262 pthread_mutexattr_destroy(&att);
263
264 (*mtx)->owner = NULL;
265 assert(flags != 0);
266 (*mtx)->flags = flags;
267 }
268
269 static void
270 mtxenter(struct rumpuser_mtx *mtx)
271 {
272
273 if (!(mtx->flags & RUMPUSER_MTX_KMUTEX))
274 return;
275
276 assert(mtx->owner == NULL);
277 mtx->owner = rumpuser_get_curlwp();
278 }
279
280 static void
281 mtxexit(struct rumpuser_mtx *mtx)
282 {
283
284 if (!(mtx->flags & RUMPUSER_MTX_KMUTEX))
285 return;
286
287 assert(mtx->owner != NULL);
288 mtx->owner = NULL;
289 }
290
291 void
292 rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
293 {
294
295 if (mtx->flags & RUMPUSER_MTX_SPIN) {
296 rumpuser_mutex_enter_nowrap(mtx);
297 return;
298 }
299
300 assert(mtx->flags & RUMPUSER_MTX_KMUTEX);
301 if (pthread_mutex_trylock(&mtx->pthmtx) != 0)
302 KLOCK_WRAP(NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx)));
303 mtxenter(mtx);
304 }
305
306 void
307 rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx)
308 {
309
310 assert(mtx->flags & RUMPUSER_MTX_SPIN);
311 NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx));
312 mtxenter(mtx);
313 }
314
315 int
316 rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
317 {
318 int rv;
319
320 rv = pthread_mutex_trylock(&mtx->pthmtx);
321 if (rv == 0) {
322 mtxenter(mtx);
323 }
324
325 return rv == 0;
326 }
327
328 void
329 rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
330 {
331
332 mtxexit(mtx);
333 NOFAIL_ERRNO(pthread_mutex_unlock(&mtx->pthmtx));
334 }
335
336 void
337 rumpuser_mutex_destroy(struct rumpuser_mtx *mtx)
338 {
339
340 NOFAIL_ERRNO(pthread_mutex_destroy(&mtx->pthmtx));
341 free(mtx);
342 }
343
344 struct lwp *
345 rumpuser_mutex_owner(struct rumpuser_mtx *mtx)
346 {
347
348 if (__predict_false(!(mtx->flags & RUMPUSER_MTX_KMUTEX))) {
349 printf("panic: rumpuser_mutex_held unsupported on non-kmtx\n");
350 abort();
351 }
352
353 return mtx->owner;
354 }
355
356 void
357 rumpuser_rw_init(struct rumpuser_rw **rw)
358 {
359
360 NOFAIL(*rw = malloc(sizeof(struct rumpuser_rw)));
361 NOFAIL_ERRNO(pthread_rwlock_init(&((*rw)->pthrw), NULL));
362 NOFAIL_ERRNO(pthread_spin_init(&((*rw)->spin),PTHREAD_PROCESS_PRIVATE));
363 (*rw)->readers = 0;
364 (*rw)->writer = NULL;
365 }
366
367 void
368 rumpuser_rw_enter(struct rumpuser_rw *rw, int iswrite)
369 {
370
371 if (iswrite) {
372 if (pthread_rwlock_trywrlock(&rw->pthrw) != 0)
373 KLOCK_WRAP(NOFAIL_ERRNO(
374 pthread_rwlock_wrlock(&rw->pthrw)));
375 RURW_SETWRITE(rw);
376 } else {
377 if (pthread_rwlock_tryrdlock(&rw->pthrw) != 0)
378 KLOCK_WRAP(NOFAIL_ERRNO(
379 pthread_rwlock_rdlock(&rw->pthrw)));
380 RURW_INCREAD(rw);
381 }
382 }
383
384 int
385 rumpuser_rw_tryenter(struct rumpuser_rw *rw, int iswrite)
386 {
387 int rv;
388
389 if (iswrite) {
390 rv = pthread_rwlock_trywrlock(&rw->pthrw);
391 if (rv == 0)
392 RURW_SETWRITE(rw);
393 } else {
394 rv = pthread_rwlock_tryrdlock(&rw->pthrw);
395 if (rv == 0)
396 RURW_INCREAD(rw);
397 }
398
399 return rv == 0;
400 }
401
402 void
403 rumpuser_rw_exit(struct rumpuser_rw *rw)
404 {
405
406 if (RURW_HASREAD(rw))
407 RURW_DECREAD(rw);
408 else
409 RURW_CLRWRITE(rw);
410 NOFAIL_ERRNO(pthread_rwlock_unlock(&rw->pthrw));
411 }
412
413 void
414 rumpuser_rw_destroy(struct rumpuser_rw *rw)
415 {
416
417 NOFAIL_ERRNO(pthread_rwlock_destroy(&rw->pthrw));
418 NOFAIL_ERRNO(pthread_spin_destroy(&rw->spin));
419 free(rw);
420 }
421
422 int
423 rumpuser_rw_held(struct rumpuser_rw *rw)
424 {
425
426 return rw->readers != 0;
427 }
428
429 int
430 rumpuser_rw_rdheld(struct rumpuser_rw *rw)
431 {
432
433 return RURW_HASREAD(rw);
434 }
435
436 int
437 rumpuser_rw_wrheld(struct rumpuser_rw *rw)
438 {
439
440 return RURW_AMWRITER(rw);
441 }
442
443 void
444 rumpuser_cv_init(struct rumpuser_cv **cv)
445 {
446
447 NOFAIL(*cv = malloc(sizeof(struct rumpuser_cv)));
448 NOFAIL_ERRNO(pthread_cond_init(&((*cv)->pthcv), NULL));
449 (*cv)->nwaiters = 0;
450 }
451
452 void
453 rumpuser_cv_destroy(struct rumpuser_cv *cv)
454 {
455
456 NOFAIL_ERRNO(pthread_cond_destroy(&cv->pthcv));
457 free(cv);
458 }
459
460 void
461 rumpuser_cv_wait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
462 {
463 int nlocks;
464
465 cv->nwaiters++;
466 rumpuser__unschedule(0, &nlocks, mtx);
467 mtxexit(mtx);
468 NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx));
469 mtxenter(mtx);
470 rumpuser__reschedule(nlocks, mtx);
471 cv->nwaiters--;
472 }
473
474 void
475 rumpuser_cv_wait_nowrap(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
476 {
477
478 cv->nwaiters++;
479 mtxexit(mtx);
480 NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx));
481 mtxenter(mtx);
482 cv->nwaiters--;
483 }
484
485 int
486 rumpuser_cv_timedwait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx,
487 int64_t sec, int64_t nsec)
488 {
489 struct timespec ts;
490 int rv, nlocks;
491
492 /*
493 * Get clock already here, just in case we will be put to sleep
494 * after releasing the kernel context.
495 *
496 * The condition variables should use CLOCK_MONOTONIC, but since
497 * that's not available everywhere, leave it for another day.
498 */
499 clock_gettime(CLOCK_REALTIME, &ts);
500
501 cv->nwaiters++;
502 rumpuser__unschedule(0, &nlocks, mtx);
503 mtxexit(mtx);
504
505 ts.tv_sec += sec;
506 ts.tv_nsec += nsec;
507 if (ts.tv_nsec >= 1000*1000*1000) {
508 ts.tv_sec++;
509 ts.tv_nsec -= 1000*1000*1000;
510 }
511 rv = pthread_cond_timedwait(&cv->pthcv, &mtx->pthmtx, &ts);
512 mtxenter(mtx);
513 rumpuser__reschedule(nlocks, mtx);
514 cv->nwaiters--;
515 if (rv != 0 && rv != ETIMEDOUT)
516 abort();
517
518 return rv == ETIMEDOUT;
519 }
520
521 void
522 rumpuser_cv_signal(struct rumpuser_cv *cv)
523 {
524
525 NOFAIL_ERRNO(pthread_cond_signal(&cv->pthcv));
526 }
527
528 void
529 rumpuser_cv_broadcast(struct rumpuser_cv *cv)
530 {
531
532 NOFAIL_ERRNO(pthread_cond_broadcast(&cv->pthcv));
533 }
534
535 int
536 rumpuser_cv_has_waiters(struct rumpuser_cv *cv)
537 {
538
539 return cv->nwaiters;
540 }
541
542 /*
543 * curlwp
544 */
545
546 void
547 rumpuser_set_curlwp(struct lwp *l)
548 {
549
550 assert(pthread_getspecific(curlwpkey) == NULL || l == NULL);
551 pthread_setspecific(curlwpkey, l);
552 }
553
554 struct lwp *
555 rumpuser_get_curlwp(void)
556 {
557
558 return pthread_getspecific(curlwpkey);
559 }
560