1 1.14 martin /* $NetBSD: locks_up.c,v 1.14 2023/11/02 10:31:55 martin Exp $ */ 2 1.1 pooka 3 1.1 pooka /* 4 1.1 pooka * Copyright (c) 2010 Antti Kantee. All Rights Reserved. 5 1.1 pooka * 6 1.1 pooka * Redistribution and use in source and binary forms, with or without 7 1.1 pooka * modification, are permitted provided that the following conditions 8 1.1 pooka * are met: 9 1.1 pooka * 1. Redistributions of source code must retain the above copyright 10 1.1 pooka * notice, this list of conditions and the following disclaimer. 11 1.1 pooka * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 pooka * notice, this list of conditions and the following disclaimer in the 13 1.1 pooka * documentation and/or other materials provided with the distribution. 14 1.1 pooka * 15 1.1 pooka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 1.1 pooka * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 1.1 pooka * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 1.1 pooka * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 1.1 pooka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 1.1 pooka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 1.1 pooka * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 1.1 pooka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 1.1 pooka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 1.1 pooka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 1.1 pooka * SUCH DAMAGE. 26 1.1 pooka */ 27 1.1 pooka 28 1.1 pooka /* 29 1.1 pooka * Virtual uniprocessor rump kernel version of locks. Since the entire 30 1.1 pooka * kernel is running on only one CPU in the system, there is no need 31 1.1 pooka * to perform slow cache-coherent MP locking operations. This speeds 32 1.1 pooka * up things quite dramatically and is a good example of that two 33 1.1 pooka * disjoint kernels running simultaneously in an MP system can be 34 1.1 pooka * massively faster than one with fine-grained locking. 35 1.1 pooka */ 36 1.1 pooka 37 1.1 pooka #include <sys/cdefs.h> 38 1.14 martin __KERNEL_RCSID(0, "$NetBSD: locks_up.c,v 1.14 2023/11/02 10:31:55 martin Exp $"); 39 1.1 pooka 40 1.1 pooka #include <sys/param.h> 41 1.1 pooka #include <sys/kernel.h> 42 1.1 pooka #include <sys/kmem.h> 43 1.1 pooka #include <sys/mutex.h> 44 1.1 pooka #include <sys/rwlock.h> 45 1.1 pooka 46 1.10 pooka #include <rump-sys/kern.h> 47 1.10 pooka 48 1.1 pooka #include <rump/rumpuser.h> 49 1.1 pooka 50 1.1 pooka struct upmtx { 51 1.1 pooka struct lwp *upm_owner; 52 1.1 pooka int upm_wanted; 53 1.1 pooka struct rumpuser_cv *upm_rucv; 54 1.1 pooka }; 55 1.1 pooka #define UPMTX(mtx) struct upmtx *upm = *(struct upmtx **)mtx 56 1.1 pooka 57 1.1 pooka static inline void 58 1.1 pooka checkncpu(void) 59 1.1 pooka { 60 1.1 pooka 61 1.1 pooka if (__predict_false(ncpu != 1)) 62 1.1 pooka panic("UP lock implementation requires RUMP_NCPU == 1"); 63 1.1 pooka } 64 1.1 pooka 65 1.1 pooka void 66 1.1 pooka mutex_init(kmutex_t *mtx, kmutex_type_t type, int ipl) 67 1.1 pooka { 68 1.1 pooka struct upmtx *upm; 69 1.1 pooka 70 1.1 pooka CTASSERT(sizeof(kmutex_t) >= sizeof(void *)); 71 1.1 pooka checkncpu(); 72 1.1 pooka 73 1.1 pooka /* 74 1.7 pooka * In uniprocessor locking we don't need to differentiate 75 1.7 pooka * between spin mutexes and adaptive ones. We could 76 1.7 pooka * replace mutex_enter() with a NOP for spin mutexes, but 77 1.7 pooka * not bothering with that for now. 78 1.7 pooka */ 79 1.7 pooka 80 1.7 pooka /* 81 1.1 pooka * XXX: pool_cache would be nice, but not easily possible, 82 1.1 pooka * as pool cache init wants to call mutex_init() ... 83 1.1 pooka */ 84 1.3 pooka upm = rump_hypermalloc(sizeof(*upm), 0, true, "mutex_init"); 85 1.1 pooka memset(upm, 0, sizeof(*upm)); 86 1.1 pooka rumpuser_cv_init(&upm->upm_rucv); 87 1.1 pooka memcpy(mtx, &upm, sizeof(void *)); 88 1.1 pooka } 89 1.1 pooka 90 1.1 pooka void 91 1.1 pooka mutex_destroy(kmutex_t *mtx) 92 1.1 pooka { 93 1.1 pooka UPMTX(mtx); 94 1.1 pooka 95 1.1 pooka KASSERT(upm->upm_owner == NULL); 96 1.1 pooka KASSERT(upm->upm_wanted == 0); 97 1.1 pooka rumpuser_cv_destroy(upm->upm_rucv); 98 1.4 pooka rump_hyperfree(upm, sizeof(*upm)); 99 1.1 pooka } 100 1.1 pooka 101 1.1 pooka void 102 1.1 pooka mutex_enter(kmutex_t *mtx) 103 1.1 pooka { 104 1.1 pooka UPMTX(mtx); 105 1.1 pooka 106 1.1 pooka /* fastpath? */ 107 1.1 pooka if (mutex_tryenter(mtx)) 108 1.1 pooka return; 109 1.1 pooka 110 1.1 pooka /* 111 1.1 pooka * No? bummer, do it the slow and painful way then. 112 1.1 pooka */ 113 1.1 pooka upm->upm_wanted++; 114 1.1 pooka while (!mutex_tryenter(mtx)) { 115 1.1 pooka rump_schedlock_cv_wait(upm->upm_rucv); 116 1.1 pooka } 117 1.1 pooka upm->upm_wanted--; 118 1.1 pooka 119 1.1 pooka KASSERT(upm->upm_wanted >= 0); 120 1.1 pooka } 121 1.1 pooka 122 1.1 pooka void 123 1.1 pooka mutex_spin_enter(kmutex_t *mtx) 124 1.1 pooka { 125 1.1 pooka 126 1.1 pooka mutex_enter(mtx); 127 1.1 pooka } 128 1.1 pooka 129 1.1 pooka int 130 1.1 pooka mutex_tryenter(kmutex_t *mtx) 131 1.1 pooka { 132 1.1 pooka UPMTX(mtx); 133 1.1 pooka 134 1.1 pooka if (upm->upm_owner) 135 1.1 pooka return 0; 136 1.1 pooka 137 1.1 pooka upm->upm_owner = curlwp; 138 1.1 pooka return 1; 139 1.1 pooka } 140 1.1 pooka 141 1.1 pooka void 142 1.1 pooka mutex_exit(kmutex_t *mtx) 143 1.1 pooka { 144 1.1 pooka UPMTX(mtx); 145 1.1 pooka 146 1.1 pooka if (upm->upm_wanted) { 147 1.1 pooka rumpuser_cv_signal(upm->upm_rucv); /* CPU is our interlock */ 148 1.1 pooka } 149 1.1 pooka upm->upm_owner = NULL; 150 1.1 pooka } 151 1.1 pooka 152 1.1 pooka void 153 1.1 pooka mutex_spin_exit(kmutex_t *mtx) 154 1.1 pooka { 155 1.1 pooka 156 1.1 pooka mutex_exit(mtx); 157 1.1 pooka } 158 1.1 pooka 159 1.1 pooka int 160 1.1 pooka mutex_owned(kmutex_t *mtx) 161 1.1 pooka { 162 1.1 pooka UPMTX(mtx); 163 1.1 pooka 164 1.1 pooka return upm->upm_owner == curlwp; 165 1.1 pooka } 166 1.1 pooka 167 1.1 pooka struct uprw { 168 1.1 pooka struct lwp *uprw_owner; 169 1.1 pooka int uprw_readers; 170 1.1 pooka uint16_t uprw_rwant; 171 1.1 pooka uint16_t uprw_wwant; 172 1.1 pooka struct rumpuser_cv *uprw_rucv_reader; 173 1.1 pooka struct rumpuser_cv *uprw_rucv_writer; 174 1.1 pooka }; 175 1.1 pooka 176 1.1 pooka #define UPRW(rw) struct uprw *uprw = *(struct uprw **)rw 177 1.1 pooka 178 1.1 pooka /* reader/writer locks */ 179 1.1 pooka 180 1.1 pooka void 181 1.1 pooka rw_init(krwlock_t *rw) 182 1.1 pooka { 183 1.1 pooka struct uprw *uprw; 184 1.1 pooka 185 1.1 pooka CTASSERT(sizeof(krwlock_t) >= sizeof(void *)); 186 1.1 pooka checkncpu(); 187 1.1 pooka 188 1.3 pooka uprw = rump_hypermalloc(sizeof(*uprw), 0, true, "rwinit"); 189 1.1 pooka memset(uprw, 0, sizeof(*uprw)); 190 1.1 pooka rumpuser_cv_init(&uprw->uprw_rucv_reader); 191 1.1 pooka rumpuser_cv_init(&uprw->uprw_rucv_writer); 192 1.1 pooka memcpy(rw, &uprw, sizeof(void *)); 193 1.1 pooka } 194 1.1 pooka 195 1.1 pooka void 196 1.1 pooka rw_destroy(krwlock_t *rw) 197 1.1 pooka { 198 1.1 pooka UPRW(rw); 199 1.1 pooka 200 1.1 pooka rumpuser_cv_destroy(uprw->uprw_rucv_reader); 201 1.1 pooka rumpuser_cv_destroy(uprw->uprw_rucv_writer); 202 1.4 pooka rump_hyperfree(uprw, sizeof(*uprw)); 203 1.1 pooka } 204 1.1 pooka 205 1.1 pooka /* take rwlock. prefer writers over readers (see rw_tryenter and rw_exit) */ 206 1.1 pooka void 207 1.1 pooka rw_enter(krwlock_t *rw, const krw_t op) 208 1.1 pooka { 209 1.1 pooka UPRW(rw); 210 1.1 pooka struct rumpuser_cv *rucv; 211 1.1 pooka uint16_t *wp; 212 1.1 pooka 213 1.1 pooka if (rw_tryenter(rw, op)) 214 1.1 pooka return; 215 1.1 pooka 216 1.1 pooka /* lagpath */ 217 1.1 pooka if (op == RW_READER) { 218 1.1 pooka rucv = uprw->uprw_rucv_reader; 219 1.1 pooka wp = &uprw->uprw_rwant; 220 1.1 pooka } else { 221 1.1 pooka rucv = uprw->uprw_rucv_writer; 222 1.1 pooka wp = &uprw->uprw_wwant; 223 1.1 pooka } 224 1.1 pooka 225 1.1 pooka (*wp)++; 226 1.1 pooka while (!rw_tryenter(rw, op)) { 227 1.1 pooka rump_schedlock_cv_wait(rucv); 228 1.1 pooka } 229 1.1 pooka (*wp)--; 230 1.1 pooka } 231 1.1 pooka 232 1.1 pooka int 233 1.1 pooka rw_tryenter(krwlock_t *rw, const krw_t op) 234 1.1 pooka { 235 1.1 pooka UPRW(rw); 236 1.1 pooka 237 1.1 pooka switch (op) { 238 1.1 pooka case RW_READER: 239 1.1 pooka if (uprw->uprw_owner == NULL && uprw->uprw_wwant == 0) { 240 1.1 pooka uprw->uprw_readers++; 241 1.1 pooka return 1; 242 1.1 pooka } 243 1.1 pooka break; 244 1.1 pooka case RW_WRITER: 245 1.1 pooka if (uprw->uprw_owner == NULL && uprw->uprw_readers == 0) { 246 1.1 pooka uprw->uprw_owner = curlwp; 247 1.1 pooka return 1; 248 1.1 pooka } 249 1.1 pooka break; 250 1.1 pooka } 251 1.1 pooka 252 1.1 pooka return 0; 253 1.1 pooka } 254 1.1 pooka 255 1.1 pooka void 256 1.1 pooka rw_exit(krwlock_t *rw) 257 1.1 pooka { 258 1.1 pooka UPRW(rw); 259 1.1 pooka 260 1.1 pooka if (uprw->uprw_readers > 0) { 261 1.1 pooka uprw->uprw_readers--; 262 1.1 pooka } else { 263 1.1 pooka KASSERT(uprw->uprw_owner == curlwp); 264 1.1 pooka uprw->uprw_owner = NULL; 265 1.1 pooka } 266 1.1 pooka 267 1.1 pooka if (uprw->uprw_wwant) { 268 1.1 pooka rumpuser_cv_signal(uprw->uprw_rucv_writer); 269 1.1 pooka } else if (uprw->uprw_rwant) { 270 1.1 pooka rumpuser_cv_signal(uprw->uprw_rucv_reader); 271 1.1 pooka } 272 1.1 pooka } 273 1.1 pooka 274 1.1 pooka int 275 1.1 pooka rw_tryupgrade(krwlock_t *rw) 276 1.1 pooka { 277 1.1 pooka UPRW(rw); 278 1.1 pooka 279 1.1 pooka if (uprw->uprw_readers == 1 && uprw->uprw_owner == NULL) { 280 1.1 pooka uprw->uprw_readers = 0; 281 1.1 pooka uprw->uprw_owner = curlwp; 282 1.1 pooka return 1; 283 1.1 pooka } else { 284 1.1 pooka return 0; 285 1.1 pooka } 286 1.1 pooka } 287 1.1 pooka 288 1.1 pooka int 289 1.1 pooka rw_write_held(krwlock_t *rw) 290 1.1 pooka { 291 1.1 pooka UPRW(rw); 292 1.1 pooka 293 1.1 pooka return uprw->uprw_owner == curlwp; 294 1.1 pooka } 295 1.1 pooka 296 1.1 pooka int 297 1.1 pooka rw_read_held(krwlock_t *rw) 298 1.1 pooka { 299 1.1 pooka UPRW(rw); 300 1.1 pooka 301 1.1 pooka return uprw->uprw_readers > 0; 302 1.1 pooka } 303 1.1 pooka 304 1.1 pooka int 305 1.1 pooka rw_lock_held(krwlock_t *rw) 306 1.1 pooka { 307 1.1 pooka UPRW(rw); 308 1.1 pooka 309 1.1 pooka return uprw->uprw_owner || uprw->uprw_readers; 310 1.1 pooka } 311 1.1 pooka 312 1.11 ad krw_t 313 1.11 ad rw_lock_op(krwlock_t *rw) 314 1.11 ad { 315 1.11 ad 316 1.11 ad return rw_write_held(rw) ? RW_WRITER : RW_READER; 317 1.11 ad } 318 1.1 pooka 319 1.1 pooka /* 320 1.1 pooka * Condvars are almost the same as in the MP case except that we 321 1.1 pooka * use the scheduler mutex as the pthread interlock instead of the 322 1.1 pooka * mutex associated with the condvar. 323 1.1 pooka */ 324 1.1 pooka 325 1.1 pooka #define RUMPCV(cv) (*(struct rumpuser_cv **)(cv)) 326 1.1 pooka 327 1.1 pooka void 328 1.1 pooka cv_init(kcondvar_t *cv, const char *msg) 329 1.1 pooka { 330 1.1 pooka 331 1.1 pooka CTASSERT(sizeof(kcondvar_t) >= sizeof(void *)); 332 1.1 pooka checkncpu(); 333 1.1 pooka 334 1.1 pooka rumpuser_cv_init((struct rumpuser_cv **)cv); 335 1.1 pooka } 336 1.1 pooka 337 1.1 pooka void 338 1.1 pooka cv_destroy(kcondvar_t *cv) 339 1.1 pooka { 340 1.1 pooka 341 1.1 pooka rumpuser_cv_destroy(RUMPCV(cv)); 342 1.1 pooka } 343 1.1 pooka 344 1.1 pooka void 345 1.1 pooka cv_wait(kcondvar_t *cv, kmutex_t *mtx) 346 1.1 pooka { 347 1.1 pooka #ifdef DIAGNOSTIC 348 1.1 pooka UPMTX(mtx); 349 1.1 pooka KASSERT(upm->upm_owner == curlwp); 350 1.1 pooka 351 1.1 pooka if (rump_threads == 0) 352 1.1 pooka panic("cv_wait without threads"); 353 1.1 pooka #endif 354 1.1 pooka 355 1.1 pooka /* 356 1.1 pooka * NOTE: we must atomically release the *CPU* here, i.e. 357 1.1 pooka * nothing between mutex_exit and entering rumpuser condwait 358 1.1 pooka * may preempt us from the virtual CPU. 359 1.1 pooka */ 360 1.1 pooka mutex_exit(mtx); 361 1.1 pooka rump_schedlock_cv_wait(RUMPCV(cv)); 362 1.1 pooka mutex_enter(mtx); 363 1.1 pooka } 364 1.1 pooka 365 1.1 pooka int 366 1.1 pooka cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx) 367 1.1 pooka { 368 1.1 pooka 369 1.1 pooka cv_wait(cv, mtx); 370 1.1 pooka return 0; 371 1.1 pooka } 372 1.1 pooka 373 1.1 pooka int 374 1.1 pooka cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int ticks) 375 1.1 pooka { 376 1.8 pooka struct timespec ts; 377 1.1 pooka 378 1.1 pooka #ifdef DIAGNOSTIC 379 1.1 pooka UPMTX(mtx); 380 1.1 pooka KASSERT(upm->upm_owner == curlwp); 381 1.1 pooka #endif 382 1.1 pooka 383 1.8 pooka ts.tv_sec = ticks / hz; 384 1.8 pooka ts.tv_nsec = (ticks % hz) * (1000000000/hz); 385 1.1 pooka 386 1.1 pooka if (ticks == 0) { 387 1.1 pooka cv_wait(cv, mtx); 388 1.1 pooka return 0; 389 1.1 pooka } else { 390 1.1 pooka int rv; 391 1.1 pooka mutex_exit(mtx); 392 1.1 pooka rv = rump_schedlock_cv_timedwait(RUMPCV(cv), &ts); 393 1.1 pooka mutex_enter(mtx); 394 1.1 pooka if (rv) 395 1.1 pooka return EWOULDBLOCK; 396 1.1 pooka else 397 1.1 pooka return 0; 398 1.1 pooka } 399 1.1 pooka } 400 1.1 pooka 401 1.1 pooka int 402 1.1 pooka cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mtx, int ticks) 403 1.1 pooka { 404 1.1 pooka 405 1.1 pooka return cv_timedwait(cv, mtx, ticks); 406 1.1 pooka } 407 1.1 pooka 408 1.1 pooka void 409 1.1 pooka cv_signal(kcondvar_t *cv) 410 1.1 pooka { 411 1.1 pooka 412 1.1 pooka /* CPU == interlock */ 413 1.1 pooka rumpuser_cv_signal(RUMPCV(cv)); 414 1.1 pooka } 415 1.1 pooka 416 1.1 pooka void 417 1.1 pooka cv_broadcast(kcondvar_t *cv) 418 1.1 pooka { 419 1.1 pooka 420 1.1 pooka /* CPU == interlock */ 421 1.1 pooka rumpuser_cv_broadcast(RUMPCV(cv)); 422 1.1 pooka } 423 1.1 pooka 424 1.1 pooka bool 425 1.1 pooka cv_has_waiters(kcondvar_t *cv) 426 1.1 pooka { 427 1.9 pooka int n; 428 1.1 pooka 429 1.9 pooka rumpuser_cv_has_waiters(RUMPCV(cv), &n); 430 1.9 pooka 431 1.9 pooka return n > 0; 432 1.1 pooka } 433 1.1 pooka 434 1.1 pooka /* this is not much of an attempt, but ... */ 435 1.1 pooka bool 436 1.1 pooka cv_is_valid(kcondvar_t *cv) 437 1.1 pooka { 438 1.1 pooka 439 1.1 pooka return RUMPCV(cv) != NULL; 440 1.1 pooka } 441