Home | History | Annotate | Line # | Download | only in linux
ww_mutex.h revision 1.4.4.1
      1 /*	$NetBSD: ww_mutex.h,v 1.4.4.1 2014/09/21 18:31:06 snj 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 	uint64_t	wwx_ticket;
     51 	unsigned	wwx_acquired;
     52 	bool		wwx_acquire_done;
     53 	struct rb_node	wwx_rb_node;
     54 };
     55 
     56 static inline int
     57 ww_acquire_ctx_compare(void *cookie __unused, const void *va, const void *vb)
     58 {
     59 	const struct ww_acquire_ctx *const ctx_a = va;
     60 	const struct ww_acquire_ctx *const ctx_b = vb;
     61 
     62 	if (ctx_a->wwx_ticket < ctx_b->wwx_ticket)
     63 		return -1;
     64 	if (ctx_a->wwx_ticket > ctx_b->wwx_ticket)
     65 		return -1;
     66 	return 0;
     67 }
     68 
     69 static inline int
     70 ww_acquire_ctx_compare_key(void *cookie __unused, const void *vn,
     71     const void *vk)
     72 {
     73 	const struct ww_acquire_ctx *const ctx = vn;
     74 	const uint64_t *const ticketp = vk, ticket = *ticketp;
     75 
     76 	if (ctx->wwx_ticket < ticket)
     77 		return -1;
     78 	if (ctx->wwx_ticket > ticket)
     79 		return -1;
     80 	return 0;
     81 }
     82 
     83 static const rb_tree_ops_t ww_acquire_ctx_rb_ops = {
     84 	.rbto_compare_nodes = &ww_acquire_ctx_compare,
     85 	.rbto_compare_key = &ww_acquire_ctx_compare_key,
     86 	.rbto_node_offset = offsetof(struct ww_acquire_ctx, wwx_rb_node),
     87 	.rbto_context = NULL,
     88 };
     89 
     90 static inline void
     91 ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *class)
     92 {
     93 
     94 	ctx->wwx_class = class;
     95 	ctx->wwx_ticket = atomic_inc_64_nv(&class->wwc_ticket);
     96 	ctx->wwx_acquired = 0;
     97 	ctx->wwx_acquire_done = false;
     98 }
     99 
    100 static inline void
    101 ww_acquire_done(struct ww_acquire_ctx *ctx)
    102 {
    103 
    104 	ctx->wwx_acquire_done = true;
    105 }
    106 
    107 static inline void
    108 ww_acquire_fini(struct ww_acquire_ctx *ctx)
    109 {
    110 
    111 	KASSERTMSG((ctx->wwx_acquired == 0), "ctx %p still holds %u locks",
    112 	    ctx, ctx->wwx_acquired);
    113 	ctx->wwx_acquired = ~0U;	/* Fail if called again. */
    114 }
    115 
    116 struct ww_mutex {
    117 	kmutex_t		wwm_lock;
    118 	enum ww_mutex_state {
    119 		WW_UNLOCKED,
    120 		WW_OWNED,
    121 		WW_CTX,
    122 		WW_WANTOWN,
    123 	}			wwm_state;
    124 	union {
    125 		struct lwp		*owner;
    126 		struct ww_acquire_ctx	*ctx;
    127 	}			wwm_u;
    128 	struct ww_class		*wwm_class;
    129 	struct rb_tree		wwm_waiters;
    130 	kcondvar_t		wwm_cv;
    131 };
    132 
    133 static inline void
    134 ww_mutex_init(struct ww_mutex *mutex, struct ww_class *class)
    135 {
    136 
    137 	/*
    138 	 * XXX Apparently Linux takes these with spin locks held.  That
    139 	 * strikes me as a bad idea, but so it is...
    140 	 */
    141 	mutex_init(&mutex->wwm_lock, MUTEX_DEFAULT, IPL_VM);
    142 	mutex->wwm_state = WW_UNLOCKED;
    143 	mutex->wwm_class = class;
    144 	rb_tree_init(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops);
    145 	cv_init(&mutex->wwm_cv, "linuxwwm");
    146 }
    147 
    148 static inline void
    149 ww_mutex_destroy(struct ww_mutex *mutex)
    150 {
    151 
    152 	cv_destroy(&mutex->wwm_cv);
    153 #if 0
    154 	rb_tree_destroy(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops);
    155 #endif
    156 	KASSERT(mutex->wwm_state == WW_UNLOCKED);
    157 	mutex_destroy(&mutex->wwm_lock);
    158 }
    159 
    160 /*
    161  * XXX WARNING: This returns true if it is locked by ANYONE.  Does not
    162  * mean `Do I hold this lock?' (answering which really requires an
    163  * acquire context).
    164  */
    165 static inline bool
    166 ww_mutex_is_locked(struct ww_mutex *mutex)
    167 {
    168 	int locked;
    169 
    170 	mutex_enter(&mutex->wwm_lock);
    171 	switch (mutex->wwm_state) {
    172 	case WW_UNLOCKED:
    173 		locked = false;
    174 		break;
    175 	case WW_OWNED:
    176 	case WW_CTX:
    177 	case WW_WANTOWN:
    178 		locked = true;
    179 		break;
    180 	default:
    181 		panic("wait/wound mutex %p in bad state: %d", mutex,
    182 		    (int)mutex->wwm_state);
    183 	}
    184 	mutex_exit(&mutex->wwm_lock);
    185 
    186 	return locked;
    187 }
    188 
    189 static inline void
    190 ww_mutex_state_wait(struct ww_mutex *mutex, enum ww_mutex_state state)
    191 {
    192 
    193 	KASSERT(mutex->wwm_state == state);
    194 	do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock);
    195 	while (mutex->wwm_state == state);
    196 }
    197 
    198 static inline int
    199 ww_mutex_state_wait_sig(struct ww_mutex *mutex, enum ww_mutex_state state)
    200 {
    201 	int ret;
    202 
    203 	KASSERT(mutex->wwm_state == state);
    204 	do {
    205 		/* XXX errno NetBSD->Linux */
    206 		ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock);
    207 		if (ret)
    208 			break;
    209 	} while (mutex->wwm_state == state);
    210 
    211 	return ret;
    212 }
    213 
    214 static inline void
    215 ww_mutex_lock_wait(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
    216 {
    217 	struct ww_acquire_ctx *collision __diagused;
    218 
    219 	KASSERT(mutex_owned(&mutex->wwm_lock));
    220 
    221 	KASSERT(mutex->wwm_state == WW_CTX);
    222 	KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
    223 	    "ww mutex class mismatch: %p != %p",
    224 	    ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
    225 	KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket),
    226 	    "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
    227 	    ctx->wwx_ticket, ctx,
    228 	    mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx);
    229 
    230 	collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx);
    231 	KASSERTMSG((collision == ctx),
    232 	    "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
    233 	    ctx->wwx_ticket, ctx, collision->wwx_ticket, collision);
    234 
    235 	do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock);
    236 	while (!((mutex->wwm_state == WW_CTX) && (mutex->wwm_u.ctx == ctx)));
    237 
    238 	rb_tree_remove_node(&mutex->wwm_waiters, ctx);
    239 }
    240 
    241 static inline int
    242 ww_mutex_lock_wait_sig(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
    243 {
    244 	struct ww_acquire_ctx *collision __diagused;
    245 	int ret;
    246 
    247 	KASSERT(mutex_owned(&mutex->wwm_lock));
    248 
    249 	KASSERT(mutex->wwm_state == WW_CTX);
    250 	KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
    251 	    "ww mutex class mismatch: %p != %p",
    252 	    ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
    253 	KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket),
    254 	    "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
    255 	    ctx->wwx_ticket, ctx,
    256 	    mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx);
    257 
    258 	collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx);
    259 	KASSERTMSG((collision == ctx),
    260 	    "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
    261 	    ctx->wwx_ticket, ctx, collision->wwx_ticket, collision);
    262 
    263 	do {
    264 		/* XXX errno NetBSD->Linux */
    265 		ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock);
    266 		if (ret)
    267 			goto out;
    268 	} while (!((mutex->wwm_state == WW_CTX) && (mutex->wwm_u.ctx == ctx)));
    269 
    270 out:	rb_tree_remove_node(&mutex->wwm_waiters, ctx);
    271 	return ret;
    272 }
    273 
    274 static inline void
    275 ww_mutex_lock_noctx(struct ww_mutex *mutex)
    276 {
    277 
    278 	mutex_enter(&mutex->wwm_lock);
    279 retry:	switch (mutex->wwm_state) {
    280 	case WW_UNLOCKED:
    281 		mutex->wwm_state = WW_OWNED;
    282 		mutex->wwm_u.owner = curlwp;
    283 		break;
    284 	case WW_OWNED:
    285 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
    286 		    "locking against myself: %p", curlwp);
    287 		ww_mutex_state_wait(mutex, WW_OWNED);
    288 		goto retry;
    289 	case WW_CTX:
    290 		KASSERT(mutex->wwm_u.ctx != NULL);
    291 		mutex->wwm_state = WW_WANTOWN;
    292 	case WW_WANTOWN:
    293 		ww_mutex_state_wait(mutex, WW_WANTOWN);
    294 		goto retry;
    295 	default:
    296 		panic("wait/wound mutex %p in bad state: %d",
    297 		    mutex, (int)mutex->wwm_state);
    298 	}
    299 	KASSERT(mutex->wwm_state == WW_OWNED);
    300 	KASSERT(mutex->wwm_u.owner == curlwp);
    301 	mutex_exit(&mutex->wwm_lock);
    302 }
    303 
    304 static inline int
    305 ww_mutex_lock_noctx_sig(struct ww_mutex *mutex)
    306 {
    307 	int ret;
    308 
    309 	mutex_enter(&mutex->wwm_lock);
    310 retry:	switch (mutex->wwm_state) {
    311 	case WW_UNLOCKED:
    312 		mutex->wwm_state = WW_OWNED;
    313 		mutex->wwm_u.owner = curlwp;
    314 		break;
    315 	case WW_OWNED:
    316 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
    317 		    "locking against myself: %p", curlwp);
    318 		ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
    319 		if (ret)
    320 			goto out;
    321 		goto retry;
    322 	case WW_CTX:
    323 		KASSERT(mutex->wwm_u.ctx != NULL);
    324 		mutex->wwm_state = WW_WANTOWN;
    325 	case WW_WANTOWN:
    326 		ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
    327 		if (ret)
    328 			goto out;
    329 		goto retry;
    330 	default:
    331 		panic("wait/wound mutex %p in bad state: %d",
    332 		    mutex, (int)mutex->wwm_state);
    333 	}
    334 	KASSERT(mutex->wwm_state == WW_OWNED);
    335 	KASSERT(mutex->wwm_u.owner == curlwp);
    336 	ret = 0;
    337 out:	mutex_exit(&mutex->wwm_lock);
    338 	return ret;
    339 }
    340 
    341 static inline int
    342 ww_mutex_lock(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
    343 {
    344 
    345 	ASSERT_SLEEPABLE();
    346 
    347 	if (ctx == NULL) {
    348 		ww_mutex_lock_noctx(mutex);
    349 		return 0;
    350 	}
    351 
    352 	KASSERT(!ctx->wwx_acquire_done);
    353 
    354 	mutex_enter(&mutex->wwm_lock);
    355 retry:	switch (mutex->wwm_state) {
    356 	case WW_UNLOCKED:
    357 		mutex->wwm_state = WW_CTX;
    358 		mutex->wwm_u.ctx = ctx;
    359 		goto locked;
    360 	case WW_OWNED:
    361 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
    362 		    "locking against myself: %p", curlwp);
    363 		ww_mutex_state_wait(mutex, WW_OWNED);
    364 		goto retry;
    365 	case WW_CTX:
    366 		break;
    367 	case WW_WANTOWN:
    368 		ww_mutex_state_wait(mutex, WW_WANTOWN);
    369 		goto retry;
    370 	default:
    371 		panic("wait/wound mutex %p in bad state: %d",
    372 		    mutex, (int)mutex->wwm_state);
    373 	}
    374 	KASSERT(mutex->wwm_state == WW_CTX);
    375 	KASSERT(mutex->wwm_u.ctx != NULL);
    376 	if (mutex->wwm_u.ctx == ctx) {
    377 		/*
    378 		 * We already own it.  Yes, this can happen correctly
    379 		 * for objects whose locking order is determined by
    380 		 * userland.
    381 		 */
    382 		mutex_exit(&mutex->wwm_lock);
    383 		return -EALREADY;
    384 	} else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
    385 		/*
    386 		 * Owned by a higher-priority party.  Tell the caller
    387 		 * to unlock everything and start over.
    388 		 */
    389 		KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
    390 		    "ww mutex class mismatch: %p != %p",
    391 		    ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
    392 		mutex_exit(&mutex->wwm_lock);
    393 		return -EDEADLK;
    394 	} else {
    395 		/*
    396 		 * Owned by a lower-priority party.  Ask that party to
    397 		 * wake us when it is done or it realizes it needs to
    398 		 * back off.
    399 		 */
    400 		ww_mutex_lock_wait(mutex, ctx);
    401 	}
    402 locked:	ctx->wwx_acquired++;
    403 	KASSERT(mutex->wwm_state == WW_CTX);
    404 	KASSERT(mutex->wwm_u.ctx == ctx);
    405 	mutex_exit(&mutex->wwm_lock);
    406 	return 0;
    407 }
    408 
    409 static inline int
    410 ww_mutex_lock_interruptible(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
    411 {
    412 	int ret;
    413 
    414 	ASSERT_SLEEPABLE();
    415 
    416 	if (ctx == NULL)
    417 		return ww_mutex_lock_noctx_sig(mutex);
    418 
    419 	KASSERT(!ctx->wwx_acquire_done);
    420 
    421 	mutex_enter(&mutex->wwm_lock);
    422 retry:	switch (mutex->wwm_state) {
    423 	case WW_UNLOCKED:
    424 		mutex->wwm_state = WW_CTX;
    425 		mutex->wwm_u.ctx = ctx;
    426 		goto locked;
    427 	case WW_OWNED:
    428 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
    429 		    "locking against myself: %p", curlwp);
    430 		ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
    431 		if (ret)
    432 			goto out;
    433 		goto retry;
    434 	case WW_CTX:
    435 		break;
    436 	case WW_WANTOWN:
    437 		ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
    438 		if (ret)
    439 			goto out;
    440 		goto retry;
    441 	default:
    442 		panic("wait/wound mutex %p in bad state: %d",
    443 		    mutex, (int)mutex->wwm_state);
    444 	}
    445 	KASSERT(mutex->wwm_state == WW_CTX);
    446 	KASSERT(mutex->wwm_u.ctx != NULL);
    447 	if (mutex->wwm_u.ctx == ctx) {
    448 		/*
    449 		 * We already own it.  Yes, this can happen correctly
    450 		 * for objects whose locking order is determined by
    451 		 * userland.
    452 		 */
    453 		mutex_exit(&mutex->wwm_lock);
    454 		return -EALREADY;
    455 	} else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
    456 		/*
    457 		 * Owned by a higher-priority party.  Tell the caller
    458 		 * to unlock everything and start over.
    459 		 */
    460 		KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
    461 		    "ww mutex class mismatch: %p != %p",
    462 		    ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
    463 		mutex_exit(&mutex->wwm_lock);
    464 		return -EDEADLK;
    465 	} else {
    466 		/*
    467 		 * Owned by a lower-priority party.  Ask that party to
    468 		 * wake us when it is done or it realizes it needs to
    469 		 * back off.
    470 		 */
    471 		ret = ww_mutex_lock_wait_sig(mutex, ctx);
    472 		if (ret)
    473 			goto out;
    474 	}
    475 locked:	KASSERT(mutex->wwm_state == WW_CTX);
    476 	KASSERT(mutex->wwm_u.ctx == ctx);
    477 	ctx->wwx_acquired++;
    478 	ret = 0;
    479 out:	mutex_exit(&mutex->wwm_lock);
    480 	return ret;
    481 }
    482 
    483 static inline void
    484 ww_mutex_lock_slow(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
    485 {
    486 
    487 	ASSERT_SLEEPABLE();
    488 
    489 	if (ctx == NULL) {
    490 		ww_mutex_lock_noctx(mutex);
    491 		return;
    492 	}
    493 
    494 	KASSERT(!ctx->wwx_acquire_done);
    495 
    496 	mutex_enter(&mutex->wwm_lock);
    497 retry:	switch (mutex->wwm_state) {
    498 	case WW_UNLOCKED:
    499 		mutex->wwm_state = WW_CTX;
    500 		mutex->wwm_u.ctx = ctx;
    501 		goto locked;
    502 	case WW_OWNED:
    503 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
    504 		    "locking against myself: %p", curlwp);
    505 		ww_mutex_state_wait(mutex, WW_OWNED);
    506 		goto retry;
    507 	case WW_CTX:
    508 		break;
    509 	case WW_WANTOWN:
    510 		ww_mutex_state_wait(mutex, WW_WANTOWN);
    511 		goto retry;
    512 	default:
    513 		panic("wait/wound mutex %p in bad state: %d",
    514 		    mutex, (int)mutex->wwm_state);
    515 	}
    516 	KASSERT(mutex->wwm_state == WW_CTX);
    517 	KASSERT(mutex->wwm_u.ctx != NULL);
    518 	/*
    519 	 * Owned by another party, of any priority.  Ask that party to
    520 	 * wake us when it's done.
    521 	 */
    522 	ww_mutex_lock_wait(mutex, ctx);
    523 locked:	KASSERT(mutex->wwm_state == WW_CTX);
    524 	KASSERT(mutex->wwm_u.ctx == ctx);
    525 	ctx->wwx_acquired++;
    526 	mutex_exit(&mutex->wwm_lock);
    527 }
    528 
    529 static inline int
    530 ww_mutex_lock_slow_interruptible(struct ww_mutex *mutex,
    531     struct ww_acquire_ctx *ctx)
    532 {
    533 	int ret;
    534 
    535 	ASSERT_SLEEPABLE();
    536 
    537 	if (ctx == NULL)
    538 		return ww_mutex_lock_noctx_sig(mutex);
    539 
    540 	KASSERT(!ctx->wwx_acquire_done);
    541 
    542 	mutex_enter(&mutex->wwm_lock);
    543 retry:	switch (mutex->wwm_state) {
    544 	case WW_UNLOCKED:
    545 		mutex->wwm_state = WW_CTX;
    546 		mutex->wwm_u.ctx = ctx;
    547 		goto locked;
    548 	case WW_OWNED:
    549 		KASSERTMSG((mutex->wwm_u.owner != curlwp),
    550 		    "locking against myself: %p", curlwp);
    551 		ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
    552 		if (ret)
    553 			goto out;
    554 		goto retry;
    555 	case WW_CTX:
    556 		break;
    557 	case WW_WANTOWN:
    558 		ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
    559 		if (ret)
    560 			goto out;
    561 		goto retry;
    562 	default:
    563 		panic("wait/wound mutex %p in bad state: %d",
    564 		    mutex, (int)mutex->wwm_state);
    565 	}
    566 	KASSERT(mutex->wwm_state == WW_CTX);
    567 	KASSERT(mutex->wwm_u.ctx != NULL);
    568 	/*
    569 	 * Owned by another party, of any priority.  Ask that party to
    570 	 * wake us when it's done.
    571 	 */
    572 	ret = ww_mutex_lock_wait_sig(mutex, ctx);
    573 	if (ret)
    574 		goto out;
    575 locked:	KASSERT(mutex->wwm_state == WW_CTX);
    576 	KASSERT(mutex->wwm_u.ctx == ctx);
    577 	ctx->wwx_acquired++;
    578 	ret = 0;
    579 out:	mutex_exit(&mutex->wwm_lock);
    580 	return ret;
    581 }
    582 
    583 static inline int
    584 ww_mutex_trylock(struct ww_mutex *mutex)
    585 {
    586 	int ret;
    587 
    588 	mutex_enter(&mutex->wwm_lock);
    589 	if (mutex->wwm_state == WW_UNLOCKED) {
    590 		mutex->wwm_state = WW_OWNED;
    591 		mutex->wwm_u.owner = curlwp;
    592 		ret = 1;
    593 	} else {
    594 		ret = 0;
    595 	}
    596 	mutex_exit(&mutex->wwm_lock);
    597 
    598 	return ret;
    599 }
    600 
    601 static inline void
    602 ww_mutex_unlock(struct ww_mutex *mutex)
    603 {
    604 	struct ww_acquire_ctx *ctx;
    605 
    606 	mutex_enter(&mutex->wwm_lock);
    607 	KASSERT(mutex->wwm_state != WW_UNLOCKED);
    608 	switch (mutex->wwm_state) {
    609 	case WW_UNLOCKED:
    610 		panic("unlocking unlocked wait/wound mutex: %p", mutex);
    611 	case WW_OWNED:
    612 		/* Let the context lockers fight over it.  */
    613 		mutex->wwm_u.owner = NULL;
    614 		mutex->wwm_state = WW_UNLOCKED;
    615 		break;
    616 	case WW_CTX:
    617 		KASSERT(mutex->wwm_u.ctx != NULL);
    618 		mutex->wwm_u.ctx->wwx_acquired--;
    619 		mutex->wwm_u.ctx = NULL;
    620 		/*
    621 		 * If there are any waiters with contexts, grant the
    622 		 * lock to the highest-priority one.  Otherwise, just
    623 		 * unlock it.
    624 		 */
    625 		if ((ctx = RB_TREE_MIN(&mutex->wwm_waiters)) != NULL) {
    626 			mutex->wwm_state = WW_CTX;
    627 			mutex->wwm_u.ctx = ctx;
    628 		} else {
    629 			mutex->wwm_state = WW_UNLOCKED;
    630 		}
    631 		break;
    632 	case WW_WANTOWN:
    633 		/* Let the non-context lockers fight over it.  */
    634 		mutex->wwm_state = WW_UNLOCKED;
    635 		break;
    636 	}
    637 	cv_broadcast(&mutex->wwm_cv);
    638 	mutex_exit(&mutex->wwm_lock);
    639 }
    640 
    641 #endif  /* _ASM_WW_MUTEX_H_ */
    642