Module Name: src
Committed By: riastradh
Date: Thu Jan 8 23:35:47 UTC 2015
Modified Files:
src/sys/external/bsd/drm2/include/linux: ww_mutex.h
src/sys/external/bsd/drm2/linux: files.drmkms_linux
src/sys/modules/drmkms_linux: Makefile
Added Files:
src/sys/external/bsd/drm2/linux: linux_ww_mutex.c
Log Message:
Move Linux ww_mutex code into a .c file where it belongs.
To generate a diff of this commit:
cvs rdiff -u -r1.9 -r1.10 src/sys/external/bsd/drm2/include/linux/ww_mutex.h
cvs rdiff -u -r1.7 -r1.8 src/sys/external/bsd/drm2/linux/files.drmkms_linux
cvs rdiff -u -r0 -r1.1 src/sys/external/bsd/drm2/linux/linux_ww_mutex.c
cvs rdiff -u -r1.5 -r1.6 src/sys/modules/drmkms_linux/Makefile
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/external/bsd/drm2/include/linux/ww_mutex.h
diff -u src/sys/external/bsd/drm2/include/linux/ww_mutex.h:1.9 src/sys/external/bsd/drm2/include/linux/ww_mutex.h:1.10
--- src/sys/external/bsd/drm2/include/linux/ww_mutex.h:1.9 Thu Jan 1 01:15:42 2015
+++ src/sys/external/bsd/drm2/include/linux/ww_mutex.h Thu Jan 8 23:35:47 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: ww_mutex.h,v 1.9 2015/01/01 01:15:42 mrg Exp $ */
+/* $NetBSD: ww_mutex.h,v 1.10 2015/01/08 23:35:47 riastradh Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -32,10 +32,11 @@
#ifndef _ASM_WW_MUTEX_H_
#define _ASM_WW_MUTEX_H_
+#include <sys/types.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
#include <sys/rbtree.h>
-#include <linux/mutex.h>
-
struct ww_class {
volatile uint64_t wwc_ticket;
};
@@ -54,74 +55,6 @@ struct ww_acquire_ctx {
struct rb_node wwx_rb_node;
};
-static inline int
-ww_acquire_ctx_compare(void *cookie __unused, const void *va, const void *vb)
-{
- const struct ww_acquire_ctx *const ctx_a = va;
- const struct ww_acquire_ctx *const ctx_b = vb;
-
- if (ctx_a->wwx_ticket < ctx_b->wwx_ticket)
- return -1;
- if (ctx_a->wwx_ticket > ctx_b->wwx_ticket)
- return -1;
- return 0;
-}
-
-static inline int
-ww_acquire_ctx_compare_key(void *cookie __unused, const void *vn,
- const void *vk)
-{
- const struct ww_acquire_ctx *const ctx = vn;
- const uint64_t *const ticketp = vk, ticket = *ticketp;
-
- if (ctx->wwx_ticket < ticket)
- return -1;
- if (ctx->wwx_ticket > ticket)
- return -1;
- return 0;
-}
-
-static const rb_tree_ops_t ww_acquire_ctx_rb_ops = {
- .rbto_compare_nodes = &ww_acquire_ctx_compare,
- .rbto_compare_key = &ww_acquire_ctx_compare_key,
- .rbto_node_offset = offsetof(struct ww_acquire_ctx, wwx_rb_node),
- .rbto_context = NULL,
-};
-
-static inline void
-ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *class)
-{
-
- ctx->wwx_class = class;
- ctx->wwx_owner = curlwp;
- ctx->wwx_ticket = atomic_inc_64_nv(&class->wwc_ticket);
- ctx->wwx_acquired = 0;
- ctx->wwx_acquire_done = false;
-}
-
-static inline void
-ww_acquire_done(struct ww_acquire_ctx *ctx)
-{
-
- KASSERTMSG((ctx->wwx_owner == curlwp),
- "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
-
- ctx->wwx_acquire_done = true;
-}
-
-static inline void
-ww_acquire_fini(struct ww_acquire_ctx *ctx)
-{
-
- KASSERTMSG((ctx->wwx_owner == curlwp),
- "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
- KASSERTMSG((ctx->wwx_acquired == 0), "ctx %p still holds %u locks",
- ctx, ctx->wwx_acquired);
-
- ctx->wwx_acquired = ~0U; /* Fail if called again. */
- ctx->wwx_owner = NULL;
-}
-
struct ww_mutex {
kmutex_t wwm_lock;
enum ww_mutex_state {
@@ -139,601 +72,41 @@ struct ww_mutex {
kcondvar_t wwm_cv;
};
-static inline void
-ww_mutex_init(struct ww_mutex *mutex, struct ww_class *class)
-{
-
- /*
- * XXX Apparently Linux takes these with spin locks held. That
- * strikes me as a bad idea, but so it is...
- */
- mutex_init(&mutex->wwm_lock, MUTEX_DEFAULT, IPL_VM);
- mutex->wwm_state = WW_UNLOCKED;
- mutex->wwm_class = class;
- rb_tree_init(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops);
- cv_init(&mutex->wwm_cv, "linuxwwm");
-}
-
-static inline void
-ww_mutex_destroy(struct ww_mutex *mutex)
-{
-
- cv_destroy(&mutex->wwm_cv);
-#if 0
- rb_tree_destroy(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops);
-#endif
- KASSERT(mutex->wwm_state == WW_UNLOCKED);
- mutex_destroy(&mutex->wwm_lock);
-}
+/* XXX Make the nm output a little more greppable... */
+#define ww_acquire_done linux_ww_acquire_done
+#define ww_acquire_fini linux_ww_acquire_fini
+#define ww_acquire_init linux_ww_acquire_init
+#define ww_mutex_destroy linux_ww_mutex_destroy
+#define ww_mutex_init linux_ww_mutex_init
+#define ww_mutex_is_locked linux_ww_mutex_is_locked
+#define ww_mutex_lock linux_ww_mutex_lock
+#define ww_mutex_lock_interruptible linux_ww_mutex_lock_interruptible
+#define ww_mutex_lock_slow linux_ww_mutex_lock_slow
+#define ww_mutex_lock_slow_interruptible linux_ww_mutex_lock_slow_interruptible
+#define ww_mutex_trylock linux_ww_mutex_trylock
+#define ww_mutex_unlock linux_ww_mutex_unlock
+
+void ww_acquire_init(struct ww_acquire_ctx *, struct ww_class *);
+void ww_acquire_done(struct ww_acquire_ctx *);
+void ww_acquire_fini(struct ww_acquire_ctx *);
+
+void ww_mutex_init(struct ww_mutex *, struct ww_class *);
+void ww_mutex_destroy(struct ww_mutex *);
/*
- * XXX WARNING: This returns true if it is locked by ANYONE. Does not
- * mean `Do I hold this lock?' (answering which really requires an
- * acquire context).
+ * WARNING: ww_mutex_is_locked returns true if it is locked by ANYONE.
+ * Does NOT mean `Do I hold this lock?' (answering which really
+ * requires an acquire context).
*/
-static inline bool
-ww_mutex_is_locked(struct ww_mutex *mutex)
-{
- int locked;
-
- mutex_enter(&mutex->wwm_lock);
- switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- locked = false;
- break;
- case WW_OWNED:
- case WW_CTX:
- case WW_WANTOWN:
- locked = true;
- break;
- default:
- panic("wait/wound mutex %p in bad state: %d", mutex,
- (int)mutex->wwm_state);
- }
- mutex_exit(&mutex->wwm_lock);
+bool ww_mutex_is_locked(struct ww_mutex *);
- return locked;
-}
-
-static inline void
-ww_mutex_state_wait(struct ww_mutex *mutex, enum ww_mutex_state state)
-{
-
- KASSERT(mutex->wwm_state == state);
- do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock);
- while (mutex->wwm_state == state);
-}
-
-static inline int
-ww_mutex_state_wait_sig(struct ww_mutex *mutex, enum ww_mutex_state state)
-{
- int ret;
-
- KASSERT(mutex->wwm_state == state);
- do {
- /* XXX errno NetBSD->Linux */
- ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock);
- if (ret)
- break;
- } while (mutex->wwm_state == state);
-
- return ret;
-}
-
-static inline void
-ww_mutex_lock_wait(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
-{
- struct ww_acquire_ctx *collision __diagused;
-
- KASSERT(mutex_owned(&mutex->wwm_lock));
-
- KASSERT((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN));
- KASSERT(mutex->wwm_u.ctx != ctx);
- KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
- "ww mutex class mismatch: %p != %p",
- ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
- KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket),
- "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
- ctx->wwx_ticket, ctx,
- mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx);
-
- collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx);
- KASSERTMSG((collision == ctx),
- "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
- ctx->wwx_ticket, ctx, collision->wwx_ticket, collision);
-
- do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock);
- while (!(((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN)) &&
- (mutex->wwm_u.ctx == ctx)));
-
- rb_tree_remove_node(&mutex->wwm_waiters, ctx);
-}
-
-static inline int
-ww_mutex_lock_wait_sig(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
-{
- struct ww_acquire_ctx *collision __diagused;
- int ret;
-
- KASSERT(mutex_owned(&mutex->wwm_lock));
-
- KASSERT((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN));
- KASSERT(mutex->wwm_u.ctx != ctx);
- KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
- "ww mutex class mismatch: %p != %p",
- ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
- KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket),
- "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
- ctx->wwx_ticket, ctx,
- mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx);
-
- collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx);
- KASSERTMSG((collision == ctx),
- "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
- ctx->wwx_ticket, ctx, collision->wwx_ticket, collision);
-
- do {
- /* XXX errno NetBSD->Linux */
- ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock);
- if (ret)
- goto out;
- } while (!(((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN)) &&
- (mutex->wwm_u.ctx == ctx)));
-
-out: rb_tree_remove_node(&mutex->wwm_waiters, ctx);
- return ret;
-}
-
-static inline void
-ww_mutex_lock_noctx(struct ww_mutex *mutex)
-{
-
- mutex_enter(&mutex->wwm_lock);
-retry: switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- mutex->wwm_state = WW_OWNED;
- mutex->wwm_u.owner = curlwp;
- break;
- case WW_OWNED:
- KASSERTMSG((mutex->wwm_u.owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ww_mutex_state_wait(mutex, WW_OWNED);
- goto retry;
- case WW_CTX:
- KASSERT(mutex->wwm_u.ctx != NULL);
- mutex->wwm_state = WW_WANTOWN;
- /* FALLTHROUGH */
- case WW_WANTOWN:
- KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ww_mutex_state_wait(mutex, WW_WANTOWN);
- goto retry;
- default:
- panic("wait/wound mutex %p in bad state: %d",
- mutex, (int)mutex->wwm_state);
- }
- KASSERT(mutex->wwm_state == WW_OWNED);
- KASSERT(mutex->wwm_u.owner == curlwp);
- mutex_exit(&mutex->wwm_lock);
-}
-
-static inline int
-ww_mutex_lock_noctx_sig(struct ww_mutex *mutex)
-{
- int ret;
-
- mutex_enter(&mutex->wwm_lock);
-retry: switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- mutex->wwm_state = WW_OWNED;
- mutex->wwm_u.owner = curlwp;
- break;
- case WW_OWNED:
- KASSERTMSG((mutex->wwm_u.owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
- if (ret)
- goto out;
- goto retry;
- case WW_CTX:
- KASSERT(mutex->wwm_u.ctx != NULL);
- mutex->wwm_state = WW_WANTOWN;
- /* FALLTHROUGH */
- case WW_WANTOWN:
- KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
- if (ret)
- goto out;
- goto retry;
- default:
- panic("wait/wound mutex %p in bad state: %d",
- mutex, (int)mutex->wwm_state);
- }
- KASSERT(mutex->wwm_state == WW_OWNED);
- KASSERT(mutex->wwm_u.owner == curlwp);
- ret = 0;
-out: mutex_exit(&mutex->wwm_lock);
- return ret;
-}
-
-static inline int
-ww_mutex_lock(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
-{
-
- ASSERT_SLEEPABLE();
-
- if (ctx == NULL) {
- ww_mutex_lock_noctx(mutex);
- return 0;
- }
-
- KASSERTMSG((ctx->wwx_owner == curlwp),
- "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
- KASSERTMSG(!ctx->wwx_acquire_done,
- "ctx %p done acquiring locks, can't acquire more", ctx);
- KASSERTMSG((ctx->wwx_acquired != ~0U),
- "ctx %p finished, can't be used any more", ctx);
- KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
- "ctx %p in class %p, mutex %p in class %p",
- ctx, ctx->wwx_class, mutex, mutex->wwm_class);
-
- mutex_enter(&mutex->wwm_lock);
-retry: switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- mutex->wwm_state = WW_CTX;
- mutex->wwm_u.ctx = ctx;
- goto locked;
- case WW_OWNED:
- KASSERTMSG((mutex->wwm_u.owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ww_mutex_state_wait(mutex, WW_OWNED);
- goto retry;
- case WW_CTX:
- break;
- case WW_WANTOWN:
- ww_mutex_state_wait(mutex, WW_WANTOWN);
- goto retry;
- default:
- panic("wait/wound mutex %p in bad state: %d",
- mutex, (int)mutex->wwm_state);
- }
- KASSERT(mutex->wwm_state == WW_CTX);
- KASSERT(mutex->wwm_u.ctx != NULL);
- KASSERT((mutex->wwm_u.ctx == ctx) ||
- (mutex->wwm_u.ctx->wwx_owner != curlwp));
- if (mutex->wwm_u.ctx == ctx) {
- /*
- * We already own it. Yes, this can happen correctly
- * for objects whose locking order is determined by
- * userland.
- */
- mutex_exit(&mutex->wwm_lock);
- return -EALREADY;
- } else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
- /*
- * Owned by a higher-priority party. Tell the caller
- * to unlock everything and start over.
- */
- KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
- "ww mutex class mismatch: %p != %p",
- ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
- mutex_exit(&mutex->wwm_lock);
- return -EDEADLK;
- } else {
- /*
- * Owned by a lower-priority party. Ask that party to
- * wake us when it is done or it realizes it needs to
- * back off.
- */
- ww_mutex_lock_wait(mutex, ctx);
- }
-locked: ctx->wwx_acquired++;
- KASSERT((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN));
- KASSERT(mutex->wwm_u.ctx == ctx);
- mutex_exit(&mutex->wwm_lock);
- return 0;
-}
-
-static inline int
-ww_mutex_lock_interruptible(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
-{
- int ret;
-
- ASSERT_SLEEPABLE();
-
- if (ctx == NULL)
- return ww_mutex_lock_noctx_sig(mutex);
-
- KASSERTMSG((ctx->wwx_owner == curlwp),
- "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
- KASSERTMSG(!ctx->wwx_acquire_done,
- "ctx %p done acquiring locks, can't acquire more", ctx);
- KASSERTMSG((ctx->wwx_acquired != ~0U),
- "ctx %p finished, can't be used any more", ctx);
- KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
- "ctx %p in class %p, mutex %p in class %p",
- ctx, ctx->wwx_class, mutex, mutex->wwm_class);
-
- mutex_enter(&mutex->wwm_lock);
-retry: switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- mutex->wwm_state = WW_CTX;
- mutex->wwm_u.ctx = ctx;
- goto locked;
- case WW_OWNED:
- KASSERTMSG((mutex->wwm_u.owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
- if (ret)
- goto out;
- goto retry;
- case WW_CTX:
- break;
- case WW_WANTOWN:
- ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
- if (ret)
- goto out;
- goto retry;
- default:
- panic("wait/wound mutex %p in bad state: %d",
- mutex, (int)mutex->wwm_state);
- }
- KASSERT(mutex->wwm_state == WW_CTX);
- KASSERT(mutex->wwm_u.ctx != NULL);
- KASSERT((mutex->wwm_u.ctx == ctx) ||
- (mutex->wwm_u.ctx->wwx_owner != curlwp));
- if (mutex->wwm_u.ctx == ctx) {
- /*
- * We already own it. Yes, this can happen correctly
- * for objects whose locking order is determined by
- * userland.
- */
- mutex_exit(&mutex->wwm_lock);
- return -EALREADY;
- } else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
- /*
- * Owned by a higher-priority party. Tell the caller
- * to unlock everything and start over.
- */
- KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
- "ww mutex class mismatch: %p != %p",
- ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
- mutex_exit(&mutex->wwm_lock);
- return -EDEADLK;
- } else {
- /*
- * Owned by a lower-priority party. Ask that party to
- * wake us when it is done or it realizes it needs to
- * back off.
- */
- ret = ww_mutex_lock_wait_sig(mutex, ctx);
- if (ret)
- goto out;
- }
-locked: KASSERT((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN));
- KASSERT(mutex->wwm_u.ctx == ctx);
- ctx->wwx_acquired++;
- ret = 0;
-out: mutex_exit(&mutex->wwm_lock);
- return ret;
-}
-
-static inline void
-ww_mutex_lock_slow(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
-{
-
- ASSERT_SLEEPABLE();
-
- if (ctx == NULL) {
- ww_mutex_lock_noctx(mutex);
- return;
- }
-
- KASSERTMSG((ctx->wwx_owner == curlwp),
- "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
- KASSERTMSG(!ctx->wwx_acquire_done,
- "ctx %p done acquiring locks, can't acquire more", ctx);
- KASSERTMSG((ctx->wwx_acquired != ~0U),
- "ctx %p finished, can't be used any more", ctx);
- KASSERTMSG((ctx->wwx_acquired == 0),
- "ctx %p still holds %u locks, not allowed in slow path",
- ctx, ctx->wwx_acquired);
- KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
- "ctx %p in class %p, mutex %p in class %p",
- ctx, ctx->wwx_class, mutex, mutex->wwm_class);
-
- mutex_enter(&mutex->wwm_lock);
-retry: switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- mutex->wwm_state = WW_CTX;
- mutex->wwm_u.ctx = ctx;
- goto locked;
- case WW_OWNED:
- KASSERTMSG((mutex->wwm_u.owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ww_mutex_state_wait(mutex, WW_OWNED);
- goto retry;
- case WW_CTX:
- break;
- case WW_WANTOWN:
- ww_mutex_state_wait(mutex, WW_WANTOWN);
- goto retry;
- default:
- panic("wait/wound mutex %p in bad state: %d",
- mutex, (int)mutex->wwm_state);
- }
- KASSERT(mutex->wwm_state == WW_CTX);
- KASSERT(mutex->wwm_u.ctx != NULL);
- KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- /*
- * Owned by another party, of any priority. Ask that party to
- * wake us when it's done.
- */
- ww_mutex_lock_wait(mutex, ctx);
-locked: KASSERT((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN));
- KASSERT(mutex->wwm_u.ctx == ctx);
- ctx->wwx_acquired++;
- mutex_exit(&mutex->wwm_lock);
-}
-
-static inline int
-ww_mutex_lock_slow_interruptible(struct ww_mutex *mutex,
- struct ww_acquire_ctx *ctx)
-{
- int ret;
-
- ASSERT_SLEEPABLE();
-
- if (ctx == NULL)
- return ww_mutex_lock_noctx_sig(mutex);
-
- KASSERTMSG((ctx->wwx_owner == curlwp),
- "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
- KASSERTMSG(!ctx->wwx_acquire_done,
- "ctx %p done acquiring locks, can't acquire more", ctx);
- KASSERTMSG((ctx->wwx_acquired != ~0U),
- "ctx %p finished, can't be used any more", ctx);
- KASSERTMSG((ctx->wwx_acquired == 0),
- "ctx %p still holds %u locks, not allowed in slow path",
- ctx, ctx->wwx_acquired);
- KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
- "ctx %p in class %p, mutex %p in class %p",
- ctx, ctx->wwx_class, mutex, mutex->wwm_class);
-
- mutex_enter(&mutex->wwm_lock);
-retry: switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- mutex->wwm_state = WW_CTX;
- mutex->wwm_u.ctx = ctx;
- goto locked;
- case WW_OWNED:
- KASSERTMSG((mutex->wwm_u.owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
- if (ret)
- goto out;
- goto retry;
- case WW_CTX:
- break;
- case WW_WANTOWN:
- ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
- if (ret)
- goto out;
- goto retry;
- default:
- panic("wait/wound mutex %p in bad state: %d",
- mutex, (int)mutex->wwm_state);
- }
- KASSERT(mutex->wwm_state == WW_CTX);
- KASSERT(mutex->wwm_u.ctx != NULL);
- KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
- "locking %p against myself: %p", mutex, curlwp);
- /*
- * Owned by another party, of any priority. Ask that party to
- * wake us when it's done.
- */
- ret = ww_mutex_lock_wait_sig(mutex, ctx);
- if (ret)
- goto out;
-locked: KASSERT((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN));
- KASSERT(mutex->wwm_u.ctx == ctx);
- ctx->wwx_acquired++;
- ret = 0;
-out: mutex_exit(&mutex->wwm_lock);
- return ret;
-}
-
-static inline int
-ww_mutex_trylock(struct ww_mutex *mutex)
-{
- int ret;
-
- mutex_enter(&mutex->wwm_lock);
- if (mutex->wwm_state == WW_UNLOCKED) {
- mutex->wwm_state = WW_OWNED;
- mutex->wwm_u.owner = curlwp;
- ret = 1;
- } else {
- KASSERTMSG(((mutex->wwm_state != WW_OWNED) ||
- (mutex->wwm_u.owner != curlwp)),
- "locking %p against myself: %p", mutex, curlwp);
- KASSERTMSG(((mutex->wwm_state != WW_CTX) ||
- (mutex->wwm_u.ctx->wwx_owner != curlwp)),
- "locking %p against myself: %p", mutex, curlwp);
- KASSERTMSG(((mutex->wwm_state != WW_WANTOWN) ||
- (mutex->wwm_u.ctx->wwx_owner != curlwp)),
- "locking %p against myself: %p", mutex, curlwp);
- ret = 0;
- }
- mutex_exit(&mutex->wwm_lock);
-
- return ret;
-}
-
-static inline void
-ww_mutex_unlock_release(struct ww_mutex *mutex)
-{
-
- KASSERT(mutex_owned(&mutex->wwm_lock));
- KASSERT((mutex->wwm_state == WW_CTX) ||
- (mutex->wwm_state == WW_WANTOWN));
- KASSERT(mutex->wwm_u.ctx != NULL);
- KASSERTMSG((mutex->wwm_u.ctx->wwx_owner == curlwp),
- "ww_mutex %p ctx %p held by %p, not by self (%p)",
- mutex, mutex->wwm_u.ctx, mutex->wwm_u.ctx->wwx_owner,
- curlwp);
- KASSERT(mutex->wwm_u.ctx->wwx_acquired != ~0U);
- mutex->wwm_u.ctx->wwx_acquired--;
- mutex->wwm_u.ctx = NULL;
-}
-
-static inline void
-ww_mutex_unlock(struct ww_mutex *mutex)
-{
- struct ww_acquire_ctx *ctx;
-
- mutex_enter(&mutex->wwm_lock);
- KASSERT(mutex->wwm_state != WW_UNLOCKED);
- switch (mutex->wwm_state) {
- case WW_UNLOCKED:
- panic("unlocking unlocked wait/wound mutex: %p", mutex);
- case WW_OWNED:
- /* Let the context lockers fight over it. */
- mutex->wwm_u.owner = NULL;
- mutex->wwm_state = WW_UNLOCKED;
- break;
- case WW_CTX:
- ww_mutex_unlock_release(mutex);
- /*
- * If there are any waiters with contexts, grant the
- * lock to the highest-priority one. Otherwise, just
- * unlock it.
- */
- if ((ctx = RB_TREE_MIN(&mutex->wwm_waiters)) != NULL) {
- mutex->wwm_state = WW_CTX;
- mutex->wwm_u.ctx = ctx;
- } else {
- mutex->wwm_state = WW_UNLOCKED;
- }
- break;
- case WW_WANTOWN:
- ww_mutex_unlock_release(mutex);
- /* Let the non-context lockers fight over it. */
- mutex->wwm_state = WW_UNLOCKED;
- break;
- }
- cv_broadcast(&mutex->wwm_cv);
- mutex_exit(&mutex->wwm_lock);
-}
+int ww_mutex_lock(struct ww_mutex *, struct ww_acquire_ctx *);
+int ww_mutex_lock_interruptible(struct ww_mutex *,
+ struct ww_acquire_ctx *);
+void ww_mutex_lock_slow(struct ww_mutex *, struct ww_acquire_ctx *);
+int ww_mutex_lock_slow_interruptible(struct ww_mutex *,
+ struct ww_acquire_ctx *);
+int ww_mutex_trylock(struct ww_mutex *);
+void ww_mutex_unlock(struct ww_mutex *);
#endif /* _ASM_WW_MUTEX_H_ */
Index: src/sys/external/bsd/drm2/linux/files.drmkms_linux
diff -u src/sys/external/bsd/drm2/linux/files.drmkms_linux:1.7 src/sys/external/bsd/drm2/linux/files.drmkms_linux:1.8
--- src/sys/external/bsd/drm2/linux/files.drmkms_linux:1.7 Thu Jul 17 13:52:22 2014
+++ src/sys/external/bsd/drm2/linux/files.drmkms_linux Thu Jan 8 23:35:47 2015
@@ -1,4 +1,4 @@
-# $NetBSD: files.drmkms_linux,v 1.7 2014/07/17 13:52:22 riastradh Exp $
+# $NetBSD: files.drmkms_linux,v 1.8 2015/01/08 23:35:47 riastradh Exp $
define drmkms_linux: i2cexec, i2c_bitbang
@@ -13,3 +13,4 @@ file external/bsd/drm2/linux/linux_list_
file external/bsd/drm2/linux/linux_module.c drmkms_linux
file external/bsd/drm2/linux/linux_work.c drmkms_linux
file external/bsd/drm2/linux/linux_writecomb.c drmkms_linux
+file external/bsd/drm2/linux/linux_ww_mutex.c drmkms_linux
Index: src/sys/modules/drmkms_linux/Makefile
diff -u src/sys/modules/drmkms_linux/Makefile:1.5 src/sys/modules/drmkms_linux/Makefile:1.6
--- src/sys/modules/drmkms_linux/Makefile:1.5 Sun Sep 14 20:09:18 2014
+++ src/sys/modules/drmkms_linux/Makefile Thu Jan 8 23:35:47 2015
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.5 2014/09/14 20:09:18 riastradh Exp $
+# $NetBSD: Makefile,v 1.6 2015/01/08 23:35:47 riastradh Exp $
.include "../Makefile.inc"
@@ -20,5 +20,6 @@ SRCS+= linux_list_sort.c
SRCS+= linux_module.c
SRCS+= linux_work.c
SRCS+= linux_writecomb.c
+SRCS+= linux_ww_mutex.c
.include <bsd.kmodule.mk>
Added files:
Index: src/sys/external/bsd/drm2/linux/linux_ww_mutex.c
diff -u /dev/null src/sys/external/bsd/drm2/linux/linux_ww_mutex.c:1.1
--- /dev/null Thu Jan 8 23:35:47 2015
+++ src/sys/external/bsd/drm2/linux/linux_ww_mutex.c Thu Jan 8 23:35:47 2015
@@ -0,0 +1,707 @@
+/* $NetBSD: linux_ww_mutex.c,v 1.1 2015/01/08 23:35:47 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R. Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: linux_ww_mutex.c,v 1.1 2015/01/08 23:35:47 riastradh Exp $");
+
+#include <sys/types.h>
+#include <sys/atomic.h>
+#include <sys/condvar.h>
+#include <sys/lwp.h>
+#include <sys/mutex.h>
+#include <sys/rbtree.h>
+
+#include <linux/ww_mutex.h>
+
+static int
+ww_acquire_ctx_compare(void *cookie __unused, const void *va, const void *vb)
+{
+ const struct ww_acquire_ctx *const ctx_a = va;
+ const struct ww_acquire_ctx *const ctx_b = vb;
+
+ if (ctx_a->wwx_ticket < ctx_b->wwx_ticket)
+ return -1;
+ if (ctx_a->wwx_ticket > ctx_b->wwx_ticket)
+ return -1;
+ return 0;
+}
+
+static int
+ww_acquire_ctx_compare_key(void *cookie __unused, const void *vn,
+ const void *vk)
+{
+ const struct ww_acquire_ctx *const ctx = vn;
+ const uint64_t *const ticketp = vk, ticket = *ticketp;
+
+ if (ctx->wwx_ticket < ticket)
+ return -1;
+ if (ctx->wwx_ticket > ticket)
+ return -1;
+ return 0;
+}
+
+static const rb_tree_ops_t ww_acquire_ctx_rb_ops = {
+ .rbto_compare_nodes = &ww_acquire_ctx_compare,
+ .rbto_compare_key = &ww_acquire_ctx_compare_key,
+ .rbto_node_offset = offsetof(struct ww_acquire_ctx, wwx_rb_node),
+ .rbto_context = NULL,
+};
+
+void
+ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *class)
+{
+
+ ctx->wwx_class = class;
+ ctx->wwx_owner = curlwp;
+ ctx->wwx_ticket = atomic_inc_64_nv(&class->wwc_ticket);
+ ctx->wwx_acquired = 0;
+ ctx->wwx_acquire_done = false;
+}
+
+void
+ww_acquire_done(struct ww_acquire_ctx *ctx)
+{
+
+ KASSERTMSG((ctx->wwx_owner == curlwp),
+ "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
+
+ ctx->wwx_acquire_done = true;
+}
+
+void
+ww_acquire_fini(struct ww_acquire_ctx *ctx)
+{
+
+ KASSERTMSG((ctx->wwx_owner == curlwp),
+ "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
+ KASSERTMSG((ctx->wwx_acquired == 0), "ctx %p still holds %u locks",
+ ctx, ctx->wwx_acquired);
+
+ ctx->wwx_acquired = ~0U; /* Fail if called again. */
+ ctx->wwx_owner = NULL;
+}
+
+void
+ww_mutex_init(struct ww_mutex *mutex, struct ww_class *class)
+{
+
+ /*
+ * XXX Apparently Linux takes these with spin locks held. That
+ * strikes me as a bad idea, but so it is...
+ */
+ mutex_init(&mutex->wwm_lock, MUTEX_DEFAULT, IPL_VM);
+ mutex->wwm_state = WW_UNLOCKED;
+ mutex->wwm_class = class;
+ rb_tree_init(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops);
+ cv_init(&mutex->wwm_cv, "linuxwwm");
+}
+
+void
+ww_mutex_destroy(struct ww_mutex *mutex)
+{
+
+ cv_destroy(&mutex->wwm_cv);
+#if 0
+ rb_tree_destroy(&mutex->wwm_waiters, &ww_acquire_ctx_rb_ops);
+#endif
+ KASSERT(mutex->wwm_state == WW_UNLOCKED);
+ mutex_destroy(&mutex->wwm_lock);
+}
+
+/*
+ * XXX WARNING: This returns true if it is locked by ANYONE. Does not
+ * mean `Do I hold this lock?' (answering which really requires an
+ * acquire context).
+ */
+bool
+ww_mutex_is_locked(struct ww_mutex *mutex)
+{
+ int locked;
+
+ mutex_enter(&mutex->wwm_lock);
+ switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ locked = false;
+ break;
+ case WW_OWNED:
+ case WW_CTX:
+ case WW_WANTOWN:
+ locked = true;
+ break;
+ default:
+ panic("wait/wound mutex %p in bad state: %d", mutex,
+ (int)mutex->wwm_state);
+ }
+ mutex_exit(&mutex->wwm_lock);
+
+ return locked;
+}
+
+static void
+ww_mutex_state_wait(struct ww_mutex *mutex, enum ww_mutex_state state)
+{
+
+ KASSERT(mutex->wwm_state == state);
+ do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock);
+ while (mutex->wwm_state == state);
+}
+
+static int
+ww_mutex_state_wait_sig(struct ww_mutex *mutex, enum ww_mutex_state state)
+{
+ int ret;
+
+ KASSERT(mutex->wwm_state == state);
+ do {
+ /* XXX errno NetBSD->Linux */
+ ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock);
+ if (ret)
+ break;
+ } while (mutex->wwm_state == state);
+
+ return ret;
+}
+
+static void
+ww_mutex_lock_wait(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
+{
+ struct ww_acquire_ctx *collision __diagused;
+
+ KASSERT(mutex_owned(&mutex->wwm_lock));
+
+ KASSERT((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN));
+ KASSERT(mutex->wwm_u.ctx != ctx);
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
+ "ww mutex class mismatch: %p != %p",
+ ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
+ KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket),
+ "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
+ ctx->wwx_ticket, ctx,
+ mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx);
+
+ collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx);
+ KASSERTMSG((collision == ctx),
+ "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
+ ctx->wwx_ticket, ctx, collision->wwx_ticket, collision);
+
+ do cv_wait(&mutex->wwm_cv, &mutex->wwm_lock);
+ while (!(((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN)) &&
+ (mutex->wwm_u.ctx == ctx)));
+
+ rb_tree_remove_node(&mutex->wwm_waiters, ctx);
+}
+
+static int
+ww_mutex_lock_wait_sig(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
+{
+ struct ww_acquire_ctx *collision __diagused;
+ int ret;
+
+ KASSERT(mutex_owned(&mutex->wwm_lock));
+
+ KASSERT((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN));
+ KASSERT(mutex->wwm_u.ctx != ctx);
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
+ "ww mutex class mismatch: %p != %p",
+ ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
+ KASSERTMSG((mutex->wwm_u.ctx->wwx_ticket != ctx->wwx_ticket),
+ "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
+ ctx->wwx_ticket, ctx,
+ mutex->wwm_u.ctx->wwx_ticket, mutex->wwm_u.ctx);
+
+ collision = rb_tree_insert_node(&mutex->wwm_waiters, ctx);
+ KASSERTMSG((collision == ctx),
+ "ticket number reused: %"PRId64" (%p) %"PRId64" (%p)",
+ ctx->wwx_ticket, ctx, collision->wwx_ticket, collision);
+
+ do {
+ /* XXX errno NetBSD->Linux */
+ ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock);
+ if (ret)
+ goto out;
+ } while (!(((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN)) &&
+ (mutex->wwm_u.ctx == ctx)));
+
+out: rb_tree_remove_node(&mutex->wwm_waiters, ctx);
+ return ret;
+}
+
+static void
+ww_mutex_lock_noctx(struct ww_mutex *mutex)
+{
+
+ mutex_enter(&mutex->wwm_lock);
+retry: switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ mutex->wwm_state = WW_OWNED;
+ mutex->wwm_u.owner = curlwp;
+ break;
+ case WW_OWNED:
+ KASSERTMSG((mutex->wwm_u.owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ww_mutex_state_wait(mutex, WW_OWNED);
+ goto retry;
+ case WW_CTX:
+ KASSERT(mutex->wwm_u.ctx != NULL);
+ mutex->wwm_state = WW_WANTOWN;
+ /* FALLTHROUGH */
+ case WW_WANTOWN:
+ KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ww_mutex_state_wait(mutex, WW_WANTOWN);
+ goto retry;
+ default:
+ panic("wait/wound mutex %p in bad state: %d",
+ mutex, (int)mutex->wwm_state);
+ }
+ KASSERT(mutex->wwm_state == WW_OWNED);
+ KASSERT(mutex->wwm_u.owner == curlwp);
+ mutex_exit(&mutex->wwm_lock);
+}
+
+static int
+ww_mutex_lock_noctx_sig(struct ww_mutex *mutex)
+{
+ int ret;
+
+ mutex_enter(&mutex->wwm_lock);
+retry: switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ mutex->wwm_state = WW_OWNED;
+ mutex->wwm_u.owner = curlwp;
+ break;
+ case WW_OWNED:
+ KASSERTMSG((mutex->wwm_u.owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
+ if (ret)
+ goto out;
+ goto retry;
+ case WW_CTX:
+ KASSERT(mutex->wwm_u.ctx != NULL);
+ mutex->wwm_state = WW_WANTOWN;
+ /* FALLTHROUGH */
+ case WW_WANTOWN:
+ KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
+ if (ret)
+ goto out;
+ goto retry;
+ default:
+ panic("wait/wound mutex %p in bad state: %d",
+ mutex, (int)mutex->wwm_state);
+ }
+ KASSERT(mutex->wwm_state == WW_OWNED);
+ KASSERT(mutex->wwm_u.owner == curlwp);
+ ret = 0;
+out: mutex_exit(&mutex->wwm_lock);
+ return ret;
+}
+
+int
+ww_mutex_lock(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
+{
+
+ ASSERT_SLEEPABLE();
+
+ if (ctx == NULL) {
+ ww_mutex_lock_noctx(mutex);
+ return 0;
+ }
+
+ KASSERTMSG((ctx->wwx_owner == curlwp),
+ "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
+ KASSERTMSG(!ctx->wwx_acquire_done,
+ "ctx %p done acquiring locks, can't acquire more", ctx);
+ KASSERTMSG((ctx->wwx_acquired != ~0U),
+ "ctx %p finished, can't be used any more", ctx);
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
+ "ctx %p in class %p, mutex %p in class %p",
+ ctx, ctx->wwx_class, mutex, mutex->wwm_class);
+
+ mutex_enter(&mutex->wwm_lock);
+retry: switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ mutex->wwm_state = WW_CTX;
+ mutex->wwm_u.ctx = ctx;
+ goto locked;
+ case WW_OWNED:
+ KASSERTMSG((mutex->wwm_u.owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ww_mutex_state_wait(mutex, WW_OWNED);
+ goto retry;
+ case WW_CTX:
+ break;
+ case WW_WANTOWN:
+ ww_mutex_state_wait(mutex, WW_WANTOWN);
+ goto retry;
+ default:
+ panic("wait/wound mutex %p in bad state: %d",
+ mutex, (int)mutex->wwm_state);
+ }
+ KASSERT(mutex->wwm_state == WW_CTX);
+ KASSERT(mutex->wwm_u.ctx != NULL);
+ KASSERT((mutex->wwm_u.ctx == ctx) ||
+ (mutex->wwm_u.ctx->wwx_owner != curlwp));
+ if (mutex->wwm_u.ctx == ctx) {
+ /*
+ * We already own it. Yes, this can happen correctly
+ * for objects whose locking order is determined by
+ * userland.
+ */
+ mutex_exit(&mutex->wwm_lock);
+ return -EALREADY;
+ } else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
+ /*
+ * Owned by a higher-priority party. Tell the caller
+ * to unlock everything and start over.
+ */
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
+ "ww mutex class mismatch: %p != %p",
+ ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
+ mutex_exit(&mutex->wwm_lock);
+ return -EDEADLK;
+ } else {
+ /*
+ * Owned by a lower-priority party. Ask that party to
+ * wake us when it is done or it realizes it needs to
+ * back off.
+ */
+ ww_mutex_lock_wait(mutex, ctx);
+ }
+locked: ctx->wwx_acquired++;
+ KASSERT((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN));
+ KASSERT(mutex->wwm_u.ctx == ctx);
+ mutex_exit(&mutex->wwm_lock);
+ return 0;
+}
+
+int
+ww_mutex_lock_interruptible(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
+{
+ int ret;
+
+ ASSERT_SLEEPABLE();
+
+ if (ctx == NULL)
+ return ww_mutex_lock_noctx_sig(mutex);
+
+ KASSERTMSG((ctx->wwx_owner == curlwp),
+ "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
+ KASSERTMSG(!ctx->wwx_acquire_done,
+ "ctx %p done acquiring locks, can't acquire more", ctx);
+ KASSERTMSG((ctx->wwx_acquired != ~0U),
+ "ctx %p finished, can't be used any more", ctx);
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
+ "ctx %p in class %p, mutex %p in class %p",
+ ctx, ctx->wwx_class, mutex, mutex->wwm_class);
+
+ mutex_enter(&mutex->wwm_lock);
+retry: switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ mutex->wwm_state = WW_CTX;
+ mutex->wwm_u.ctx = ctx;
+ goto locked;
+ case WW_OWNED:
+ KASSERTMSG((mutex->wwm_u.owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
+ if (ret)
+ goto out;
+ goto retry;
+ case WW_CTX:
+ break;
+ case WW_WANTOWN:
+ ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
+ if (ret)
+ goto out;
+ goto retry;
+ default:
+ panic("wait/wound mutex %p in bad state: %d",
+ mutex, (int)mutex->wwm_state);
+ }
+ KASSERT(mutex->wwm_state == WW_CTX);
+ KASSERT(mutex->wwm_u.ctx != NULL);
+ KASSERT((mutex->wwm_u.ctx == ctx) ||
+ (mutex->wwm_u.ctx->wwx_owner != curlwp));
+ if (mutex->wwm_u.ctx == ctx) {
+ /*
+ * We already own it. Yes, this can happen correctly
+ * for objects whose locking order is determined by
+ * userland.
+ */
+ mutex_exit(&mutex->wwm_lock);
+ return -EALREADY;
+ } else if (mutex->wwm_u.ctx->wwx_ticket < ctx->wwx_ticket) {
+ /*
+ * Owned by a higher-priority party. Tell the caller
+ * to unlock everything and start over.
+ */
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_u.ctx->wwx_class),
+ "ww mutex class mismatch: %p != %p",
+ ctx->wwx_class, mutex->wwm_u.ctx->wwx_class);
+ mutex_exit(&mutex->wwm_lock);
+ return -EDEADLK;
+ } else {
+ /*
+ * Owned by a lower-priority party. Ask that party to
+ * wake us when it is done or it realizes it needs to
+ * back off.
+ */
+ ret = ww_mutex_lock_wait_sig(mutex, ctx);
+ if (ret)
+ goto out;
+ }
+locked: KASSERT((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN));
+ KASSERT(mutex->wwm_u.ctx == ctx);
+ ctx->wwx_acquired++;
+ ret = 0;
+out: mutex_exit(&mutex->wwm_lock);
+ return ret;
+}
+
+void
+ww_mutex_lock_slow(struct ww_mutex *mutex, struct ww_acquire_ctx *ctx)
+{
+
+ ASSERT_SLEEPABLE();
+
+ if (ctx == NULL) {
+ ww_mutex_lock_noctx(mutex);
+ return;
+ }
+
+ KASSERTMSG((ctx->wwx_owner == curlwp),
+ "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
+ KASSERTMSG(!ctx->wwx_acquire_done,
+ "ctx %p done acquiring locks, can't acquire more", ctx);
+ KASSERTMSG((ctx->wwx_acquired != ~0U),
+ "ctx %p finished, can't be used any more", ctx);
+ KASSERTMSG((ctx->wwx_acquired == 0),
+ "ctx %p still holds %u locks, not allowed in slow path",
+ ctx, ctx->wwx_acquired);
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
+ "ctx %p in class %p, mutex %p in class %p",
+ ctx, ctx->wwx_class, mutex, mutex->wwm_class);
+
+ mutex_enter(&mutex->wwm_lock);
+retry: switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ mutex->wwm_state = WW_CTX;
+ mutex->wwm_u.ctx = ctx;
+ goto locked;
+ case WW_OWNED:
+ KASSERTMSG((mutex->wwm_u.owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ww_mutex_state_wait(mutex, WW_OWNED);
+ goto retry;
+ case WW_CTX:
+ break;
+ case WW_WANTOWN:
+ ww_mutex_state_wait(mutex, WW_WANTOWN);
+ goto retry;
+ default:
+ panic("wait/wound mutex %p in bad state: %d",
+ mutex, (int)mutex->wwm_state);
+ }
+ KASSERT(mutex->wwm_state == WW_CTX);
+ KASSERT(mutex->wwm_u.ctx != NULL);
+ KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ /*
+ * Owned by another party, of any priority. Ask that party to
+ * wake us when it's done.
+ */
+ ww_mutex_lock_wait(mutex, ctx);
+locked: KASSERT((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN));
+ KASSERT(mutex->wwm_u.ctx == ctx);
+ ctx->wwx_acquired++;
+ mutex_exit(&mutex->wwm_lock);
+}
+
+int
+ww_mutex_lock_slow_interruptible(struct ww_mutex *mutex,
+ struct ww_acquire_ctx *ctx)
+{
+ int ret;
+
+ ASSERT_SLEEPABLE();
+
+ if (ctx == NULL)
+ return ww_mutex_lock_noctx_sig(mutex);
+
+ KASSERTMSG((ctx->wwx_owner == curlwp),
+ "ctx %p owned by %p, not self (%p)", ctx, ctx->wwx_owner, curlwp);
+ KASSERTMSG(!ctx->wwx_acquire_done,
+ "ctx %p done acquiring locks, can't acquire more", ctx);
+ KASSERTMSG((ctx->wwx_acquired != ~0U),
+ "ctx %p finished, can't be used any more", ctx);
+ KASSERTMSG((ctx->wwx_acquired == 0),
+ "ctx %p still holds %u locks, not allowed in slow path",
+ ctx, ctx->wwx_acquired);
+ KASSERTMSG((ctx->wwx_class == mutex->wwm_class),
+ "ctx %p in class %p, mutex %p in class %p",
+ ctx, ctx->wwx_class, mutex, mutex->wwm_class);
+
+ mutex_enter(&mutex->wwm_lock);
+retry: switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ mutex->wwm_state = WW_CTX;
+ mutex->wwm_u.ctx = ctx;
+ goto locked;
+ case WW_OWNED:
+ KASSERTMSG((mutex->wwm_u.owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ ret = ww_mutex_state_wait_sig(mutex, WW_OWNED);
+ if (ret)
+ goto out;
+ goto retry;
+ case WW_CTX:
+ break;
+ case WW_WANTOWN:
+ ret = ww_mutex_state_wait_sig(mutex, WW_WANTOWN);
+ if (ret)
+ goto out;
+ goto retry;
+ default:
+ panic("wait/wound mutex %p in bad state: %d",
+ mutex, (int)mutex->wwm_state);
+ }
+ KASSERT(mutex->wwm_state == WW_CTX);
+ KASSERT(mutex->wwm_u.ctx != NULL);
+ KASSERTMSG((mutex->wwm_u.ctx->wwx_owner != curlwp),
+ "locking %p against myself: %p", mutex, curlwp);
+ /*
+ * Owned by another party, of any priority. Ask that party to
+ * wake us when it's done.
+ */
+ ret = ww_mutex_lock_wait_sig(mutex, ctx);
+ if (ret)
+ goto out;
+locked: KASSERT((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN));
+ KASSERT(mutex->wwm_u.ctx == ctx);
+ ctx->wwx_acquired++;
+ ret = 0;
+out: mutex_exit(&mutex->wwm_lock);
+ return ret;
+}
+
+int
+ww_mutex_trylock(struct ww_mutex *mutex)
+{
+ int ret;
+
+ mutex_enter(&mutex->wwm_lock);
+ if (mutex->wwm_state == WW_UNLOCKED) {
+ mutex->wwm_state = WW_OWNED;
+ mutex->wwm_u.owner = curlwp;
+ ret = 1;
+ } else {
+ KASSERTMSG(((mutex->wwm_state != WW_OWNED) ||
+ (mutex->wwm_u.owner != curlwp)),
+ "locking %p against myself: %p", mutex, curlwp);
+ KASSERTMSG(((mutex->wwm_state != WW_CTX) ||
+ (mutex->wwm_u.ctx->wwx_owner != curlwp)),
+ "locking %p against myself: %p", mutex, curlwp);
+ KASSERTMSG(((mutex->wwm_state != WW_WANTOWN) ||
+ (mutex->wwm_u.ctx->wwx_owner != curlwp)),
+ "locking %p against myself: %p", mutex, curlwp);
+ ret = 0;
+ }
+ mutex_exit(&mutex->wwm_lock);
+
+ return ret;
+}
+
+static void
+ww_mutex_unlock_release(struct ww_mutex *mutex)
+{
+
+ KASSERT(mutex_owned(&mutex->wwm_lock));
+ KASSERT((mutex->wwm_state == WW_CTX) ||
+ (mutex->wwm_state == WW_WANTOWN));
+ KASSERT(mutex->wwm_u.ctx != NULL);
+ KASSERTMSG((mutex->wwm_u.ctx->wwx_owner == curlwp),
+ "ww_mutex %p ctx %p held by %p, not by self (%p)",
+ mutex, mutex->wwm_u.ctx, mutex->wwm_u.ctx->wwx_owner,
+ curlwp);
+ KASSERT(mutex->wwm_u.ctx->wwx_acquired != ~0U);
+ mutex->wwm_u.ctx->wwx_acquired--;
+ mutex->wwm_u.ctx = NULL;
+}
+
+void
+ww_mutex_unlock(struct ww_mutex *mutex)
+{
+ struct ww_acquire_ctx *ctx;
+
+ mutex_enter(&mutex->wwm_lock);
+ KASSERT(mutex->wwm_state != WW_UNLOCKED);
+ switch (mutex->wwm_state) {
+ case WW_UNLOCKED:
+ panic("unlocking unlocked wait/wound mutex: %p", mutex);
+ case WW_OWNED:
+ /* Let the context lockers fight over it. */
+ mutex->wwm_u.owner = NULL;
+ mutex->wwm_state = WW_UNLOCKED;
+ break;
+ case WW_CTX:
+ ww_mutex_unlock_release(mutex);
+ /*
+ * If there are any waiters with contexts, grant the
+ * lock to the highest-priority one. Otherwise, just
+ * unlock it.
+ */
+ if ((ctx = RB_TREE_MIN(&mutex->wwm_waiters)) != NULL) {
+ mutex->wwm_state = WW_CTX;
+ mutex->wwm_u.ctx = ctx;
+ } else {
+ mutex->wwm_state = WW_UNLOCKED;
+ }
+ break;
+ case WW_WANTOWN:
+ ww_mutex_unlock_release(mutex);
+ /* Let the non-context lockers fight over it. */
+ mutex->wwm_state = WW_UNLOCKED;
+ break;
+ }
+ cv_broadcast(&mutex->wwm_cv);
+ mutex_exit(&mutex->wwm_lock);
+}