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