1 /* $NetBSD: ww_mutex.h,v 1.7 2014/09/15 20:24:55 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Taylor R. Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #ifndef _ASM_WW_MUTEX_H_ 33 #define _ASM_WW_MUTEX_H_ 34 35 #include <sys/rbtree.h> 36 37 #include <linux/mutex.h> 38 39 struct ww_class { 40 volatile uint64_t wwc_ticket; 41 }; 42 43 #define DEFINE_WW_CLASS(CLASS) \ 44 struct ww_class CLASS = { \ 45 .wwc_ticket = 0, \ 46 } 47 48 struct ww_acquire_ctx { 49 struct ww_class *wwx_class __diagused; 50 struct lwp *wwx_owner __diagused; 51 uint64_t wwx_ticket; 52 unsigned wwx_acquired; 53 bool wwx_acquire_done; 54 struct rb_node wwx_rb_node; 55 }; 56 57 static inline int 58 ww_acquire_ctx_compare(void *cookie __unused, const void *va, const void *vb) 59 { 60 const struct ww_acquire_ctx *const ctx_a = va; 61 const struct ww_acquire_ctx *const ctx_b = vb; 62 63 if (ctx_a->wwx_ticket < ctx_b->wwx_ticket) 64 return -1; 65 if (ctx_a->wwx_ticket > ctx_b->wwx_ticket) 66 return -1; 67 return 0; 68 } 69 70 static inline int 71 ww_acquire_ctx_compare_key(void *cookie __unused, const void *vn, 72 const void *vk) 73 { 74 const struct ww_acquire_ctx *const ctx = vn; 75 const uint64_t *const ticketp = vk, ticket = *ticketp; 76 77 if (ctx->wwx_ticket < ticket) 78 return -1; 79 if (ctx->wwx_ticket > ticket) 80 return -1; 81 return 0; 82 } 83 84 static const rb_tree_ops_t ww_acquire_ctx_rb_ops = { 85 .rbto_compare_nodes = &ww_acquire_ctx_compare, 86 .rbto_compare_key = &ww_acquire_ctx_compare_key, 87 .rbto_node_offset = offsetof(struct ww_acquire_ctx, wwx_rb_node), 88 .rbto_context = NULL, 89 }; 90 91 static inline void 92 ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *class) 93 { 94 95 ctx->wwx_class = class; 96 ctx->wwx_owner = curlwp; 97 ctx->wwx_ticket = atomic_inc_64_nv(&class->wwc_ticket); 98 ctx->wwx_acquired = 0; 99 ctx->wwx_acquire_done = false; 100 } 101 102 static inline void 103 ww_acquire_done(struct ww_acquire_ctx *ctx) 104 { 105 106 KASSERTMSG((ctx->wwx_owner == curlwp), 107 "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp); 108 109 ctx->wwx_acquire_done = true; 110 } 111 112 static inline void 113 ww_acquire_fini(struct ww_acquire_ctx *ctx) 114 { 115 116 KASSERTMSG((ctx->wwx_owner == curlwp), 117 "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp); 118 KASSERTMSG((ctx->wwx_acquired == 0), "ctx %p still holds %u locks", 119 ctx, ctx->wwx_acquired); 120 121 ctx->wwx_acquired = ~0U; /* Fail if called again. */ 122 ctx->wwx_owner = NULL; 123 } 124 125 struct ww_mutex { 126 kmutex_t wwm_lock; 127 enum ww_mutex_state { 128 WW_UNLOCKED, /* nobody owns it */ 129 WW_OWNED, /* owned by a lwp without a context */ 130 WW_CTX, /* owned by a context */ 131 WW_WANTOWN, /* owned by ctx, waiters w/o ctx waiting */ 132 } wwm_state; 133 union { 134 struct lwp *owner; 135 struct ww_acquire_ctx *ctx; 136 } wwm_u; 137 struct ww_class *wwm_class; 138 struct rb_tree wwm_waiters; 139 kcondvar_t wwm_cv; 140 }; 141 142 static inline void 143 ww_mutex_init(struct ww_mutex *mutex, struct ww_class *class) 144 { 145 146 /* 147 * XXX Apparently Linux takes these with spin locks held. That 148 * strikes me as a bad idea, but so it is... 149 */ 150 mutex_init(&mutex->wwm_lock, MUTEX_DEFAULT, IPL_VM); 151 mutex->wwm_state = WW_UNLOCKED; 152 mutex->wwm_class = class; 153 rb_tree_init(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops); 154 cv_init(&mutex->wwm_cv, "linuxwwm"); 155 } 156 157 static inline void 158 ww_mutex_destroy(struct ww_mutex *mutex) 159 { 160 161 cv_destroy(&mutex->wwm_cv); 162 #if 0 163 rb_tree_destroy(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops); 164 #endif 165 KASSERT(mutex->wwm_state == WW_UNLOCKED); 166 mutex_destroy(&mutex->wwm_lock); 167 } 168 169 /* 170 * XXX WARNING: This returns true if it is locked by ANYONE. Does not 171 * mean `Do I hold this lock?' (answering which really requires an 172 * acquire context). 173 */ 174 static inline bool 175 ww_mutex_is_locked(struct ww_mutex *mutex) 176 { 177 int locked; 178 179 mutex_enter(&mutex->wwm_lock); 180 switch (mutex->wwm_state) { 181 case WW_UNLOCKED: 182 locked = false; 183 break; 184 case WW_OWNED: 185 case WW_CTX: 186 case WW_WANTOWN: 187 locked = true; 188 break; 189 default: 190 panic("wait/wound mutex %p in bad state: %d", mutex, 191 (int)mutex->wwm_state); 192 } 193 mutex_exit(&mutex->wwm_lock); 194 195 return locked; 196 } 197 198 static inline void 199 ww_mutex_state_wait(struct ww_mutex *mutex, enum ww_mutex_state state) 200 { 201 202 KASSERT(mutex->wwm_state == state); 203 do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock); 204 while (mutex->wwm_state == state); 205 } 206 207 static inline int 208 ww_mutex_state_wait_sig(struct ww_mutex *mutex, enum ww_mutex_state state) 209 { 210 int ret; 211 212 KASSERT(mutex->wwm_state == state); 213 do { 214 /* XXX errno NetBSD->Linux */ 215 ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock); 216 if (ret) 217 break; 218 } while (mutex->wwm_state == state); 219 220 return ret; 221 } 222 223 static inline void 224 ww_mutex_lock_wait(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx) 225 { 226 struct ww_acquire_ctx *collision __diagused; 227 228 KASSERT(mutex_owned(&mutex->wwm_lock)); 229 230 KASSERT((mutex->wwm_state == WW_CTX) || 231 (mutex->wwm_state == WW_WANTOWN)); 232 KASSERT(mutex->wwm_u.ctx != ctx); 233 KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class), 234 "ww mutex class mismatch: %p != %p", 235 ctx->wwx_class, mutex->wwm_u.ctx->wwx_class); 236 KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket), 237 "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)", 238 ctx->wwx_ticket, ctx, 239 mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx); 240 241 collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx); 242 KASSERTMSG((collision == ctx), 243 "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)", 244 ctx->wwx_ticket, ctx, collision->wwx_ticket, collision); 245 246 do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock); 247 while (!(((mutex->wwm_state == WW_CTX) || 248 (mutex->wwm_state == WW_WANTOWN)) && 249 (mutex->wwm_u.ctx == ctx))); 250 251 rb_tree_remove_node(&mutex->wwm_waiters, ctx); 252 } 253 254 static inline int 255 ww_mutex_lock_wait_sig(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx) 256 { 257 struct ww_acquire_ctx *collision __diagused; 258 int ret; 259 260 KASSERT(mutex_owned(&mutex->wwm_lock)); 261 262 KASSERT((mutex->wwm_state == WW_CTX) || 263 (mutex->wwm_state == WW_WANTOWN)); 264 KASSERT(mutex->wwm_u.ctx != ctx); 265 KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class), 266 "ww mutex class mismatch: %p != %p", 267 ctx->wwx_class, mutex->wwm_u.ctx->wwx_class); 268 KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket), 269 "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)", 270 ctx->wwx_ticket, ctx, 271 mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx); 272 273 collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx); 274 KASSERTMSG((collision == ctx), 275 "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)", 276 ctx->wwx_ticket, ctx, collision->wwx_ticket, collision); 277 278 do { 279 /* XXX errno NetBSD->Linux */ 280 ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock); 281 if (ret) 282 goto out; 283 } while (!(((mutex->wwm_state == WW_CTX) || 284 (mutex->wwm_state == WW_WANTOWN)) && 285 (mutex->wwm_u.ctx == ctx))); 286 287 out: rb_tree_remove_node(&mutex->wwm_waiters, ctx); 288 return ret; 289 } 290 291 static inline void 292 ww_mutex_lock_noctx(struct ww_mutex *mutex) 293 { 294 295 mutex_enter(&mutex->wwm_lock); 296 retry: switch (mutex->wwm_state) { 297 case WW_UNLOCKED: 298 mutex->wwm_state = WW_OWNED; 299 mutex->wwm_u.owner = curlwp; 300 break; 301 case WW_OWNED: 302 KASSERTMSG((mutex->wwm_u.owner != curlwp), 303 "locking %p against myself: %p", mutex, curlwp); 304 ww_mutex_state_wait(mutex, WW_OWNED); 305 goto retry; 306 case WW_CTX: 307 KASSERT(mutex->wwm_u.ctx != NULL); 308 mutex->wwm_state = WW_WANTOWN; 309 /* FALLTHROUGH */ 310 case WW_WANTOWN: 311 KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp), 312 "locking %p against myself: %p", mutex, curlwp); 313 ww_mutex_state_wait(mutex, WW_WANTOWN); 314 goto retry; 315 default: 316 panic("wait/wound mutex %p in bad state: %d", 317 mutex, (int)mutex->wwm_state); 318 } 319 KASSERT(mutex->wwm_state == WW_OWNED); 320 KASSERT(mutex->wwm_u.owner == curlwp); 321 mutex_exit(&mutex->wwm_lock); 322 } 323 324 static inline int 325 ww_mutex_lock_noctx_sig(struct ww_mutex *mutex) 326 { 327 int ret; 328 329 mutex_enter(&mutex->wwm_lock); 330 retry: switch (mutex->wwm_state) { 331 case WW_UNLOCKED: 332 mutex->wwm_state = WW_OWNED; 333 mutex->wwm_u.owner = curlwp; 334 break; 335 case WW_OWNED: 336 KASSERTMSG((mutex->wwm_u.owner != curlwp), 337 "locking %p against myself: %p", mutex, curlwp); 338 ret = ww_mutex_state_wait_sig(mutex, WW_OWNED); 339 if (ret) 340 goto out; 341 goto retry; 342 case WW_CTX: 343 KASSERT(mutex->wwm_u.ctx != NULL); 344 mutex->wwm_state = WW_WANTOWN; 345 /* FALLTHROUGH */ 346 case WW_WANTOWN: 347 KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp), 348 "locking %p against myself: %p", mutex, curlwp); 349 ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN); 350 if (ret) 351 goto out; 352 goto retry; 353 default: 354 panic("wait/wound mutex %p in bad state: %d", 355 mutex, (int)mutex->wwm_state); 356 } 357 KASSERT(mutex->wwm_state == WW_OWNED); 358 KASSERT(mutex->wwm_u.owner == curlwp); 359 ret = 0; 360 out: mutex_exit(&mutex->wwm_lock); 361 return ret; 362 } 363 364 static inline int 365 ww_mutex_lock(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx) 366 { 367 368 ASSERT_SLEEPABLE(); 369 370 if (ctx == NULL) { 371 ww_mutex_lock_noctx(mutex); 372 return 0; 373 } 374 375 KASSERTMSG((ctx->wwx_owner == curlwp), 376 "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp); 377 KASSERTMSG(!ctx->wwx_acquire_done, 378 "ctx %p done acquiring locks, can't acquire more", ctx); 379 KASSERTMSG((ctx->wwx_acquired != ~0U), 380 "ctx %p finished, can't be used any more", ctx); 381 KASSERTMSG((ctx->wwx_class == mutex->wwm_class), 382 "ctx %p in class %p, mutex %p in class %p", 383 ctx, ctx->wwx_class, mutex, mutex->wwm_class); 384 385 mutex_enter(&mutex->wwm_lock); 386 retry: switch (mutex->wwm_state) { 387 case WW_UNLOCKED: 388 mutex->wwm_state = WW_CTX; 389 mutex->wwm_u.ctx = ctx; 390 goto locked; 391 case WW_OWNED: 392 KASSERTMSG((mutex->wwm_u.owner != curlwp), 393 "locking %p against myself: %p", mutex, curlwp); 394 ww_mutex_state_wait(mutex, WW_OWNED); 395 goto retry; 396 case WW_CTX: 397 break; 398 case WW_WANTOWN: 399 ww_mutex_state_wait(mutex, WW_WANTOWN); 400 goto retry; 401 default: 402 panic("wait/wound mutex %p in bad state: %d", 403 mutex, (int)mutex->wwm_state); 404 } 405 KASSERT(mutex->wwm_state == WW_CTX); 406 KASSERT(mutex->wwm_u.ctx != NULL); 407 KASSERT((mutex->wwm_u.ctx == ctx) || 408 (mutex->wwm_u.ctx->wwx_owner != curlwp)); 409 if (mutex->wwm_u.ctx == ctx) { 410 /* 411 * We already own it. Yes, this can happen correctly 412 * for objects whose locking order is determined by 413 * userland. 414 */ 415 mutex_exit(&mutex->wwm_lock); 416 return -EALREADY; 417 } else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) { 418 /* 419 * Owned by a higher-priority party. Tell the caller 420 * to unlock everything and start over. 421 */ 422 KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class), 423 "ww mutex class mismatch: %p != %p", 424 ctx->wwx_class, mutex->wwm_u.ctx->wwx_class); 425 mutex_exit(&mutex->wwm_lock); 426 return -EDEADLK; 427 } else { 428 /* 429 * Owned by a lower-priority party. Ask that party to 430 * wake us when it is done or it realizes it needs to 431 * back off. 432 */ 433 ww_mutex_lock_wait(mutex, ctx); 434 } 435 locked: ctx->wwx_acquired++; 436 KASSERT((mutex->wwm_state == WW_CTX) || 437 (mutex->wwm_state == WW_WANTOWN)); 438 KASSERT(mutex->wwm_u.ctx == ctx); 439 mutex_exit(&mutex->wwm_lock); 440 return 0; 441 } 442 443 static inline int 444 ww_mutex_lock_interruptible(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx) 445 { 446 int ret; 447 448 ASSERT_SLEEPABLE(); 449 450 if (ctx == NULL) 451 return ww_mutex_lock_noctx_sig(mutex); 452 453 KASSERTMSG((ctx->wwx_owner == curlwp), 454 "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp); 455 KASSERTMSG(!ctx->wwx_acquire_done, 456 "ctx %p done acquiring locks, can't acquire more", ctx); 457 KASSERTMSG((ctx->wwx_acquired != ~0U), 458 "ctx %p finished, can't be used any more", ctx); 459 KASSERTMSG((ctx->wwx_class == mutex->wwm_class), 460 "ctx %p in class %p, mutex %p in class %p", 461 ctx, ctx->wwx_class, mutex, mutex->wwm_class); 462 463 mutex_enter(&mutex->wwm_lock); 464 retry: switch (mutex->wwm_state) { 465 case WW_UNLOCKED: 466 mutex->wwm_state = WW_CTX; 467 mutex->wwm_u.ctx = ctx; 468 goto locked; 469 case WW_OWNED: 470 KASSERTMSG((mutex->wwm_u.owner != curlwp), 471 "locking %p against myself: %p", mutex, curlwp); 472 ret = ww_mutex_state_wait_sig(mutex, WW_OWNED); 473 if (ret) 474 goto out; 475 goto retry; 476 case WW_CTX: 477 break; 478 case WW_WANTOWN: 479 ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN); 480 if (ret) 481 goto out; 482 goto retry; 483 default: 484 panic("wait/wound mutex %p in bad state: %d", 485 mutex, (int)mutex->wwm_state); 486 } 487 KASSERT(mutex->wwm_state == WW_CTX); 488 KASSERT(mutex->wwm_u.ctx != NULL); 489 KASSERT((mutex->wwm_u.ctx == ctx) || 490 (mutex->wwm_u.ctx->wwx_owner != curlwp)); 491 if (mutex->wwm_u.ctx == ctx) { 492 /* 493 * We already own it. Yes, this can happen correctly 494 * for objects whose locking order is determined by 495 * userland. 496 */ 497 mutex_exit(&mutex->wwm_lock); 498 return -EALREADY; 499 } else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) { 500 /* 501 * Owned by a higher-priority party. Tell the caller 502 * to unlock everything and start over. 503 */ 504 KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class), 505 "ww mutex class mismatch: %p != %p", 506 ctx->wwx_class, mutex->wwm_u.ctx->wwx_class); 507 mutex_exit(&mutex->wwm_lock); 508 return -EDEADLK; 509 } else { 510 /* 511 * Owned by a lower-priority party. Ask that party to 512 * wake us when it is done or it realizes it needs to 513 * back off. 514 */ 515 ret = ww_mutex_lock_wait_sig(mutex, ctx); 516 if (ret) 517 goto out; 518 } 519 locked: KASSERT((mutex->wwm_state == WW_CTX) || 520 (mutex->wwm_state == WW_WANTOWN)); 521 KASSERT(mutex->wwm_u.ctx == ctx); 522 ctx->wwx_acquired++; 523 ret = 0; 524 out: mutex_exit(&mutex->wwm_lock); 525 return ret; 526 } 527 528 static inline void 529 ww_mutex_lock_slow(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx) 530 { 531 532 ASSERT_SLEEPABLE(); 533 534 if (ctx == NULL) { 535 ww_mutex_lock_noctx(mutex); 536 return; 537 } 538 539 KASSERTMSG((ctx->wwx_owner == curlwp), 540 "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp); 541 KASSERTMSG(!ctx->wwx_acquire_done, 542 "ctx %p done acquiring locks, can't acquire more", ctx); 543 KASSERTMSG((ctx->wwx_acquired != ~0U), 544 "ctx %p finished, can't be used any more", ctx); 545 KASSERTMSG((ctx->wwx_acquired == 0), 546 "ctx %p still holds %u locks, not allowed in slow path", 547 ctx, ctx->wwx_acquired); 548 KASSERTMSG((ctx->wwx_class == mutex->wwm_class), 549 "ctx %p in class %p, mutex %p in class %p", 550 ctx, ctx->wwx_class, mutex, mutex->wwm_class); 551 552 mutex_enter(&mutex->wwm_lock); 553 retry: switch (mutex->wwm_state) { 554 case WW_UNLOCKED: 555 mutex->wwm_state = WW_CTX; 556 mutex->wwm_u.ctx = ctx; 557 goto locked; 558 case WW_OWNED: 559 KASSERTMSG((mutex->wwm_u.owner != curlwp), 560 "locking %p against myself: %p", mutex, curlwp); 561 ww_mutex_state_wait(mutex, WW_OWNED); 562 goto retry; 563 case WW_CTX: 564 break; 565 case WW_WANTOWN: 566 ww_mutex_state_wait(mutex, WW_WANTOWN); 567 goto retry; 568 default: 569 panic("wait/wound mutex %p in bad state: %d", 570 mutex, (int)mutex->wwm_state); 571 } 572 KASSERT(mutex->wwm_state == WW_CTX); 573 KASSERT(mutex->wwm_u.ctx != NULL); 574 KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp), 575 "locking %p against myself: %p", mutex, curlwp); 576 /* 577 * Owned by another party, of any priority. Ask that party to 578 * wake us when it's done. 579 */ 580 ww_mutex_lock_wait(mutex, ctx); 581 locked: KASSERT((mutex->wwm_state == WW_CTX) || 582 (mutex->wwm_state == WW_WANTOWN)); 583 KASSERT(mutex->wwm_u.ctx == ctx); 584 ctx->wwx_acquired++; 585 mutex_exit(&mutex->wwm_lock); 586 } 587 588 static inline int 589 ww_mutex_lock_slow_interruptible(struct ww_mutex *mutex, 590 struct ww_acquire_ctx *ctx) 591 { 592 int ret; 593 594 ASSERT_SLEEPABLE(); 595 596 if (ctx == NULL) 597 return ww_mutex_lock_noctx_sig(mutex); 598 599 KASSERTMSG((ctx->wwx_owner == curlwp), 600 "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp); 601 KASSERTMSG(!ctx->wwx_acquire_done, 602 "ctx %p done acquiring locks, can't acquire more", ctx); 603 KASSERTMSG((ctx->wwx_acquired != ~0U), 604 "ctx %p finished, can't be used any more", ctx); 605 KASSERTMSG((ctx->wwx_acquired == 0), 606 "ctx %p still holds %u locks, not allowed in slow path", 607 ctx, ctx->wwx_acquired); 608 KASSERTMSG((ctx->wwx_class == mutex->wwm_class), 609 "ctx %p in class %p, mutex %p in class %p", 610 ctx, ctx->wwx_class, mutex, mutex->wwm_class); 611 612 mutex_enter(&mutex->wwm_lock); 613 retry: switch (mutex->wwm_state) { 614 case WW_UNLOCKED: 615 mutex->wwm_state = WW_CTX; 616 mutex->wwm_u.ctx = ctx; 617 goto locked; 618 case WW_OWNED: 619 KASSERTMSG((mutex->wwm_u.owner != curlwp), 620 "locking %p against myself: %p", mutex, curlwp); 621 ret = ww_mutex_state_wait_sig(mutex, WW_OWNED); 622 if (ret) 623 goto out; 624 goto retry; 625 case WW_CTX: 626 break; 627 case WW_WANTOWN: 628 ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN); 629 if (ret) 630 goto out; 631 goto retry; 632 default: 633 panic("wait/wound mutex %p in bad state: %d", 634 mutex, (int)mutex->wwm_state); 635 } 636 KASSERT(mutex->wwm_state == WW_CTX); 637 KASSERT(mutex->wwm_u.ctx != NULL); 638 KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp), 639 "locking %p against myself: %p", mutex, curlwp); 640 /* 641 * Owned by another party, of any priority. Ask that party to 642 * wake us when it's done. 643 */ 644 ret = ww_mutex_lock_wait_sig(mutex, ctx); 645 if (ret) 646 goto out; 647 locked: KASSERT((mutex->wwm_state == WW_CTX) || 648 (mutex->wwm_state == WW_WANTOWN)); 649 KASSERT(mutex->wwm_u.ctx == ctx); 650 ctx->wwx_acquired++; 651 ret = 0; 652 out: mutex_exit(&mutex->wwm_lock); 653 return ret; 654 } 655 656 static inline int 657 ww_mutex_trylock(struct ww_mutex *mutex) 658 { 659 int ret; 660 661 mutex_enter(&mutex->wwm_lock); 662 if (mutex->wwm_state == WW_UNLOCKED) { 663 mutex->wwm_state = WW_OWNED; 664 mutex->wwm_u.owner = curlwp; 665 ret = 1; 666 } else { 667 KASSERTMSG(((mutex->wwm_state != WW_OWNED) || 668 (mutex->wwm_u.owner != curlwp)), 669 "locking %p against myself: %p", mutex, curlwp); 670 KASSERTMSG(((mutex->wwm_state != WW_CTX) || 671 (mutex->wwm_u.ctx->wwx_owner != curlwp)), 672 "locking %p against myself: %p", mutex, curlwp); 673 KASSERTMSG(((mutex->wwm_state != WW_WANTOWN) || 674 (mutex->wwm_u.ctx->wwx_owner != curlwp)), 675 "locking %p against myself: %p", mutex, curlwp); 676 ret = 0; 677 } 678 mutex_exit(&mutex->wwm_lock); 679 680 return ret; 681 } 682 683 static inline void 684 ww_mutex_unlock_release(struct ww_mutex *mutex) 685 { 686 687 KASSERT(mutex_owned(&mutex->wwm_lock)); 688 KASSERT((mutex->wwm_state == WW_CTX) || 689 (mutex->wwm_state == WW_WANTOWN)); 690 KASSERT(mutex->wwm_u.ctx != NULL); 691 KASSERTMSG((mutex->wwm_u.ctx->wwx_owner == curlwp), 692 "ww_mutex %p ctx %p held by %p, not by self (%p)", 693 mutex, mutex->wwm_u.ctx, mutex->wwm_u.ctx->wwx_owner, 694 curlwp); 695 KASSERT(mutex->wwm_u.ctx->wwx_acquired != ~0U); 696 mutex->wwm_u.ctx->wwx_acquired--; 697 mutex->wwm_u.ctx = NULL; 698 } 699 700 static inline void 701 ww_mutex_unlock(struct ww_mutex *mutex) 702 { 703 struct ww_acquire_ctx *ctx; 704 705 mutex_enter(&mutex->wwm_lock); 706 KASSERT(mutex->wwm_state != WW_UNLOCKED); 707 switch (mutex->wwm_state) { 708 case WW_UNLOCKED: 709 panic("unlocking unlocked wait/wound mutex: %p", mutex); 710 case WW_OWNED: 711 /* Let the context lockers fight over it. */ 712 mutex->wwm_u.owner = NULL; 713 mutex->wwm_state = WW_UNLOCKED; 714 break; 715 case WW_CTX: 716 ww_mutex_unlock_release(mutex); 717 /* 718 * If there are any waiters with contexts, grant the 719 * lock to the highest-priority one. Otherwise, just 720 * unlock it. 721 */ 722 if ((ctx = RB_TREE_MIN(&mutex->wwm_waiters)) != NULL) { 723 mutex->wwm_state = WW_CTX; 724 mutex->wwm_u.ctx = ctx; 725 } else { 726 mutex->wwm_state = WW_UNLOCKED; 727 } 728 break; 729 case WW_WANTOWN: 730 ww_mutex_unlock_release(mutex); 731 /* Let the non-context lockers fight over it. */ 732 mutex->wwm_state = WW_UNLOCKED; 733 break; 734 } 735 cv_broadcast(&mutex->wwm_cv); 736 mutex_exit(&mutex->wwm_lock); 737 } 738 739 #endif /* _ASM_WW_MUTEX_H_ */ 740