Home | History | Annotate | Line # | Download | only in librumpuser
rumpuser_pth.c revision 1.2
      1 /*	$NetBSD: rumpuser_pth.c,v 1.2 2010/05/18 14:58:41 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 <sys/cdefs.h>
     29 #if !defined(lint)
     30 __RCSID("$NetBSD: rumpuser_pth.c,v 1.2 2010/05/18 14:58:41 pooka Exp $");
     31 #endif /* !lint */
     32 
     33 #ifdef __linux__
     34 #define _XOPEN_SOURCE 500
     35 #define _BSD_SOURCE
     36 #define _FILE_OFFSET_BITS 64
     37 #endif
     38 
     39 #include <assert.h>
     40 #include <errno.h>
     41 #include <pthread.h>
     42 #include <stdlib.h>
     43 #include <stdio.h>
     44 #include <string.h>
     45 #include <stdint.h>
     46 #include <unistd.h>
     47 
     48 #include <rump/rumpuser.h>
     49 
     50 #include "rumpuser_int.h"
     51 
     52 static pthread_key_t curlwpkey;
     53 
     54 #define NOFAIL(a) do {if (!(a)) abort();} while (/*CONSTCOND*/0)
     55 #define NOFAIL_ERRNO(a)							\
     56 do {									\
     57 	int fail_rv = (a);						\
     58 	if (fail_rv) {							\
     59 		printf("panic: rumpuser fatal failure %d (%s)\n",	\
     60 		    fail_rv, strerror(fail_rv));			\
     61 		    abort();						\
     62 	}								\
     63 } while (/*CONSTCOND*/0)
     64 
     65 #define RUMTX_INCRECURSION(mtx) ((mtx)->recursion++)
     66 #define RUMTX_DECRECURSION(mtx) ((mtx)->recursion--)
     67 struct rumpuser_mtx {
     68 	pthread_mutex_t pthmtx;
     69 	pthread_t owner;
     70 	unsigned recursion;
     71 };
     72 
     73 #define RURW_AMWRITER(rw) (pthread_equal(rw->writer, pthread_self())	\
     74 				&& rw->readers == -1)
     75 #define RURW_HASREAD(rw)  (rw->readers > 0)
     76 
     77 #define RURW_SETWRITE(rw)						\
     78 do {									\
     79 	assert(rw->readers == 0);					\
     80 	rw->writer = pthread_self();					\
     81 	rw->readers = -1;						\
     82 } while (/*CONSTCOND*/0)
     83 #define RURW_CLRWRITE(rw)						\
     84 do {									\
     85 	assert(rw->readers == -1 && RURW_AMWRITER(rw));			\
     86 	rw->readers = 0;						\
     87 } while (/*CONSTCOND*/0)
     88 #define RURW_INCREAD(rw)						\
     89 do {									\
     90 	pthread_spin_lock(&rw->spin);					\
     91 	assert(rw->readers >= 0);					\
     92 	++(rw)->readers;						\
     93 	pthread_spin_unlock(&rw->spin);					\
     94 } while (/*CONSTCOND*/0)
     95 #define RURW_DECREAD(rw)						\
     96 do {									\
     97 	pthread_spin_lock(&rw->spin);					\
     98 	assert(rw->readers > 0);					\
     99 	--(rw)->readers;						\
    100 	pthread_spin_unlock(&rw->spin);					\
    101 } while (/*CONSTCOND*/0)
    102 
    103 struct rumpuser_rw {
    104 	pthread_rwlock_t pthrw;
    105 	pthread_spinlock_t spin;
    106 	int readers;
    107 	pthread_t writer;
    108 };
    109 
    110 struct rumpuser_cv {
    111 	pthread_cond_t pthcv;
    112 	int nwaiters;
    113 };
    114 
    115 struct rumpuser_mtx rumpuser_aio_mtx;
    116 struct rumpuser_cv rumpuser_aio_cv;
    117 int rumpuser_aio_head, rumpuser_aio_tail;
    118 struct rumpuser_aio rumpuser_aios[N_AIOS];
    119 
    120 kernel_lockfn	rumpuser__klock;
    121 kernel_unlockfn	rumpuser__kunlock;
    122 int		rumpuser__wantthreads;
    123 
    124 void
    125 /*ARGSUSED*/
    126 rumpuser_biothread(void *arg)
    127 {
    128 	struct rumpuser_aio *rua;
    129 	rump_biodone_fn biodone = arg;
    130 	ssize_t rv;
    131 	int error, dummy;
    132 
    133 	/* unschedule from CPU.  we reschedule before running the interrupt */
    134 	rumpuser__kunlock(0, &dummy, NULL);
    135 	assert(dummy == 0);
    136 
    137 	NOFAIL_ERRNO(pthread_mutex_lock(&rumpuser_aio_mtx.pthmtx));
    138 	for (;;) {
    139 		while (rumpuser_aio_head == rumpuser_aio_tail) {
    140 			NOFAIL_ERRNO(pthread_cond_wait(&rumpuser_aio_cv.pthcv,
    141 			    &rumpuser_aio_mtx.pthmtx));
    142 		}
    143 
    144 		rua = &rumpuser_aios[rumpuser_aio_tail];
    145 		assert(rua->rua_bp != NULL);
    146 		pthread_mutex_unlock(&rumpuser_aio_mtx.pthmtx);
    147 
    148 		if (rua->rua_op & RUA_OP_READ) {
    149 			error = 0;
    150 			rv = pread(rua->rua_fd, rua->rua_data,
    151 			    rua->rua_dlen, rua->rua_off);
    152 			if (rv < 0) {
    153 				rv = 0;
    154 				error = errno;
    155 			}
    156 		} else {
    157 			error = 0;
    158 			rv = pwrite(rua->rua_fd, rua->rua_data,
    159 			    rua->rua_dlen, rua->rua_off);
    160 			if (rv < 0) {
    161 				rv = 0;
    162 				error = errno;
    163 			} else if (rua->rua_op & RUA_OP_SYNC) {
    164 #ifdef __NetBSD__
    165 				fsync_range(rua->rua_fd, FDATASYNC,
    166 				    rua->rua_off, rua->rua_dlen);
    167 #else
    168 				fsync(rua->rua_fd);
    169 #endif
    170 			}
    171 		}
    172 		rumpuser__klock(0, NULL);
    173 		biodone(rua->rua_bp, (size_t)rv, error);
    174 		rumpuser__kunlock(0, &dummy, NULL);
    175 
    176 		rua->rua_bp = NULL;
    177 
    178 		NOFAIL_ERRNO(pthread_mutex_lock(&rumpuser_aio_mtx.pthmtx));
    179 		rumpuser_aio_tail = (rumpuser_aio_tail+1) % N_AIOS;
    180 		pthread_cond_signal(&rumpuser_aio_cv.pthcv);
    181 	}
    182 
    183 	/*NOTREACHED*/
    184 	fprintf(stderr, "error: rumpuser_biothread reached unreachable\n");
    185 	abort();
    186 }
    187 
    188 void
    189 rumpuser_thrinit(kernel_lockfn lockfn, kernel_unlockfn unlockfn, int threads)
    190 {
    191 
    192 	pthread_mutex_init(&rumpuser_aio_mtx.pthmtx, NULL);
    193 	pthread_cond_init(&rumpuser_aio_cv.pthcv, NULL);
    194 
    195 	pthread_key_create(&curlwpkey, NULL);
    196 
    197 	rumpuser__klock = lockfn;
    198 	rumpuser__kunlock = unlockfn;
    199 	rumpuser__wantthreads = threads;
    200 }
    201 
    202 #if 0
    203 void
    204 rumpuser__thrdestroy(void)
    205 {
    206 
    207 	pthread_key_delete(curlwpkey);
    208 }
    209 #endif
    210 
    211 int
    212 rumpuser_thread_create(void *(*f)(void *), void *arg, const char *thrname)
    213 {
    214 	pthread_t ptid;
    215 	int rv;
    216 
    217 	rv = pthread_create(&ptid, NULL, f, arg);
    218 #ifdef __NetBSD__
    219 	if (rv == 0 && thrname)
    220 		pthread_setname_np(ptid, thrname, NULL);
    221 #endif
    222 
    223 	return rv;
    224 }
    225 
    226 __dead void
    227 rumpuser_thread_exit(void)
    228 {
    229 
    230 	pthread_exit(NULL);
    231 }
    232 
    233 void
    234 rumpuser_mutex_init(struct rumpuser_mtx **mtx)
    235 {
    236 	pthread_mutexattr_t att;
    237 
    238 	NOFAIL(*mtx = malloc(sizeof(struct rumpuser_mtx)));
    239 
    240 	pthread_mutexattr_init(&att);
    241 	pthread_mutexattr_settype(&att, PTHREAD_MUTEX_ERRORCHECK);
    242 	NOFAIL_ERRNO(pthread_mutex_init(&((*mtx)->pthmtx), &att));
    243 	pthread_mutexattr_destroy(&att);
    244 
    245 	(*mtx)->owner = NULL;
    246 	(*mtx)->recursion = 0;
    247 }
    248 
    249 void
    250 rumpuser_mutex_recursive_init(struct rumpuser_mtx **mtx)
    251 {
    252 	pthread_mutexattr_t mattr;
    253 
    254 	pthread_mutexattr_init(&mattr);
    255 	pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
    256 
    257 	NOFAIL(*mtx = malloc(sizeof(struct rumpuser_mtx)));
    258 	NOFAIL_ERRNO(pthread_mutex_init(&((*mtx)->pthmtx), &mattr));
    259 	(*mtx)->owner = NULL;
    260 	(*mtx)->recursion = 0;
    261 
    262 	pthread_mutexattr_destroy(&mattr);
    263 }
    264 
    265 static void
    266 mtxenter(struct rumpuser_mtx *mtx)
    267 {
    268 
    269 	if (mtx->recursion++ == 0) {
    270 		assert(mtx->owner == NULL);
    271 		mtx->owner = pthread_self();
    272 	} else {
    273 		assert(pthread_equal(mtx->owner, pthread_self()));
    274 	}
    275 }
    276 
    277 static void
    278 mtxexit(struct rumpuser_mtx *mtx)
    279 {
    280 
    281 	assert(mtx->owner != NULL);
    282 	if (--mtx->recursion == 0)
    283 		mtx->owner = NULL;
    284 }
    285 
    286 void
    287 rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
    288 {
    289 
    290 	if (pthread_mutex_trylock(&mtx->pthmtx) != 0)
    291 		KLOCK_WRAP(NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx)));
    292 	mtxenter(mtx);
    293 }
    294 
    295 void
    296 rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx)
    297 {
    298 
    299 	NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx));
    300 	mtxenter(mtx);
    301 }
    302 
    303 int
    304 rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
    305 {
    306 	int rv;
    307 
    308 	rv = pthread_mutex_trylock(&mtx->pthmtx);
    309 	if (rv == 0) {
    310 		mtxenter(mtx);
    311 	}
    312 
    313 	return rv == 0;
    314 }
    315 
    316 void
    317 rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
    318 {
    319 
    320 	mtxexit(mtx);
    321 	NOFAIL_ERRNO(pthread_mutex_unlock(&mtx->pthmtx));
    322 }
    323 
    324 void
    325 rumpuser_mutex_destroy(struct rumpuser_mtx *mtx)
    326 {
    327 
    328 	NOFAIL_ERRNO(pthread_mutex_destroy(&mtx->pthmtx));
    329 	free(mtx);
    330 }
    331 
    332 int
    333 rumpuser_mutex_held(struct rumpuser_mtx *mtx)
    334 {
    335 
    336 	return mtx->recursion && pthread_equal(mtx->owner, pthread_self());
    337 }
    338 
    339 void
    340 rumpuser_rw_init(struct rumpuser_rw **rw)
    341 {
    342 
    343 	NOFAIL(*rw = malloc(sizeof(struct rumpuser_rw)));
    344 	NOFAIL_ERRNO(pthread_rwlock_init(&((*rw)->pthrw), NULL));
    345 	NOFAIL_ERRNO(pthread_spin_init(&((*rw)->spin), PTHREAD_PROCESS_SHARED));
    346 	(*rw)->readers = 0;
    347 	(*rw)->writer = NULL;
    348 }
    349 
    350 void
    351 rumpuser_rw_enter(struct rumpuser_rw *rw, int iswrite)
    352 {
    353 
    354 	if (iswrite) {
    355 		if (pthread_rwlock_trywrlock(&rw->pthrw) != 0)
    356 			KLOCK_WRAP(NOFAIL_ERRNO(
    357 			    pthread_rwlock_wrlock(&rw->pthrw)));
    358 		RURW_SETWRITE(rw);
    359 	} else {
    360 		if (pthread_rwlock_tryrdlock(&rw->pthrw) != 0)
    361 			KLOCK_WRAP(NOFAIL_ERRNO(
    362 			    pthread_rwlock_rdlock(&rw->pthrw)));
    363 		RURW_INCREAD(rw);
    364 	}
    365 }
    366 
    367 int
    368 rumpuser_rw_tryenter(struct rumpuser_rw *rw, int iswrite)
    369 {
    370 	int rv;
    371 
    372 	if (iswrite) {
    373 		rv = pthread_rwlock_trywrlock(&rw->pthrw);
    374 		if (rv == 0)
    375 			RURW_SETWRITE(rw);
    376 	} else {
    377 		rv = pthread_rwlock_tryrdlock(&rw->pthrw);
    378 		if (rv == 0)
    379 			RURW_INCREAD(rw);
    380 	}
    381 
    382 	return rv == 0;
    383 }
    384 
    385 void
    386 rumpuser_rw_exit(struct rumpuser_rw *rw)
    387 {
    388 
    389 	if (RURW_HASREAD(rw))
    390 		RURW_DECREAD(rw);
    391 	else
    392 		RURW_CLRWRITE(rw);
    393 	NOFAIL_ERRNO(pthread_rwlock_unlock(&rw->pthrw));
    394 }
    395 
    396 void
    397 rumpuser_rw_destroy(struct rumpuser_rw *rw)
    398 {
    399 
    400 	NOFAIL_ERRNO(pthread_rwlock_destroy(&rw->pthrw));
    401 	NOFAIL_ERRNO(pthread_spin_destroy(&rw->spin));
    402 	free(rw);
    403 }
    404 
    405 int
    406 rumpuser_rw_held(struct rumpuser_rw *rw)
    407 {
    408 
    409 	return rw->readers != 0;
    410 }
    411 
    412 int
    413 rumpuser_rw_rdheld(struct rumpuser_rw *rw)
    414 {
    415 
    416 	return RURW_HASREAD(rw);
    417 }
    418 
    419 int
    420 rumpuser_rw_wrheld(struct rumpuser_rw *rw)
    421 {
    422 
    423 	return RURW_AMWRITER(rw);
    424 }
    425 
    426 void
    427 rumpuser_cv_init(struct rumpuser_cv **cv)
    428 {
    429 
    430 	NOFAIL(*cv = malloc(sizeof(struct rumpuser_cv)));
    431 	NOFAIL_ERRNO(pthread_cond_init(&((*cv)->pthcv), NULL));
    432 	(*cv)->nwaiters = 0;
    433 }
    434 
    435 void
    436 rumpuser_cv_destroy(struct rumpuser_cv *cv)
    437 {
    438 
    439 	NOFAIL_ERRNO(pthread_cond_destroy(&cv->pthcv));
    440 	free(cv);
    441 }
    442 
    443 void
    444 rumpuser_cv_wait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
    445 {
    446 	int nlocks;
    447 
    448 	cv->nwaiters++;
    449 	rumpuser__kunlock(0, &nlocks, mtx);
    450 	assert(mtx->recursion == 1);
    451 	mtxexit(mtx);
    452 	NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx));
    453 	mtxenter(mtx);
    454 	rumpuser__klock(nlocks, mtx);
    455 	cv->nwaiters--;
    456 }
    457 
    458 void
    459 rumpuser_cv_wait_nowrap(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
    460 {
    461 
    462 	cv->nwaiters++;
    463 	assert(mtx->recursion == 1);
    464 	mtxexit(mtx);
    465 	NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx));
    466 	mtxenter(mtx);
    467 	cv->nwaiters--;
    468 }
    469 
    470 int
    471 rumpuser_cv_timedwait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx,
    472 	int64_t sec, int64_t nsec)
    473 {
    474 	struct timespec ts;
    475 	int rv, nlocks;
    476 
    477 	/* LINTED */
    478 	ts.tv_sec = sec; ts.tv_nsec = nsec;
    479 
    480 	cv->nwaiters++;
    481 	rumpuser__kunlock(0, &nlocks, mtx);
    482 	mtxexit(mtx);
    483 	rv = pthread_cond_timedwait(&cv->pthcv, &mtx->pthmtx, &ts);
    484 	mtxenter(mtx);
    485 	rumpuser__klock(nlocks, mtx);
    486 	cv->nwaiters--;
    487 	if (rv != 0 && rv != ETIMEDOUT)
    488 		abort();
    489 
    490 	return rv == ETIMEDOUT;
    491 }
    492 
    493 void
    494 rumpuser_cv_signal(struct rumpuser_cv *cv)
    495 {
    496 
    497 	NOFAIL_ERRNO(pthread_cond_signal(&cv->pthcv));
    498 }
    499 
    500 void
    501 rumpuser_cv_broadcast(struct rumpuser_cv *cv)
    502 {
    503 
    504 	NOFAIL_ERRNO(pthread_cond_broadcast(&cv->pthcv));
    505 }
    506 
    507 int
    508 rumpuser_cv_has_waiters(struct rumpuser_cv *cv)
    509 {
    510 
    511 	return cv->nwaiters;
    512 }
    513 
    514 /*
    515  * curlwp
    516  */
    517 
    518 void
    519 rumpuser_set_curlwp(struct lwp *l)
    520 {
    521 
    522 	assert(pthread_getspecific(curlwpkey) == NULL || l == NULL);
    523 	pthread_setspecific(curlwpkey, l);
    524 }
    525 
    526 struct lwp *
    527 rumpuser_get_curlwp(void)
    528 {
    529 
    530 	return pthread_getspecific(curlwpkey);
    531 }
    532