xref: /netbsd-src/sys/external/bsd/drm2/include/linux/ww_mutex.h (revision 80d9064ac03cbb6a4174695f0d5b237c8766d3d0)
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