Home | History | Annotate | Line # | Download | only in rumpkern
locks_up.c revision 1.6
      1 /*	$NetBSD: locks_up.c,v 1.6 2012/04/28 18:04:02 stacktic 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.6 2012/04/28 18:04:02 stacktic 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 = rump_hypermalloc(sizeof(*upm), 0, true, "mutex_init");
     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 	rump_hyperfree(upm, sizeof(*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 lwp *
    161 mutex_owner(kmutex_t *mtx)
    162 {
    163 	UPMTX(mtx);
    164 
    165 	return upm->upm_owner;
    166 }
    167 
    168 struct uprw {
    169 	struct lwp *uprw_owner;
    170 	int uprw_readers;
    171 	uint16_t uprw_rwant;
    172 	uint16_t uprw_wwant;
    173 	struct rumpuser_cv *uprw_rucv_reader;
    174 	struct rumpuser_cv *uprw_rucv_writer;
    175 };
    176 
    177 #define UPRW(rw) struct uprw *uprw = *(struct uprw **)rw
    178 
    179 /* reader/writer locks */
    180 
    181 void
    182 rw_init(krwlock_t *rw)
    183 {
    184 	struct uprw *uprw;
    185 
    186 	CTASSERT(sizeof(krwlock_t) >= sizeof(void *));
    187 	checkncpu();
    188 
    189 	uprw = rump_hypermalloc(sizeof(*uprw), 0, true, "rwinit");
    190 	memset(uprw, 0, sizeof(*uprw));
    191 	rumpuser_cv_init(&uprw->uprw_rucv_reader);
    192 	rumpuser_cv_init(&uprw->uprw_rucv_writer);
    193 	memcpy(rw, &uprw, sizeof(void *));
    194 }
    195 
    196 void
    197 rw_destroy(krwlock_t *rw)
    198 {
    199 	UPRW(rw);
    200 
    201 	rumpuser_cv_destroy(uprw->uprw_rucv_reader);
    202 	rumpuser_cv_destroy(uprw->uprw_rucv_writer);
    203 	rump_hyperfree(uprw, sizeof(*uprw));
    204 }
    205 
    206 /* take rwlock.  prefer writers over readers (see rw_tryenter and rw_exit) */
    207 void
    208 rw_enter(krwlock_t *rw, const krw_t op)
    209 {
    210 	UPRW(rw);
    211 	struct rumpuser_cv *rucv;
    212 	uint16_t *wp;
    213 
    214 	if (rw_tryenter(rw, op))
    215 		return;
    216 
    217 	/* lagpath */
    218 	if (op == RW_READER) {
    219 		rucv = uprw->uprw_rucv_reader;
    220 		wp = &uprw->uprw_rwant;
    221 	} else {
    222 		rucv = uprw->uprw_rucv_writer;
    223 		wp = &uprw->uprw_wwant;
    224 	}
    225 
    226 	(*wp)++;
    227 	while (!rw_tryenter(rw, op)) {
    228 		rump_schedlock_cv_wait(rucv);
    229 	}
    230 	(*wp)--;
    231 }
    232 
    233 int
    234 rw_tryenter(krwlock_t *rw, const krw_t op)
    235 {
    236 	UPRW(rw);
    237 
    238 	switch (op) {
    239 	case RW_READER:
    240 		if (uprw->uprw_owner == NULL && uprw->uprw_wwant == 0) {
    241 			uprw->uprw_readers++;
    242 			return 1;
    243 		}
    244 		break;
    245 	case RW_WRITER:
    246 		if (uprw->uprw_owner == NULL && uprw->uprw_readers == 0) {
    247 			uprw->uprw_owner = curlwp;
    248 			return 1;
    249 		}
    250 		break;
    251 	}
    252 
    253 	return 0;
    254 }
    255 
    256 void
    257 rw_exit(krwlock_t *rw)
    258 {
    259 	UPRW(rw);
    260 
    261 	if (uprw->uprw_readers > 0) {
    262 		uprw->uprw_readers--;
    263 	} else {
    264 		KASSERT(uprw->uprw_owner == curlwp);
    265 		uprw->uprw_owner = NULL;
    266 	}
    267 
    268 	if (uprw->uprw_wwant) {
    269 		rumpuser_cv_signal(uprw->uprw_rucv_writer);
    270 	} else if (uprw->uprw_rwant) {
    271 		rumpuser_cv_signal(uprw->uprw_rucv_reader);
    272 	}
    273 }
    274 
    275 int
    276 rw_tryupgrade(krwlock_t *rw)
    277 {
    278 	UPRW(rw);
    279 
    280 	if (uprw->uprw_readers == 1 && uprw->uprw_owner == NULL) {
    281 		uprw->uprw_readers = 0;
    282 		uprw->uprw_owner = curlwp;
    283 		return 1;
    284 	} else {
    285 		return 0;
    286 	}
    287 }
    288 
    289 int
    290 rw_write_held(krwlock_t *rw)
    291 {
    292 	UPRW(rw);
    293 
    294 	return uprw->uprw_owner == curlwp;
    295 }
    296 
    297 int
    298 rw_read_held(krwlock_t *rw)
    299 {
    300 	UPRW(rw);
    301 
    302 	return uprw->uprw_readers > 0;
    303 }
    304 
    305 int
    306 rw_lock_held(krwlock_t *rw)
    307 {
    308 	UPRW(rw);
    309 
    310 	return uprw->uprw_owner || uprw->uprw_readers;
    311 }
    312 
    313 
    314 /*
    315  * Condvars are almost the same as in the MP case except that we
    316  * use the scheduler mutex as the pthread interlock instead of the
    317  * mutex associated with the condvar.
    318  */
    319 
    320 #define RUMPCV(cv) (*(struct rumpuser_cv **)(cv))
    321 
    322 void
    323 cv_init(kcondvar_t *cv, const char *msg)
    324 {
    325 
    326 	CTASSERT(sizeof(kcondvar_t) >= sizeof(void *));
    327 	checkncpu();
    328 
    329 	rumpuser_cv_init((struct rumpuser_cv **)cv);
    330 }
    331 
    332 void
    333 cv_destroy(kcondvar_t *cv)
    334 {
    335 
    336 	rumpuser_cv_destroy(RUMPCV(cv));
    337 }
    338 
    339 void
    340 cv_wait(kcondvar_t *cv, kmutex_t *mtx)
    341 {
    342 #ifdef DIAGNOSTIC
    343 	UPMTX(mtx);
    344 	KASSERT(upm->upm_owner == curlwp);
    345 
    346 	if (rump_threads == 0)
    347 		panic("cv_wait without threads");
    348 #endif
    349 
    350 	/*
    351 	 * NOTE: we must atomically release the *CPU* here, i.e.
    352 	 * nothing between mutex_exit and entering rumpuser condwait
    353 	 * may preempt us from the virtual CPU.
    354 	 */
    355 	mutex_exit(mtx);
    356 	rump_schedlock_cv_wait(RUMPCV(cv));
    357 	mutex_enter(mtx);
    358 }
    359 
    360 int
    361 cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx)
    362 {
    363 
    364 	cv_wait(cv, mtx);
    365 	return 0;
    366 }
    367 
    368 int
    369 cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int ticks)
    370 {
    371 	struct timespec ts, tstick;
    372 
    373 #ifdef DIAGNOSTIC
    374 	UPMTX(mtx);
    375 	KASSERT(upm->upm_owner == curlwp);
    376 #endif
    377 
    378 	/*
    379 	 * XXX: this fetches rump kernel time, but rumpuser_cv_timedwait
    380 	 * uses host time.
    381 	 */
    382 	nanotime(&ts);
    383 	tstick.tv_sec = ticks / hz;
    384 	tstick.tv_nsec = (ticks % hz) * (1000000000/hz);
    385 	timespecadd(&ts, &tstick, &ts);
    386 
    387 	if (ticks == 0) {
    388 		cv_wait(cv, mtx);
    389 		return 0;
    390 	} else {
    391 		int rv;
    392 		mutex_exit(mtx);
    393 		rv = rump_schedlock_cv_timedwait(RUMPCV(cv), &ts);
    394 		mutex_enter(mtx);
    395 		if (rv)
    396 			return EWOULDBLOCK;
    397 		else
    398 			return 0;
    399 	}
    400 }
    401 
    402 int
    403 cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mtx, int ticks)
    404 {
    405 
    406 	return cv_timedwait(cv, mtx, ticks);
    407 }
    408 
    409 void
    410 cv_signal(kcondvar_t *cv)
    411 {
    412 
    413 	/* CPU == interlock */
    414 	rumpuser_cv_signal(RUMPCV(cv));
    415 }
    416 
    417 void
    418 cv_broadcast(kcondvar_t *cv)
    419 {
    420 
    421 	/* CPU == interlock */
    422 	rumpuser_cv_broadcast(RUMPCV(cv));
    423 }
    424 
    425 bool
    426 cv_has_waiters(kcondvar_t *cv)
    427 {
    428 
    429 	return rumpuser_cv_has_waiters(RUMPCV(cv));
    430 }
    431 
    432 /* this is not much of an attempt, but ... */
    433 bool
    434 cv_is_valid(kcondvar_t *cv)
    435 {
    436 
    437 	return RUMPCV(cv) != NULL;
    438 }
    439