Home | History | Annotate | Line # | Download | only in rumpkern
locks_up.c revision 1.1.2.2
      1 /*	$NetBSD: locks_up.c,v 1.1.2.2 2010/05/30 05:18:06 rmind Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 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 /*
     29  * Virtual uniprocessor rump kernel version of locks.  Since the entire
     30  * kernel is running on only one CPU in the system, there is no need
     31  * to perform slow cache-coherent MP locking operations.  This speeds
     32  * up things quite dramatically and is a good example of that two
     33  * disjoint kernels running simultaneously in an MP system can be
     34  * massively faster than one with fine-grained locking.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: locks_up.c,v 1.1.2.2 2010/05/30 05:18:06 rmind Exp $");
     39 
     40 #include <sys/param.h>
     41 #include <sys/kernel.h>
     42 #include <sys/kmem.h>
     43 #include <sys/mutex.h>
     44 #include <sys/rwlock.h>
     45 
     46 #include <rump/rumpuser.h>
     47 
     48 #include "rump_private.h"
     49 
     50 struct upmtx {
     51 	struct lwp *upm_owner;
     52 	int upm_wanted;
     53 	struct rumpuser_cv *upm_rucv;
     54 };
     55 #define UPMTX(mtx) struct upmtx *upm = *(struct upmtx **)mtx
     56 
     57 static inline void
     58 checkncpu(void)
     59 {
     60 
     61 	if (__predict_false(ncpu != 1))
     62 		panic("UP lock implementation requires RUMP_NCPU == 1");
     63 }
     64 
     65 void
     66 mutex_init(kmutex_t *mtx, kmutex_type_t type, int ipl)
     67 {
     68 	struct upmtx *upm;
     69 
     70 	CTASSERT(sizeof(kmutex_t) >= sizeof(void *));
     71 	checkncpu();
     72 
     73 	/*
     74 	 * XXX: pool_cache would be nice, but not easily possible,
     75 	 * as pool cache init wants to call mutex_init() ...
     76 	 */
     77 	upm = rumpuser_malloc(sizeof(*upm), 1);
     78 	memset(upm, 0, sizeof(*upm));
     79 	rumpuser_cv_init(&upm->upm_rucv);
     80 	memcpy(mtx, &upm, sizeof(void *));
     81 }
     82 
     83 void
     84 mutex_destroy(kmutex_t *mtx)
     85 {
     86 	UPMTX(mtx);
     87 
     88 	KASSERT(upm->upm_owner == NULL);
     89 	KASSERT(upm->upm_wanted == 0);
     90 	rumpuser_cv_destroy(upm->upm_rucv);
     91 	rumpuser_free(upm);
     92 }
     93 
     94 void
     95 mutex_enter(kmutex_t *mtx)
     96 {
     97 	UPMTX(mtx);
     98 
     99 	/* fastpath? */
    100 	if (mutex_tryenter(mtx))
    101 		return;
    102 
    103 	/*
    104 	 * No?  bummer, do it the slow and painful way then.
    105 	 */
    106 	upm->upm_wanted++;
    107 	while (!mutex_tryenter(mtx)) {
    108 		rump_schedlock_cv_wait(upm->upm_rucv);
    109 	}
    110 	upm->upm_wanted--;
    111 
    112 	KASSERT(upm->upm_wanted >= 0);
    113 }
    114 
    115 void
    116 mutex_spin_enter(kmutex_t *mtx)
    117 {
    118 
    119 	mutex_enter(mtx);
    120 }
    121 
    122 int
    123 mutex_tryenter(kmutex_t *mtx)
    124 {
    125 	UPMTX(mtx);
    126 
    127 	if (upm->upm_owner)
    128 		return 0;
    129 
    130 	upm->upm_owner = curlwp;
    131 	return 1;
    132 }
    133 
    134 void
    135 mutex_exit(kmutex_t *mtx)
    136 {
    137 	UPMTX(mtx);
    138 
    139 	if (upm->upm_wanted) {
    140 		rumpuser_cv_signal(upm->upm_rucv); /* CPU is our interlock */
    141 	}
    142 	upm->upm_owner = NULL;
    143 }
    144 
    145 void
    146 mutex_spin_exit(kmutex_t *mtx)
    147 {
    148 
    149 	mutex_exit(mtx);
    150 }
    151 
    152 int
    153 mutex_owned(kmutex_t *mtx)
    154 {
    155 	UPMTX(mtx);
    156 
    157 	return upm->upm_owner == curlwp;
    158 }
    159 
    160 struct uprw {
    161 	struct lwp *uprw_owner;
    162 	int uprw_readers;
    163 	uint16_t uprw_rwant;
    164 	uint16_t uprw_wwant;
    165 	struct rumpuser_cv *uprw_rucv_reader;
    166 	struct rumpuser_cv *uprw_rucv_writer;
    167 };
    168 
    169 #define UPRW(rw) struct uprw *uprw = *(struct uprw **)rw
    170 
    171 /* reader/writer locks */
    172 
    173 void
    174 rw_init(krwlock_t *rw)
    175 {
    176 	struct uprw *uprw;
    177 
    178 	CTASSERT(sizeof(krwlock_t) >= sizeof(void *));
    179 	checkncpu();
    180 
    181 	uprw = rumpuser_malloc(sizeof(*uprw), 0);
    182 	memset(uprw, 0, sizeof(*uprw));
    183 	rumpuser_cv_init(&uprw->uprw_rucv_reader);
    184 	rumpuser_cv_init(&uprw->uprw_rucv_writer);
    185 	memcpy(rw, &uprw, sizeof(void *));
    186 }
    187 
    188 void
    189 rw_destroy(krwlock_t *rw)
    190 {
    191 	UPRW(rw);
    192 
    193 	rumpuser_cv_destroy(uprw->uprw_rucv_reader);
    194 	rumpuser_cv_destroy(uprw->uprw_rucv_writer);
    195 	rumpuser_free(uprw);
    196 }
    197 
    198 /* take rwlock.  prefer writers over readers (see rw_tryenter and rw_exit) */
    199 void
    200 rw_enter(krwlock_t *rw, const krw_t op)
    201 {
    202 	UPRW(rw);
    203 	struct rumpuser_cv *rucv;
    204 	uint16_t *wp;
    205 
    206 	if (rw_tryenter(rw, op))
    207 		return;
    208 
    209 	/* lagpath */
    210 	if (op == RW_READER) {
    211 		rucv = uprw->uprw_rucv_reader;
    212 		wp = &uprw->uprw_rwant;
    213 	} else {
    214 		rucv = uprw->uprw_rucv_writer;
    215 		wp = &uprw->uprw_wwant;
    216 	}
    217 
    218 	(*wp)++;
    219 	while (!rw_tryenter(rw, op)) {
    220 		rump_schedlock_cv_wait(rucv);
    221 	}
    222 	(*wp)--;
    223 }
    224 
    225 int
    226 rw_tryenter(krwlock_t *rw, const krw_t op)
    227 {
    228 	UPRW(rw);
    229 
    230 	switch (op) {
    231 	case RW_READER:
    232 		if (uprw->uprw_owner == NULL && uprw->uprw_wwant == 0) {
    233 			uprw->uprw_readers++;
    234 			return 1;
    235 		}
    236 		break;
    237 	case RW_WRITER:
    238 		if (uprw->uprw_owner == NULL && uprw->uprw_readers == 0) {
    239 			uprw->uprw_owner = curlwp;
    240 			return 1;
    241 		}
    242 		break;
    243 	}
    244 
    245 	return 0;
    246 }
    247 
    248 void
    249 rw_exit(krwlock_t *rw)
    250 {
    251 	UPRW(rw);
    252 
    253 	if (uprw->uprw_readers > 0) {
    254 		uprw->uprw_readers--;
    255 	} else {
    256 		KASSERT(uprw->uprw_owner == curlwp);
    257 		uprw->uprw_owner = NULL;
    258 	}
    259 
    260 	if (uprw->uprw_wwant) {
    261 		rumpuser_cv_signal(uprw->uprw_rucv_writer);
    262 	} else if (uprw->uprw_rwant) {
    263 		rumpuser_cv_signal(uprw->uprw_rucv_reader);
    264 	}
    265 }
    266 
    267 int
    268 rw_tryupgrade(krwlock_t *rw)
    269 {
    270 	UPRW(rw);
    271 
    272 	if (uprw->uprw_readers == 1 && uprw->uprw_owner == NULL) {
    273 		uprw->uprw_readers = 0;
    274 		uprw->uprw_owner = curlwp;
    275 		return 1;
    276 	} else {
    277 		return 0;
    278 	}
    279 }
    280 
    281 int
    282 rw_write_held(krwlock_t *rw)
    283 {
    284 	UPRW(rw);
    285 
    286 	return uprw->uprw_owner == curlwp;
    287 }
    288 
    289 int
    290 rw_read_held(krwlock_t *rw)
    291 {
    292 	UPRW(rw);
    293 
    294 	return uprw->uprw_readers > 0;
    295 }
    296 
    297 int
    298 rw_lock_held(krwlock_t *rw)
    299 {
    300 	UPRW(rw);
    301 
    302 	return uprw->uprw_owner || uprw->uprw_readers;
    303 }
    304 
    305 
    306 /*
    307  * Condvars are almost the same as in the MP case except that we
    308  * use the scheduler mutex as the pthread interlock instead of the
    309  * mutex associated with the condvar.
    310  */
    311 
    312 #define RUMPCV(cv) (*(struct rumpuser_cv **)(cv))
    313 
    314 void
    315 cv_init(kcondvar_t *cv, const char *msg)
    316 {
    317 
    318 	CTASSERT(sizeof(kcondvar_t) >= sizeof(void *));
    319 	checkncpu();
    320 
    321 	rumpuser_cv_init((struct rumpuser_cv **)cv);
    322 }
    323 
    324 void
    325 cv_destroy(kcondvar_t *cv)
    326 {
    327 
    328 	rumpuser_cv_destroy(RUMPCV(cv));
    329 }
    330 
    331 void
    332 cv_wait(kcondvar_t *cv, kmutex_t *mtx)
    333 {
    334 #ifdef DIAGNOSTIC
    335 	UPMTX(mtx);
    336 	KASSERT(upm->upm_owner == curlwp);
    337 
    338 	if (rump_threads == 0)
    339 		panic("cv_wait without threads");
    340 #endif
    341 
    342 	/*
    343 	 * NOTE: we must atomically release the *CPU* here, i.e.
    344 	 * nothing between mutex_exit and entering rumpuser condwait
    345 	 * may preempt us from the virtual CPU.
    346 	 */
    347 	mutex_exit(mtx);
    348 	rump_schedlock_cv_wait(RUMPCV(cv));
    349 	mutex_enter(mtx);
    350 }
    351 
    352 int
    353 cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx)
    354 {
    355 
    356 	cv_wait(cv, mtx);
    357 	return 0;
    358 }
    359 
    360 int
    361 cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int ticks)
    362 {
    363 	struct timespec ts, tstick;
    364 
    365 #ifdef DIAGNOSTIC
    366 	UPMTX(mtx);
    367 	KASSERT(upm->upm_owner == curlwp);
    368 #endif
    369 
    370 	/*
    371 	 * XXX: this fetches rump kernel time, but rumpuser_cv_timedwait
    372 	 * uses host time.
    373 	 */
    374 	nanotime(&ts);
    375 	tstick.tv_sec = ticks / hz;
    376 	tstick.tv_nsec = (ticks % hz) * (1000000000/hz);
    377 	timespecadd(&ts, &tstick, &ts);
    378 
    379 	if (ticks == 0) {
    380 		cv_wait(cv, mtx);
    381 		return 0;
    382 	} else {
    383 		int rv;
    384 		mutex_exit(mtx);
    385 		rv = rump_schedlock_cv_timedwait(RUMPCV(cv), &ts);
    386 		mutex_enter(mtx);
    387 		if (rv)
    388 			return EWOULDBLOCK;
    389 		else
    390 			return 0;
    391 	}
    392 }
    393 
    394 int
    395 cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mtx, int ticks)
    396 {
    397 
    398 	return cv_timedwait(cv, mtx, ticks);
    399 }
    400 
    401 void
    402 cv_signal(kcondvar_t *cv)
    403 {
    404 
    405 	/* CPU == interlock */
    406 	rumpuser_cv_signal(RUMPCV(cv));
    407 }
    408 
    409 void
    410 cv_broadcast(kcondvar_t *cv)
    411 {
    412 
    413 	/* CPU == interlock */
    414 	rumpuser_cv_broadcast(RUMPCV(cv));
    415 }
    416 
    417 bool
    418 cv_has_waiters(kcondvar_t *cv)
    419 {
    420 
    421 	return rumpuser_cv_has_waiters(RUMPCV(cv));
    422 }
    423 
    424 /* this is not much of an attempt, but ... */
    425 bool
    426 cv_is_valid(kcondvar_t *cv)
    427 {
    428 
    429 	return RUMPCV(cv) != NULL;
    430 }
    431