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