Module Name: src Committed By: riastradh Date: Mon Sep 15 20:24:55 UTC 2014
Modified Files: src/sys/external/bsd/drm2/include/linux: ww_mutex.h Log Message: Fix mistakes in Linux ww_mutex code. I wrote some cases for the WW_WANTOWN state forgetting that there is an acquisition context associated with it. - On acceptance of a lock, allow WW_WANTOWN as well as WW_CTX. - On unlock when WW_WANTOWN, decrement the context's acquire count. Fixes KASSERT(ctx->wwx_acquired == 0) failure in ww_acquire_fini, and would fix unlikely but possible kasserts in ww_mutex_lock* if another lwp swoops in to lock without a context quickly enough. While here, add some comments and kassert up the wazoo. To generate a diff of this commit: cvs rdiff -u -r1.6 -r1.7 src/sys/external/bsd/drm2/include/linux/ww_mutex.h 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.6 src/sys/external/bsd/drm2/include/linux/ww_mutex.h:1.7 --- src/sys/external/bsd/drm2/include/linux/ww_mutex.h:1.6 Sat Sep 13 00:33:45 2014 +++ src/sys/external/bsd/drm2/include/linux/ww_mutex.h Mon Sep 15 20:24:55 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: ww_mutex.h,v 1.6 2014/09/13 00:33:45 riastradh Exp $ */ +/* $NetBSD: ww_mutex.h,v 1.7 2014/09/15 20:24:55 riastradh Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -47,6 +47,7 @@ struct ww_class { struct ww_acquire_ctx { struct ww_class *wwx_class __diagused; + struct lwp *wwx_owner __diagused; uint64_t wwx_ticket; unsigned wwx_acquired; bool wwx_acquire_done; @@ -92,6 +93,7 @@ ww_acquire_init(struct ww_acquire_ctx *c { 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; @@ -101,6 +103,9 @@ 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; } @@ -108,18 +113,22 @@ 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 { - WW_UNLOCKED, - WW_OWNED, - WW_CTX, - WW_WANTOWN, + WW_UNLOCKED, /* nobody owns it */ + WW_OWNED, /* owned by a lwp without a context */ + WW_CTX, /* owned by a context */ + WW_WANTOWN, /* owned by ctx, waiters w/o ctx waiting */ } wwm_state; union { struct lwp *owner; @@ -218,7 +227,9 @@ ww_mutex_lock_wait(struct ww_mutex *mute KASSERT(mutex_owned(&mutex->wwm_lock)); - KASSERT(mutex->wwm_state == WW_CTX); + 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); @@ -233,7 +244,9 @@ ww_mutex_lock_wait(struct ww_mutex *mute 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_u.ctx == ctx))); + while (!(((mutex->wwm_state == WW_CTX) || + (mutex->wwm_state == WW_WANTOWN)) && + (mutex->wwm_u.ctx == ctx))); rb_tree_remove_node(&mutex->wwm_waiters, ctx); } @@ -246,7 +259,9 @@ ww_mutex_lock_wait_sig(struct ww_mutex * KASSERT(mutex_owned(&mutex->wwm_lock)); - KASSERT(mutex->wwm_state == WW_CTX); + 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); @@ -265,7 +280,9 @@ ww_mutex_lock_wait_sig(struct ww_mutex * ret = -cv_wait_sig(&mutex->wwm_cv, &mutex->wwm_lock); if (ret) goto out; - } while (!((mutex->wwm_state == WW_CTX) && (mutex->wwm_u.ctx == ctx))); + } 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; @@ -283,13 +300,16 @@ retry: switch (mutex->wwm_state) { break; case WW_OWNED: KASSERTMSG((mutex->wwm_u.owner != curlwp), - "locking against myself: %p", 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: @@ -314,7 +334,7 @@ retry: switch (mutex->wwm_state) { break; case WW_OWNED: KASSERTMSG((mutex->wwm_u.owner != curlwp), - "locking against myself: %p", curlwp); + "locking %p against myself: %p", mutex, curlwp); ret = ww_mutex_state_wait_sig(mutex, WW_OWNED); if (ret) goto out; @@ -322,7 +342,10 @@ retry: switch (mutex->wwm_state) { 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; @@ -349,7 +372,15 @@ ww_mutex_lock(struct ww_mutex *mutex, st return 0; } - KASSERT(!ctx->wwx_acquire_done); + 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) { @@ -359,7 +390,7 @@ retry: switch (mutex->wwm_state) { goto locked; case WW_OWNED: KASSERTMSG((mutex->wwm_u.owner != curlwp), - "locking against myself: %p", curlwp); + "locking %p against myself: %p", mutex, curlwp); ww_mutex_state_wait(mutex, WW_OWNED); goto retry; case WW_CTX: @@ -373,6 +404,8 @@ retry: switch (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 @@ -400,7 +433,8 @@ retry: switch (mutex->wwm_state) { ww_mutex_lock_wait(mutex, ctx); } locked: ctx->wwx_acquired++; - KASSERT(mutex->wwm_state == WW_CTX); + KASSERT((mutex->wwm_state == WW_CTX) || + (mutex->wwm_state == WW_WANTOWN)); KASSERT(mutex->wwm_u.ctx == ctx); mutex_exit(&mutex->wwm_lock); return 0; @@ -416,7 +450,15 @@ ww_mutex_lock_interruptible(struct ww_mu if (ctx == NULL) return ww_mutex_lock_noctx_sig(mutex); - KASSERT(!ctx->wwx_acquire_done); + 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) { @@ -426,7 +468,7 @@ retry: switch (mutex->wwm_state) { goto locked; case WW_OWNED: KASSERTMSG((mutex->wwm_u.owner != curlwp), - "locking against myself: %p", curlwp); + "locking %p against myself: %p", mutex, curlwp); ret = ww_mutex_state_wait_sig(mutex, WW_OWNED); if (ret) goto out; @@ -444,6 +486,8 @@ retry: switch (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 @@ -472,7 +516,8 @@ retry: switch (mutex->wwm_state) { if (ret) goto out; } -locked: KASSERT(mutex->wwm_state == WW_CTX); +locked: KASSERT((mutex->wwm_state == WW_CTX) || + (mutex->wwm_state == WW_WANTOWN)); KASSERT(mutex->wwm_u.ctx == ctx); ctx->wwx_acquired++; ret = 0; @@ -491,7 +536,18 @@ ww_mutex_lock_slow(struct ww_mutex *mute return; } - KASSERT(!ctx->wwx_acquire_done); + 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) { @@ -501,7 +557,7 @@ retry: switch (mutex->wwm_state) { goto locked; case WW_OWNED: KASSERTMSG((mutex->wwm_u.owner != curlwp), - "locking against myself: %p", curlwp); + "locking %p against myself: %p", mutex, curlwp); ww_mutex_state_wait(mutex, WW_OWNED); goto retry; case WW_CTX: @@ -515,12 +571,15 @@ retry: switch (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); +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); @@ -537,7 +596,18 @@ ww_mutex_lock_slow_interruptible(struct if (ctx == NULL) return ww_mutex_lock_noctx_sig(mutex); - KASSERT(!ctx->wwx_acquire_done); + 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) { @@ -547,7 +617,7 @@ retry: switch (mutex->wwm_state) { goto locked; case WW_OWNED: KASSERTMSG((mutex->wwm_u.owner != curlwp), - "locking against myself: %p", curlwp); + "locking %p against myself: %p", mutex, curlwp); ret = ww_mutex_state_wait_sig(mutex, WW_OWNED); if (ret) goto out; @@ -565,6 +635,8 @@ retry: switch (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. @@ -572,7 +644,8 @@ retry: switch (mutex->wwm_state) { ret = ww_mutex_lock_wait_sig(mutex, ctx); if (ret) goto out; -locked: KASSERT(mutex->wwm_state == WW_CTX); +locked: KASSERT((mutex->wwm_state == WW_CTX) || + (mutex->wwm_state == WW_WANTOWN)); KASSERT(mutex->wwm_u.ctx == ctx); ctx->wwx_acquired++; ret = 0; @@ -591,6 +664,15 @@ ww_mutex_trylock(struct ww_mutex *mutex) 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); @@ -599,6 +681,23 @@ ww_mutex_trylock(struct ww_mutex *mutex) } 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; @@ -614,9 +713,7 @@ ww_mutex_unlock(struct ww_mutex *mutex) mutex->wwm_state = WW_UNLOCKED; break; case WW_CTX: - KASSERT(mutex->wwm_u.ctx != NULL); - mutex->wwm_u.ctx->wwx_acquired--; - mutex->wwm_u.ctx = NULL; + ww_mutex_unlock_release(mutex); /* * If there are any waiters with contexts, grant the * lock to the highest-priority one. Otherwise, just @@ -630,6 +727,7 @@ ww_mutex_unlock(struct ww_mutex *mutex) } break; case WW_WANTOWN: + ww_mutex_unlock_release(mutex); /* Let the non-context lockers fight over it. */ mutex->wwm_state = WW_UNLOCKED; break;